From 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 16 Apr 2005 15:20:36 -0700 Subject: Linux-2.6.12-rc2 Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip! --- arch/ppc/4xx_io/Makefile | 6 + arch/ppc/4xx_io/serial_sicc.c | 2012 +++++++++++++ arch/ppc/8260_io/Kconfig | 65 + arch/ppc/8260_io/Makefile | 6 + arch/ppc/8260_io/enet.c | 867 ++++++ arch/ppc/8260_io/fcc_enet.c | 2395 ++++++++++++++++ arch/ppc/8xx_io/Kconfig | 138 + arch/ppc/8xx_io/Makefile | 10 + arch/ppc/8xx_io/commproc.c | 464 +++ arch/ppc/8xx_io/cs4218.h | 167 ++ arch/ppc/8xx_io/cs4218_tdm.c | 2836 +++++++++++++++++++ arch/ppc/8xx_io/enet.c | 971 +++++++ arch/ppc/8xx_io/fec.c | 1973 +++++++++++++ arch/ppc/8xx_io/micropatch.c | 744 +++++ arch/ppc/Kconfig | 1317 +++++++++ arch/ppc/Kconfig.debug | 72 + arch/ppc/Makefile | 138 + arch/ppc/amiga/Makefile | 8 + arch/ppc/amiga/amiga_ksyms.c | 1 + arch/ppc/amiga/amiints.c | 323 +++ arch/ppc/amiga/amisound.c | 1 + arch/ppc/amiga/bootinfo.c | 80 + arch/ppc/amiga/chipram.c | 1 + arch/ppc/amiga/cia.c | 178 ++ arch/ppc/amiga/config.c | 962 +++++++ arch/ppc/amiga/ints.c | 160 ++ arch/ppc/amiga/pcmcia.c | 1 + arch/ppc/amiga/time.c | 58 + arch/ppc/boot/Makefile | 34 + arch/ppc/boot/common/Makefile | 13 + arch/ppc/boot/common/bootinfo.c | 70 + arch/ppc/boot/common/crt0.S | 81 + arch/ppc/boot/common/misc-common.c | 553 ++++ arch/ppc/boot/common/ns16550.c | 99 + arch/ppc/boot/common/serial_stub.c | 23 + arch/ppc/boot/common/string.S | 150 + arch/ppc/boot/common/util.S | 293 ++ arch/ppc/boot/images/Makefile | 27 + arch/ppc/boot/include/cpc700.h | 26 + arch/ppc/boot/include/iso_font.h | 257 ++ arch/ppc/boot/include/mpc10x.h | 65 + arch/ppc/boot/include/mpsc_defs.h | 146 + arch/ppc/boot/include/nonstdio.h | 34 + arch/ppc/boot/include/of1275.h | 39 + arch/ppc/boot/include/rs6000.h | 243 ++ arch/ppc/boot/include/serial.h | 46 + arch/ppc/boot/ld.script | 88 + arch/ppc/boot/lib/Makefile | 23 + arch/ppc/boot/lib/div64.S | 58 + arch/ppc/boot/lib/kbd.c | 248 ++ arch/ppc/boot/lib/vreset.c | 805 ++++++ arch/ppc/boot/of1275/Makefile | 6 + arch/ppc/boot/of1275/claim.c | 34 + arch/ppc/boot/of1275/enter.c | 22 + arch/ppc/boot/of1275/exit.c | 24 + arch/ppc/boot/of1275/finddevice.c | 31 + arch/ppc/boot/of1275/getprop.c | 37 + arch/ppc/boot/of1275/map.c | 48 + arch/ppc/boot/of1275/ofinit.c | 27 + arch/ppc/boot/of1275/ofstdio.c | 32 + arch/ppc/boot/of1275/read.c | 35 + arch/ppc/boot/of1275/release.c | 30 + arch/ppc/boot/of1275/write.c | 35 + arch/ppc/boot/openfirmware/Makefile | 188 ++ arch/ppc/boot/openfirmware/chrpmain.c | 101 + arch/ppc/boot/openfirmware/coffmain.c | 101 + arch/ppc/boot/openfirmware/common.c | 162 ++ arch/ppc/boot/openfirmware/dummy.c | 4 + arch/ppc/boot/openfirmware/misc.S | 67 + arch/ppc/boot/openfirmware/newworldmain.c | 94 + arch/ppc/boot/openfirmware/start.c | 172 ++ arch/ppc/boot/simple/Makefile | 252 ++ arch/ppc/boot/simple/chrpmap.c | 12 + arch/ppc/boot/simple/clear.S | 19 + arch/ppc/boot/simple/cpc700_memory.c | 36 + arch/ppc/boot/simple/dummy.c | 4 + arch/ppc/boot/simple/embed_config.c | 981 +++++++ arch/ppc/boot/simple/head.S | 142 + arch/ppc/boot/simple/iic.c | 214 ++ arch/ppc/boot/simple/m8260_tty.c | 325 +++ arch/ppc/boot/simple/m8xx_tty.c | 290 ++ arch/ppc/boot/simple/misc-chestnut.c | 35 + arch/ppc/boot/simple/misc-cpci690.c | 27 + arch/ppc/boot/simple/misc-embedded.c | 275 ++ arch/ppc/boot/simple/misc-ev64260.c | 57 + arch/ppc/boot/simple/misc-katana.c | 37 + arch/ppc/boot/simple/misc-mv64x60.c | 61 + arch/ppc/boot/simple/misc-prep.c | 212 ++ arch/ppc/boot/simple/misc-radstone_ppc7d.c | 26 + arch/ppc/boot/simple/misc-spruce.c | 274 ++ arch/ppc/boot/simple/misc.c | 284 ++ arch/ppc/boot/simple/mpc10x_memory.c | 111 + arch/ppc/boot/simple/mpc52xx_tty.c | 140 + arch/ppc/boot/simple/mv64x60_tty.c | 360 +++ arch/ppc/boot/simple/openbios.c | 37 + arch/ppc/boot/simple/pci.c | 274 ++ arch/ppc/boot/simple/pibs.c | 103 + arch/ppc/boot/simple/prepmap.c | 12 + arch/ppc/boot/simple/qspan_pci.c | 269 ++ arch/ppc/boot/simple/relocate.S | 216 ++ arch/ppc/boot/simple/rw4/ppc_40x.h | 664 +++++ arch/ppc/boot/simple/rw4/rw4_init.S | 78 + arch/ppc/boot/simple/rw4/rw4_init_brd.S | 1125 ++++++++ arch/ppc/boot/simple/rw4/stb.h | 239 ++ arch/ppc/boot/utils/addRamDisk.c | 203 ++ arch/ppc/boot/utils/addSystemMap.c | 186 ++ arch/ppc/boot/utils/addnote.c | 175 ++ arch/ppc/boot/utils/elf.pl | 33 + arch/ppc/boot/utils/hack-coff.c | 84 + arch/ppc/boot/utils/mkbugboot.c | 187 ++ arch/ppc/boot/utils/mknote.c | 44 + arch/ppc/boot/utils/mkprep.c | 293 ++ arch/ppc/boot/utils/mktree.c | 152 + arch/ppc/configs/FADS_defconfig | 520 ++++ arch/ppc/configs/IVMS8_defconfig | 548 ++++ arch/ppc/configs/SM850_defconfig | 522 ++++ arch/ppc/configs/SPD823TS_defconfig | 520 ++++ arch/ppc/configs/TQM823L_defconfig | 521 ++++ arch/ppc/configs/TQM8260_defconfig | 499 ++++ arch/ppc/configs/TQM850L_defconfig | 521 ++++ arch/ppc/configs/TQM860L_defconfig | 549 ++++ arch/ppc/configs/adir_defconfig | 805 ++++++ arch/ppc/configs/ads8272_defconfig | 582 ++++ arch/ppc/configs/apus_defconfig | 920 ++++++ arch/ppc/configs/ash_defconfig | 666 +++++ arch/ppc/configs/beech_defconfig | 615 ++++ arch/ppc/configs/bseip_defconfig | 517 ++++ arch/ppc/configs/bubinga_defconfig | 592 ++++ arch/ppc/configs/cedar_defconfig | 534 ++++ arch/ppc/configs/chestnut_defconfig | 794 ++++++ arch/ppc/configs/common_defconfig | 1421 ++++++++++ arch/ppc/configs/cpci405_defconfig | 631 +++++ arch/ppc/configs/cpci690_defconfig | 686 +++++ arch/ppc/configs/ebony_defconfig | 585 ++++ arch/ppc/configs/ep405_defconfig | 572 ++++ arch/ppc/configs/est8260_defconfig | 491 ++++ arch/ppc/configs/ev64260_defconfig | 759 +++++ arch/ppc/configs/gemini_defconfig | 618 ++++ arch/ppc/configs/hdpu_defconfig | 890 ++++++ arch/ppc/configs/ibmchrp_defconfig | 875 ++++++ arch/ppc/configs/k2_defconfig | 680 +++++ arch/ppc/configs/katana_defconfig | 861 ++++++ arch/ppc/configs/lite5200_defconfig | 436 +++ arch/ppc/configs/lopec_defconfig | 814 ++++++ arch/ppc/configs/luan_defconfig | 668 +++++ arch/ppc/configs/mbx_defconfig | 512 ++++ arch/ppc/configs/mcpn765_defconfig | 579 ++++ arch/ppc/configs/menf1_defconfig | 621 ++++ arch/ppc/configs/mpc834x_sys_defconfig | 644 +++++ arch/ppc/configs/mpc8540_ads_defconfig | 707 +++++ arch/ppc/configs/mpc8555_cds_defconfig | 718 +++++ arch/ppc/configs/mpc8560_ads_defconfig | 719 +++++ arch/ppc/configs/mvme5100_defconfig | 746 +++++ arch/ppc/configs/oak_defconfig | 485 ++++ arch/ppc/configs/ocotea_defconfig | 599 ++++ arch/ppc/configs/pcore_defconfig | 716 +++++ arch/ppc/configs/pmac_defconfig | 1523 ++++++++++ arch/ppc/configs/power3_defconfig | 1034 +++++++ arch/ppc/configs/pplus_defconfig | 720 +++++ arch/ppc/configs/prpmc750_defconfig | 594 ++++ arch/ppc/configs/prpmc800_defconfig | 656 +++++ arch/ppc/configs/radstone_ppc7d_defconfig | 956 +++++++ arch/ppc/configs/rainier_defconfig | 599 ++++ arch/ppc/configs/redwood5_defconfig | 557 ++++ arch/ppc/configs/redwood6_defconfig | 535 ++++ arch/ppc/configs/redwood_defconfig | 540 ++++ arch/ppc/configs/rpx8260_defconfig | 555 ++++ arch/ppc/configs/rpxcllf_defconfig | 582 ++++ arch/ppc/configs/rpxlite_defconfig | 581 ++++ arch/ppc/configs/sandpoint_defconfig | 737 +++++ arch/ppc/configs/spruce_defconfig | 577 ++++ arch/ppc/configs/stx_gp3_defconfig | 972 +++++++ arch/ppc/configs/sycamore_defconfig | 664 +++++ arch/ppc/configs/walnut_defconfig | 578 ++++ arch/ppc/kernel/Makefile | 33 + arch/ppc/kernel/align.c | 398 +++ arch/ppc/kernel/asm-offsets.c | 146 + arch/ppc/kernel/bitops.c | 126 + arch/ppc/kernel/cpu_setup_6xx.S | 440 +++ arch/ppc/kernel/cpu_setup_power4.S | 201 ++ arch/ppc/kernel/cputable.c | 922 ++++++ arch/ppc/kernel/dma-mapping.c | 447 +++ arch/ppc/kernel/entry.S | 969 +++++++ arch/ppc/kernel/find_name.c | 48 + arch/ppc/kernel/head.S | 1710 +++++++++++ arch/ppc/kernel/head_44x.S | 753 +++++ arch/ppc/kernel/head_4xx.S | 1010 +++++++ arch/ppc/kernel/head_8xx.S | 862 ++++++ arch/ppc/kernel/head_booke.h | 340 +++ arch/ppc/kernel/head_fsl_booke.S | 952 +++++++ arch/ppc/kernel/idle.c | 100 + arch/ppc/kernel/idle_6xx.S | 233 ++ arch/ppc/kernel/idle_power4.S | 91 + arch/ppc/kernel/irq.c | 164 ++ arch/ppc/kernel/l2cr.S | 442 +++ arch/ppc/kernel/misc.S | 1453 ++++++++++ arch/ppc/kernel/module.c | 320 +++ arch/ppc/kernel/pci.c | 1849 ++++++++++++ arch/ppc/kernel/perfmon.c | 93 + arch/ppc/kernel/perfmon_fsl_booke.c | 222 ++ arch/ppc/kernel/ppc-stub.c | 867 ++++++ arch/ppc/kernel/ppc_htab.c | 467 +++ arch/ppc/kernel/ppc_ksyms.c | 350 +++ arch/ppc/kernel/process.c | 781 +++++ arch/ppc/kernel/ptrace.c | 474 ++++ arch/ppc/kernel/semaphore.c | 131 + arch/ppc/kernel/setup.c | 778 +++++ arch/ppc/kernel/signal.c | 775 +++++ arch/ppc/kernel/smp-tbsync.c | 181 ++ arch/ppc/kernel/smp.c | 399 +++ arch/ppc/kernel/softemu8xx.c | 147 + arch/ppc/kernel/swsusp.S | 349 +++ arch/ppc/kernel/syscalls.c | 272 ++ arch/ppc/kernel/temp.c | 272 ++ arch/ppc/kernel/time.c | 447 +++ arch/ppc/kernel/traps.c | 886 ++++++ arch/ppc/kernel/vecemu.c | 345 +++ arch/ppc/kernel/vector.S | 217 ++ arch/ppc/kernel/vmlinux.lds.S | 192 ++ arch/ppc/lib/Makefile | 9 + arch/ppc/lib/checksum.S | 225 ++ arch/ppc/lib/dec_and_lock.c | 46 + arch/ppc/lib/div64.S | 58 + arch/ppc/lib/locks.c | 190 ++ arch/ppc/lib/rheap.c | 693 +++++ arch/ppc/lib/strcase.c | 23 + arch/ppc/lib/string.S | 716 +++++ arch/ppc/math-emu/Makefile | 13 + arch/ppc/math-emu/double.h | 129 + arch/ppc/math-emu/fabs.c | 18 + arch/ppc/math-emu/fadd.c | 38 + arch/ppc/math-emu/fadds.c | 39 + arch/ppc/math-emu/fcmpo.c | 46 + arch/ppc/math-emu/fcmpu.c | 42 + arch/ppc/math-emu/fctiw.c | 25 + arch/ppc/math-emu/fctiwz.c | 32 + arch/ppc/math-emu/fdiv.c | 53 + arch/ppc/math-emu/fdivs.c | 55 + arch/ppc/math-emu/fmadd.c | 48 + arch/ppc/math-emu/fmadds.c | 49 + arch/ppc/math-emu/fmr.c | 18 + arch/ppc/math-emu/fmsub.c | 51 + arch/ppc/math-emu/fmsubs.c | 52 + arch/ppc/math-emu/fmul.c | 42 + arch/ppc/math-emu/fmuls.c | 43 + arch/ppc/math-emu/fnabs.c | 18 + arch/ppc/math-emu/fneg.c | 18 + arch/ppc/math-emu/fnmadd.c | 51 + arch/ppc/math-emu/fnmadds.c | 52 + arch/ppc/math-emu/fnmsub.c | 54 + arch/ppc/math-emu/fnmsubs.c | 55 + arch/ppc/math-emu/fres.c | 12 + arch/ppc/math-emu/frsp.c | 25 + arch/ppc/math-emu/frsqrte.c | 12 + arch/ppc/math-emu/fsel.c | 38 + arch/ppc/math-emu/fsqrt.c | 37 + arch/ppc/math-emu/fsqrts.c | 38 + arch/ppc/math-emu/fsub.c | 41 + arch/ppc/math-emu/fsubs.c | 42 + arch/ppc/math-emu/lfd.c | 19 + arch/ppc/math-emu/lfs.c | 37 + arch/ppc/math-emu/math.c | 485 ++++ arch/ppc/math-emu/mcrfs.c | 31 + arch/ppc/math-emu/mffs.c | 17 + arch/ppc/math-emu/mtfsb0.c | 18 + arch/ppc/math-emu/mtfsb1.c | 18 + arch/ppc/math-emu/mtfsf.c | 45 + arch/ppc/math-emu/mtfsfi.c | 23 + arch/ppc/math-emu/op-1.h | 245 ++ arch/ppc/math-emu/op-2.h | 433 +++ arch/ppc/math-emu/op-4.h | 297 ++ arch/ppc/math-emu/op-common.h | 688 +++++ arch/ppc/math-emu/sfp-machine.h | 377 +++ arch/ppc/math-emu/single.h | 66 + arch/ppc/math-emu/soft-fp.h | 104 + arch/ppc/math-emu/stfd.c | 20 + arch/ppc/math-emu/stfiwx.c | 16 + arch/ppc/math-emu/stfs.c | 41 + arch/ppc/math-emu/types.c | 51 + arch/ppc/math-emu/udivmodti4.c | 191 ++ arch/ppc/mm/44x_mmu.c | 121 + arch/ppc/mm/4xx_mmu.c | 142 + arch/ppc/mm/Makefile | 11 + arch/ppc/mm/fault.c | 440 +++ arch/ppc/mm/fsl_booke_mmu.c | 236 ++ arch/ppc/mm/hashtable.S | 642 +++++ arch/ppc/mm/init.c | 667 +++++ arch/ppc/mm/mem_pieces.c | 163 ++ arch/ppc/mm/mem_pieces.h | 48 + arch/ppc/mm/mmu_context.c | 86 + arch/ppc/mm/mmu_decl.h | 83 + arch/ppc/mm/pgtable.c | 471 ++++ arch/ppc/mm/ppc_mmu.c | 296 ++ arch/ppc/mm/tlb.c | 183 ++ arch/ppc/oprofile/Kconfig | 23 + arch/ppc/oprofile/Makefile | 14 + arch/ppc/oprofile/common.c | 161 ++ arch/ppc/oprofile/op_impl.h | 45 + arch/ppc/oprofile/op_model_fsl_booke.c | 184 ++ arch/ppc/platforms/4xx/Kconfig | 247 ++ arch/ppc/platforms/4xx/Makefile | 27 + arch/ppc/platforms/4xx/ash.c | 250 ++ arch/ppc/platforms/4xx/ash.h | 83 + arch/ppc/platforms/4xx/bubinga.c | 263 ++ arch/ppc/platforms/4xx/bubinga.h | 69 + arch/ppc/platforms/4xx/cpci405.c | 84 + arch/ppc/platforms/4xx/cpci405.h | 37 + arch/ppc/platforms/4xx/ebony.c | 356 +++ arch/ppc/platforms/4xx/ebony.h | 91 + arch/ppc/platforms/4xx/ep405.c | 197 ++ arch/ppc/platforms/4xx/ep405.h | 54 + arch/ppc/platforms/4xx/ibm405ep.c | 143 + arch/ppc/platforms/4xx/ibm405ep.h | 148 + arch/ppc/platforms/4xx/ibm405gp.c | 120 + arch/ppc/platforms/4xx/ibm405gp.h | 151 + arch/ppc/platforms/4xx/ibm405gpr.c | 117 + arch/ppc/platforms/4xx/ibm405gpr.h | 151 + arch/ppc/platforms/4xx/ibm440gp.c | 164 ++ arch/ppc/platforms/4xx/ibm440gp.h | 66 + arch/ppc/platforms/4xx/ibm440gx.c | 234 ++ arch/ppc/platforms/4xx/ibm440gx.h | 74 + arch/ppc/platforms/4xx/ibm440sp.c | 131 + arch/ppc/platforms/4xx/ibm440sp.h | 64 + arch/ppc/platforms/4xx/ibmnp405h.c | 172 ++ arch/ppc/platforms/4xx/ibmnp405h.h | 157 ++ arch/ppc/platforms/4xx/ibmstb4.c | 83 + arch/ppc/platforms/4xx/ibmstb4.h | 238 ++ arch/ppc/platforms/4xx/ibmstbx25.c | 68 + arch/ppc/platforms/4xx/ibmstbx25.h | 261 ++ arch/ppc/platforms/4xx/luan.c | 387 +++ arch/ppc/platforms/4xx/luan.h | 80 + arch/ppc/platforms/4xx/oak.c | 255 ++ arch/ppc/platforms/4xx/oak.h | 96 + arch/ppc/platforms/4xx/oak_setup.h | 50 + arch/ppc/platforms/4xx/ocotea.c | 367 +++ arch/ppc/platforms/4xx/ocotea.h | 88 + arch/ppc/platforms/4xx/redwood5.c | 110 + arch/ppc/platforms/4xx/redwood5.h | 54 + arch/ppc/platforms/4xx/redwood6.c | 159 ++ arch/ppc/platforms/4xx/redwood6.h | 55 + arch/ppc/platforms/4xx/sycamore.c | 278 ++ arch/ppc/platforms/4xx/sycamore.h | 67 + arch/ppc/platforms/4xx/virtex-ii_pro.c | 60 + arch/ppc/platforms/4xx/virtex-ii_pro.h | 99 + arch/ppc/platforms/4xx/walnut.c | 249 ++ arch/ppc/platforms/4xx/walnut.h | 72 + arch/ppc/platforms/4xx/xilinx_ml300.c | 146 + arch/ppc/platforms/4xx/xilinx_ml300.h | 47 + .../platforms/4xx/xparameters/xparameters_ml300.h | 310 ++ arch/ppc/platforms/83xx/Makefile | 4 + arch/ppc/platforms/83xx/mpc834x_sys.c | 289 ++ arch/ppc/platforms/83xx/mpc834x_sys.h | 51 + arch/ppc/platforms/85xx/Kconfig | 76 + arch/ppc/platforms/85xx/Makefile | 8 + arch/ppc/platforms/85xx/mpc8540_ads.c | 218 ++ arch/ppc/platforms/85xx/mpc8540_ads.h | 25 + arch/ppc/platforms/85xx/mpc8555_cds.h | 26 + arch/ppc/platforms/85xx/mpc8560_ads.c | 210 ++ arch/ppc/platforms/85xx/mpc8560_ads.h | 27 + arch/ppc/platforms/85xx/mpc85xx_ads_common.c | 225 ++ arch/ppc/platforms/85xx/mpc85xx_ads_common.h | 50 + arch/ppc/platforms/85xx/mpc85xx_cds_common.c | 467 +++ arch/ppc/platforms/85xx/mpc85xx_cds_common.h | 80 + arch/ppc/platforms/85xx/sbc8560.c | 227 ++ arch/ppc/platforms/85xx/sbc8560.h | 49 + arch/ppc/platforms/85xx/sbc85xx.c | 203 ++ arch/ppc/platforms/85xx/sbc85xx.h | 55 + arch/ppc/platforms/85xx/stx_gp3.c | 355 +++ arch/ppc/platforms/85xx/stx_gp3.h | 74 + arch/ppc/platforms/Makefile | 53 + arch/ppc/platforms/adir.h | 95 + arch/ppc/platforms/adir_pci.c | 247 ++ arch/ppc/platforms/adir_pic.c | 130 + arch/ppc/platforms/adir_setup.c | 210 ++ arch/ppc/platforms/apus_pci.c | 208 ++ arch/ppc/platforms/apus_pci.h | 34 + arch/ppc/platforms/apus_setup.c | 815 ++++++ arch/ppc/platforms/bseip.h | 38 + arch/ppc/platforms/ccm.h | 28 + arch/ppc/platforms/chestnut.c | 580 ++++ arch/ppc/platforms/chestnut.h | 129 + arch/ppc/platforms/chrp_pci.c | 309 ++ arch/ppc/platforms/chrp_pegasos_eth.c | 101 + arch/ppc/platforms/chrp_setup.c | 615 ++++ arch/ppc/platforms/chrp_smp.c | 98 + arch/ppc/platforms/chrp_time.c | 194 ++ arch/ppc/platforms/cpci690.c | 491 ++++ arch/ppc/platforms/cpci690.h | 78 + arch/ppc/platforms/est8260.h | 35 + arch/ppc/platforms/ev64260.c | 651 +++++ arch/ppc/platforms/ev64260.h | 128 + arch/ppc/platforms/fads.h | 57 + arch/ppc/platforms/gemini.h | 168 ++ arch/ppc/platforms/gemini_pci.c | 41 + arch/ppc/platforms/gemini_prom.S | 93 + arch/ppc/platforms/gemini_serial.h | 41 + arch/ppc/platforms/gemini_setup.c | 584 ++++ arch/ppc/platforms/hdpu.c | 1062 +++++++ arch/ppc/platforms/hdpu.h | 82 + arch/ppc/platforms/hermes.h | 27 + arch/ppc/platforms/ip860.h | 36 + arch/ppc/platforms/ivms8.h | 56 + arch/ppc/platforms/k2.c | 613 ++++ arch/ppc/platforms/k2.h | 82 + arch/ppc/platforms/katana.c | 795 ++++++ arch/ppc/platforms/katana.h | 255 ++ arch/ppc/platforms/lantec.h | 21 + arch/ppc/platforms/lite5200.c | 236 ++ arch/ppc/platforms/lite5200.h | 23 + arch/ppc/platforms/lopec.c | 411 +++ arch/ppc/platforms/lopec.h | 39 + arch/ppc/platforms/lwmon.h | 60 + arch/ppc/platforms/mbx.h | 117 + arch/ppc/platforms/mcpn765.c | 527 ++++ arch/ppc/platforms/mcpn765.h | 122 + arch/ppc/platforms/mpc5200.c | 53 + arch/ppc/platforms/mvme5100.c | 349 +++ arch/ppc/platforms/mvme5100.h | 91 + arch/ppc/platforms/pal4.h | 42 + arch/ppc/platforms/pal4_pci.c | 77 + arch/ppc/platforms/pal4_serial.h | 39 + arch/ppc/platforms/pal4_setup.c | 175 ++ arch/ppc/platforms/pcore.c | 352 +++ arch/ppc/platforms/pcore.h | 39 + arch/ppc/platforms/pcu_e.h | 28 + arch/ppc/platforms/pmac_backlight.c | 202 ++ arch/ppc/platforms/pmac_cache.S | 325 +++ arch/ppc/platforms/pmac_cpufreq.c | 571 ++++ arch/ppc/platforms/pmac_feature.c | 2972 ++++++++++++++++++++ arch/ppc/platforms/pmac_low_i2c.c | 513 ++++ arch/ppc/platforms/pmac_nvram.c | 584 ++++ arch/ppc/platforms/pmac_pci.c | 1125 ++++++++ arch/ppc/platforms/pmac_pic.c | 689 +++++ arch/ppc/platforms/pmac_pic.h | 11 + arch/ppc/platforms/pmac_setup.c | 745 +++++ arch/ppc/platforms/pmac_sleep.S | 390 +++ arch/ppc/platforms/pmac_smp.c | 640 +++++ arch/ppc/platforms/pmac_time.c | 292 ++ arch/ppc/platforms/powerpmc250.c | 383 +++ arch/ppc/platforms/powerpmc250.h | 52 + arch/ppc/platforms/pplus.c | 917 ++++++ arch/ppc/platforms/pplus.h | 67 + arch/ppc/platforms/pq2ads.c | 26 + arch/ppc/platforms/pq2ads.h | 96 + arch/ppc/platforms/prep_pci.c | 1336 +++++++++ arch/ppc/platforms/prep_setup.c | 1181 ++++++++ arch/ppc/platforms/prpmc750.c | 364 +++ arch/ppc/platforms/prpmc750.h | 95 + arch/ppc/platforms/prpmc800.c | 477 ++++ arch/ppc/platforms/prpmc800.h | 82 + arch/ppc/platforms/radstone_ppc7d.c | 1452 ++++++++++ arch/ppc/platforms/radstone_ppc7d.h | 434 +++ arch/ppc/platforms/residual.c | 1034 +++++++ arch/ppc/platforms/rpx8260.h | 81 + arch/ppc/platforms/rpxclassic.h | 119 + arch/ppc/platforms/rpxhiox.h | 41 + arch/ppc/platforms/rpxlite.h | 96 + arch/ppc/platforms/sandpoint.c | 742 +++++ arch/ppc/platforms/sandpoint.h | 80 + arch/ppc/platforms/sbc82xx.c | 259 ++ arch/ppc/platforms/sbc82xx.h | 36 + arch/ppc/platforms/sbs8260.h | 28 + arch/ppc/platforms/spd8xx.h | 92 + arch/ppc/platforms/spruce.c | 325 +++ arch/ppc/platforms/spruce.h | 71 + arch/ppc/platforms/tqm8260.h | 23 + arch/ppc/platforms/tqm8260_setup.c | 44 + arch/ppc/platforms/tqm8xx.h | 179 ++ arch/ppc/syslib/Makefile | 115 + arch/ppc/syslib/btext.c | 861 ++++++ arch/ppc/syslib/cpc700.h | 98 + arch/ppc/syslib/cpc700_pic.c | 187 ++ arch/ppc/syslib/cpc710.h | 83 + arch/ppc/syslib/cpm2_common.c | 198 ++ arch/ppc/syslib/cpm2_pic.c | 172 ++ arch/ppc/syslib/cpm2_pic.h | 8 + arch/ppc/syslib/dcr.S | 41 + arch/ppc/syslib/gen550.h | 16 + arch/ppc/syslib/gen550_dbg.c | 182 ++ arch/ppc/syslib/gen550_kgdb.c | 86 + arch/ppc/syslib/gt64260_pic.c | 328 +++ arch/ppc/syslib/harrier.c | 302 ++ arch/ppc/syslib/hawk_common.c | 319 +++ arch/ppc/syslib/i8259.c | 211 ++ arch/ppc/syslib/ibm440gp_common.c | 76 + arch/ppc/syslib/ibm440gp_common.h | 35 + arch/ppc/syslib/ibm440gx_common.c | 270 ++ arch/ppc/syslib/ibm440gx_common.h | 57 + arch/ppc/syslib/ibm440sp_common.c | 71 + arch/ppc/syslib/ibm440sp_common.h | 25 + arch/ppc/syslib/ibm44x_common.c | 193 ++ arch/ppc/syslib/ibm44x_common.h | 42 + arch/ppc/syslib/ibm_ocp.c | 9 + arch/ppc/syslib/indirect_pci.c | 135 + arch/ppc/syslib/ipic.c | 646 +++++ arch/ppc/syslib/ipic.h | 49 + arch/ppc/syslib/m8260_pci.c | 194 ++ arch/ppc/syslib/m8260_pci.h | 76 + arch/ppc/syslib/m8260_pci_erratum9.c | 473 ++++ arch/ppc/syslib/m8260_setup.c | 264 ++ arch/ppc/syslib/m8xx_setup.c | 433 +++ arch/ppc/syslib/m8xx_wdt.c | 99 + arch/ppc/syslib/m8xx_wdt.h | 16 + arch/ppc/syslib/mpc10x_common.c | 510 ++++ arch/ppc/syslib/mpc52xx_devices.c | 318 +++ arch/ppc/syslib/mpc52xx_pci.c | 235 ++ arch/ppc/syslib/mpc52xx_pci.h | 139 + arch/ppc/syslib/mpc52xx_pic.c | 257 ++ arch/ppc/syslib/mpc52xx_setup.c | 230 ++ arch/ppc/syslib/mpc52xx_sys.c | 38 + arch/ppc/syslib/mpc83xx_devices.c | 237 ++ arch/ppc/syslib/mpc83xx_sys.c | 100 + arch/ppc/syslib/mpc85xx_devices.c | 552 ++++ arch/ppc/syslib/mpc85xx_sys.c | 118 + arch/ppc/syslib/mv64360_pic.c | 426 +++ arch/ppc/syslib/mv64x60.c | 2392 ++++++++++++++++ arch/ppc/syslib/mv64x60_dbg.c | 123 + arch/ppc/syslib/mv64x60_win.c | 1168 ++++++++ arch/ppc/syslib/ocp.c | 485 ++++ arch/ppc/syslib/of_device.c | 273 ++ arch/ppc/syslib/open_pic.c | 1083 +++++++ arch/ppc/syslib/open_pic2.c | 716 +++++ arch/ppc/syslib/open_pic_defs.h | 292 ++ arch/ppc/syslib/pci_auto.c | 517 ++++ arch/ppc/syslib/ppc403_pic.c | 127 + arch/ppc/syslib/ppc405_pci.c | 177 ++ arch/ppc/syslib/ppc4xx_dma.c | 708 +++++ arch/ppc/syslib/ppc4xx_kgdb.c | 124 + arch/ppc/syslib/ppc4xx_pic.c | 244 ++ arch/ppc/syslib/ppc4xx_pm.c | 47 + arch/ppc/syslib/ppc4xx_setup.c | 321 +++ arch/ppc/syslib/ppc4xx_sgdma.c | 467 +++ arch/ppc/syslib/ppc83xx_setup.c | 138 + arch/ppc/syslib/ppc83xx_setup.h | 53 + arch/ppc/syslib/ppc85xx_common.c | 33 + arch/ppc/syslib/ppc85xx_common.h | 25 + arch/ppc/syslib/ppc85xx_setup.c | 354 +++ arch/ppc/syslib/ppc85xx_setup.h | 59 + arch/ppc/syslib/ppc8xx_pic.c | 130 + arch/ppc/syslib/ppc8xx_pic.h | 21 + arch/ppc/syslib/ppc_sys.c | 103 + arch/ppc/syslib/prep_nvram.c | 141 + arch/ppc/syslib/prom.c | 1447 ++++++++++ arch/ppc/syslib/prom_init.c | 1002 +++++++ arch/ppc/syslib/qspan_pci.c | 381 +++ arch/ppc/syslib/todc_time.c | 513 ++++ arch/ppc/syslib/xilinx_pic.c | 157 ++ arch/ppc/xmon/Makefile | 8 + arch/ppc/xmon/adb.c | 212 ++ arch/ppc/xmon/ansidecl.h | 141 + arch/ppc/xmon/nonstdio.h | 22 + arch/ppc/xmon/ppc-dis.c | 190 ++ arch/ppc/xmon/ppc-opc.c | 2721 ++++++++++++++++++ arch/ppc/xmon/ppc.h | 240 ++ arch/ppc/xmon/privinst.h | 91 + arch/ppc/xmon/setjmp.c | 29 + arch/ppc/xmon/start.c | 646 +++++ arch/ppc/xmon/start_8xx.c | 287 ++ arch/ppc/xmon/subr_prf.c | 55 + arch/ppc/xmon/xmon.c | 2008 +++++++++++++ 560 files changed, 174909 insertions(+) create mode 100644 arch/ppc/4xx_io/Makefile create mode 100644 arch/ppc/4xx_io/serial_sicc.c create mode 100644 arch/ppc/8260_io/Kconfig create mode 100644 arch/ppc/8260_io/Makefile create mode 100644 arch/ppc/8260_io/enet.c create mode 100644 arch/ppc/8260_io/fcc_enet.c create mode 100644 arch/ppc/8xx_io/Kconfig create mode 100644 arch/ppc/8xx_io/Makefile create mode 100644 arch/ppc/8xx_io/commproc.c create mode 100644 arch/ppc/8xx_io/cs4218.h create mode 100644 arch/ppc/8xx_io/cs4218_tdm.c create mode 100644 arch/ppc/8xx_io/enet.c create mode 100644 arch/ppc/8xx_io/fec.c create mode 100644 arch/ppc/8xx_io/micropatch.c create mode 100644 arch/ppc/Kconfig create mode 100644 arch/ppc/Kconfig.debug create mode 100644 arch/ppc/Makefile create mode 100644 arch/ppc/amiga/Makefile create mode 100644 arch/ppc/amiga/amiga_ksyms.c create mode 100644 arch/ppc/amiga/amiints.c create mode 100644 arch/ppc/amiga/amisound.c create mode 100644 arch/ppc/amiga/bootinfo.c create mode 100644 arch/ppc/amiga/chipram.c create mode 100644 arch/ppc/amiga/cia.c create mode 100644 arch/ppc/amiga/config.c create mode 100644 arch/ppc/amiga/ints.c create mode 100644 arch/ppc/amiga/pcmcia.c create mode 100644 arch/ppc/amiga/time.c create mode 100644 arch/ppc/boot/Makefile create mode 100644 arch/ppc/boot/common/Makefile create mode 100644 arch/ppc/boot/common/bootinfo.c create mode 100644 arch/ppc/boot/common/crt0.S create mode 100644 arch/ppc/boot/common/misc-common.c create mode 100644 arch/ppc/boot/common/ns16550.c create mode 100644 arch/ppc/boot/common/serial_stub.c create mode 100644 arch/ppc/boot/common/string.S create mode 100644 arch/ppc/boot/common/util.S create mode 100644 arch/ppc/boot/images/Makefile create mode 100644 arch/ppc/boot/include/cpc700.h create mode 100644 arch/ppc/boot/include/iso_font.h create mode 100644 arch/ppc/boot/include/mpc10x.h create mode 100644 arch/ppc/boot/include/mpsc_defs.h create mode 100644 arch/ppc/boot/include/nonstdio.h create mode 100644 arch/ppc/boot/include/of1275.h create mode 100644 arch/ppc/boot/include/rs6000.h create mode 100644 arch/ppc/boot/include/serial.h create mode 100644 arch/ppc/boot/ld.script create mode 100644 arch/ppc/boot/lib/Makefile create mode 100644 arch/ppc/boot/lib/div64.S create mode 100644 arch/ppc/boot/lib/kbd.c create mode 100644 arch/ppc/boot/lib/vreset.c create mode 100644 arch/ppc/boot/of1275/Makefile create mode 100644 arch/ppc/boot/of1275/claim.c create mode 100644 arch/ppc/boot/of1275/enter.c create mode 100644 arch/ppc/boot/of1275/exit.c create mode 100644 arch/ppc/boot/of1275/finddevice.c create mode 100644 arch/ppc/boot/of1275/getprop.c create mode 100644 arch/ppc/boot/of1275/map.c create mode 100644 arch/ppc/boot/of1275/ofinit.c create mode 100644 arch/ppc/boot/of1275/ofstdio.c create mode 100644 arch/ppc/boot/of1275/read.c create mode 100644 arch/ppc/boot/of1275/release.c create mode 100644 arch/ppc/boot/of1275/write.c create mode 100644 arch/ppc/boot/openfirmware/Makefile create mode 100644 arch/ppc/boot/openfirmware/chrpmain.c create mode 100644 arch/ppc/boot/openfirmware/coffmain.c create mode 100644 arch/ppc/boot/openfirmware/common.c create mode 100644 arch/ppc/boot/openfirmware/dummy.c create mode 100644 arch/ppc/boot/openfirmware/misc.S create mode 100644 arch/ppc/boot/openfirmware/newworldmain.c create mode 100644 arch/ppc/boot/openfirmware/start.c create mode 100644 arch/ppc/boot/simple/Makefile create mode 100644 arch/ppc/boot/simple/chrpmap.c create mode 100644 arch/ppc/boot/simple/clear.S create mode 100644 arch/ppc/boot/simple/cpc700_memory.c create mode 100644 arch/ppc/boot/simple/dummy.c create mode 100644 arch/ppc/boot/simple/embed_config.c create mode 100644 arch/ppc/boot/simple/head.S create mode 100644 arch/ppc/boot/simple/iic.c create mode 100644 arch/ppc/boot/simple/m8260_tty.c create mode 100644 arch/ppc/boot/simple/m8xx_tty.c create mode 100644 arch/ppc/boot/simple/misc-chestnut.c create mode 100644 arch/ppc/boot/simple/misc-cpci690.c create mode 100644 arch/ppc/boot/simple/misc-embedded.c create mode 100644 arch/ppc/boot/simple/misc-ev64260.c create mode 100644 arch/ppc/boot/simple/misc-katana.c create mode 100644 arch/ppc/boot/simple/misc-mv64x60.c create mode 100644 arch/ppc/boot/simple/misc-prep.c create mode 100644 arch/ppc/boot/simple/misc-radstone_ppc7d.c create mode 100644 arch/ppc/boot/simple/misc-spruce.c create mode 100644 arch/ppc/boot/simple/misc.c create mode 100644 arch/ppc/boot/simple/mpc10x_memory.c create mode 100644 arch/ppc/boot/simple/mpc52xx_tty.c create mode 100644 arch/ppc/boot/simple/mv64x60_tty.c create mode 100644 arch/ppc/boot/simple/openbios.c create mode 100644 arch/ppc/boot/simple/pci.c create mode 100644 arch/ppc/boot/simple/pibs.c create mode 100644 arch/ppc/boot/simple/prepmap.c create mode 100644 arch/ppc/boot/simple/qspan_pci.c create mode 100644 arch/ppc/boot/simple/relocate.S create mode 100644 arch/ppc/boot/simple/rw4/ppc_40x.h create mode 100644 arch/ppc/boot/simple/rw4/rw4_init.S create mode 100644 arch/ppc/boot/simple/rw4/rw4_init_brd.S create mode 100644 arch/ppc/boot/simple/rw4/stb.h create mode 100644 arch/ppc/boot/utils/addRamDisk.c create mode 100644 arch/ppc/boot/utils/addSystemMap.c create mode 100644 arch/ppc/boot/utils/addnote.c create mode 100644 arch/ppc/boot/utils/elf.pl create mode 100644 arch/ppc/boot/utils/hack-coff.c create mode 100644 arch/ppc/boot/utils/mkbugboot.c create mode 100644 arch/ppc/boot/utils/mknote.c create mode 100644 arch/ppc/boot/utils/mkprep.c create mode 100644 arch/ppc/boot/utils/mktree.c create mode 100644 arch/ppc/configs/FADS_defconfig create mode 100644 arch/ppc/configs/IVMS8_defconfig create mode 100644 arch/ppc/configs/SM850_defconfig create mode 100644 arch/ppc/configs/SPD823TS_defconfig create mode 100644 arch/ppc/configs/TQM823L_defconfig create mode 100644 arch/ppc/configs/TQM8260_defconfig create mode 100644 arch/ppc/configs/TQM850L_defconfig create mode 100644 arch/ppc/configs/TQM860L_defconfig create mode 100644 arch/ppc/configs/adir_defconfig create mode 100644 arch/ppc/configs/ads8272_defconfig create mode 100644 arch/ppc/configs/apus_defconfig create mode 100644 arch/ppc/configs/ash_defconfig create mode 100644 arch/ppc/configs/beech_defconfig create mode 100644 arch/ppc/configs/bseip_defconfig create mode 100644 arch/ppc/configs/bubinga_defconfig create mode 100644 arch/ppc/configs/cedar_defconfig create mode 100644 arch/ppc/configs/chestnut_defconfig create mode 100644 arch/ppc/configs/common_defconfig create mode 100644 arch/ppc/configs/cpci405_defconfig create mode 100644 arch/ppc/configs/cpci690_defconfig create mode 100644 arch/ppc/configs/ebony_defconfig create mode 100644 arch/ppc/configs/ep405_defconfig create mode 100644 arch/ppc/configs/est8260_defconfig create mode 100644 arch/ppc/configs/ev64260_defconfig create mode 100644 arch/ppc/configs/gemini_defconfig create mode 100644 arch/ppc/configs/hdpu_defconfig create mode 100644 arch/ppc/configs/ibmchrp_defconfig create mode 100644 arch/ppc/configs/k2_defconfig create mode 100644 arch/ppc/configs/katana_defconfig create mode 100644 arch/ppc/configs/lite5200_defconfig create mode 100644 arch/ppc/configs/lopec_defconfig create mode 100644 arch/ppc/configs/luan_defconfig create mode 100644 arch/ppc/configs/mbx_defconfig create mode 100644 arch/ppc/configs/mcpn765_defconfig create mode 100644 arch/ppc/configs/menf1_defconfig create mode 100644 arch/ppc/configs/mpc834x_sys_defconfig create mode 100644 arch/ppc/configs/mpc8540_ads_defconfig create mode 100644 arch/ppc/configs/mpc8555_cds_defconfig create mode 100644 arch/ppc/configs/mpc8560_ads_defconfig create mode 100644 arch/ppc/configs/mvme5100_defconfig create mode 100644 arch/ppc/configs/oak_defconfig create mode 100644 arch/ppc/configs/ocotea_defconfig create mode 100644 arch/ppc/configs/pcore_defconfig create mode 100644 arch/ppc/configs/pmac_defconfig create mode 100644 arch/ppc/configs/power3_defconfig create mode 100644 arch/ppc/configs/pplus_defconfig create mode 100644 arch/ppc/configs/prpmc750_defconfig create mode 100644 arch/ppc/configs/prpmc800_defconfig create mode 100644 arch/ppc/configs/radstone_ppc7d_defconfig create mode 100644 arch/ppc/configs/rainier_defconfig create mode 100644 arch/ppc/configs/redwood5_defconfig create mode 100644 arch/ppc/configs/redwood6_defconfig create mode 100644 arch/ppc/configs/redwood_defconfig create mode 100644 arch/ppc/configs/rpx8260_defconfig create mode 100644 arch/ppc/configs/rpxcllf_defconfig create mode 100644 arch/ppc/configs/rpxlite_defconfig create mode 100644 arch/ppc/configs/sandpoint_defconfig create mode 100644 arch/ppc/configs/spruce_defconfig create mode 100644 arch/ppc/configs/stx_gp3_defconfig create mode 100644 arch/ppc/configs/sycamore_defconfig create mode 100644 arch/ppc/configs/walnut_defconfig create mode 100644 arch/ppc/kernel/Makefile create mode 100644 arch/ppc/kernel/align.c create mode 100644 arch/ppc/kernel/asm-offsets.c create mode 100644 arch/ppc/kernel/bitops.c create mode 100644 arch/ppc/kernel/cpu_setup_6xx.S create mode 100644 arch/ppc/kernel/cpu_setup_power4.S create mode 100644 arch/ppc/kernel/cputable.c create mode 100644 arch/ppc/kernel/dma-mapping.c create mode 100644 arch/ppc/kernel/entry.S create mode 100644 arch/ppc/kernel/find_name.c create mode 100644 arch/ppc/kernel/head.S create mode 100644 arch/ppc/kernel/head_44x.S create mode 100644 arch/ppc/kernel/head_4xx.S create mode 100644 arch/ppc/kernel/head_8xx.S create mode 100644 arch/ppc/kernel/head_booke.h create mode 100644 arch/ppc/kernel/head_fsl_booke.S create mode 100644 arch/ppc/kernel/idle.c create mode 100644 arch/ppc/kernel/idle_6xx.S create mode 100644 arch/ppc/kernel/idle_power4.S create mode 100644 arch/ppc/kernel/irq.c create mode 100644 arch/ppc/kernel/l2cr.S create mode 100644 arch/ppc/kernel/misc.S create mode 100644 arch/ppc/kernel/module.c create mode 100644 arch/ppc/kernel/pci.c create mode 100644 arch/ppc/kernel/perfmon.c create mode 100644 arch/ppc/kernel/perfmon_fsl_booke.c create mode 100644 arch/ppc/kernel/ppc-stub.c create mode 100644 arch/ppc/kernel/ppc_htab.c create mode 100644 arch/ppc/kernel/ppc_ksyms.c create mode 100644 arch/ppc/kernel/process.c create mode 100644 arch/ppc/kernel/ptrace.c create mode 100644 arch/ppc/kernel/semaphore.c create mode 100644 arch/ppc/kernel/setup.c create mode 100644 arch/ppc/kernel/signal.c create mode 100644 arch/ppc/kernel/smp-tbsync.c create mode 100644 arch/ppc/kernel/smp.c create mode 100644 arch/ppc/kernel/softemu8xx.c create mode 100644 arch/ppc/kernel/swsusp.S create mode 100644 arch/ppc/kernel/syscalls.c create mode 100644 arch/ppc/kernel/temp.c create mode 100644 arch/ppc/kernel/time.c create mode 100644 arch/ppc/kernel/traps.c create mode 100644 arch/ppc/kernel/vecemu.c create mode 100644 arch/ppc/kernel/vector.S create mode 100644 arch/ppc/kernel/vmlinux.lds.S create mode 100644 arch/ppc/lib/Makefile create mode 100644 arch/ppc/lib/checksum.S create mode 100644 arch/ppc/lib/dec_and_lock.c create mode 100644 arch/ppc/lib/div64.S create mode 100644 arch/ppc/lib/locks.c create mode 100644 arch/ppc/lib/rheap.c create mode 100644 arch/ppc/lib/strcase.c create mode 100644 arch/ppc/lib/string.S create mode 100644 arch/ppc/math-emu/Makefile create mode 100644 arch/ppc/math-emu/double.h create mode 100644 arch/ppc/math-emu/fabs.c create mode 100644 arch/ppc/math-emu/fadd.c create mode 100644 arch/ppc/math-emu/fadds.c create mode 100644 arch/ppc/math-emu/fcmpo.c create mode 100644 arch/ppc/math-emu/fcmpu.c create mode 100644 arch/ppc/math-emu/fctiw.c create mode 100644 arch/ppc/math-emu/fctiwz.c create mode 100644 arch/ppc/math-emu/fdiv.c create mode 100644 arch/ppc/math-emu/fdivs.c create mode 100644 arch/ppc/math-emu/fmadd.c create mode 100644 arch/ppc/math-emu/fmadds.c create mode 100644 arch/ppc/math-emu/fmr.c create mode 100644 arch/ppc/math-emu/fmsub.c create mode 100644 arch/ppc/math-emu/fmsubs.c create mode 100644 arch/ppc/math-emu/fmul.c create mode 100644 arch/ppc/math-emu/fmuls.c create mode 100644 arch/ppc/math-emu/fnabs.c create mode 100644 arch/ppc/math-emu/fneg.c create mode 100644 arch/ppc/math-emu/fnmadd.c create mode 100644 arch/ppc/math-emu/fnmadds.c create mode 100644 arch/ppc/math-emu/fnmsub.c create mode 100644 arch/ppc/math-emu/fnmsubs.c create mode 100644 arch/ppc/math-emu/fres.c create mode 100644 arch/ppc/math-emu/frsp.c create mode 100644 arch/ppc/math-emu/frsqrte.c create mode 100644 arch/ppc/math-emu/fsel.c create mode 100644 arch/ppc/math-emu/fsqrt.c create mode 100644 arch/ppc/math-emu/fsqrts.c create mode 100644 arch/ppc/math-emu/fsub.c create mode 100644 arch/ppc/math-emu/fsubs.c create mode 100644 arch/ppc/math-emu/lfd.c create mode 100644 arch/ppc/math-emu/lfs.c create mode 100644 arch/ppc/math-emu/math.c create mode 100644 arch/ppc/math-emu/mcrfs.c create mode 100644 arch/ppc/math-emu/mffs.c create mode 100644 arch/ppc/math-emu/mtfsb0.c create mode 100644 arch/ppc/math-emu/mtfsb1.c create mode 100644 arch/ppc/math-emu/mtfsf.c create mode 100644 arch/ppc/math-emu/mtfsfi.c create mode 100644 arch/ppc/math-emu/op-1.h create mode 100644 arch/ppc/math-emu/op-2.h create mode 100644 arch/ppc/math-emu/op-4.h create mode 100644 arch/ppc/math-emu/op-common.h create mode 100644 arch/ppc/math-emu/sfp-machine.h create mode 100644 arch/ppc/math-emu/single.h create mode 100644 arch/ppc/math-emu/soft-fp.h create mode 100644 arch/ppc/math-emu/stfd.c create mode 100644 arch/ppc/math-emu/stfiwx.c create mode 100644 arch/ppc/math-emu/stfs.c create mode 100644 arch/ppc/math-emu/types.c create mode 100644 arch/ppc/math-emu/udivmodti4.c create mode 100644 arch/ppc/mm/44x_mmu.c create mode 100644 arch/ppc/mm/4xx_mmu.c create mode 100644 arch/ppc/mm/Makefile create mode 100644 arch/ppc/mm/fault.c create mode 100644 arch/ppc/mm/fsl_booke_mmu.c create mode 100644 arch/ppc/mm/hashtable.S create mode 100644 arch/ppc/mm/init.c create mode 100644 arch/ppc/mm/mem_pieces.c create mode 100644 arch/ppc/mm/mem_pieces.h create mode 100644 arch/ppc/mm/mmu_context.c create mode 100644 arch/ppc/mm/mmu_decl.h create mode 100644 arch/ppc/mm/pgtable.c create mode 100644 arch/ppc/mm/ppc_mmu.c create mode 100644 arch/ppc/mm/tlb.c create mode 100644 arch/ppc/oprofile/Kconfig create mode 100644 arch/ppc/oprofile/Makefile create mode 100644 arch/ppc/oprofile/common.c create mode 100644 arch/ppc/oprofile/op_impl.h create mode 100644 arch/ppc/oprofile/op_model_fsl_booke.c create mode 100644 arch/ppc/platforms/4xx/Kconfig create mode 100644 arch/ppc/platforms/4xx/Makefile create mode 100644 arch/ppc/platforms/4xx/ash.c create mode 100644 arch/ppc/platforms/4xx/ash.h create mode 100644 arch/ppc/platforms/4xx/bubinga.c create mode 100644 arch/ppc/platforms/4xx/bubinga.h create mode 100644 arch/ppc/platforms/4xx/cpci405.c create mode 100644 arch/ppc/platforms/4xx/cpci405.h create mode 100644 arch/ppc/platforms/4xx/ebony.c create mode 100644 arch/ppc/platforms/4xx/ebony.h create mode 100644 arch/ppc/platforms/4xx/ep405.c create mode 100644 arch/ppc/platforms/4xx/ep405.h create mode 100644 arch/ppc/platforms/4xx/ibm405ep.c create mode 100644 arch/ppc/platforms/4xx/ibm405ep.h create mode 100644 arch/ppc/platforms/4xx/ibm405gp.c create mode 100644 arch/ppc/platforms/4xx/ibm405gp.h create mode 100644 arch/ppc/platforms/4xx/ibm405gpr.c create mode 100644 arch/ppc/platforms/4xx/ibm405gpr.h create mode 100644 arch/ppc/platforms/4xx/ibm440gp.c create mode 100644 arch/ppc/platforms/4xx/ibm440gp.h create mode 100644 arch/ppc/platforms/4xx/ibm440gx.c create mode 100644 arch/ppc/platforms/4xx/ibm440gx.h create mode 100644 arch/ppc/platforms/4xx/ibm440sp.c create mode 100644 arch/ppc/platforms/4xx/ibm440sp.h create mode 100644 arch/ppc/platforms/4xx/ibmnp405h.c create mode 100644 arch/ppc/platforms/4xx/ibmnp405h.h create mode 100644 arch/ppc/platforms/4xx/ibmstb4.c create mode 100644 arch/ppc/platforms/4xx/ibmstb4.h create mode 100644 arch/ppc/platforms/4xx/ibmstbx25.c create mode 100644 arch/ppc/platforms/4xx/ibmstbx25.h create mode 100644 arch/ppc/platforms/4xx/luan.c create mode 100644 arch/ppc/platforms/4xx/luan.h create mode 100644 arch/ppc/platforms/4xx/oak.c create mode 100644 arch/ppc/platforms/4xx/oak.h create mode 100644 arch/ppc/platforms/4xx/oak_setup.h create mode 100644 arch/ppc/platforms/4xx/ocotea.c create mode 100644 arch/ppc/platforms/4xx/ocotea.h create mode 100644 arch/ppc/platforms/4xx/redwood5.c create mode 100644 arch/ppc/platforms/4xx/redwood5.h create mode 100644 arch/ppc/platforms/4xx/redwood6.c create mode 100644 arch/ppc/platforms/4xx/redwood6.h create mode 100644 arch/ppc/platforms/4xx/sycamore.c create mode 100644 arch/ppc/platforms/4xx/sycamore.h create mode 100644 arch/ppc/platforms/4xx/virtex-ii_pro.c create mode 100644 arch/ppc/platforms/4xx/virtex-ii_pro.h create mode 100644 arch/ppc/platforms/4xx/walnut.c create mode 100644 arch/ppc/platforms/4xx/walnut.h create mode 100644 arch/ppc/platforms/4xx/xilinx_ml300.c create mode 100644 arch/ppc/platforms/4xx/xilinx_ml300.h create mode 100644 arch/ppc/platforms/4xx/xparameters/xparameters_ml300.h create mode 100644 arch/ppc/platforms/83xx/Makefile create mode 100644 arch/ppc/platforms/83xx/mpc834x_sys.c create mode 100644 arch/ppc/platforms/83xx/mpc834x_sys.h create mode 100644 arch/ppc/platforms/85xx/Kconfig create mode 100644 arch/ppc/platforms/85xx/Makefile create mode 100644 arch/ppc/platforms/85xx/mpc8540_ads.c create mode 100644 arch/ppc/platforms/85xx/mpc8540_ads.h create mode 100644 arch/ppc/platforms/85xx/mpc8555_cds.h create mode 100644 arch/ppc/platforms/85xx/mpc8560_ads.c create mode 100644 arch/ppc/platforms/85xx/mpc8560_ads.h create mode 100644 arch/ppc/platforms/85xx/mpc85xx_ads_common.c create mode 100644 arch/ppc/platforms/85xx/mpc85xx_ads_common.h create mode 100644 arch/ppc/platforms/85xx/mpc85xx_cds_common.c create mode 100644 arch/ppc/platforms/85xx/mpc85xx_cds_common.h create mode 100644 arch/ppc/platforms/85xx/sbc8560.c create mode 100644 arch/ppc/platforms/85xx/sbc8560.h create mode 100644 arch/ppc/platforms/85xx/sbc85xx.c create mode 100644 arch/ppc/platforms/85xx/sbc85xx.h create mode 100644 arch/ppc/platforms/85xx/stx_gp3.c create mode 100644 arch/ppc/platforms/85xx/stx_gp3.h create mode 100644 arch/ppc/platforms/Makefile create mode 100644 arch/ppc/platforms/adir.h create mode 100644 arch/ppc/platforms/adir_pci.c create mode 100644 arch/ppc/platforms/adir_pic.c create mode 100644 arch/ppc/platforms/adir_setup.c create mode 100644 arch/ppc/platforms/apus_pci.c create mode 100644 arch/ppc/platforms/apus_pci.h create mode 100644 arch/ppc/platforms/apus_setup.c create mode 100644 arch/ppc/platforms/bseip.h create mode 100644 arch/ppc/platforms/ccm.h create mode 100644 arch/ppc/platforms/chestnut.c create mode 100644 arch/ppc/platforms/chestnut.h create mode 100644 arch/ppc/platforms/chrp_pci.c create mode 100644 arch/ppc/platforms/chrp_pegasos_eth.c create mode 100644 arch/ppc/platforms/chrp_setup.c create mode 100644 arch/ppc/platforms/chrp_smp.c create mode 100644 arch/ppc/platforms/chrp_time.c create mode 100644 arch/ppc/platforms/cpci690.c create mode 100644 arch/ppc/platforms/cpci690.h create mode 100644 arch/ppc/platforms/est8260.h create mode 100644 arch/ppc/platforms/ev64260.c create mode 100644 arch/ppc/platforms/ev64260.h create mode 100644 arch/ppc/platforms/fads.h create mode 100644 arch/ppc/platforms/gemini.h create mode 100644 arch/ppc/platforms/gemini_pci.c create mode 100644 arch/ppc/platforms/gemini_prom.S create mode 100644 arch/ppc/platforms/gemini_serial.h create mode 100644 arch/ppc/platforms/gemini_setup.c create mode 100644 arch/ppc/platforms/hdpu.c create mode 100644 arch/ppc/platforms/hdpu.h create mode 100644 arch/ppc/platforms/hermes.h create mode 100644 arch/ppc/platforms/ip860.h create mode 100644 arch/ppc/platforms/ivms8.h create mode 100644 arch/ppc/platforms/k2.c create mode 100644 arch/ppc/platforms/k2.h create mode 100644 arch/ppc/platforms/katana.c create mode 100644 arch/ppc/platforms/katana.h create mode 100644 arch/ppc/platforms/lantec.h create mode 100644 arch/ppc/platforms/lite5200.c create mode 100644 arch/ppc/platforms/lite5200.h create mode 100644 arch/ppc/platforms/lopec.c create mode 100644 arch/ppc/platforms/lopec.h create mode 100644 arch/ppc/platforms/lwmon.h create mode 100644 arch/ppc/platforms/mbx.h create mode 100644 arch/ppc/platforms/mcpn765.c create mode 100644 arch/ppc/platforms/mcpn765.h create mode 100644 arch/ppc/platforms/mpc5200.c create mode 100644 arch/ppc/platforms/mvme5100.c create mode 100644 arch/ppc/platforms/mvme5100.h create mode 100644 arch/ppc/platforms/pal4.h create mode 100644 arch/ppc/platforms/pal4_pci.c create mode 100644 arch/ppc/platforms/pal4_serial.h create mode 100644 arch/ppc/platforms/pal4_setup.c create mode 100644 arch/ppc/platforms/pcore.c create mode 100644 arch/ppc/platforms/pcore.h create mode 100644 arch/ppc/platforms/pcu_e.h create mode 100644 arch/ppc/platforms/pmac_backlight.c create mode 100644 arch/ppc/platforms/pmac_cache.S create mode 100644 arch/ppc/platforms/pmac_cpufreq.c create mode 100644 arch/ppc/platforms/pmac_feature.c create mode 100644 arch/ppc/platforms/pmac_low_i2c.c create mode 100644 arch/ppc/platforms/pmac_nvram.c create mode 100644 arch/ppc/platforms/pmac_pci.c create mode 100644 arch/ppc/platforms/pmac_pic.c create mode 100644 arch/ppc/platforms/pmac_pic.h create mode 100644 arch/ppc/platforms/pmac_setup.c create mode 100644 arch/ppc/platforms/pmac_sleep.S create mode 100644 arch/ppc/platforms/pmac_smp.c create mode 100644 arch/ppc/platforms/pmac_time.c create mode 100644 arch/ppc/platforms/powerpmc250.c create mode 100644 arch/ppc/platforms/powerpmc250.h create mode 100644 arch/ppc/platforms/pplus.c create mode 100644 arch/ppc/platforms/pplus.h create mode 100644 arch/ppc/platforms/pq2ads.c create mode 100644 arch/ppc/platforms/pq2ads.h create mode 100644 arch/ppc/platforms/prep_pci.c create mode 100644 arch/ppc/platforms/prep_setup.c create mode 100644 arch/ppc/platforms/prpmc750.c create mode 100644 arch/ppc/platforms/prpmc750.h create mode 100644 arch/ppc/platforms/prpmc800.c create mode 100644 arch/ppc/platforms/prpmc800.h create mode 100644 arch/ppc/platforms/radstone_ppc7d.c create mode 100644 arch/ppc/platforms/radstone_ppc7d.h create mode 100644 arch/ppc/platforms/residual.c create mode 100644 arch/ppc/platforms/rpx8260.h create mode 100644 arch/ppc/platforms/rpxclassic.h create mode 100644 arch/ppc/platforms/rpxhiox.h create mode 100644 arch/ppc/platforms/rpxlite.h create mode 100644 arch/ppc/platforms/sandpoint.c create mode 100644 arch/ppc/platforms/sandpoint.h create mode 100644 arch/ppc/platforms/sbc82xx.c create mode 100644 arch/ppc/platforms/sbc82xx.h create mode 100644 arch/ppc/platforms/sbs8260.h create mode 100644 arch/ppc/platforms/spd8xx.h create mode 100644 arch/ppc/platforms/spruce.c create mode 100644 arch/ppc/platforms/spruce.h create mode 100644 arch/ppc/platforms/tqm8260.h create mode 100644 arch/ppc/platforms/tqm8260_setup.c create mode 100644 arch/ppc/platforms/tqm8xx.h create mode 100644 arch/ppc/syslib/Makefile create mode 100644 arch/ppc/syslib/btext.c create mode 100644 arch/ppc/syslib/cpc700.h create mode 100644 arch/ppc/syslib/cpc700_pic.c create mode 100644 arch/ppc/syslib/cpc710.h create mode 100644 arch/ppc/syslib/cpm2_common.c create mode 100644 arch/ppc/syslib/cpm2_pic.c create mode 100644 arch/ppc/syslib/cpm2_pic.h create mode 100644 arch/ppc/syslib/dcr.S create mode 100644 arch/ppc/syslib/gen550.h create mode 100644 arch/ppc/syslib/gen550_dbg.c create mode 100644 arch/ppc/syslib/gen550_kgdb.c create mode 100644 arch/ppc/syslib/gt64260_pic.c create mode 100644 arch/ppc/syslib/harrier.c create mode 100644 arch/ppc/syslib/hawk_common.c create mode 100644 arch/ppc/syslib/i8259.c create mode 100644 arch/ppc/syslib/ibm440gp_common.c create mode 100644 arch/ppc/syslib/ibm440gp_common.h create mode 100644 arch/ppc/syslib/ibm440gx_common.c create mode 100644 arch/ppc/syslib/ibm440gx_common.h create mode 100644 arch/ppc/syslib/ibm440sp_common.c create mode 100644 arch/ppc/syslib/ibm440sp_common.h create mode 100644 arch/ppc/syslib/ibm44x_common.c create mode 100644 arch/ppc/syslib/ibm44x_common.h create mode 100644 arch/ppc/syslib/ibm_ocp.c create mode 100644 arch/ppc/syslib/indirect_pci.c create mode 100644 arch/ppc/syslib/ipic.c create mode 100644 arch/ppc/syslib/ipic.h create mode 100644 arch/ppc/syslib/m8260_pci.c create mode 100644 arch/ppc/syslib/m8260_pci.h create mode 100644 arch/ppc/syslib/m8260_pci_erratum9.c create mode 100644 arch/ppc/syslib/m8260_setup.c create mode 100644 arch/ppc/syslib/m8xx_setup.c create mode 100644 arch/ppc/syslib/m8xx_wdt.c create mode 100644 arch/ppc/syslib/m8xx_wdt.h create mode 100644 arch/ppc/syslib/mpc10x_common.c create mode 100644 arch/ppc/syslib/mpc52xx_devices.c create mode 100644 arch/ppc/syslib/mpc52xx_pci.c create mode 100644 arch/ppc/syslib/mpc52xx_pci.h create mode 100644 arch/ppc/syslib/mpc52xx_pic.c create mode 100644 arch/ppc/syslib/mpc52xx_setup.c create mode 100644 arch/ppc/syslib/mpc52xx_sys.c create mode 100644 arch/ppc/syslib/mpc83xx_devices.c create mode 100644 arch/ppc/syslib/mpc83xx_sys.c create mode 100644 arch/ppc/syslib/mpc85xx_devices.c create mode 100644 arch/ppc/syslib/mpc85xx_sys.c create mode 100644 arch/ppc/syslib/mv64360_pic.c create mode 100644 arch/ppc/syslib/mv64x60.c create mode 100644 arch/ppc/syslib/mv64x60_dbg.c create mode 100644 arch/ppc/syslib/mv64x60_win.c create mode 100644 arch/ppc/syslib/ocp.c create mode 100644 arch/ppc/syslib/of_device.c create mode 100644 arch/ppc/syslib/open_pic.c create mode 100644 arch/ppc/syslib/open_pic2.c create mode 100644 arch/ppc/syslib/open_pic_defs.h create mode 100644 arch/ppc/syslib/pci_auto.c create mode 100644 arch/ppc/syslib/ppc403_pic.c create mode 100644 arch/ppc/syslib/ppc405_pci.c create mode 100644 arch/ppc/syslib/ppc4xx_dma.c create mode 100644 arch/ppc/syslib/ppc4xx_kgdb.c create mode 100644 arch/ppc/syslib/ppc4xx_pic.c create mode 100644 arch/ppc/syslib/ppc4xx_pm.c create mode 100644 arch/ppc/syslib/ppc4xx_setup.c create mode 100644 arch/ppc/syslib/ppc4xx_sgdma.c create mode 100644 arch/ppc/syslib/ppc83xx_setup.c create mode 100644 arch/ppc/syslib/ppc83xx_setup.h create mode 100644 arch/ppc/syslib/ppc85xx_common.c create mode 100644 arch/ppc/syslib/ppc85xx_common.h create mode 100644 arch/ppc/syslib/ppc85xx_setup.c create mode 100644 arch/ppc/syslib/ppc85xx_setup.h create mode 100644 arch/ppc/syslib/ppc8xx_pic.c create mode 100644 arch/ppc/syslib/ppc8xx_pic.h create mode 100644 arch/ppc/syslib/ppc_sys.c create mode 100644 arch/ppc/syslib/prep_nvram.c create mode 100644 arch/ppc/syslib/prom.c create mode 100644 arch/ppc/syslib/prom_init.c create mode 100644 arch/ppc/syslib/qspan_pci.c create mode 100644 arch/ppc/syslib/todc_time.c create mode 100644 arch/ppc/syslib/xilinx_pic.c create mode 100644 arch/ppc/xmon/Makefile create mode 100644 arch/ppc/xmon/adb.c create mode 100644 arch/ppc/xmon/ansidecl.h create mode 100644 arch/ppc/xmon/nonstdio.h create mode 100644 arch/ppc/xmon/ppc-dis.c create mode 100644 arch/ppc/xmon/ppc-opc.c create mode 100644 arch/ppc/xmon/ppc.h create mode 100644 arch/ppc/xmon/privinst.h create mode 100644 arch/ppc/xmon/setjmp.c create mode 100644 arch/ppc/xmon/start.c create mode 100644 arch/ppc/xmon/start_8xx.c create mode 100644 arch/ppc/xmon/subr_prf.c create mode 100644 arch/ppc/xmon/xmon.c (limited to 'arch/ppc') diff --git a/arch/ppc/4xx_io/Makefile b/arch/ppc/4xx_io/Makefile new file mode 100644 index 00000000000..6a8cd575f38 --- /dev/null +++ b/arch/ppc/4xx_io/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for the linux MPC4xx ppc-specific parts +# + + +obj-$(CONFIG_SERIAL_SICC) += serial_sicc.o diff --git a/arch/ppc/4xx_io/serial_sicc.c b/arch/ppc/4xx_io/serial_sicc.c new file mode 100644 index 00000000000..e95c48d5757 --- /dev/null +++ b/arch/ppc/4xx_io/serial_sicc.c @@ -0,0 +1,2012 @@ +/* + * arch/ppc/4xx_io/serial_sicc.c + * + * Driver for IBM STB3xxx SICC serial port + * + * Based on drivers/char/serial_amba.c, by ARM Ltd. + * + * Copyright 2001 IBM Crop. + * Author: IBM China Research Lab + * Yudong Yang + * Yi Ge + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * This is a driver for SICC serial port on IBM Redwood 4 evaluation board. + * The driver support both as a console device and normal serial device and + * is compatible with normal ttyS* devices. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +#include + + +/* ----------------------------------------------------------------------------- + * From STB03xxx SICC UART Specification + * ----------------------------------------------------------------------------- + * UART Register Offsets. + */ + +#define BL_SICC_LSR 0x0000000 /* line status register read/clear */ +#define BL_SICC_LSRS 0x0000001 /* set line status register read/set */ +#define BL_SICC_HSR 0x0000002 /* handshake status register r/clear */ +#define BL_SICC_HSRS 0x0000003 /* set handshake status register r/set */ +#define BL_SICC_BRDH 0x0000004 /* baudrate divisor high reg r/w */ +#define BL_SICC_BRDL 0x0000005 /* baudrate divisor low reg r/w */ +#define BL_SICC_LCR 0x0000006 /* control register r/w */ +#define BL_SICC_RCR 0x0000007 /* receiver command register r/w */ +#define BL_SICC_TxCR 0x0000008 /* transmitter command register r/w */ +#define BL_SICC_RBR 0x0000009 /* receive buffer r */ +#define BL_SICC_TBR 0x0000009 /* transmit buffer w */ +#define BL_SICC_CTL2 0x000000A /* added for Vesta */ +#define BL_SICC_IrCR 0x000000B /* added for Vesta IR */ + +/* masks and definitions for serial port control register */ + +#define _LCR_LM_MASK 0xc0 /* loop back modes */ +#define _LCR_DTR_MASK 0x20 /* data terminal ready 0-inactive */ +#define _LCR_RTS_MASK 0x10 /* request to send 0-inactive */ +#define _LCR_DB_MASK 0x08 /* data bits mask */ +#define _LCR_PE_MASK 0x04 /* parity enable */ +#define _LCR_PTY_MASK 0x02 /* parity */ +#define _LCR_SB_MASK 0x01 /* stop bit mask */ + +#define _LCR_LM_NORM 0x00 /* normal operation */ +#define _LCR_LM_LOOP 0x40 /* internal loopback mode */ +#define _LCR_LM_ECHO 0x80 /* automatic echo mode */ +#define _LCR_LM_RES 0xc0 /* reserved */ + +#define _LCR_DTR_ACTIVE _LCR_DTR_MASK /* DTR is active */ +#define _LCR_RTS_ACTIVE _LCR_RTS_MASK /* RTS is active */ +#define _LCR_DB_8_BITS _LCR_DB_MASK /* 8 data bits */ +#define _LCR_DB_7_BITS 0x00 /* 7 data bits */ +#define _LCR_PE_ENABLE _LCR_PE_MASK /* parity enabled */ +#define _LCR_PE_DISABLE 0x00 /* parity disabled */ +#define _LCR_PTY_EVEN 0x00 /* even parity */ +#define _LCR_PTY_ODD _LCR_PTY_MASK /* odd parity */ +#define _LCR_SB_1_BIT 0x00 /* one stop bit */ +#define _LCR_SB_2_BIT _LCR_SB_MASK /* two stop bit */ + +/* serial port handshake register */ + +#define _HSR_DIS_MASK 0x80 /* DSR input inactive error mask */ +#define _HSR_CS_MASK 0x40 /* CTS input inactive error mask */ +#define _HSR_DIS_ACT 0x00 /* dsr input is active */ +#define _HSR_DIS_INACT _HSR_DIS_MASK /* dsr input is inactive */ +#define _HSR_CS_ACT 0x00 /* cts input is active */ +#define _HSR_CS_INACT _HSR_CS_MASK /* cts input is active */ + +/* serial port line status register */ + +#define _LSR_RBR_MASK 0x80 /* receive buffer ready mask */ +#define _LSR_FE_MASK 0x40 /* framing error */ +#define _LSR_OE_MASK 0x20 /* overrun error */ +#define _LSR_PE_MASK 0x10 /* parity error */ +#define _LSR_LB_MASK 0x08 /* line break */ +#define _LSR_TBR_MASK 0x04 /* transmit buffer ready */ +#define _LSR_TSR_MASK 0x02 /* transmit shift register ready */ + +#define _LSR_RBR_FULL _LSR_RBR_MASK /* receive buffer is full */ +#define _LSR_FE_ERROR _LSR_FE_MASK /* framing error detected */ +#define _LSR_OE_ERROR _LSR_OE_MASK /* overrun error detected */ +#define _LSR_PE_ERROR _LSR_PE_MASK /* parity error detected */ +#define _LSR_LB_BREAK _LSR_LB_MASK /* line break detected */ +#define _LSR_TBR_EMPTY _LSR_TBR_MASK /* transmit buffer is ready */ +#define _LSR_TSR_EMPTY _LSR_TSR_MASK /* transmit shift register is empty */ +#define _LSR_TX_ALL 0x06 /* all physical transmit is done */ + +#define _LSR_RX_ERR (_LSR_LB_BREAK | _LSR_FE_MASK | _LSR_OE_MASK | \ + _LSR_PE_MASK ) + +/* serial port receiver command register */ + +#define _RCR_ER_MASK 0x80 /* enable receiver mask */ +#define _RCR_DME_MASK 0x60 /* dma mode */ +#define _RCR_EIE_MASK 0x10 /* error interrupt enable mask */ +#define _RCR_PME_MASK 0x08 /* pause mode mask */ + +#define _RCR_ER_ENABLE _RCR_ER_MASK /* receiver enabled */ +#define _RCR_DME_DISABLE 0x00 /* dma disabled */ +#define _RCR_DME_RXRDY 0x20 /* dma disabled, RxRDY interrupt enabled*/ +#define _RCR_DME_ENABLE2 0x40 /* dma enabled,receiver src channel 2 */ +#define _RCR_DME_ENABLE3 0x60 /* dma enabled,receiver src channel 3 */ +#define _RCR_PME_HARD _RCR_PME_MASK /* RTS controlled by hardware */ +#define _RCR_PME_SOFT 0x00 /* RTS controlled by software */ + +/* serial port transmit command register */ + +#define _TxCR_ET_MASK 0x80 /* transmiter enable mask */ +#define _TxCR_DME_MASK 0x60 /* dma mode mask */ +#define _TxCR_TIE_MASK 0x10 /* empty interrupt enable mask */ +#define _TxCR_EIE_MASK 0x08 /* error interrupt enable mask */ +#define _TxCR_SPE_MASK 0x04 /* stop/pause mask */ +#define _TxCR_TB_MASK 0x02 /* transmit break mask */ + +#define _TxCR_ET_ENABLE _TxCR_ET_MASK /* transmiter enabled */ +#define _TxCR_DME_DISABLE 0x00 /* transmiter disabled, TBR intr disabled */ +#define _TxCR_DME_TBR 0x20 /* transmiter disabled, TBR intr enabled */ +#define _TxCR_DME_CHAN_2 0x40 /* dma enabled, destination chann 2 */ +#define _TxCR_DME_CHAN_3 0x60 /* dma enabled, destination chann 3 */ + +/* serial ctl reg 2 - added for Vesta */ + +#define _CTL2_EXTERN 0x80 /* */ +#define _CTL2_USEFIFO 0x40 /* */ +#define _CTL2_RESETRF 0x08 /* */ +#define _CTL2_RESETTF 0x04 /* */ + + + +#define SERIAL_SICC_NAME "ttySICC" +#define SERIAL_SICC_MAJOR 150 +#define SERIAL_SICC_MINOR 1 +#define SERIAL_SICC_NR 1 + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +/* + * Things needed by tty driver + */ +static struct tty_driver *siccnormal_driver; + +#if defined(CONFIG_SERIAL_SICC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +/* + * Things needed internally to this driver + */ + +/* + * tmp_buf is used as a temporary buffer by serial_write. We need to + * lock it in case the copy_from_user blocks while swapping in a page, + * and some other program tries to do a serial write at the same time. + * Since the lock will only come under contention when the system is + * swapping and available memory is low, it makes sense to share one + * buffer across all the serial ports, since it significantly saves + * memory if large numbers of serial ports are open. + */ +static u_char *tmp_buf; +static DECLARE_MUTEX(tmp_buf_sem); + +#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) + +/* number of characters left in xmit buffer before we ask for more */ +#define WAKEUP_CHARS 256 +#define SICC_ISR_PASS_LIMIT 256 + +#define EVT_WRITE_WAKEUP 0 + +struct SICC_icount { + __u32 cts; + __u32 dsr; + __u32 rng; + __u32 dcd; + __u32 rx; + __u32 tx; + __u32 frame; + __u32 overrun; + __u32 parity; + __u32 brk; + __u32 buf_overrun; +}; + +/* + * Static information about the port + */ +struct SICC_port { + unsigned int uart_base; + unsigned int uart_base_phys; + unsigned int irqrx; + unsigned int irqtx; + unsigned int uartclk; + unsigned int fifosize; + unsigned int tiocm_support; + void (*set_mctrl)(struct SICC_port *, u_int mctrl); +}; + +/* + * This is the state information which is persistent across opens + */ +struct SICC_state { + struct SICC_icount icount; + unsigned int line; + unsigned int close_delay; + unsigned int closing_wait; + unsigned int custom_divisor; + unsigned int flags; + int count; + struct SICC_info *info; + spinlock_t sicc_lock; +}; + +#define SICC_XMIT_SIZE 1024 +/* + * This is the state information which is only valid when the port is open. + */ +struct SICC_info { + struct SICC_port *port; + struct SICC_state *state; + struct tty_struct *tty; + unsigned char x_char; + unsigned char old_status; + unsigned char read_status_mask; + unsigned char ignore_status_mask; + struct circ_buf xmit; + unsigned int flags; +#ifdef SUPPORT_SYSRQ + unsigned long sysrq; +#endif + + unsigned int event; + unsigned int timeout; + unsigned int lcr_h; + unsigned int mctrl; + int blocked_open; + + struct tasklet_struct tlet; + + wait_queue_head_t open_wait; + wait_queue_head_t close_wait; + wait_queue_head_t delta_msr_wait; +}; + +#ifdef CONFIG_SERIAL_SICC_CONSOLE +static struct console siccuart_cons; +#endif +static void siccuart_change_speed(struct SICC_info *info, struct termios *old_termios); +static void siccuart_wait_until_sent(struct tty_struct *tty, int timeout); + + + +static void powerpcMtcic_cr(unsigned long value) +{ + mtdcr(DCRN_CICCR, value); +} + +static unsigned long powerpcMfcic_cr(void) +{ + return mfdcr(DCRN_CICCR); +} + +static unsigned long powerpcMfclkgpcr(void) +{ + return mfdcr(DCRN_SCCR); +} + +static void sicc_set_mctrl_null(struct SICC_port *port, u_int mctrl) +{ +} + +static struct SICC_port sicc_ports[SERIAL_SICC_NR] = { + { + .uart_base = 0, + .uart_base_phys = SICC0_IO_BASE, + .irqrx = SICC0_INTRX, + .irqtx = SICC0_INTTX, +// .uartclk = 0, + .fifosize = 1, + .set_mctrl = sicc_set_mctrl_null, + } +}; + +static struct SICC_state sicc_state[SERIAL_SICC_NR]; + +static void siccuart_enable_rx_interrupt(struct SICC_info *info) +{ + unsigned char cr; + + cr = readb(info->port->uart_base+BL_SICC_RCR); + cr &= ~_RCR_DME_MASK; + cr |= _RCR_DME_RXRDY; + writeb(cr, info->port->uart_base+BL_SICC_RCR); +} + +static void siccuart_disable_rx_interrupt(struct SICC_info *info) +{ + unsigned char cr; + + cr = readb(info->port->uart_base+BL_SICC_RCR); + cr &= ~_RCR_DME_MASK; + cr |= _RCR_DME_DISABLE; + writeb(cr, info->port->uart_base+BL_SICC_RCR); +} + + +static void siccuart_enable_tx_interrupt(struct SICC_info *info) +{ + unsigned char cr; + + cr = readb(info->port->uart_base+BL_SICC_TxCR); + cr &= ~_TxCR_DME_MASK; + cr |= _TxCR_DME_TBR; + writeb(cr, info->port->uart_base+BL_SICC_TxCR); +} + +static void siccuart_disable_tx_interrupt(struct SICC_info *info) +{ + unsigned char cr; + + cr = readb(info->port->uart_base+BL_SICC_TxCR); + cr &= ~_TxCR_DME_MASK; + cr |= _TxCR_DME_DISABLE; + writeb(cr, info->port->uart_base+BL_SICC_TxCR); +} + + +static void siccuart_stop(struct tty_struct *tty) +{ + struct SICC_info *info = tty->driver_data; + unsigned long flags; + + /* disable interrupts while stopping serial port interrupts */ + spin_lock_irqsave(&info->state->sicc_lock,flags); + siccuart_disable_tx_interrupt(info); + spin_unlock_irqrestore(&info->state->sicc_lock,flags); +} + +static void siccuart_start(struct tty_struct *tty) +{ + struct SICC_info *info = tty->driver_data; + unsigned long flags; + + /* disable interrupts while starting serial port interrupts */ + spin_lock_irqsave(&info->state->sicc_lock,flags); + if (info->xmit.head != info->xmit.tail + && info->xmit.buf) + siccuart_enable_tx_interrupt(info); + spin_unlock_irqrestore(&info->state->sicc_lock,flags); +} + + +/* + * This routine is used by the interrupt handler to schedule + * processing in the software interrupt portion of the driver. + */ +static void siccuart_event(struct SICC_info *info, int event) +{ + info->event |= 1 << event; + tasklet_schedule(&info->tlet); +} + +static void +siccuart_rx_chars(struct SICC_info *info, struct pt_regs *regs) +{ + struct tty_struct *tty = info->tty; + unsigned int status, ch, rsr, flg, ignored = 0; + struct SICC_icount *icount = &info->state->icount; + struct SICC_port *port = info->port; + + status = readb(port->uart_base+BL_SICC_LSR ); + while (status & _LSR_RBR_FULL) { + ch = readb(port->uart_base+BL_SICC_RBR); + + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + goto ignore_char; + icount->rx++; + + flg = TTY_NORMAL; + + /* + * Note that the error handling code is + * out of the main execution path + */ + rsr = readb(port->uart_base+BL_SICC_LSR); + if (rsr & _LSR_RX_ERR) + goto handle_error; +#ifdef SUPPORT_SYSRQ + if (info->sysrq) { + if (ch && time_before(jiffies, info->sysrq)) { + handle_sysrq(ch, regs, NULL); + info->sysrq = 0; + goto ignore_char; + } + info->sysrq = 0; + } +#endif + error_return: + *tty->flip.flag_buf_ptr++ = flg; + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + ignore_char: + status = readb(port->uart_base+BL_SICC_LSR ); + } +out: + tty_flip_buffer_push(tty); + return; + +handle_error: + if (rsr & _LSR_LB_BREAK) { + rsr &= ~(_LSR_FE_MASK | _LSR_PE_MASK); + icount->brk++; + +#ifdef SUPPORT_SYSRQ + if (info->state->line == siccuart_cons.index) { + if (!info->sysrq) { + info->sysrq = jiffies + HZ*5; + goto ignore_char; + } + } +#endif + } else if (rsr & _LSR_PE_MASK) + icount->parity++; + else if (rsr & _LSR_FE_MASK) + icount->frame++; + if (rsr & _LSR_OE_MASK) + icount->overrun++; + + if (rsr & info->ignore_status_mask) { + if (++ignored > 100) + goto out; + goto ignore_char; + } + rsr &= info->read_status_mask; + + if (rsr & _LSR_LB_BREAK) + flg = TTY_BREAK; + else if (rsr & _LSR_PE_MASK) + flg = TTY_PARITY; + else if (rsr & _LSR_FE_MASK) + flg = TTY_FRAME; + + if (rsr & _LSR_OE_MASK) { + /* + * CHECK: does overrun affect the current character? + * ASSUMPTION: it does not. + */ + *tty->flip.flag_buf_ptr++ = flg; + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + goto ignore_char; + ch = 0; + flg = TTY_OVERRUN; + } +#ifdef SUPPORT_SYSRQ + info->sysrq = 0; +#endif + goto error_return; +} + +static void siccuart_tx_chars(struct SICC_info *info) +{ + struct SICC_port *port = info->port; + int count; + unsigned char status; + + + if (info->x_char) { + writeb(info->x_char, port->uart_base+ BL_SICC_TBR); + info->state->icount.tx++; + info->x_char = 0; + return; + } + if (info->xmit.head == info->xmit.tail + || info->tty->stopped + || info->tty->hw_stopped) { + siccuart_disable_tx_interrupt(info); + writeb(status&(~_LSR_RBR_MASK),port->uart_base+BL_SICC_LSR); + return; + } + + count = port->fifosize; + do { + writeb(info->xmit.buf[info->xmit.tail], port->uart_base+ BL_SICC_TBR); + info->xmit.tail = (info->xmit.tail + 1) & (SICC_XMIT_SIZE - 1); + info->state->icount.tx++; + if (info->xmit.head == info->xmit.tail) + break; + } while (--count > 0); + + if (CIRC_CNT(info->xmit.head, + info->xmit.tail, + SICC_XMIT_SIZE) < WAKEUP_CHARS) + siccuart_event(info, EVT_WRITE_WAKEUP); + + if (info->xmit.head == info->xmit.tail) { + siccuart_disable_tx_interrupt(info); + } +} + + +static irqreturn_t siccuart_int_rx(int irq, void *dev_id, struct pt_regs *regs) +{ + struct SICC_info *info = dev_id; + siccuart_rx_chars(info, regs); + return IRQ_HANDLED; +} + + +static irqreturn_t siccuart_int_tx(int irq, void *dev_id, struct pt_regs *regs) +{ + struct SICC_info *info = dev_id; + siccuart_tx_chars(info); + return IRQ_HANDLED; +} + +static void siccuart_tasklet_action(unsigned long data) +{ + struct SICC_info *info = (struct SICC_info *)data; + struct tty_struct *tty; + + tty = info->tty; + if (!tty || !test_and_clear_bit(EVT_WRITE_WAKEUP, &info->event)) + return; + + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + wake_up_interruptible(&tty->write_wait); +} + +static int siccuart_startup(struct SICC_info *info) +{ + unsigned long flags; + unsigned long page; + int retval = 0; + + if (info->flags & ASYNC_INITIALIZED) { + return 0; + } + + page = get_zeroed_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + if (info->port->uart_base == 0) + info->port->uart_base = (int)ioremap(info->port->uart_base_phys, PAGE_SIZE); + if (info->port->uart_base == 0) { + free_page(page); + return -ENOMEM; + } + + /* lock access to info while doing setup */ + spin_lock_irqsave(&info->state->sicc_lock,flags); + + if (info->xmit.buf) + free_page(page); + else + info->xmit.buf = (unsigned char *) page; + + + info->mctrl = 0; + if (info->tty->termios->c_cflag & CBAUD) + info->mctrl = TIOCM_RTS | TIOCM_DTR; + info->port->set_mctrl(info->port, info->mctrl); + + /* + * initialise the old status of the modem signals + */ + info->old_status = 0; // UART_GET_FR(info->port) & AMBA_UARTFR_MODEM_ANY; + + + if (info->tty) + clear_bit(TTY_IO_ERROR, &info->tty->flags); + info->xmit.head = info->xmit.tail = 0; + + /* + * Set up the tty->alt_speed kludge + */ + if (info->tty) { + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->tty->alt_speed = 57600; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->tty->alt_speed = 115200; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->tty->alt_speed = 230400; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->tty->alt_speed = 460800; + } + + + writeb( 0x00, info->port->uart_base + BL_SICC_IrCR ); // disable IrDA + + + /* + * and set the speed of the serial port + */ + siccuart_change_speed(info, 0); + + // enable rx/tx ports + writeb(_RCR_ER_ENABLE /*| _RCR_PME_HARD*/, info->port->uart_base + BL_SICC_RCR); + writeb(_TxCR_ET_ENABLE , info->port->uart_base + BL_SICC_TxCR); + + readb(info->port->uart_base + BL_SICC_RBR); // clear rx port + + writeb(0xf8, info->port->uart_base + BL_SICC_LSR); /* reset bits 0-4 of LSR */ + + /* + * Finally, enable interrupts + */ + + /* + * Allocate the IRQ + */ + retval = request_irq(info->port->irqrx, siccuart_int_rx, 0, "SICC rx", info); + if (retval) { + if (capable(CAP_SYS_ADMIN)) { + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + retval = 0; + } + goto errout; + } + retval = request_irq(info->port->irqtx, siccuart_int_tx, 0, "SICC tx", info); + if (retval) { + if (capable(CAP_SYS_ADMIN)) { + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + retval = 0; + } + free_irq(info->port->irqrx, info); + goto errout; + } + + siccuart_enable_rx_interrupt(info); + + info->flags |= ASYNC_INITIALIZED; + spin_unlock_irqrestore(&info->state->sicc_lock,flags); + return 0; + + +errout: + spin_unlock_irqrestore(&info->state->sicc_lock,flags); + return retval; +} + +/* + * This routine will shutdown a serial port; interrupts are disabled, and + * DTR is dropped if the hangup on close termio flag is on. + */ +static void siccuart_shutdown(struct SICC_info *info) +{ + unsigned long flags; + + if (!(info->flags & ASYNC_INITIALIZED)) + return; + + /* lock while shutting down port */ + spin_lock_irqsave(&info->state->sicc_lock,flags); /* Disable interrupts */ + + /* + * clear delta_msr_wait queue to avoid mem leaks: we may free the irq + * here so the queue might never be woken up + */ + wake_up_interruptible(&info->delta_msr_wait); + + /* + * disable all interrupts, disable the port + */ + siccuart_disable_rx_interrupt(info); + siccuart_disable_tx_interrupt(info); + + /* + * Free the IRQ + */ + free_irq(info->port->irqtx, info); + free_irq(info->port->irqrx, info); + + if (info->xmit.buf) { + unsigned long pg = (unsigned long) info->xmit.buf; + info->xmit.buf = NULL; + free_page(pg); + } + + + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) + info->mctrl &= ~(TIOCM_DTR|TIOCM_RTS); + info->port->set_mctrl(info->port, info->mctrl); + + /* kill off our tasklet */ + tasklet_kill(&info->tlet); + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + + info->flags &= ~ASYNC_INITIALIZED; + + spin_unlock_irqrestore(&info->state->sicc_lock,flags); +} + + +static void siccuart_change_speed(struct SICC_info *info, struct termios *old_termios) +{ + unsigned int lcr_h, baud, quot, cflag, old_rcr, old_tcr, bits; + unsigned long flags; + + if (!info->tty || !info->tty->termios) + return; + + cflag = info->tty->termios->c_cflag; + + pr_debug("siccuart_set_cflag(0x%x) called\n", cflag); + /* byte size and parity */ + switch (cflag & CSIZE) { + case CS7: lcr_h = _LCR_PE_DISABLE | _LCR_DB_7_BITS | _LCR_SB_1_BIT; bits = 9; break; + default: lcr_h = _LCR_PE_DISABLE | _LCR_DB_8_BITS | _LCR_SB_1_BIT; bits = 10; break; // CS8 + } + if (cflag & CSTOPB) { + lcr_h |= _LCR_SB_2_BIT; + bits ++; + } + if (cflag & PARENB) { + lcr_h |= _LCR_PE_ENABLE; + bits++; + if (!(cflag & PARODD)) + lcr_h |= _LCR_PTY_ODD; + else + lcr_h |= _LCR_PTY_EVEN; + } + + do { + /* Determine divisor based on baud rate */ + baud = tty_get_baud_rate(info->tty); + if (!baud) + baud = 9600; + + + { + // here is ppc403SetBaud(com_port, baud); + unsigned long divisor, clockSource, temp; + + /* Ensure CICCR[7] is 0 to select Internal Baud Clock */ + powerpcMtcic_cr((unsigned long)(powerpcMfcic_cr() & 0xFEFFFFFF)); + + /* Determine Internal Baud Clock Frequency */ + /* powerpcMfclkgpcr() reads DCR 0x120 - the*/ + /* SCCR (Serial Clock Control Register) on Vesta */ + temp = powerpcMfclkgpcr(); + + if(temp & 0x00000080) { + clockSource = 324000000; + } + else { + clockSource = 216000000; + } + clockSource = clockSource/(unsigned long)((temp&0x00FC0000)>>18); + divisor = clockSource/(16*baud) - 1; + /* divisor has only 12 bits of resolution */ + if(divisor>0x00000FFF){ + divisor=0x00000FFF; + } + + quot = divisor; + } + + if (baud == 38400 && + ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) + quot = info->state->custom_divisor; + + if (!quot && old_termios) { + info->tty->termios->c_cflag &= ~CBAUD; + info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD); + old_termios = NULL; + } + } while (quot == 0 && old_termios); + + /* As a last resort, if the quotient is zero, default to 9600 bps */ + if (!quot) + quot = (info->port->uartclk / (16 * 9600)) - 1; + + info->timeout = info->port->fifosize * HZ * bits / baud; + info->timeout += HZ/50; /* Add .02 seconds of slop */ + + if (cflag & CRTSCTS) + info->flags |= ASYNC_CTS_FLOW; + else + info->flags &= ~ASYNC_CTS_FLOW; + if (cflag & CLOCAL) + info->flags &= ~ASYNC_CHECK_CD; + else + info->flags |= ASYNC_CHECK_CD; + + /* + * Set up parity check flag + */ +#define RELEVENT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) + + info->read_status_mask = _LSR_OE_MASK; + if (I_INPCK(info->tty)) + info->read_status_mask |= _LSR_FE_MASK | _LSR_PE_MASK; + if (I_BRKINT(info->tty) || I_PARMRK(info->tty)) + info->read_status_mask |= _LSR_LB_MASK; + + /* + * Characters to ignore + */ + info->ignore_status_mask = 0; + if (I_IGNPAR(info->tty)) + info->ignore_status_mask |= _LSR_FE_MASK | _LSR_PE_MASK; + if (I_IGNBRK(info->tty)) { + info->ignore_status_mask |= _LSR_LB_MASK; + /* + * If we're ignoring parity and break indicators, + * ignore overruns to (for real raw support). + */ + if (I_IGNPAR(info->tty)) + info->ignore_status_mask |= _LSR_OE_MASK; + } + + /* disable interrupts while reading and clearing registers */ + spin_lock_irqsave(&info->state->sicc_lock,flags); + + old_rcr = readb(info->port->uart_base + BL_SICC_RCR); + old_tcr = readb(info->port->uart_base + BL_SICC_TxCR); + + + writeb(0, info->port->uart_base + BL_SICC_RCR); + writeb(0, info->port->uart_base + BL_SICC_TxCR); + + /*RLBtrace (&ppc403Chan0, 0x2000000c, 0, 0);*/ + + + spin_unlock_irqrestore(&info->state->sicc_lock,flags); + + + /* Set baud rate */ + writeb((quot & 0x00000F00)>>8, info->port->uart_base + BL_SICC_BRDH ); + writeb( quot & 0x00000FF, info->port->uart_base + BL_SICC_BRDL ); + + /* Set CTL2 reg to use external clock (ExtClk) and enable FIFOs. */ + /* For now, do NOT use FIFOs since 403 UART did not have this */ + /* capability and this driver was inherited from 403UART. */ + writeb(_CTL2_EXTERN, info->port->uart_base + BL_SICC_CTL2); + + writeb(lcr_h, info->port->uart_base + BL_SICC_LCR); + + writeb(old_rcr, info->port->uart_base + BL_SICC_RCR); // restore rcr + writeb(old_tcr, info->port->uart_base + BL_SICC_TxCR); // restore txcr + +} + + +static void siccuart_put_char(struct tty_struct *tty, u_char ch) +{ + struct SICC_info *info = tty->driver_data; + unsigned long flags; + + if (!tty || !info->xmit.buf) + return; + + /* lock info->xmit while adding character to tx buffer */ + spin_lock_irqsave(&info->state->sicc_lock,flags); + if (CIRC_SPACE(info->xmit.head, info->xmit.tail, SICC_XMIT_SIZE) != 0) { + info->xmit.buf[info->xmit.head] = ch; + info->xmit.head = (info->xmit.head + 1) & (SICC_XMIT_SIZE - 1); + } + spin_unlock_irqrestore(&info->state->sicc_lock,flags); +} + +static void siccuart_flush_chars(struct tty_struct *tty) +{ + struct SICC_info *info = tty->driver_data; + unsigned long flags; + + if (info->xmit.head == info->xmit.tail + || tty->stopped + || tty->hw_stopped + || !info->xmit.buf) + return; + + /* disable interrupts while transmitting characters */ + spin_lock_irqsave(&info->state->sicc_lock,flags); + siccuart_enable_tx_interrupt(info); + spin_unlock_irqrestore(&info->state->sicc_lock,flags); +} + +static int siccuart_write(struct tty_struct *tty, + const u_char * buf, int count) +{ + struct SICC_info *info = tty->driver_data; + unsigned long flags; + int c, ret = 0; + + if (!tty || !info->xmit.buf || !tmp_buf) + return 0; + + /* lock info->xmit while removing characters from buffer */ + spin_lock_irqsave(&info->state->sicc_lock,flags); + while (1) { + c = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SICC_XMIT_SIZE); + if (count < c) + c = count; + if (c <= 0) + break; + memcpy(info->xmit.buf + info->xmit.head, buf, c); + info->xmit.head = (info->xmit.head + c) & + (SICC_XMIT_SIZE - 1); + buf += c; + count -= c; + ret += c; + } + if (info->xmit.head != info->xmit.tail + && !tty->stopped + && !tty->hw_stopped) + siccuart_enable_tx_interrupt(info); + spin_unlock_irqrestore(&info->state->sicc_lock,flags); + return ret; +} + +static int siccuart_write_room(struct tty_struct *tty) +{ + struct SICC_info *info = tty->driver_data; + + return CIRC_SPACE(info->xmit.head, info->xmit.tail, SICC_XMIT_SIZE); +} + +static int siccuart_chars_in_buffer(struct tty_struct *tty) +{ + struct SICC_info *info = tty->driver_data; + + return CIRC_CNT(info->xmit.head, info->xmit.tail, SICC_XMIT_SIZE); +} + +static void siccuart_flush_buffer(struct tty_struct *tty) +{ + struct SICC_info *info = tty->driver_data; + unsigned long flags; + + pr_debug("siccuart_flush_buffer(%d) called\n", tty->index); + /* lock info->xmit while zeroing buffer counts */ + spin_lock_irqsave(&info->state->sicc_lock,flags); + info->xmit.head = info->xmit.tail = 0; + spin_unlock_irqrestore(&info->state->sicc_lock,flags); + wake_up_interruptible(&tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); +} + +/* + * This function is used to send a high-priority XON/XOFF character to + * the device + */ +static void siccuart_send_xchar(struct tty_struct *tty, char ch) +{ + struct SICC_info *info = tty->driver_data; + + info->x_char = ch; + if (ch) + siccuart_enable_tx_interrupt(info); +} + +static void siccuart_throttle(struct tty_struct *tty) +{ + struct SICC_info *info = tty->driver_data; + unsigned long flags; + + if (I_IXOFF(tty)) + siccuart_send_xchar(tty, STOP_CHAR(tty)); + + if (tty->termios->c_cflag & CRTSCTS) { + /* disable interrupts while setting modem control lines */ + spin_lock_irqsave(&info->state->sicc_lock,flags); + info->mctrl &= ~TIOCM_RTS; + info->port->set_mctrl(info->port, info->mctrl); + spin_unlock_irqrestore(&info->state->sicc_lock,flags); + } +} + +static void siccuart_unthrottle(struct tty_struct *tty) +{ + struct SICC_info *info = (struct SICC_info *) tty->driver_data; + unsigned long flags; + + if (I_IXOFF(tty)) { + if (info->x_char) + info->x_char = 0; + else + siccuart_send_xchar(tty, START_CHAR(tty)); + } + + if (tty->termios->c_cflag & CRTSCTS) { + /* disable interrupts while setting modem control lines */ + spin_lock_irqsave(&info->state->sicc_lock,flags); + info->mctrl |= TIOCM_RTS; + info->port->set_mctrl(info->port, info->mctrl); + spin_unlock_irqrestore(&info->state->sicc_lock,flags); + } +} + +static int get_serial_info(struct SICC_info *info, struct serial_struct *retinfo) +{ + struct SICC_state *state = info->state; + struct SICC_port *port = info->port; + struct serial_struct tmp; + + memset(&tmp, 0, sizeof(tmp)); + tmp.type = 0; + tmp.line = state->line; + tmp.port = port->uart_base; + if (HIGH_BITS_OFFSET) + tmp.port_high = port->uart_base >> HIGH_BITS_OFFSET; + tmp.irq = port->irqrx; + tmp.flags = 0; + tmp.xmit_fifo_size = port->fifosize; + tmp.baud_base = port->uartclk / 16; + tmp.close_delay = state->close_delay; + tmp.closing_wait = state->closing_wait; + tmp.custom_divisor = state->custom_divisor; + + if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) + return -EFAULT; + return 0; +} + +static int set_serial_info(struct SICC_info *info, + struct serial_struct *newinfo) +{ + struct serial_struct new_serial; + struct SICC_state *state, old_state; + struct SICC_port *port; + unsigned long new_port; + unsigned int i, change_irq, change_port; + int retval = 0; + + if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) + return -EFAULT; + + state = info->state; + old_state = *state; + port = info->port; + + new_port = new_serial.port; + if (HIGH_BITS_OFFSET) + new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET; + + change_irq = new_serial.irq != port->irqrx; + change_port = new_port != port->uart_base; + + if (!capable(CAP_SYS_ADMIN)) { + if (change_irq || change_port || + (new_serial.baud_base != port->uartclk / 16) || + (new_serial.close_delay != state->close_delay) || + (new_serial.xmit_fifo_size != port->fifosize) || + ((new_serial.flags & ~ASYNC_USR_MASK) != + (state->flags & ~ASYNC_USR_MASK))) + return -EPERM; + state->flags = ((state->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + info->flags = ((info->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + state->custom_divisor = new_serial.custom_divisor; + goto check_and_exit; + } + + if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) || + (new_serial.baud_base < 9600)) + return -EINVAL; + + if (new_serial.type && change_port) { + for (i = 0; i < SERIAL_SICC_NR; i++) + if ((port != sicc_ports + i) && + sicc_ports[i].uart_base != new_port) + return -EADDRINUSE; + } + + if ((change_port || change_irq) && (state->count > 1)) + return -EBUSY; + + /* + * OK, past this point, all the error checking has been done. + * At this point, we start making changes..... + */ + port->uartclk = new_serial.baud_base * 16; + state->flags = ((state->flags & ~ASYNC_FLAGS) | + (new_serial.flags & ASYNC_FLAGS)); + info->flags = ((state->flags & ~ASYNC_INTERNAL_FLAGS) | + (info->flags & ASYNC_INTERNAL_FLAGS)); + state->custom_divisor = new_serial.custom_divisor; + state->close_delay = new_serial.close_delay * HZ / 100; + state->closing_wait = new_serial.closing_wait * HZ / 100; + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + port->fifosize = new_serial.xmit_fifo_size; + + if (change_port || change_irq) { + /* + * We need to shutdown the serial port at the old + * port/irq combination. + */ + siccuart_shutdown(info); + port->irqrx = new_serial.irq; + port->uart_base = new_port; + } + +check_and_exit: + if (!port->uart_base) + return 0; + if (info->flags & ASYNC_INITIALIZED) { + if ((old_state.flags & ASYNC_SPD_MASK) != + (state->flags & ASYNC_SPD_MASK) || + (old_state.custom_divisor != state->custom_divisor)) { + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->tty->alt_speed = 57600; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->tty->alt_speed = 115200; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->tty->alt_speed = 230400; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->tty->alt_speed = 460800; + siccuart_change_speed(info, NULL); + } + } else + retval = siccuart_startup(info); + return retval; +} + + +/* + * get_lsr_info - get line status register info + */ +static int get_lsr_info(struct SICC_info *info, unsigned int *value) +{ + unsigned int result, status; + unsigned long flags; + + /* disable interrupts while reading status from port */ + spin_lock_irqsave(&info->state->sicc_lock,flags); + status = readb(info->port->uart_base + BL_SICC_LSR); + spin_unlock_irqrestore(&info->state->sicc_lock,flags); + result = status & _LSR_TSR_EMPTY ? TIOCSER_TEMT : 0; + + /* + * If we're about to load something into the transmit + * register, we'll pretend the transmitter isn't empty to + * avoid a race condition (depending on when the transmit + * interrupt happens). + */ + if (info->x_char || + ((CIRC_CNT(info->xmit.head, info->xmit.tail, + SICC_XMIT_SIZE) > 0) && + !info->tty->stopped && !info->tty->hw_stopped)) + result &= TIOCSER_TEMT; + + return put_user(result, value); +} + +static int get_modem_info(struct SICC_info *info, unsigned int *value) +{ + unsigned int result = info->mctrl; + + return put_user(result, value); +} + +static int set_modem_info(struct SICC_info *info, unsigned int cmd, + unsigned int *value) +{ + unsigned int arg, old; + unsigned long flags; + + if (get_user(arg, value)) + return -EFAULT; + + old = info->mctrl; + switch (cmd) { + case TIOCMBIS: + info->mctrl |= arg; + break; + + case TIOCMBIC: + info->mctrl &= ~arg; + break; + + case TIOCMSET: + info->mctrl = arg; + break; + + default: + return -EINVAL; + } + /* disable interrupts while setting modem control lines */ + spin_lock_irqsave(&info->state->sicc_lock,flags); + if (old != info->mctrl) + info->port->set_mctrl(info->port, info->mctrl); + spin_unlock_irqrestore(&info->state->sicc_lock,flags); + return 0; +} + +static void siccuart_break_ctl(struct tty_struct *tty, int break_state) +{ + struct SICC_info *info = tty->driver_data; + unsigned long flags; + unsigned int lcr_h; + + + /* disable interrupts while setting break state */ + spin_lock_irqsave(&info->state->sicc_lock,flags); + lcr_h = readb(info->port + BL_SICC_LSR); + if (break_state == -1) + lcr_h |= _LSR_LB_MASK; + else + lcr_h &= ~_LSR_LB_MASK; + writeb(lcr_h, info->port + BL_SICC_LSRS); + spin_unlock_irqrestore(&info->state->sicc_lock,flags); +} + +static int siccuart_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct SICC_info *info = tty->driver_data; + struct SICC_icount cnow; + struct serial_icounter_struct icount; + unsigned long flags; + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && + (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && + (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } + + switch (cmd) { + case TIOCMGET: + return get_modem_info(info, (unsigned int *)arg); + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + return set_modem_info(info, cmd, (unsigned int *)arg); + case TIOCGSERIAL: + return get_serial_info(info, + (struct serial_struct *)arg); + case TIOCSSERIAL: + return set_serial_info(info, + (struct serial_struct *)arg); + case TIOCSERGETLSR: /* Get line status register */ + return get_lsr_info(info, (unsigned int *)arg); + /* + * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change + * - mask passed in arg for lines of interest + * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) + * Caller should use TIOCGICOUNT to see which one it was + */ + case TIOCMIWAIT: + return 0; + /* + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + * Return: write counters to the user passed counter struct + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ + case TIOCGICOUNT: + /* disable interrupts while getting interrupt count */ + spin_lock_irqsave(&info->state->sicc_lock,flags); + cnow = info->state->icount; + spin_unlock_irqrestore(&info->state->sicc_lock,flags); + icount.cts = cnow.cts; + icount.dsr = cnow.dsr; + icount.rng = cnow.rng; + icount.dcd = cnow.dcd; + icount.rx = cnow.rx; + icount.tx = cnow.tx; + icount.frame = cnow.frame; + icount.overrun = cnow.overrun; + icount.parity = cnow.parity; + icount.brk = cnow.brk; + icount.buf_overrun = cnow.buf_overrun; + + return copy_to_user((void *)arg, &icount, sizeof(icount)) + ? -EFAULT : 0; + + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static void siccuart_set_termios(struct tty_struct *tty, struct termios *old_termios) +{ + struct SICC_info *info = tty->driver_data; + unsigned long flags; + unsigned int cflag = tty->termios->c_cflag; + + if ((cflag ^ old_termios->c_cflag) == 0 && + RELEVENT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0) + return; + + siccuart_change_speed(info, old_termios); + + /* Handle transition to B0 status */ + if ((old_termios->c_cflag & CBAUD) && + !(cflag & CBAUD)) { + /* disable interrupts while setting break state */ + spin_lock_irqsave(&info->state->sicc_lock,flags); + info->mctrl &= ~(TIOCM_RTS | TIOCM_DTR); + info->port->set_mctrl(info->port, info->mctrl); + spin_unlock_irqrestore(&info->state->sicc_lock,flags); + } + + /* Handle transition away from B0 status */ + if (!(old_termios->c_cflag & CBAUD) && + (cflag & CBAUD)) { + /* disable interrupts while setting break state */ + spin_lock_irqsave(&info->state->sicc_lock,flags); + info->mctrl |= TIOCM_DTR; + if (!(cflag & CRTSCTS) || + !test_bit(TTY_THROTTLED, &tty->flags)) + info->mctrl |= TIOCM_RTS; + info->port->set_mctrl(info->port, info->mctrl); + spin_unlock_irqrestore(&info->state->sicc_lock,flags); + } + + /* Handle turning off CRTSCTS */ + if ((old_termios->c_cflag & CRTSCTS) && + !(cflag & CRTSCTS)) { + tty->hw_stopped = 0; + siccuart_start(tty); + } + +#if 0 + /* + * No need to wake up processes in open wait, since they + * sample the CLOCAL flag once, and don't recheck it. + * XXX It's not clear whether the current behavior is correct + * or not. Hence, this may change..... + */ + if (!(old_termios->c_cflag & CLOCAL) && + (tty->termios->c_cflag & CLOCAL)) + wake_up_interruptible(&info->open_wait); +#endif +} + +static void siccuart_close(struct tty_struct *tty, struct file *filp) +{ + struct SICC_info *info = tty->driver_data; + struct SICC_state *state; + unsigned long flags; + + if (!info) + return; + + state = info->state; + + //pr_debug("siccuart_close() called\n"); + + /* lock tty->driver_data while closing port */ + spin_lock_irqsave(&info->state->sicc_lock,flags); + + if (tty_hung_up_p(filp)) { + goto quick_close; + } + + if ((tty->count == 1) && (state->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. state->count should always + * be one in these conditions. If it's greater than + * one, we've got real problems, since it means the + * serial port won't be shutdown. + */ + printk("siccuart_close: bad serial port count; tty->count is 1, state->count is %d\n", state->count); + state->count = 1; + } + if (--state->count < 0) { + printk("rs_close: bad serial port count for %s: %d\n", tty->name, state->count); + state->count = 0; + } + if (state->count) { + goto quick_close; + } + info->flags |= ASYNC_CLOSING; + spin_unlock_irqrestore(&info->state->sicc_lock,flags); + /* + * Now we wait for the transmit buffer to clear; and we notify + * the line discipline to only process XON/XOFF characters. + */ + tty->closing = 1; + if (info->state->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->state->closing_wait); + /* + * At this point, we stop accepting input. To do this, we + * disable the receive line status interrupts. + */ + if (info->flags & ASYNC_INITIALIZED) { + siccuart_disable_rx_interrupt(info); + /* + * Before we drop DTR, make sure the UART transmitter + * has completely drained; this is especially + * important if there is a transmit FIFO! + */ + siccuart_wait_until_sent(tty, info->timeout); + } + siccuart_shutdown(info); + if (tty->driver->flush_buffer) + tty->driver->flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + tty->closing = 0; + info->event = 0; + info->tty = NULL; + if (info->blocked_open) { + if (info->state->close_delay) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(info->state->close_delay); + } + wake_up_interruptible(&info->open_wait); + } + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); + wake_up_interruptible(&info->close_wait); + return; + +quick_close: + spin_unlock_irqrestore(&info->state->sicc_lock,flags); + return; +} + +static void siccuart_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct SICC_info *info = (struct SICC_info *) tty->driver_data; + unsigned long char_time, expire; + + if (info->port->fifosize == 0) + return; + + /* + * Set the check interval to be 1/5 of the estimated time to + * send a single character, and make it at least 1. The check + * interval should also be less than the timeout. + * + * Note: we have to use pretty tight timings here to satisfy + * the NIST-PCTS. + */ + char_time = (info->timeout - HZ/50) / info->port->fifosize; + char_time = char_time / 5; + if (char_time == 0) + char_time = 1; + + // Crazy!! sometimes the input arg 'timeout' can be negtive numbers :-( + if (timeout >= 0 && timeout < char_time) + char_time = timeout; + /* + * If the transmitter hasn't cleared in twice the approximate + * amount of time to send the entire FIFO, it probably won't + * ever clear. This assumes the UART isn't doing flow + * control, which is currently the case. Hence, if it ever + * takes longer than info->timeout, this is probably due to a + * UART bug of some kind. So, we clamp the timeout parameter at + * 2*info->timeout. + */ + if (!timeout || timeout > 2 * info->timeout) + timeout = 2 * info->timeout; + + expire = jiffies + timeout; + pr_debug("siccuart_wait_until_sent(%d), jiff=%lu, expire=%lu char_time=%lu...\n", + tty->index, jiffies, + expire, char_time); + while ((readb(info->port->uart_base + BL_SICC_LSR) & _LSR_TX_ALL) != _LSR_TX_ALL) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(char_time); + if (signal_pending(current)) + break; + if (timeout && time_after(jiffies, expire)) + break; + } + set_current_state(TASK_RUNNING); +} + +static void siccuart_hangup(struct tty_struct *tty) +{ + struct SICC_info *info = tty->driver_data; + struct SICC_state *state = info->state; + + siccuart_flush_buffer(tty); + if (info->flags & ASYNC_CLOSING) + return; + siccuart_shutdown(info); + info->event = 0; + state->count = 0; + info->flags &= ~ASYNC_NORMAL_ACTIVE; + info->tty = NULL; + wake_up_interruptible(&info->open_wait); +} + +static int block_til_ready(struct tty_struct *tty, struct file *filp, + struct SICC_info *info) +{ + DECLARE_WAITQUEUE(wait, current); + struct SICC_state *state = info->state; + unsigned long flags; + int do_clocal = 0, extra_count = 0, retval; + + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (tty_hung_up_p(filp) || + (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); + return (info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS; + } + + /* + * If non-blocking mode is set, or the port is not enabled, + * then make the check up front and then exit. + */ + if ((filp->f_flags & O_NONBLOCK) || + (tty->flags & (1 << TTY_IO_ERROR))) { + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + + if (tty->termios->c_cflag & CLOCAL) + do_clocal = 1; + + /* + * Block waiting for the carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, state->count is dropped by one, so that + * rs_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + retval = 0; + add_wait_queue(&info->open_wait, &wait); + /* lock while decrementing state->count */ + spin_lock_irqsave(&info->state->sicc_lock,flags); + if (!tty_hung_up_p(filp)) { + extra_count = 1; + state->count--; + } + spin_unlock_irqrestore(&info->state->sicc_lock,flags); + info->blocked_open++; + while (1) { + /* disable interrupts while setting modem control lines */ + spin_lock_irqsave(&info->state->sicc_lock,flags); + if (tty->termios->c_cflag & CBAUD) { + info->mctrl = TIOCM_DTR | TIOCM_RTS; + info->port->set_mctrl(info->port, info->mctrl); + } + spin_unlock_irqrestore(&info->state->sicc_lock,flags); + set_current_state(TASK_INTERRUPTIBLE); + if (tty_hung_up_p(filp) || + !(info->flags & ASYNC_INITIALIZED)) { + if (info->flags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; + break; + } + if (!(info->flags & ASYNC_CLOSING) && + (do_clocal /*|| (UART_GET_FR(info->port) & SICC_UARTFR_DCD)*/)) + break; + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + schedule(); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&info->open_wait, &wait); + if (extra_count) + state->count++; + info->blocked_open--; + if (retval) + return retval; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} + +static struct SICC_info *siccuart_get(int line) +{ + struct SICC_info *info; + struct SICC_state *state = sicc_state + line; + + state->count++; + if (state->info) + return state->info; + info = kmalloc(sizeof(struct SICC_info), GFP_KERNEL); + if (info) { + memset(info, 0, sizeof(struct SICC_info)); + init_waitqueue_head(&info->open_wait); + init_waitqueue_head(&info->close_wait); + init_waitqueue_head(&info->delta_msr_wait); + info->flags = state->flags; + info->state = state; + info->port = sicc_ports + line; + tasklet_init(&info->tlet, siccuart_tasklet_action, + (unsigned long)info); + } + if (state->info) { + kfree(info); + return state->info; + } + state->info = info; + return info; +} + +static int siccuart_open(struct tty_struct *tty, struct file *filp) +{ + struct SICC_info *info; + int retval, line = tty->index; + + + // is this a line that we've got? + if (line >= SERIAL_SICC_NR) { + return -ENODEV; + } + + info = siccuart_get(line); + if (!info) + return -ENOMEM; + + tty->driver_data = info; + info->tty = tty; + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + + /* + * Make sure we have the temporary buffer allocated + */ + if (!tmp_buf) { + unsigned long page = get_zeroed_page(GFP_KERNEL); + if (tmp_buf) + free_page(page); + else if (!page) { + return -ENOMEM; + } + tmp_buf = (u_char *)page; + } + + /* + * If the port is in the middle of closing, bail out now. + */ + if (tty_hung_up_p(filp) || + (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); + return -EAGAIN; + } + + /* + * Start up the serial port + */ + retval = siccuart_startup(info); + if (retval) { + return retval; + } + + retval = block_til_ready(tty, filp, info); + if (retval) { + return retval; + } + +#ifdef CONFIG_SERIAL_SICC_CONSOLE + if (siccuart_cons.cflag && siccuart_cons.index == line) { + tty->termios->c_cflag = siccuart_cons.cflag; + siccuart_cons.cflag = 0; + siccuart_change_speed(info, NULL); + } +#endif + return 0; +} + +static struct tty_operations sicc_ops = { + .open = siccuart_open, + .close = siccuart_close, + .write = siccuart_write, + .put_char = siccuart_put_char, + .flush_chars = siccuart_flush_chars, + .write_room = siccuart_write_room, + .chars_in_buffer = siccuart_chars_in_buffer, + .flush_buffer = siccuart_flush_buffer, + .ioctl = siccuart_ioctl, + .throttle = siccuart_throttle, + .unthrottle = siccuart_unthrottle, + .send_xchar = siccuart_send_xchar, + .set_termios = siccuart_set_termios, + .stop = siccuart_stop, + .start = siccuart_start, + .hangup = siccuart_hangup, + .break_ctl = siccuart_break_ctl, + .wait_until_sent = siccuart_wait_until_sent, +}; + +int __init siccuart_init(void) +{ + int i; + siccnormal_driver = alloc_tty_driver(SERIAL_SICC_NR); + if (!siccnormal_driver) + return -ENOMEM; + printk("IBM Vesta SICC serial port driver V 0.1 by Yudong Yang and Yi Ge / IBM CRL .\n"); + siccnormal_driver->driver_name = "serial_sicc"; + siccnormal_driver->owner = THIS_MODULE; + siccnormal_driver->name = SERIAL_SICC_NAME; + siccnormal_driver->major = SERIAL_SICC_MAJOR; + siccnormal_driver->minor_start = SERIAL_SICC_MINOR; + siccnormal_driver->type = TTY_DRIVER_TYPE_SERIAL; + siccnormal_driver->subtype = SERIAL_TYPE_NORMAL; + siccnormal_driver->init_termios = tty_std_termios; + siccnormal_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + siccnormal_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; + tty_set_operations(siccnormal_driver, &sicc_ops); + + if (tty_register_driver(siccnormal_driver)) + panic("Couldn't register SICC serial driver\n"); + + for (i = 0; i < SERIAL_SICC_NR; i++) { + struct SICC_state *state = sicc_state + i; + state->line = i; + state->close_delay = 5 * HZ / 10; + state->closing_wait = 30 * HZ; + spin_lock_init(&state->sicc_lock); + } + + + return 0; +} + +__initcall(siccuart_init); + +#ifdef CONFIG_SERIAL_SICC_CONSOLE +/************** console driver *****************/ + +/* + * This code is currently never used; console->read is never called. + * Therefore, although we have an implementation, we don't use it. + * FIXME: the "const char *s" should be fixed to "char *s" some day. + * (when the definition in include/linux/console.h is also fixed) + */ +#ifdef used_and_not_const_char_pointer +static int siccuart_console_read(struct console *co, const char *s, u_int count) +{ + struct SICC_port *port = &sicc_ports[co->index]; + unsigned int status; + char *w; + int c; + + pr_debug("siccuart_console_read() called\n"); + + c = 0; + w = s; + while (c < count) { + if(readb(port->uart_base + BL_SICC_LSR) & _LSR_RBR_FULL) { + *w++ = readb(port->uart_base + BL_SICC_RBR); + c++; + } else { + // nothing more to get, return + return c; + } + } + // return the count + return c; +} +#endif + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + * + * The console_lock must be held when we get here. + */ +static void siccuart_console_write(struct console *co, const char *s, u_int count) +{ + struct SICC_port *port = &sicc_ports[co->index]; + unsigned int old_cr; + int i; + + /* + * First save the CR then disable the interrupts + */ + old_cr = readb(port->uart_base + BL_SICC_TxCR); + writeb(old_cr & ~_TxCR_DME_MASK, port->uart_base + BL_SICC_TxCR); + + /* + * Now, do each character + */ + for (i = 0; i < count; i++) { + while ((readb(port->uart_base + BL_SICC_LSR)&_LSR_TX_ALL) != _LSR_TX_ALL); + writeb(s[i], port->uart_base + BL_SICC_TBR); + if (s[i] == '\n') { + while ((readb(port->uart_base + BL_SICC_LSR)&_LSR_TX_ALL) != _LSR_TX_ALL); + writeb('\r', port->uart_base + BL_SICC_TBR); + } + } + + /* + * Finally, wait for transmitter to become empty + * and restore the TCR + */ + while ((readb(port->uart_base + BL_SICC_LSR)&_LSR_TX_ALL) != _LSR_TX_ALL); + writeb(old_cr, port->uart_base + BL_SICC_TxCR); +} + +/* + * Receive character from the serial port + */ +static int siccuart_console_wait_key(struct console *co) +{ + struct SICC_port *port = &sicc_ports[co->index]; + int c; + + while(!(readb(port->uart_base + BL_SICC_LSR) & _LSR_RBR_FULL)); + c = readb(port->uart_base + BL_SICC_RBR); + return c; +} + +static struct tty_driver *siccuart_console_device(struct console *c, int *index) +{ + *index = c->index; + return siccnormal_driver; +} + +static int __init siccuart_console_setup(struct console *co, char *options) +{ + struct SICC_port *port; + int baud = 9600; + int bits = 8; + int parity = 'n'; + u_int cflag = CREAD | HUPCL | CLOCAL; + u_int lcr_h, quot; + + + if (co->index >= SERIAL_SICC_NR) + co->index = 0; + + port = &sicc_ports[co->index]; + + if (port->uart_base == 0) + port->uart_base = (int)ioremap(port->uart_base_phys, PAGE_SIZE); + + if (options) { + char *s = options; + baud = simple_strtoul(s, NULL, 10); + while (*s >= '0' && *s <= '9') + s++; + if (*s) parity = *s++; + if (*s) bits = *s - '0'; + } + + /* + * Now construct a cflag setting. + */ + switch (baud) { + case 1200: cflag |= B1200; break; + case 2400: cflag |= B2400; break; + case 4800: cflag |= B4800; break; + default: cflag |= B9600; baud = 9600; break; + case 19200: cflag |= B19200; break; + case 38400: cflag |= B38400; break; + case 57600: cflag |= B57600; break; + case 115200: cflag |= B115200; break; + } + switch (bits) { + case 7: cflag |= CS7; lcr_h = _LCR_PE_DISABLE | _LCR_DB_7_BITS | _LCR_SB_1_BIT; break; + default: cflag |= CS8; lcr_h = _LCR_PE_DISABLE | _LCR_DB_8_BITS | _LCR_SB_1_BIT; break; + } + switch (parity) { + case 'o': + case 'O': cflag |= PARODD; lcr_h |= _LCR_PTY_ODD; break; + case 'e': + case 'E': cflag |= PARENB; lcr_h |= _LCR_PE_ENABLE | _LCR_PTY_ODD; break; + } + + co->cflag = cflag; + + + { + // a copy of is inserted here ppc403SetBaud(com_port, (int)9600); + unsigned long divisor, clockSource, temp; + unsigned int rate = baud; + + /* Ensure CICCR[7] is 0 to select Internal Baud Clock */ + powerpcMtcic_cr((unsigned long)(powerpcMfcic_cr() & 0xFEFFFFFF)); + + /* Determine Internal Baud Clock Frequency */ + /* powerpcMfclkgpcr() reads DCR 0x120 - the*/ + /* SCCR (Serial Clock Control Register) on Vesta */ + temp = powerpcMfclkgpcr(); + + if(temp & 0x00000080) { + clockSource = 324000000; + } + else { + clockSource = 216000000; + } + clockSource = clockSource/(unsigned long)((temp&0x00FC0000)>>18); + divisor = clockSource/(16*rate) - 1; + /* divisor has only 12 bits of resolution */ + if(divisor>0x00000FFF){ + divisor=0x00000FFF; + } + + quot = divisor; + } + + writeb((quot & 0x00000F00)>>8, port->uart_base + BL_SICC_BRDH ); + writeb( quot & 0x00000FF, port->uart_base + BL_SICC_BRDL ); + + /* Set CTL2 reg to use external clock (ExtClk) and enable FIFOs. */ + /* For now, do NOT use FIFOs since 403 UART did not have this */ + /* capability and this driver was inherited from 403UART. */ + writeb(_CTL2_EXTERN, port->uart_base + BL_SICC_CTL2); + + writeb(lcr_h, port->uart_base + BL_SICC_LCR); + writeb(_RCR_ER_ENABLE | _RCR_PME_HARD, port->uart_base + BL_SICC_RCR); + writeb( _TxCR_ET_ENABLE , port->uart_base + BL_SICC_TxCR); + + // writeb(, info->port->uart_base + BL_SICC_RCR ); + /* + * Transmitter Command Register: Transmitter enabled & DMA + TBR interrupt + * + Transmitter Empty interrupt + Transmitter error interrupt disabled & + * Stop mode when CTS active enabled & Transmit Break + Pattern Generation + * mode disabled. + */ + + writeb( 0x00, port->uart_base + BL_SICC_IrCR ); // disable IrDA + + readb(port->uart_base + BL_SICC_RBR); + + writeb(0xf8, port->uart_base + BL_SICC_LSR); /* reset bits 0-4 of LSR */ + + /* we will enable the port as we need it */ + + return 0; +} + +static struct console siccuart_cons = +{ + .name = SERIAL_SICC_NAME, + .write = siccuart_console_write, +#ifdef used_and_not_const_char_pointer + .read = siccuart_console_read, +#endif + .device = siccuart_console_device, + .wait_key = siccuart_console_wait_key, + .setup = siccuart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + +void __init sicc_console_init(void) +{ + register_console(&siccuart_cons); +} + +#endif /* CONFIG_SERIAL_SICC_CONSOLE */ diff --git a/arch/ppc/8260_io/Kconfig b/arch/ppc/8260_io/Kconfig new file mode 100644 index 00000000000..ea9651e2dd6 --- /dev/null +++ b/arch/ppc/8260_io/Kconfig @@ -0,0 +1,65 @@ +# +# CPM2 Communication options +# + +menu "CPM2 Options" + depends on CPM2 + +config SCC_ENET + bool "CPM SCC Ethernet" + depends on NET_ETHERNET + +# +# CONFIG_FEC_ENET is only used to get netdevices to call our init +# function. Any combination of FCC1,2,3 are supported. +# +config FEC_ENET + bool "FCC Ethernet" + depends on NET_ETHERNET + +config FCC1_ENET + bool "Ethernet on FCC1" + depends on FEC_ENET + help + Use CPM2 fast Ethernet controller 1 to drive Ethernet (default). + +config FCC2_ENET + bool "Ethernet on FCC2" + depends on FEC_ENET + help + Use CPM2 fast Ethernet controller 2 to drive Ethernet. + +config FCC3_ENET + bool "Ethernet on FCC3" + depends on FEC_ENET + help + Use CPM2 fast Ethernet controller 3 to drive Ethernet. + +config USE_MDIO + bool "Use MDIO for PHY configuration" + depends on FEC_ENET + +choice + prompt "Type of PHY" + depends on 8260 && USE_MDIO + default FCC_GENERIC_PHY + +config FCC_LXT970 + bool "LXT970" + +config FCC_LXT971 + bool "LXT971" + +config FCC_QS6612 + bool "QS6612" + +config FCC_DM9131 + bool "DM9131" + +config FCC_DM9161 + bool "DM9161" + +config FCC_GENERIC_PHY + bool "Generic" +endchoice +endmenu diff --git a/arch/ppc/8260_io/Makefile b/arch/ppc/8260_io/Makefile new file mode 100644 index 00000000000..971f292c5d4 --- /dev/null +++ b/arch/ppc/8260_io/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for the linux ppc-specific parts of comm processor (v2) +# + +obj-$(CONFIG_FEC_ENET) += fcc_enet.o +obj-$(CONFIG_SCC_ENET) += enet.o diff --git a/arch/ppc/8260_io/enet.c b/arch/ppc/8260_io/enet.c new file mode 100644 index 00000000000..ac6d55fe223 --- /dev/null +++ b/arch/ppc/8260_io/enet.c @@ -0,0 +1,867 @@ +/* + * Ethernet driver for Motorola MPC8260. + * Copyright (c) 1999 Dan Malek (dmalek@jlc.net) + * Copyright (c) 2000 MontaVista Software Inc. (source@mvista.com) + * 2.3.99 Updates + * + * I copied this from the 8xx CPM Ethernet driver, so follow the + * credits back through that. + * + * This version of the driver is somewhat selectable for the different + * processor/board combinations. It works for the boards I know about + * now, and should be easily modified to include others. Some of the + * configuration information is contained in and the + * remainder is here. + * + * Buffer descriptors are kept in the CPM dual port RAM, and the frame + * buffers are in the host memory. + * + * Right now, I am very watseful with the buffers. I allocate memory + * pages and then divide them into 2K frame buffers. This way I know I + * have buffers large enough to hold one frame within one buffer descriptor. + * Once I get this working, I will use 64 or 128 byte CPM buffers, which + * will be much more memory efficient and will easily handle lots of + * small packets. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* + * Theory of Operation + * + * The MPC8260 CPM performs the Ethernet processing on an SCC. It can use + * an aribtrary number of buffers on byte boundaries, but must have at + * least two receive buffers to prevent constant overrun conditions. + * + * The buffer descriptors are allocated from the CPM dual port memory + * with the data buffers allocated from host memory, just like all other + * serial communication protocols. The host memory buffers are allocated + * from the free page pool, and then divided into smaller receive and + * transmit buffers. The size of the buffers should be a power of two, + * since that nicely divides the page. This creates a ring buffer + * structure similar to the LANCE and other controllers. + * + * Like the LANCE driver: + * The driver runs as two independent, single-threaded flows of control. One + * is the send-packet routine, which enforces single-threaded use by the + * cep->tx_busy flag. The other thread is the interrupt handler, which is + * single threaded by the hardware and other software. + */ + +/* The transmitter timeout + */ +#define TX_TIMEOUT (2*HZ) + +/* The number of Tx and Rx buffers. These are allocated from the page + * pool. The code may assume these are power of two, so it is best + * to keep them that size. + * We don't need to allocate pages for the transmitter. We just use + * the skbuffer directly. + */ +#define CPM_ENET_RX_PAGES 4 +#define CPM_ENET_RX_FRSIZE 2048 +#define CPM_ENET_RX_FRPPG (PAGE_SIZE / CPM_ENET_RX_FRSIZE) +#define RX_RING_SIZE (CPM_ENET_RX_FRPPG * CPM_ENET_RX_PAGES) +#define TX_RING_SIZE 8 /* Must be power of two */ +#define TX_RING_MOD_MASK 7 /* for this to work */ + +/* The CPM stores dest/src/type, data, and checksum for receive packets. + */ +#define PKT_MAXBUF_SIZE 1518 +#define PKT_MINBUF_SIZE 64 +#define PKT_MAXBLR_SIZE 1520 + +/* The CPM buffer descriptors track the ring buffers. The rx_bd_base and + * tx_bd_base always point to the base of the buffer descriptors. The + * cur_rx and cur_tx point to the currently available buffer. + * The dirty_tx tracks the current buffer that is being sent by the + * controller. The cur_tx and dirty_tx are equal under both completely + * empty and completely full conditions. The empty/ready indicator in + * the buffer descriptor determines the actual condition. + */ +struct scc_enet_private { + /* The saved address of a sent-in-place packet/buffer, for skfree(). */ + struct sk_buff* tx_skbuff[TX_RING_SIZE]; + ushort skb_cur; + ushort skb_dirty; + + /* CPM dual port RAM relative addresses. + */ + cbd_t *rx_bd_base; /* Address of Rx and Tx buffers. */ + cbd_t *tx_bd_base; + cbd_t *cur_rx, *cur_tx; /* The next free ring entry */ + cbd_t *dirty_tx; /* The ring entries to be free()ed. */ + scc_t *sccp; + struct net_device_stats stats; + uint tx_full; + spinlock_t lock; +}; + +static int scc_enet_open(struct net_device *dev); +static int scc_enet_start_xmit(struct sk_buff *skb, struct net_device *dev); +static int scc_enet_rx(struct net_device *dev); +static irqreturn_t scc_enet_interrupt(int irq, void *dev_id, struct pt_regs *); +static int scc_enet_close(struct net_device *dev); +static struct net_device_stats *scc_enet_get_stats(struct net_device *dev); +static void set_multicast_list(struct net_device *dev); + +/* These will be configurable for the SCC choice. +*/ +#define CPM_ENET_BLOCK CPM_CR_SCC1_SBLOCK +#define CPM_ENET_PAGE CPM_CR_SCC1_PAGE +#define PROFF_ENET PROFF_SCC1 +#define SCC_ENET 0 +#define SIU_INT_ENET SIU_INT_SCC1 + +/* These are both board and SCC dependent.... +*/ +#define PD_ENET_RXD ((uint)0x00000001) +#define PD_ENET_TXD ((uint)0x00000002) +#define PD_ENET_TENA ((uint)0x00000004) +#define PC_ENET_RENA ((uint)0x00020000) +#define PC_ENET_CLSN ((uint)0x00000004) +#define PC_ENET_TXCLK ((uint)0x00000800) +#define PC_ENET_RXCLK ((uint)0x00000400) +#define CMX_CLK_ROUTE ((uint)0x25000000) +#define CMX_CLK_MASK ((uint)0xff000000) + +/* Specific to a board. +*/ +#define PC_EST8260_ENET_LOOPBACK ((uint)0x80000000) +#define PC_EST8260_ENET_SQE ((uint)0x40000000) +#define PC_EST8260_ENET_NOTFD ((uint)0x20000000) + +static int +scc_enet_open(struct net_device *dev) +{ + + /* I should reset the ring buffers here, but I don't yet know + * a simple way to do that. + */ + netif_start_queue(dev); + return 0; /* Always succeed */ +} + +static int +scc_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct scc_enet_private *cep = (struct scc_enet_private *)dev->priv; + volatile cbd_t *bdp; + + + /* Fill in a Tx ring entry */ + bdp = cep->cur_tx; + +#ifndef final_version + if (bdp->cbd_sc & BD_ENET_TX_READY) { + /* Ooops. All transmit buffers are full. Bail out. + * This should not happen, since cep->tx_full should be set. + */ + printk("%s: tx queue full!.\n", dev->name); + return 1; + } +#endif + + /* Clear all of the status flags. + */ + bdp->cbd_sc &= ~BD_ENET_TX_STATS; + + /* If the frame is short, tell CPM to pad it. + */ + if (skb->len <= ETH_ZLEN) + bdp->cbd_sc |= BD_ENET_TX_PAD; + else + bdp->cbd_sc &= ~BD_ENET_TX_PAD; + + /* Set buffer length and buffer pointer. + */ + bdp->cbd_datlen = skb->len; + bdp->cbd_bufaddr = __pa(skb->data); + + /* Save skb pointer. + */ + cep->tx_skbuff[cep->skb_cur] = skb; + + cep->stats.tx_bytes += skb->len; + cep->skb_cur = (cep->skb_cur+1) & TX_RING_MOD_MASK; + + spin_lock_irq(&cep->lock); + + /* Send it on its way. Tell CPM its ready, interrupt when done, + * its the last BD of the frame, and to put the CRC on the end. + */ + bdp->cbd_sc |= (BD_ENET_TX_READY | BD_ENET_TX_INTR | BD_ENET_TX_LAST | BD_ENET_TX_TC); + + dev->trans_start = jiffies; + + /* If this was the last BD in the ring, start at the beginning again. + */ + if (bdp->cbd_sc & BD_ENET_TX_WRAP) + bdp = cep->tx_bd_base; + else + bdp++; + + if (bdp->cbd_sc & BD_ENET_TX_READY) { + netif_stop_queue(dev); + cep->tx_full = 1; + } + + cep->cur_tx = (cbd_t *)bdp; + + spin_unlock_irq(&cep->lock); + + return 0; +} + +static void +scc_enet_timeout(struct net_device *dev) +{ + struct scc_enet_private *cep = (struct scc_enet_private *)dev->priv; + + printk("%s: transmit timed out.\n", dev->name); + cep->stats.tx_errors++; +#ifndef final_version + { + int i; + cbd_t *bdp; + printk(" Ring data dump: cur_tx %p%s cur_rx %p.\n", + cep->cur_tx, cep->tx_full ? " (full)" : "", + cep->cur_rx); + bdp = cep->tx_bd_base; + printk(" Tx @base %p :\n", bdp); + for (i = 0 ; i < TX_RING_SIZE; i++, bdp++) + printk("%04x %04x %08x\n", + bdp->cbd_sc, + bdp->cbd_datlen, + bdp->cbd_bufaddr); + bdp = cep->rx_bd_base; + printk(" Rx @base %p :\n", bdp); + for (i = 0 ; i < RX_RING_SIZE; i++, bdp++) + printk("%04x %04x %08x\n", + bdp->cbd_sc, + bdp->cbd_datlen, + bdp->cbd_bufaddr); + } +#endif + if (!cep->tx_full) + netif_wake_queue(dev); +} + +/* The interrupt handler. + * This is called from the CPM handler, not the MPC core interrupt. + */ +static irqreturn_t +scc_enet_interrupt(int irq, void * dev_id, struct pt_regs * regs) +{ + struct net_device *dev = dev_id; + volatile struct scc_enet_private *cep; + volatile cbd_t *bdp; + ushort int_events; + int must_restart; + + cep = (struct scc_enet_private *)dev->priv; + + /* Get the interrupt events that caused us to be here. + */ + int_events = cep->sccp->scc_scce; + cep->sccp->scc_scce = int_events; + must_restart = 0; + + /* Handle receive event in its own function. + */ + if (int_events & SCCE_ENET_RXF) + scc_enet_rx(dev_id); + + /* Check for a transmit error. The manual is a little unclear + * about this, so the debug code until I get it figured out. It + * appears that if TXE is set, then TXB is not set. However, + * if carrier sense is lost during frame transmission, the TXE + * bit is set, "and continues the buffer transmission normally." + * I don't know if "normally" implies TXB is set when the buffer + * descriptor is closed.....trial and error :-). + */ + + /* Transmit OK, or non-fatal error. Update the buffer descriptors. + */ + if (int_events & (SCCE_ENET_TXE | SCCE_ENET_TXB)) { + spin_lock(&cep->lock); + bdp = cep->dirty_tx; + while ((bdp->cbd_sc&BD_ENET_TX_READY)==0) { + if ((bdp==cep->cur_tx) && (cep->tx_full == 0)) + break; + + if (bdp->cbd_sc & BD_ENET_TX_HB) /* No heartbeat */ + cep->stats.tx_heartbeat_errors++; + if (bdp->cbd_sc & BD_ENET_TX_LC) /* Late collision */ + cep->stats.tx_window_errors++; + if (bdp->cbd_sc & BD_ENET_TX_RL) /* Retrans limit */ + cep->stats.tx_aborted_errors++; + if (bdp->cbd_sc & BD_ENET_TX_UN) /* Underrun */ + cep->stats.tx_fifo_errors++; + if (bdp->cbd_sc & BD_ENET_TX_CSL) /* Carrier lost */ + cep->stats.tx_carrier_errors++; + + + /* No heartbeat or Lost carrier are not really bad errors. + * The others require a restart transmit command. + */ + if (bdp->cbd_sc & + (BD_ENET_TX_LC | BD_ENET_TX_RL | BD_ENET_TX_UN)) { + must_restart = 1; + cep->stats.tx_errors++; + } + + cep->stats.tx_packets++; + + /* Deferred means some collisions occurred during transmit, + * but we eventually sent the packet OK. + */ + if (bdp->cbd_sc & BD_ENET_TX_DEF) + cep->stats.collisions++; + + /* Free the sk buffer associated with this last transmit. + */ + dev_kfree_skb_irq(cep->tx_skbuff[cep->skb_dirty]); + cep->skb_dirty = (cep->skb_dirty + 1) & TX_RING_MOD_MASK; + + /* Update pointer to next buffer descriptor to be transmitted. + */ + if (bdp->cbd_sc & BD_ENET_TX_WRAP) + bdp = cep->tx_bd_base; + else + bdp++; + + /* I don't know if we can be held off from processing these + * interrupts for more than one frame time. I really hope + * not. In such a case, we would now want to check the + * currently available BD (cur_tx) and determine if any + * buffers between the dirty_tx and cur_tx have also been + * sent. We would want to process anything in between that + * does not have BD_ENET_TX_READY set. + */ + + /* Since we have freed up a buffer, the ring is no longer + * full. + */ + if (cep->tx_full) { + cep->tx_full = 0; + if (netif_queue_stopped(dev)) { + netif_wake_queue(dev); + } + } + + cep->dirty_tx = (cbd_t *)bdp; + } + + if (must_restart) { + volatile cpm_cpm2_t *cp; + + /* Some transmit errors cause the transmitter to shut + * down. We now issue a restart transmit. Since the + * errors close the BD and update the pointers, the restart + * _should_ pick up without having to reset any of our + * pointers either. + */ + + cp = cpmp; + cp->cp_cpcr = + mk_cr_cmd(CPM_ENET_PAGE, CPM_ENET_BLOCK, 0, + CPM_CR_RESTART_TX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + } + spin_unlock(&cep->lock); + } + + /* Check for receive busy, i.e. packets coming but no place to + * put them. This "can't happen" because the receive interrupt + * is tossing previous frames. + */ + if (int_events & SCCE_ENET_BSY) { + cep->stats.rx_dropped++; + printk("SCC ENET: BSY can't happen.\n"); + } + + return IRQ_HANDLED; +} + +/* During a receive, the cur_rx points to the current incoming buffer. + * When we update through the ring, if the next incoming buffer has + * not been given to the system, we just set the empty indicator, + * effectively tossing the packet. + */ +static int +scc_enet_rx(struct net_device *dev) +{ + struct scc_enet_private *cep; + volatile cbd_t *bdp; + struct sk_buff *skb; + ushort pkt_len; + + cep = (struct scc_enet_private *)dev->priv; + + /* First, grab all of the stats for the incoming packet. + * These get messed up if we get called due to a busy condition. + */ + bdp = cep->cur_rx; + +for (;;) { + if (bdp->cbd_sc & BD_ENET_RX_EMPTY) + break; + +#ifndef final_version + /* Since we have allocated space to hold a complete frame, both + * the first and last indicators should be set. + */ + if ((bdp->cbd_sc & (BD_ENET_RX_FIRST | BD_ENET_RX_LAST)) != + (BD_ENET_RX_FIRST | BD_ENET_RX_LAST)) + printk("CPM ENET: rcv is not first+last\n"); +#endif + + /* Frame too long or too short. + */ + if (bdp->cbd_sc & (BD_ENET_RX_LG | BD_ENET_RX_SH)) + cep->stats.rx_length_errors++; + if (bdp->cbd_sc & BD_ENET_RX_NO) /* Frame alignment */ + cep->stats.rx_frame_errors++; + if (bdp->cbd_sc & BD_ENET_RX_CR) /* CRC Error */ + cep->stats.rx_crc_errors++; + if (bdp->cbd_sc & BD_ENET_RX_OV) /* FIFO overrun */ + cep->stats.rx_crc_errors++; + + /* Report late collisions as a frame error. + * On this error, the BD is closed, but we don't know what we + * have in the buffer. So, just drop this frame on the floor. + */ + if (bdp->cbd_sc & BD_ENET_RX_CL) { + cep->stats.rx_frame_errors++; + } + else { + + /* Process the incoming frame. + */ + cep->stats.rx_packets++; + pkt_len = bdp->cbd_datlen; + cep->stats.rx_bytes += pkt_len; + + /* This does 16 byte alignment, much more than we need. + * The packet length includes FCS, but we don't want to + * include that when passing upstream as it messes up + * bridging applications. + */ + skb = dev_alloc_skb(pkt_len-4); + + if (skb == NULL) { + printk("%s: Memory squeeze, dropping packet.\n", dev->name); + cep->stats.rx_dropped++; + } + else { + skb->dev = dev; + skb_put(skb,pkt_len-4); /* Make room */ + eth_copy_and_sum(skb, + (unsigned char *)__va(bdp->cbd_bufaddr), + pkt_len-4, 0); + skb->protocol=eth_type_trans(skb,dev); + netif_rx(skb); + } + } + + /* Clear the status flags for this buffer. + */ + bdp->cbd_sc &= ~BD_ENET_RX_STATS; + + /* Mark the buffer empty. + */ + bdp->cbd_sc |= BD_ENET_RX_EMPTY; + + /* Update BD pointer to next entry. + */ + if (bdp->cbd_sc & BD_ENET_RX_WRAP) + bdp = cep->rx_bd_base; + else + bdp++; + + } + cep->cur_rx = (cbd_t *)bdp; + + return 0; +} + +static int +scc_enet_close(struct net_device *dev) +{ + /* Don't know what to do yet. + */ + netif_stop_queue(dev); + + return 0; +} + +static struct net_device_stats *scc_enet_get_stats(struct net_device *dev) +{ + struct scc_enet_private *cep = (struct scc_enet_private *)dev->priv; + + return &cep->stats; +} + +/* Set or clear the multicast filter for this adaptor. + * Skeleton taken from sunlance driver. + * The CPM Ethernet implementation allows Multicast as well as individual + * MAC address filtering. Some of the drivers check to make sure it is + * a group multicast address, and discard those that are not. I guess I + * will do the same for now, but just remove the test if you want + * individual filtering as well (do the upper net layers want or support + * this kind of feature?). + */ + +static void set_multicast_list(struct net_device *dev) +{ + struct scc_enet_private *cep; + struct dev_mc_list *dmi; + u_char *mcptr, *tdptr; + volatile scc_enet_t *ep; + int i, j; + cep = (struct scc_enet_private *)dev->priv; + + /* Get pointer to SCC area in parameter RAM. + */ + ep = (scc_enet_t *)dev->base_addr; + + if (dev->flags&IFF_PROMISC) { + + /* Log any net taps. */ + printk("%s: Promiscuous mode enabled.\n", dev->name); + cep->sccp->scc_psmr |= SCC_PSMR_PRO; + } else { + + cep->sccp->scc_psmr &= ~SCC_PSMR_PRO; + + if (dev->flags & IFF_ALLMULTI) { + /* Catch all multicast addresses, so set the + * filter to all 1's. + */ + ep->sen_gaddr1 = 0xffff; + ep->sen_gaddr2 = 0xffff; + ep->sen_gaddr3 = 0xffff; + ep->sen_gaddr4 = 0xffff; + } + else { + /* Clear filter and add the addresses in the list. + */ + ep->sen_gaddr1 = 0; + ep->sen_gaddr2 = 0; + ep->sen_gaddr3 = 0; + ep->sen_gaddr4 = 0; + + dmi = dev->mc_list; + + for (i=0; imc_count; i++) { + + /* Only support group multicast for now. + */ + if (!(dmi->dmi_addr[0] & 1)) + continue; + + /* The address in dmi_addr is LSB first, + * and taddr is MSB first. We have to + * copy bytes MSB first from dmi_addr. + */ + mcptr = (u_char *)dmi->dmi_addr + 5; + tdptr = (u_char *)&ep->sen_taddrh; + for (j=0; j<6; j++) + *tdptr++ = *mcptr--; + + /* Ask CPM to run CRC and set bit in + * filter mask. + */ + cpmp->cp_cpcr = mk_cr_cmd(CPM_ENET_PAGE, + CPM_ENET_BLOCK, 0, + CPM_CR_SET_GADDR) | CPM_CR_FLG; + /* this delay is necessary here -- Cort */ + udelay(10); + while (cpmp->cp_cpcr & CPM_CR_FLG); + } + } + } +} + +/* Initialize the CPM Ethernet on SCC. + */ +static int __init scc_enet_init(void) +{ + struct net_device *dev; + struct scc_enet_private *cep; + int i, j, err; + uint dp_offset; + unsigned char *eap; + unsigned long mem_addr; + bd_t *bd; + volatile cbd_t *bdp; + volatile cpm_cpm2_t *cp; + volatile scc_t *sccp; + volatile scc_enet_t *ep; + volatile cpm2_map_t *immap; + volatile iop_cpm2_t *io; + + cp = cpmp; /* Get pointer to Communication Processor */ + + immap = (cpm2_map_t *)CPM_MAP_ADDR; /* and to internal registers */ + io = &immap->im_ioport; + + bd = (bd_t *)__res; + + /* Create an Ethernet device instance. + */ + dev = alloc_etherdev(sizeof(*cep)); + if (!dev) + return -ENOMEM; + + cep = dev->priv; + spin_lock_init(&cep->lock); + + /* Get pointer to SCC area in parameter RAM. + */ + ep = (scc_enet_t *)(&immap->im_dprambase[PROFF_ENET]); + + /* And another to the SCC register area. + */ + sccp = (volatile scc_t *)(&immap->im_scc[SCC_ENET]); + cep->sccp = (scc_t *)sccp; /* Keep the pointer handy */ + + /* Disable receive and transmit in case someone left it running. + */ + sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); + + /* Configure port C and D pins for SCC Ethernet. This + * won't work for all SCC possibilities....it will be + * board/port specific. + */ + io->iop_pparc |= + (PC_ENET_RENA | PC_ENET_CLSN | PC_ENET_TXCLK | PC_ENET_RXCLK); + io->iop_pdirc &= + ~(PC_ENET_RENA | PC_ENET_CLSN | PC_ENET_TXCLK | PC_ENET_RXCLK); + io->iop_psorc &= + ~(PC_ENET_RENA | PC_ENET_TXCLK | PC_ENET_RXCLK); + io->iop_psorc |= PC_ENET_CLSN; + + io->iop_ppard |= (PD_ENET_RXD | PD_ENET_TXD | PD_ENET_TENA); + io->iop_pdird |= (PD_ENET_TXD | PD_ENET_TENA); + io->iop_pdird &= ~PD_ENET_RXD; + io->iop_psord |= PD_ENET_TXD; + io->iop_psord &= ~(PD_ENET_RXD | PD_ENET_TENA); + + /* Configure Serial Interface clock routing. + * First, clear all SCC bits to zero, then set the ones we want. + */ + immap->im_cpmux.cmx_scr &= ~CMX_CLK_MASK; + immap->im_cpmux.cmx_scr |= CMX_CLK_ROUTE; + + /* Allocate space for the buffer descriptors in the DP ram. + * These are relative offsets in the DP ram address space. + * Initialize base addresses for the buffer descriptors. + */ + dp_offset = cpm_dpalloc(sizeof(cbd_t) * RX_RING_SIZE, 8); + ep->sen_genscc.scc_rbase = dp_offset; + cep->rx_bd_base = (cbd_t *)cpm_dpram_addr(dp_offset); + + dp_offset = cpm_dpalloc(sizeof(cbd_t) * TX_RING_SIZE, 8); + ep->sen_genscc.scc_tbase = dp_offset; + cep->tx_bd_base = (cbd_t *)cpm_dpram_addr(dp_offset); + + cep->dirty_tx = cep->cur_tx = cep->tx_bd_base; + cep->cur_rx = cep->rx_bd_base; + + ep->sen_genscc.scc_rfcr = CPMFCR_GBL | CPMFCR_EB; + ep->sen_genscc.scc_tfcr = CPMFCR_GBL | CPMFCR_EB; + + /* Set maximum bytes per receive buffer. + * This appears to be an Ethernet frame size, not the buffer + * fragment size. It must be a multiple of four. + */ + ep->sen_genscc.scc_mrblr = PKT_MAXBLR_SIZE; + + /* Set CRC preset and mask. + */ + ep->sen_cpres = 0xffffffff; + ep->sen_cmask = 0xdebb20e3; + + ep->sen_crcec = 0; /* CRC Error counter */ + ep->sen_alec = 0; /* alignment error counter */ + ep->sen_disfc = 0; /* discard frame counter */ + + ep->sen_pads = 0x8888; /* Tx short frame pad character */ + ep->sen_retlim = 15; /* Retry limit threshold */ + + ep->sen_maxflr = PKT_MAXBUF_SIZE; /* maximum frame length register */ + ep->sen_minflr = PKT_MINBUF_SIZE; /* minimum frame length register */ + + ep->sen_maxd1 = PKT_MAXBLR_SIZE; /* maximum DMA1 length */ + ep->sen_maxd2 = PKT_MAXBLR_SIZE; /* maximum DMA2 length */ + + /* Clear hash tables. + */ + ep->sen_gaddr1 = 0; + ep->sen_gaddr2 = 0; + ep->sen_gaddr3 = 0; + ep->sen_gaddr4 = 0; + ep->sen_iaddr1 = 0; + ep->sen_iaddr2 = 0; + ep->sen_iaddr3 = 0; + ep->sen_iaddr4 = 0; + + /* Set Ethernet station address. + * + * This is supplied in the board information structure, so we + * copy that into the controller. + */ + eap = (unsigned char *)&(ep->sen_paddrh); + for (i=5; i>=0; i--) + *eap++ = dev->dev_addr[i] = bd->bi_enetaddr[i]; + + ep->sen_pper = 0; /* 'cause the book says so */ + ep->sen_taddrl = 0; /* temp address (LSB) */ + ep->sen_taddrm = 0; + ep->sen_taddrh = 0; /* temp address (MSB) */ + + /* Now allocate the host memory pages and initialize the + * buffer descriptors. + */ + bdp = cep->tx_bd_base; + for (i=0; icbd_sc = 0; + bdp->cbd_bufaddr = 0; + bdp++; + } + + /* Set the last buffer to wrap. + */ + bdp--; + bdp->cbd_sc |= BD_SC_WRAP; + + bdp = cep->rx_bd_base; + for (i=0; icbd_sc = BD_ENET_RX_EMPTY | BD_ENET_RX_INTR; + bdp->cbd_bufaddr = __pa(mem_addr); + mem_addr += CPM_ENET_RX_FRSIZE; + bdp++; + } + } + + /* Set the last buffer to wrap. + */ + bdp--; + bdp->cbd_sc |= BD_SC_WRAP; + + /* Let's re-initialize the channel now. We have to do it later + * than the manual describes because we have just now finished + * the BD initialization. + */ + cpmp->cp_cpcr = mk_cr_cmd(CPM_ENET_PAGE, CPM_ENET_BLOCK, 0, + CPM_CR_INIT_TRX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + + cep->skb_cur = cep->skb_dirty = 0; + + sccp->scc_scce = 0xffff; /* Clear any pending events */ + + /* Enable interrupts for transmit error, complete frame + * received, and any transmit buffer we have also set the + * interrupt flag. + */ + sccp->scc_sccm = (SCCE_ENET_TXE | SCCE_ENET_RXF | SCCE_ENET_TXB); + + /* Install our interrupt handler. + */ + request_irq(SIU_INT_ENET, scc_enet_interrupt, 0, "enet", dev); + /* BUG: no check for failure */ + + /* Set GSMR_H to enable all normal operating modes. + * Set GSMR_L to enable Ethernet to MC68160. + */ + sccp->scc_gsmrh = 0; + sccp->scc_gsmrl = (SCC_GSMRL_TCI | SCC_GSMRL_TPL_48 | SCC_GSMRL_TPP_10 | SCC_GSMRL_MODE_ENET); + + /* Set sync/delimiters. + */ + sccp->scc_dsr = 0xd555; + + /* Set processing mode. Use Ethernet CRC, catch broadcast, and + * start frame search 22 bit times after RENA. + */ + sccp->scc_psmr = (SCC_PSMR_ENCRC | SCC_PSMR_NIB22); + + /* It is now OK to enable the Ethernet transmitter. + * Unfortunately, there are board implementation differences here. + */ + io->iop_pparc &= ~(PC_EST8260_ENET_LOOPBACK | + PC_EST8260_ENET_SQE | PC_EST8260_ENET_NOTFD); + io->iop_psorc &= ~(PC_EST8260_ENET_LOOPBACK | + PC_EST8260_ENET_SQE | PC_EST8260_ENET_NOTFD); + io->iop_pdirc |= (PC_EST8260_ENET_LOOPBACK | + PC_EST8260_ENET_SQE | PC_EST8260_ENET_NOTFD); + io->iop_pdatc &= ~(PC_EST8260_ENET_LOOPBACK | PC_EST8260_ENET_SQE); + io->iop_pdatc |= PC_EST8260_ENET_NOTFD; + + dev->base_addr = (unsigned long)ep; + + /* The CPM Ethernet specific entries in the device structure. */ + dev->open = scc_enet_open; + dev->hard_start_xmit = scc_enet_start_xmit; + dev->tx_timeout = scc_enet_timeout; + dev->watchdog_timeo = TX_TIMEOUT; + dev->stop = scc_enet_close; + dev->get_stats = scc_enet_get_stats; + dev->set_multicast_list = set_multicast_list; + + /* And last, enable the transmit and receive processing. + */ + sccp->scc_gsmrl |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT); + + err = register_netdev(dev); + if (err) { + free_netdev(dev); + return err; + } + + printk("%s: SCC ENET Version 0.1, ", dev->name); + for (i=0; i<5; i++) + printk("%02x:", dev->dev_addr[i]); + printk("%02x\n", dev->dev_addr[5]); + + return 0; +} + +module_init(scc_enet_init); diff --git a/arch/ppc/8260_io/fcc_enet.c b/arch/ppc/8260_io/fcc_enet.c new file mode 100644 index 00000000000..2086c6ad114 --- /dev/null +++ b/arch/ppc/8260_io/fcc_enet.c @@ -0,0 +1,2395 @@ +/* + * Fast Ethernet Controller (FCC) driver for Motorola MPC8260. + * Copyright (c) 2000 MontaVista Software, Inc. Dan Malek (dmalek@jlc.net) + * + * This version of the driver is a combination of the 8xx fec and + * 8260 SCC Ethernet drivers. This version has some additional + * configuration options, which should probably be moved out of + * here. This driver currently works for the EST SBC8260, + * SBS Diablo/BCM, Embedded Planet RPX6, TQM8260, and others. + * + * Right now, I am very watseful with the buffers. I allocate memory + * pages and then divide them into 2K frame buffers. This way I know I + * have buffers large enough to hold one frame within one buffer descriptor. + * Once I get this working, I will use 64 or 128 byte CPM buffers, which + * will be much more memory efficient and will easily handle lots of + * small packets. Since this is a cache coherent processor and CPM, + * I could also preallocate SKB's and use them directly on the interface. + * + * 2004-12 Leo Li (leoli@freescale.com) + * - Rework the FCC clock configuration part, make it easier to configure. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* We can't use the PHY interrupt if we aren't using MDIO. */ +#if !defined(CONFIG_USE_MDIO) +#undef PHY_INTERRUPT +#endif + +/* If we have a PHY interrupt, we will advertise both full-duplex and half- + * duplex capabilities. If we don't have a PHY interrupt, then we will only + * advertise half-duplex capabilities. + */ +#define MII_ADVERTISE_HALF (ADVERTISE_100HALF | ADVERTISE_10HALF | \ + ADVERTISE_CSMA) +#define MII_ADVERTISE_ALL (ADVERTISE_100FULL | ADVERTISE_10FULL | \ + MII_ADVERTISE_HALF) +#ifdef PHY_INTERRUPT +#define MII_ADVERTISE_DEFAULT MII_ADVERTISE_ALL +#else +#define MII_ADVERTISE_DEFAULT MII_ADVERTISE_HALF +#endif +#include + +/* The transmitter timeout + */ +#define TX_TIMEOUT (2*HZ) + +#ifdef CONFIG_USE_MDIO +/* Forward declarations of some structures to support different PHYs */ + +typedef struct { + uint mii_data; + void (*funct)(uint mii_reg, struct net_device *dev); +} phy_cmd_t; + +typedef struct { + uint id; + char *name; + + const phy_cmd_t *config; + const phy_cmd_t *startup; + const phy_cmd_t *ack_int; + const phy_cmd_t *shutdown; +} phy_info_t; + +/* values for phy_status */ + +#define PHY_CONF_ANE 0x0001 /* 1 auto-negotiation enabled */ +#define PHY_CONF_LOOP 0x0002 /* 1 loopback mode enabled */ +#define PHY_CONF_SPMASK 0x00f0 /* mask for speed */ +#define PHY_CONF_10HDX 0x0010 /* 10 Mbit half duplex supported */ +#define PHY_CONF_10FDX 0x0020 /* 10 Mbit full duplex supported */ +#define PHY_CONF_100HDX 0x0040 /* 100 Mbit half duplex supported */ +#define PHY_CONF_100FDX 0x0080 /* 100 Mbit full duplex supported */ + +#define PHY_STAT_LINK 0x0100 /* 1 up - 0 down */ +#define PHY_STAT_FAULT 0x0200 /* 1 remote fault */ +#define PHY_STAT_ANC 0x0400 /* 1 auto-negotiation complete */ +#define PHY_STAT_SPMASK 0xf000 /* mask for speed */ +#define PHY_STAT_10HDX 0x1000 /* 10 Mbit half duplex selected */ +#define PHY_STAT_10FDX 0x2000 /* 10 Mbit full duplex selected */ +#define PHY_STAT_100HDX 0x4000 /* 100 Mbit half duplex selected */ +#define PHY_STAT_100FDX 0x8000 /* 100 Mbit full duplex selected */ +#endif /* CONFIG_USE_MDIO */ + +/* The number of Tx and Rx buffers. These are allocated from the page + * pool. The code may assume these are power of two, so it is best + * to keep them that size. + * We don't need to allocate pages for the transmitter. We just use + * the skbuffer directly. + */ +#define FCC_ENET_RX_PAGES 16 +#define FCC_ENET_RX_FRSIZE 2048 +#define FCC_ENET_RX_FRPPG (PAGE_SIZE / FCC_ENET_RX_FRSIZE) +#define RX_RING_SIZE (FCC_ENET_RX_FRPPG * FCC_ENET_RX_PAGES) +#define TX_RING_SIZE 16 /* Must be power of two */ +#define TX_RING_MOD_MASK 15 /* for this to work */ + +/* The FCC stores dest/src/type, data, and checksum for receive packets. + * size includes support for VLAN + */ +#define PKT_MAXBUF_SIZE 1522 +#define PKT_MINBUF_SIZE 64 + +/* Maximum input DMA size. Must be a should(?) be a multiple of 4. + * size includes support for VLAN + */ +#define PKT_MAXDMA_SIZE 1524 + +/* Maximum input buffer size. Must be a multiple of 32. +*/ +#define PKT_MAXBLR_SIZE 1536 + +static int fcc_enet_open(struct net_device *dev); +static int fcc_enet_start_xmit(struct sk_buff *skb, struct net_device *dev); +static int fcc_enet_rx(struct net_device *dev); +static irqreturn_t fcc_enet_interrupt(int irq, void *dev_id, struct pt_regs *); +static int fcc_enet_close(struct net_device *dev); +static struct net_device_stats *fcc_enet_get_stats(struct net_device *dev); +/* static void set_multicast_list(struct net_device *dev); */ +static void fcc_restart(struct net_device *dev, int duplex); +static void fcc_stop(struct net_device *dev); +static int fcc_enet_set_mac_address(struct net_device *dev, void *addr); + +/* These will be configurable for the FCC choice. + * Multiple ports can be configured. There is little choice among the + * I/O pins to the PHY, except the clocks. We will need some board + * dependent clock selection. + * Why in the hell did I put these inside #ifdef's? I dunno, maybe to + * help show what pins are used for each device. + */ + +/* Since the CLK setting changes greatly from board to board, I changed + * it to a easy way. You just need to specify which CLK number to use. + * Note that only limited choices can be make on each port. + */ + +/* FCC1 Clock Source Configuration. There are board specific. + Can only choose from CLK9-12 */ +#ifdef CONFIG_SBC82xx +#define F1_RXCLK 9 +#define F1_TXCLK 10 +#elif defined(CONFIG_ADS8272) +#define F1_RXCLK 11 +#define F1_TXCLK 10 +#else +#define F1_RXCLK 12 +#define F1_TXCLK 11 +#endif + +/* FCC2 Clock Source Configuration. There are board specific. + Can only choose from CLK13-16 */ +#ifdef CONFIG_ADS8272 +#define F2_RXCLK 15 +#define F2_TXCLK 16 +#else +#define F2_RXCLK 13 +#define F2_TXCLK 14 +#endif + +/* FCC3 Clock Source Configuration. There are board specific. + Can only choose from CLK13-16 */ +#define F3_RXCLK 15 +#define F3_TXCLK 16 + +/* Automatically generates register configurations */ +#define PC_CLK(x) ((uint)(1<<(x-1))) /* FCC CLK I/O ports */ + +#define CMXFCR_RF1CS(x) ((uint)((x-5)<<27)) /* FCC1 Receive Clock Source */ +#define CMXFCR_TF1CS(x) ((uint)((x-5)<<24)) /* FCC1 Transmit Clock Source */ +#define CMXFCR_RF2CS(x) ((uint)((x-9)<<19)) /* FCC2 Receive Clock Source */ +#define CMXFCR_TF2CS(x) ((uint)((x-9)<<16)) /* FCC2 Transmit Clock Source */ +#define CMXFCR_RF3CS(x) ((uint)((x-9)<<11)) /* FCC3 Receive Clock Source */ +#define CMXFCR_TF3CS(x) ((uint)((x-9)<<8)) /* FCC3 Transmit Clock Source */ + +#define PC_F1RXCLK PC_CLK(F1_RXCLK) +#define PC_F1TXCLK PC_CLK(F1_TXCLK) +#define CMX1_CLK_ROUTE (CMXFCR_RF1CS(F1_RXCLK) | CMXFCR_TF1CS(F1_TXCLK)) +#define CMX1_CLK_MASK ((uint)0xff000000) + +#define PC_F2RXCLK PC_CLK(F2_RXCLK) +#define PC_F2TXCLK PC_CLK(F2_TXCLK) +#define CMX2_CLK_ROUTE (CMXFCR_RF2CS(F2_RXCLK) | CMXFCR_TF2CS(F2_TXCLK)) +#define CMX2_CLK_MASK ((uint)0x00ff0000) + +#define PC_F3RXCLK PC_CLK(F3_RXCLK) +#define PC_F3TXCLK PC_CLK(F3_TXCLK) +#define CMX3_CLK_ROUTE (CMXFCR_RF3CS(F3_RXCLK) | CMXFCR_TF3CS(F3_TXCLK)) +#define CMX3_CLK_MASK ((uint)0x0000ff00) + + +/* I/O Pin assignment for FCC1. I don't yet know the best way to do this, + * but there is little variation among the choices. + */ +#define PA1_COL ((uint)0x00000001) +#define PA1_CRS ((uint)0x00000002) +#define PA1_TXER ((uint)0x00000004) +#define PA1_TXEN ((uint)0x00000008) +#define PA1_RXDV ((uint)0x00000010) +#define PA1_RXER ((uint)0x00000020) +#define PA1_TXDAT ((uint)0x00003c00) +#define PA1_RXDAT ((uint)0x0003c000) +#define PA1_PSORA_BOUT (PA1_RXDAT | PA1_TXDAT) +#define PA1_PSORA_BIN (PA1_COL | PA1_CRS | PA1_TXER | PA1_TXEN | \ + PA1_RXDV | PA1_RXER) +#define PA1_DIRA_BOUT (PA1_RXDAT | PA1_CRS | PA1_COL | PA1_RXER | PA1_RXDV) +#define PA1_DIRA_BIN (PA1_TXDAT | PA1_TXEN | PA1_TXER) + + +/* I/O Pin assignment for FCC2. I don't yet know the best way to do this, + * but there is little variation among the choices. + */ +#define PB2_TXER ((uint)0x00000001) +#define PB2_RXDV ((uint)0x00000002) +#define PB2_TXEN ((uint)0x00000004) +#define PB2_RXER ((uint)0x00000008) +#define PB2_COL ((uint)0x00000010) +#define PB2_CRS ((uint)0x00000020) +#define PB2_TXDAT ((uint)0x000003c0) +#define PB2_RXDAT ((uint)0x00003c00) +#define PB2_PSORB_BOUT (PB2_RXDAT | PB2_TXDAT | PB2_CRS | PB2_COL | \ + PB2_RXER | PB2_RXDV | PB2_TXER) +#define PB2_PSORB_BIN (PB2_TXEN) +#define PB2_DIRB_BOUT (PB2_RXDAT | PB2_CRS | PB2_COL | PB2_RXER | PB2_RXDV) +#define PB2_DIRB_BIN (PB2_TXDAT | PB2_TXEN | PB2_TXER) + + +/* I/O Pin assignment for FCC3. I don't yet know the best way to do this, + * but there is little variation among the choices. + */ +#define PB3_RXDV ((uint)0x00004000) +#define PB3_RXER ((uint)0x00008000) +#define PB3_TXER ((uint)0x00010000) +#define PB3_TXEN ((uint)0x00020000) +#define PB3_COL ((uint)0x00040000) +#define PB3_CRS ((uint)0x00080000) +#ifndef CONFIG_RPX8260 +#define PB3_TXDAT ((uint)0x0f000000) +#define PC3_TXDAT ((uint)0x00000000) +#else +#define PB3_TXDAT ((uint)0x0f000000) +#define PC3_TXDAT 0 +#endif +#define PB3_RXDAT ((uint)0x00f00000) +#define PB3_PSORB_BOUT (PB3_RXDAT | PB3_TXDAT | PB3_CRS | PB3_COL | \ + PB3_RXER | PB3_RXDV | PB3_TXER | PB3_TXEN) +#define PB3_PSORB_BIN (0) +#define PB3_DIRB_BOUT (PB3_RXDAT | PB3_CRS | PB3_COL | PB3_RXER | PB3_RXDV) +#define PB3_DIRB_BIN (PB3_TXDAT | PB3_TXEN | PB3_TXER) + +#define PC3_PSORC_BOUT (PC3_TXDAT) +#define PC3_PSORC_BIN (0) +#define PC3_DIRC_BOUT (0) +#define PC3_DIRC_BIN (PC3_TXDAT) + + +/* MII status/control serial interface. +*/ +#if defined(CONFIG_RPX8260) +/* The EP8260 doesn't use Port C for MDIO */ +#define PC_MDIO ((uint)0x00000000) +#define PC_MDCK ((uint)0x00000000) +#elif defined(CONFIG_TQM8260) +/* TQM8260 has MDIO and MDCK on PC30 and PC31 respectively */ +#define PC_MDIO ((uint)0x00000002) +#define PC_MDCK ((uint)0x00000001) +#elif defined(CONFIG_ADS8272) +#define PC_MDIO ((uint)0x00002000) +#define PC_MDCK ((uint)0x00001000) +#elif defined(CONFIG_EST8260) || defined(CONFIG_ADS8260) || defined(CONFIG_PQ2FADS) +#define PC_MDIO ((uint)0x00400000) +#define PC_MDCK ((uint)0x00200000) +#else +#define PC_MDIO ((uint)0x00000004) +#define PC_MDCK ((uint)0x00000020) +#endif + +#if defined(CONFIG_USE_MDIO) && (!defined(PC_MDIO) || !defined(PC_MDCK)) +#error "Must define PC_MDIO and PC_MDCK if using MDIO" +#endif + +/* PHY addresses */ +/* default to dynamic config of phy addresses */ +#define FCC1_PHY_ADDR 0 +#ifdef CONFIG_PQ2FADS +#define FCC2_PHY_ADDR 0 +#else +#define FCC2_PHY_ADDR 2 +#endif +#define FCC3_PHY_ADDR 3 + +/* A table of information for supporting FCCs. This does two things. + * First, we know how many FCCs we have and they are always externally + * numbered from zero. Second, it holds control register and I/O + * information that could be different among board designs. + */ +typedef struct fcc_info { + uint fc_fccnum; + uint fc_phyaddr; + uint fc_cpmblock; + uint fc_cpmpage; + uint fc_proff; + uint fc_interrupt; + uint fc_trxclocks; + uint fc_clockroute; + uint fc_clockmask; + uint fc_mdio; + uint fc_mdck; +} fcc_info_t; + +static fcc_info_t fcc_ports[] = { +#ifdef CONFIG_FCC1_ENET + { 0, FCC1_PHY_ADDR, CPM_CR_FCC1_SBLOCK, CPM_CR_FCC1_PAGE, PROFF_FCC1, SIU_INT_FCC1, + (PC_F1RXCLK | PC_F1TXCLK), CMX1_CLK_ROUTE, CMX1_CLK_MASK, + PC_MDIO, PC_MDCK }, +#endif +#ifdef CONFIG_FCC2_ENET + { 1, FCC2_PHY_ADDR, CPM_CR_FCC2_SBLOCK, CPM_CR_FCC2_PAGE, PROFF_FCC2, SIU_INT_FCC2, + (PC_F2RXCLK | PC_F2TXCLK), CMX2_CLK_ROUTE, CMX2_CLK_MASK, + PC_MDIO, PC_MDCK }, +#endif +#ifdef CONFIG_FCC3_ENET + { 2, FCC3_PHY_ADDR, CPM_CR_FCC3_SBLOCK, CPM_CR_FCC3_PAGE, PROFF_FCC3, SIU_INT_FCC3, + (PC_F3RXCLK | PC_F3TXCLK), CMX3_CLK_ROUTE, CMX3_CLK_MASK, + PC_MDIO, PC_MDCK }, +#endif +}; + +/* The FCC buffer descriptors track the ring buffers. The rx_bd_base and + * tx_bd_base always point to the base of the buffer descriptors. The + * cur_rx and cur_tx point to the currently available buffer. + * The dirty_tx tracks the current buffer that is being sent by the + * controller. The cur_tx and dirty_tx are equal under both completely + * empty and completely full conditions. The empty/ready indicator in + * the buffer descriptor determines the actual condition. + */ +struct fcc_enet_private { + /* The saved address of a sent-in-place packet/buffer, for skfree(). */ + struct sk_buff* tx_skbuff[TX_RING_SIZE]; + ushort skb_cur; + ushort skb_dirty; + + /* CPM dual port RAM relative addresses. + */ + cbd_t *rx_bd_base; /* Address of Rx and Tx buffers. */ + cbd_t *tx_bd_base; + cbd_t *cur_rx, *cur_tx; /* The next free ring entry */ + cbd_t *dirty_tx; /* The ring entries to be free()ed. */ + volatile fcc_t *fccp; + volatile fcc_enet_t *ep; + struct net_device_stats stats; + uint tx_free; + spinlock_t lock; + +#ifdef CONFIG_USE_MDIO + uint phy_id; + uint phy_id_done; + uint phy_status; + phy_info_t *phy; + struct work_struct phy_relink; + struct work_struct phy_display_config; + + uint sequence_done; + + uint phy_addr; +#endif /* CONFIG_USE_MDIO */ + + int link; + int old_link; + int full_duplex; + + fcc_info_t *fip; +}; + +static void init_fcc_shutdown(fcc_info_t *fip, struct fcc_enet_private *cep, + volatile cpm2_map_t *immap); +static void init_fcc_startup(fcc_info_t *fip, struct net_device *dev); +static void init_fcc_ioports(fcc_info_t *fip, volatile iop_cpm2_t *io, + volatile cpm2_map_t *immap); +static void init_fcc_param(fcc_info_t *fip, struct net_device *dev, + volatile cpm2_map_t *immap); + +#ifdef CONFIG_USE_MDIO +static int mii_queue(struct net_device *dev, int request, void (*func)(uint, struct net_device *)); +static uint mii_send_receive(fcc_info_t *fip, uint cmd); +static void mii_do_cmd(struct net_device *dev, const phy_cmd_t *c); + +/* Make MII read/write commands for the FCC. +*/ +#define mk_mii_read(REG) (0x60020000 | (((REG) & 0x1f) << 18)) +#define mk_mii_write(REG, VAL) (0x50020000 | (((REG) & 0x1f) << 18) | \ + ((VAL) & 0xffff)) +#define mk_mii_end 0 +#endif /* CONFIG_USE_MDIO */ + + +static int +fcc_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct fcc_enet_private *cep = (struct fcc_enet_private *)dev->priv; + volatile cbd_t *bdp; + + /* Fill in a Tx ring entry */ + bdp = cep->cur_tx; + +#ifndef final_version + if (!cep->tx_free || (bdp->cbd_sc & BD_ENET_TX_READY)) { + /* Ooops. All transmit buffers are full. Bail out. + * This should not happen, since the tx queue should be stopped. + */ + printk("%s: tx queue full!.\n", dev->name); + return 1; + } +#endif + + /* Clear all of the status flags. */ + bdp->cbd_sc &= ~BD_ENET_TX_STATS; + + /* If the frame is short, tell CPM to pad it. */ + if (skb->len <= ETH_ZLEN) + bdp->cbd_sc |= BD_ENET_TX_PAD; + else + bdp->cbd_sc &= ~BD_ENET_TX_PAD; + + /* Set buffer length and buffer pointer. */ + bdp->cbd_datlen = skb->len; + bdp->cbd_bufaddr = __pa(skb->data); + + spin_lock_irq(&cep->lock); + + /* Save skb pointer. */ + cep->tx_skbuff[cep->skb_cur] = skb; + + cep->stats.tx_bytes += skb->len; + cep->skb_cur = (cep->skb_cur+1) & TX_RING_MOD_MASK; + + /* Send it on its way. Tell CPM its ready, interrupt when done, + * its the last BD of the frame, and to put the CRC on the end. + */ + bdp->cbd_sc |= (BD_ENET_TX_READY | BD_ENET_TX_INTR | BD_ENET_TX_LAST | BD_ENET_TX_TC); + +#if 0 + /* Errata says don't do this. */ + cep->fccp->fcc_ftodr = 0x8000; +#endif + dev->trans_start = jiffies; + + /* If this was the last BD in the ring, start at the beginning again. */ + if (bdp->cbd_sc & BD_ENET_TX_WRAP) + bdp = cep->tx_bd_base; + else + bdp++; + + if (!--cep->tx_free) + netif_stop_queue(dev); + + cep->cur_tx = (cbd_t *)bdp; + + spin_unlock_irq(&cep->lock); + + return 0; +} + + +static void +fcc_enet_timeout(struct net_device *dev) +{ + struct fcc_enet_private *cep = (struct fcc_enet_private *)dev->priv; + + printk("%s: transmit timed out.\n", dev->name); + cep->stats.tx_errors++; +#ifndef final_version + { + int i; + cbd_t *bdp; + printk(" Ring data dump: cur_tx %p tx_free %d cur_rx %p.\n", + cep->cur_tx, cep->tx_free, + cep->cur_rx); + bdp = cep->tx_bd_base; + printk(" Tx @base %p :\n", bdp); + for (i = 0 ; i < TX_RING_SIZE; i++, bdp++) + printk("%04x %04x %08x\n", + bdp->cbd_sc, + bdp->cbd_datlen, + bdp->cbd_bufaddr); + bdp = cep->rx_bd_base; + printk(" Rx @base %p :\n", bdp); + for (i = 0 ; i < RX_RING_SIZE; i++, bdp++) + printk("%04x %04x %08x\n", + bdp->cbd_sc, + bdp->cbd_datlen, + bdp->cbd_bufaddr); + } +#endif + if (cep->tx_free) + netif_wake_queue(dev); +} + +/* The interrupt handler. */ +static irqreturn_t +fcc_enet_interrupt(int irq, void * dev_id, struct pt_regs * regs) +{ + struct net_device *dev = dev_id; + volatile struct fcc_enet_private *cep; + volatile cbd_t *bdp; + ushort int_events; + int must_restart; + + cep = (struct fcc_enet_private *)dev->priv; + + /* Get the interrupt events that caused us to be here. + */ + int_events = cep->fccp->fcc_fcce; + cep->fccp->fcc_fcce = (int_events & cep->fccp->fcc_fccm); + must_restart = 0; + +#ifdef PHY_INTERRUPT + /* We have to be careful here to make sure that we aren't + * interrupted by a PHY interrupt. + */ + disable_irq_nosync(PHY_INTERRUPT); +#endif + + /* Handle receive event in its own function. + */ + if (int_events & FCC_ENET_RXF) + fcc_enet_rx(dev_id); + + /* Check for a transmit error. The manual is a little unclear + * about this, so the debug code until I get it figured out. It + * appears that if TXE is set, then TXB is not set. However, + * if carrier sense is lost during frame transmission, the TXE + * bit is set, "and continues the buffer transmission normally." + * I don't know if "normally" implies TXB is set when the buffer + * descriptor is closed.....trial and error :-). + */ + + /* Transmit OK, or non-fatal error. Update the buffer descriptors. + */ + if (int_events & (FCC_ENET_TXE | FCC_ENET_TXB)) { + spin_lock(&cep->lock); + bdp = cep->dirty_tx; + while ((bdp->cbd_sc&BD_ENET_TX_READY)==0) { + if (cep->tx_free == TX_RING_SIZE) + break; + + if (bdp->cbd_sc & BD_ENET_TX_HB) /* No heartbeat */ + cep->stats.tx_heartbeat_errors++; + if (bdp->cbd_sc & BD_ENET_TX_LC) /* Late collision */ + cep->stats.tx_window_errors++; + if (bdp->cbd_sc & BD_ENET_TX_RL) /* Retrans limit */ + cep->stats.tx_aborted_errors++; + if (bdp->cbd_sc & BD_ENET_TX_UN) /* Underrun */ + cep->stats.tx_fifo_errors++; + if (bdp->cbd_sc & BD_ENET_TX_CSL) /* Carrier lost */ + cep->stats.tx_carrier_errors++; + + + /* No heartbeat or Lost carrier are not really bad errors. + * The others require a restart transmit command. + */ + if (bdp->cbd_sc & + (BD_ENET_TX_LC | BD_ENET_TX_RL | BD_ENET_TX_UN)) { + must_restart = 1; + cep->stats.tx_errors++; + } + + cep->stats.tx_packets++; + + /* Deferred means some collisions occurred during transmit, + * but we eventually sent the packet OK. + */ + if (bdp->cbd_sc & BD_ENET_TX_DEF) + cep->stats.collisions++; + + /* Free the sk buffer associated with this last transmit. */ + dev_kfree_skb_irq(cep->tx_skbuff[cep->skb_dirty]); + cep->tx_skbuff[cep->skb_dirty] = NULL; + cep->skb_dirty = (cep->skb_dirty + 1) & TX_RING_MOD_MASK; + + /* Update pointer to next buffer descriptor to be transmitted. */ + if (bdp->cbd_sc & BD_ENET_TX_WRAP) + bdp = cep->tx_bd_base; + else + bdp++; + + /* I don't know if we can be held off from processing these + * interrupts for more than one frame time. I really hope + * not. In such a case, we would now want to check the + * currently available BD (cur_tx) and determine if any + * buffers between the dirty_tx and cur_tx have also been + * sent. We would want to process anything in between that + * does not have BD_ENET_TX_READY set. + */ + + /* Since we have freed up a buffer, the ring is no longer + * full. + */ + if (!cep->tx_free++) { + if (netif_queue_stopped(dev)) { + netif_wake_queue(dev); + } + } + + cep->dirty_tx = (cbd_t *)bdp; + } + + if (must_restart) { + volatile cpm_cpm2_t *cp; + + /* Some transmit errors cause the transmitter to shut + * down. We now issue a restart transmit. Since the + * errors close the BD and update the pointers, the restart + * _should_ pick up without having to reset any of our + * pointers either. Also, To workaround 8260 device erratum + * CPM37, we must disable and then re-enable the transmitter + * following a Late Collision, Underrun, or Retry Limit error. + */ + cep->fccp->fcc_gfmr &= ~FCC_GFMR_ENT; + udelay(10); /* wait a few microseconds just on principle */ + cep->fccp->fcc_gfmr |= FCC_GFMR_ENT; + + cp = cpmp; + cp->cp_cpcr = + mk_cr_cmd(cep->fip->fc_cpmpage, cep->fip->fc_cpmblock, + 0x0c, CPM_CR_RESTART_TX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + } + spin_unlock(&cep->lock); + } + + /* Check for receive busy, i.e. packets coming but no place to + * put them. + */ + if (int_events & FCC_ENET_BSY) { + cep->fccp->fcc_fcce = FCC_ENET_BSY; + cep->stats.rx_dropped++; + } + +#ifdef PHY_INTERRUPT + enable_irq(PHY_INTERRUPT); +#endif + return IRQ_HANDLED; +} + +/* During a receive, the cur_rx points to the current incoming buffer. + * When we update through the ring, if the next incoming buffer has + * not been given to the system, we just set the empty indicator, + * effectively tossing the packet. + */ +static int +fcc_enet_rx(struct net_device *dev) +{ + struct fcc_enet_private *cep; + volatile cbd_t *bdp; + struct sk_buff *skb; + ushort pkt_len; + + cep = (struct fcc_enet_private *)dev->priv; + + /* First, grab all of the stats for the incoming packet. + * These get messed up if we get called due to a busy condition. + */ + bdp = cep->cur_rx; + +for (;;) { + if (bdp->cbd_sc & BD_ENET_RX_EMPTY) + break; + +#ifndef final_version + /* Since we have allocated space to hold a complete frame, both + * the first and last indicators should be set. + */ + if ((bdp->cbd_sc & (BD_ENET_RX_FIRST | BD_ENET_RX_LAST)) != + (BD_ENET_RX_FIRST | BD_ENET_RX_LAST)) + printk("CPM ENET: rcv is not first+last\n"); +#endif + + /* Frame too long or too short. */ + if (bdp->cbd_sc & (BD_ENET_RX_LG | BD_ENET_RX_SH)) + cep->stats.rx_length_errors++; + if (bdp->cbd_sc & BD_ENET_RX_NO) /* Frame alignment */ + cep->stats.rx_frame_errors++; + if (bdp->cbd_sc & BD_ENET_RX_CR) /* CRC Error */ + cep->stats.rx_crc_errors++; + if (bdp->cbd_sc & BD_ENET_RX_OV) /* FIFO overrun */ + cep->stats.rx_crc_errors++; + if (bdp->cbd_sc & BD_ENET_RX_CL) /* Late Collision */ + cep->stats.rx_frame_errors++; + + if (!(bdp->cbd_sc & + (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO | BD_ENET_RX_CR + | BD_ENET_RX_OV | BD_ENET_RX_CL))) + { + /* Process the incoming frame. */ + cep->stats.rx_packets++; + + /* Remove the FCS from the packet length. */ + pkt_len = bdp->cbd_datlen - 4; + cep->stats.rx_bytes += pkt_len; + + /* This does 16 byte alignment, much more than we need. */ + skb = dev_alloc_skb(pkt_len); + + if (skb == NULL) { + printk("%s: Memory squeeze, dropping packet.\n", dev->name); + cep->stats.rx_dropped++; + } + else { + skb->dev = dev; + skb_put(skb,pkt_len); /* Make room */ + eth_copy_and_sum(skb, + (unsigned char *)__va(bdp->cbd_bufaddr), + pkt_len, 0); + skb->protocol=eth_type_trans(skb,dev); + netif_rx(skb); + } + } + + /* Clear the status flags for this buffer. */ + bdp->cbd_sc &= ~BD_ENET_RX_STATS; + + /* Mark the buffer empty. */ + bdp->cbd_sc |= BD_ENET_RX_EMPTY; + + /* Update BD pointer to next entry. */ + if (bdp->cbd_sc & BD_ENET_RX_WRAP) + bdp = cep->rx_bd_base; + else + bdp++; + + } + cep->cur_rx = (cbd_t *)bdp; + + return 0; +} + +static int +fcc_enet_close(struct net_device *dev) +{ +#ifdef CONFIG_USE_MDIO + struct fcc_enet_private *fep = dev->priv; +#endif + + netif_stop_queue(dev); + fcc_stop(dev); +#ifdef CONFIG_USE_MDIO + if (fep->phy) + mii_do_cmd(dev, fep->phy->shutdown); +#endif + + return 0; +} + +static struct net_device_stats *fcc_enet_get_stats(struct net_device *dev) +{ + struct fcc_enet_private *cep = (struct fcc_enet_private *)dev->priv; + + return &cep->stats; +} + +#ifdef CONFIG_USE_MDIO + +/* NOTE: Most of the following comes from the FEC driver for 860. The + * overall structure of MII code has been retained (as it's proved stable + * and well-tested), but actual transfer requests are processed "at once" + * instead of being queued (there's no interrupt-driven MII transfer + * mechanism, one has to toggle the data/clock bits manually). + */ +static int +mii_queue(struct net_device *dev, int regval, void (*func)(uint, struct net_device *)) +{ + struct fcc_enet_private *fep; + int retval, tmp; + + /* Add PHY address to register command. */ + fep = dev->priv; + regval |= fep->phy_addr << 23; + + retval = 0; + + tmp = mii_send_receive(fep->fip, regval); + if (func) + func(tmp, dev); + + return retval; +} + +static void mii_do_cmd(struct net_device *dev, const phy_cmd_t *c) +{ + int k; + + if(!c) + return; + + for(k = 0; (c+k)->mii_data != mk_mii_end; k++) + mii_queue(dev, (c+k)->mii_data, (c+k)->funct); +} + +static void mii_parse_sr(uint mii_reg, struct net_device *dev) +{ + volatile struct fcc_enet_private *fep = dev->priv; + uint s = fep->phy_status; + + s &= ~(PHY_STAT_LINK | PHY_STAT_FAULT | PHY_STAT_ANC); + + if (mii_reg & BMSR_LSTATUS) + s |= PHY_STAT_LINK; + if (mii_reg & BMSR_RFAULT) + s |= PHY_STAT_FAULT; + if (mii_reg & BMSR_ANEGCOMPLETE) + s |= PHY_STAT_ANC; + + fep->phy_status = s; +} + +static void mii_parse_cr(uint mii_reg, struct net_device *dev) +{ + volatile struct fcc_enet_private *fep = dev->priv; + uint s = fep->phy_status; + + s &= ~(PHY_CONF_ANE | PHY_CONF_LOOP); + + if (mii_reg & BMCR_ANENABLE) + s |= PHY_CONF_ANE; + if (mii_reg & BMCR_LOOPBACK) + s |= PHY_CONF_LOOP; + + fep->phy_status = s; +} + +static void mii_parse_anar(uint mii_reg, struct net_device *dev) +{ + volatile struct fcc_enet_private *fep = dev->priv; + uint s = fep->phy_status; + + s &= ~(PHY_CONF_SPMASK); + + if (mii_reg & ADVERTISE_10HALF) + s |= PHY_CONF_10HDX; + if (mii_reg & ADVERTISE_10FULL) + s |= PHY_CONF_10FDX; + if (mii_reg & ADVERTISE_100HALF) + s |= PHY_CONF_100HDX; + if (mii_reg & ADVERTISE_100FULL) + s |= PHY_CONF_100FDX; + + fep->phy_status = s; +} + +/* ------------------------------------------------------------------------- */ +/* Generic PHY support. Should work for all PHYs, but does not support link + * change interrupts. + */ +#ifdef CONFIG_FCC_GENERIC_PHY + +static phy_info_t phy_info_generic = { + 0x00000000, /* 0-->match any PHY */ + "GENERIC", + + (const phy_cmd_t []) { /* config */ + /* advertise only half-duplex capabilities */ + { mk_mii_write(MII_ADVERTISE, MII_ADVERTISE_HALF), + mii_parse_anar }, + + /* enable auto-negotiation */ + { mk_mii_write(MII_BMCR, BMCR_ANENABLE), mii_parse_cr }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* startup */ + /* restart auto-negotiation */ + { mk_mii_write(MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART), + NULL }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* ack_int */ + /* We don't actually use the ack_int table with a generic + * PHY, but putting a reference to mii_parse_sr here keeps + * us from getting a compiler warning about unused static + * functions in the case where we only compile in generic + * PHY support. + */ + { mk_mii_read(MII_BMSR), mii_parse_sr }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* shutdown */ + { mk_mii_end, } + }, +}; +#endif /* ifdef CONFIG_FCC_GENERIC_PHY */ + +/* ------------------------------------------------------------------------- */ +/* The Level one LXT970 is used by many boards */ + +#ifdef CONFIG_FCC_LXT970 + +#define MII_LXT970_MIRROR 16 /* Mirror register */ +#define MII_LXT970_IER 17 /* Interrupt Enable Register */ +#define MII_LXT970_ISR 18 /* Interrupt Status Register */ +#define MII_LXT970_CONFIG 19 /* Configuration Register */ +#define MII_LXT970_CSR 20 /* Chip Status Register */ + +static void mii_parse_lxt970_csr(uint mii_reg, struct net_device *dev) +{ + volatile struct fcc_enet_private *fep = dev->priv; + uint s = fep->phy_status; + + s &= ~(PHY_STAT_SPMASK); + + if (mii_reg & 0x0800) { + if (mii_reg & 0x1000) + s |= PHY_STAT_100FDX; + else + s |= PHY_STAT_100HDX; + } else { + if (mii_reg & 0x1000) + s |= PHY_STAT_10FDX; + else + s |= PHY_STAT_10HDX; + } + + fep->phy_status = s; +} + +static phy_info_t phy_info_lxt970 = { + 0x07810000, + "LXT970", + + (const phy_cmd_t []) { /* config */ +#if 0 +// { mk_mii_write(MII_ADVERTISE, 0x0021), NULL }, + + /* Set default operation of 100-TX....for some reason + * some of these bits are set on power up, which is wrong. + */ + { mk_mii_write(MII_LXT970_CONFIG, 0), NULL }, +#endif + { mk_mii_read(MII_BMCR), mii_parse_cr }, + { mk_mii_read(MII_ADVERTISE), mii_parse_anar }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* startup - enable interrupts */ + { mk_mii_write(MII_LXT970_IER, 0x0002), NULL }, + { mk_mii_write(MII_BMCR, 0x1200), NULL }, /* autonegotiate */ + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* ack_int */ + /* read SR and ISR to acknowledge */ + + { mk_mii_read(MII_BMSR), mii_parse_sr }, + { mk_mii_read(MII_LXT970_ISR), NULL }, + + /* find out the current status */ + + { mk_mii_read(MII_LXT970_CSR), mii_parse_lxt970_csr }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* shutdown - disable interrupts */ + { mk_mii_write(MII_LXT970_IER, 0x0000), NULL }, + { mk_mii_end, } + }, +}; + +#endif /* CONFIG_FEC_LXT970 */ + +/* ------------------------------------------------------------------------- */ +/* The Level one LXT971 is used on some of my custom boards */ + +#ifdef CONFIG_FCC_LXT971 + +/* register definitions for the 971 */ + +#define MII_LXT971_PCR 16 /* Port Control Register */ +#define MII_LXT971_SR2 17 /* Status Register 2 */ +#define MII_LXT971_IER 18 /* Interrupt Enable Register */ +#define MII_LXT971_ISR 19 /* Interrupt Status Register */ +#define MII_LXT971_LCR 20 /* LED Control Register */ +#define MII_LXT971_TCR 30 /* Transmit Control Register */ + +/* + * I had some nice ideas of running the MDIO faster... + * The 971 should support 8MHz and I tried it, but things acted really + * weird, so 2.5 MHz ought to be enough for anyone... + */ + +static void mii_parse_lxt971_sr2(uint mii_reg, struct net_device *dev) +{ + volatile struct fcc_enet_private *fep = dev->priv; + uint s = fep->phy_status; + + s &= ~(PHY_STAT_SPMASK); + + if (mii_reg & 0x4000) { + if (mii_reg & 0x0200) + s |= PHY_STAT_100FDX; + else + s |= PHY_STAT_100HDX; + } else { + if (mii_reg & 0x0200) + s |= PHY_STAT_10FDX; + else + s |= PHY_STAT_10HDX; + } + if (mii_reg & 0x0008) + s |= PHY_STAT_FAULT; + + fep->phy_status = s; +} + +static phy_info_t phy_info_lxt971 = { + 0x0001378e, + "LXT971", + + (const phy_cmd_t []) { /* config */ + /* configure link capabilities to advertise */ + { mk_mii_write(MII_ADVERTISE, MII_ADVERTISE_DEFAULT), + mii_parse_anar }, + + /* enable auto-negotiation */ + { mk_mii_write(MII_BMCR, BMCR_ANENABLE), mii_parse_cr }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* startup - enable interrupts */ + { mk_mii_write(MII_LXT971_IER, 0x00f2), NULL }, + + /* restart auto-negotiation */ + { mk_mii_write(MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART), + NULL }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* ack_int */ + /* find out the current status */ + { mk_mii_read(MII_BMSR), NULL }, + { mk_mii_read(MII_BMSR), mii_parse_sr }, + { mk_mii_read(MII_LXT971_SR2), mii_parse_lxt971_sr2 }, + + /* we only need to read ISR to acknowledge */ + { mk_mii_read(MII_LXT971_ISR), NULL }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* shutdown - disable interrupts */ + { mk_mii_write(MII_LXT971_IER, 0x0000), NULL }, + { mk_mii_end, } + }, +}; + +#endif /* CONFIG_FCC_LXT971 */ + +/* ------------------------------------------------------------------------- */ +/* The Quality Semiconductor QS6612 is used on the RPX CLLF */ + +#ifdef CONFIG_FCC_QS6612 + +/* register definitions */ + +#define MII_QS6612_MCR 17 /* Mode Control Register */ +#define MII_QS6612_FTR 27 /* Factory Test Register */ +#define MII_QS6612_MCO 28 /* Misc. Control Register */ +#define MII_QS6612_ISR 29 /* Interrupt Source Register */ +#define MII_QS6612_IMR 30 /* Interrupt Mask Register */ +#define MII_QS6612_PCR 31 /* 100BaseTx PHY Control Reg. */ + +static void mii_parse_qs6612_pcr(uint mii_reg, struct net_device *dev) +{ + volatile struct fcc_enet_private *fep = dev->priv; + uint s = fep->phy_status; + + s &= ~(PHY_STAT_SPMASK); + + switch((mii_reg >> 2) & 7) { + case 1: s |= PHY_STAT_10HDX; break; + case 2: s |= PHY_STAT_100HDX; break; + case 5: s |= PHY_STAT_10FDX; break; + case 6: s |= PHY_STAT_100FDX; break; + } + + fep->phy_status = s; +} + +static phy_info_t phy_info_qs6612 = { + 0x00181440, + "QS6612", + + (const phy_cmd_t []) { /* config */ +// { mk_mii_write(MII_ADVERTISE, 0x061), NULL }, /* 10 Mbps */ + + /* The PHY powers up isolated on the RPX, + * so send a command to allow operation. + */ + + { mk_mii_write(MII_QS6612_PCR, 0x0dc0), NULL }, + + /* parse cr and anar to get some info */ + + { mk_mii_read(MII_BMCR), mii_parse_cr }, + { mk_mii_read(MII_ADVERTISE), mii_parse_anar }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* startup - enable interrupts */ + { mk_mii_write(MII_QS6612_IMR, 0x003a), NULL }, + { mk_mii_write(MII_BMCR, 0x1200), NULL }, /* autonegotiate */ + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* ack_int */ + + /* we need to read ISR, SR and ANER to acknowledge */ + + { mk_mii_read(MII_QS6612_ISR), NULL }, + { mk_mii_read(MII_BMSR), mii_parse_sr }, + { mk_mii_read(MII_EXPANSION), NULL }, + + /* read pcr to get info */ + + { mk_mii_read(MII_QS6612_PCR), mii_parse_qs6612_pcr }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* shutdown - disable interrupts */ + { mk_mii_write(MII_QS6612_IMR, 0x0000), NULL }, + { mk_mii_end, } + }, +}; + + +#endif /* CONFIG_FEC_QS6612 */ + + +/* ------------------------------------------------------------------------- */ +/* The Davicom DM9131 is used on the HYMOD board */ + +#ifdef CONFIG_FCC_DM9131 + +/* register definitions */ + +#define MII_DM9131_ACR 16 /* Aux. Config Register */ +#define MII_DM9131_ACSR 17 /* Aux. Config/Status Register */ +#define MII_DM9131_10TCSR 18 /* 10BaseT Config/Status Reg. */ +#define MII_DM9131_INTR 21 /* Interrupt Register */ +#define MII_DM9131_RECR 22 /* Receive Error Counter Reg. */ +#define MII_DM9131_DISCR 23 /* Disconnect Counter Register */ + +static void mii_parse_dm9131_acsr(uint mii_reg, struct net_device *dev) +{ + volatile struct fcc_enet_private *fep = dev->priv; + uint s = fep->phy_status; + + s &= ~(PHY_STAT_SPMASK); + + switch ((mii_reg >> 12) & 0xf) { + case 1: s |= PHY_STAT_10HDX; break; + case 2: s |= PHY_STAT_10FDX; break; + case 4: s |= PHY_STAT_100HDX; break; + case 8: s |= PHY_STAT_100FDX; break; + } + + fep->phy_status = s; +} + +static phy_info_t phy_info_dm9131 = { + 0x00181b80, + "DM9131", + + (const phy_cmd_t []) { /* config */ + /* parse cr and anar to get some info */ + { mk_mii_read(MII_BMCR), mii_parse_cr }, + { mk_mii_read(MII_ADVERTISE), mii_parse_anar }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* startup - enable interrupts */ + { mk_mii_write(MII_DM9131_INTR, 0x0002), NULL }, + { mk_mii_write(MII_BMCR, 0x1200), NULL }, /* autonegotiate */ + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* ack_int */ + + /* we need to read INTR, SR and ANER to acknowledge */ + + { mk_mii_read(MII_DM9131_INTR), NULL }, + { mk_mii_read(MII_BMSR), mii_parse_sr }, + { mk_mii_read(MII_EXPANSION), NULL }, + + /* read acsr to get info */ + + { mk_mii_read(MII_DM9131_ACSR), mii_parse_dm9131_acsr }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* shutdown - disable interrupts */ + { mk_mii_write(MII_DM9131_INTR, 0x0f00), NULL }, + { mk_mii_end, } + }, +}; + + +#endif /* CONFIG_FEC_DM9131 */ +#ifdef CONFIG_FCC_DM9161 +/* ------------------------------------------------------------------------- */ +/* DM9161 Control register values */ +#define MIIM_DM9161_CR_STOP 0x0400 +#define MIIM_DM9161_CR_RSTAN 0x1200 + +#define MIIM_DM9161_SCR 0x10 +#define MIIM_DM9161_SCR_INIT 0x0610 + +/* DM9161 Specified Configuration and Status Register */ +#define MIIM_DM9161_SCSR 0x11 +#define MIIM_DM9161_SCSR_100F 0x8000 +#define MIIM_DM9161_SCSR_100H 0x4000 +#define MIIM_DM9161_SCSR_10F 0x2000 +#define MIIM_DM9161_SCSR_10H 0x1000 +/* DM9161 10BT register */ +#define MIIM_DM9161_10BTCSR 0x12 +#define MIIM_DM9161_10BTCSR_INIT 0x7800 +/* DM9161 Interrupt Register */ +#define MIIM_DM9161_INTR 0x15 +#define MIIM_DM9161_INTR_PEND 0x8000 +#define MIIM_DM9161_INTR_DPLX_MASK 0x0800 +#define MIIM_DM9161_INTR_SPD_MASK 0x0400 +#define MIIM_DM9161_INTR_LINK_MASK 0x0200 +#define MIIM_DM9161_INTR_MASK 0x0100 +#define MIIM_DM9161_INTR_DPLX_CHANGE 0x0010 +#define MIIM_DM9161_INTR_SPD_CHANGE 0x0008 +#define MIIM_DM9161_INTR_LINK_CHANGE 0x0004 +#define MIIM_DM9161_INTR_INIT 0x0000 +#define MIIM_DM9161_INTR_STOP \ +(MIIM_DM9161_INTR_DPLX_MASK | MIIM_DM9161_INTR_SPD_MASK \ + | MIIM_DM9161_INTR_LINK_MASK | MIIM_DM9161_INTR_MASK) + +static void mii_parse_dm9161_sr(uint mii_reg, struct net_device * dev) +{ + volatile struct fcc_enet_private *fep = dev->priv; + uint regstat, timeout=0xffff; + + while(!(mii_reg & 0x0020) && timeout--) + { + regstat=mk_mii_read(MII_BMSR); + regstat |= fep->phy_addr <<23; + mii_reg = mii_send_receive(fep->fip,regstat); + } + + mii_parse_sr(mii_reg, dev); +} + +static void mii_parse_dm9161_scsr(uint mii_reg, struct net_device * dev) +{ + volatile struct fcc_enet_private *fep = dev->priv; + uint s = fep->phy_status; + + s &= ~(PHY_STAT_SPMASK); + switch((mii_reg >>12) & 0xf) { + case 1: + { + s |= PHY_STAT_10HDX; + printk("10BaseT Half Duplex\n"); + break; + } + case 2: + { + s |= PHY_STAT_10FDX; + printk("10BaseT Full Duplex\n"); + break; + } + case 4: + { + s |= PHY_STAT_100HDX; + printk("100BaseT Half Duplex\n"); + break; + } + case 8: + { + s |= PHY_STAT_100FDX; + printk("100BaseT Full Duplex\n"); + break; + } + } + + fep->phy_status = s; + +} + +static void mii_dm9161_wait(uint mii_reg, struct net_device *dev) +{ + int timeout = HZ; + + /* Davicom takes a bit to come up after a reset, + * so wait here for a bit */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(timeout); +} + +static phy_info_t phy_info_dm9161 = { + 0x00181b88, + "Davicom DM9161E", + (const phy_cmd_t[]) { /* config */ + { mk_mii_write(MII_BMCR, MIIM_DM9161_CR_STOP), NULL}, + /* Do not bypass the scrambler/descrambler */ + { mk_mii_write(MIIM_DM9161_SCR, MIIM_DM9161_SCR_INIT), NULL}, + /* Configure 10BTCSR register */ + { mk_mii_write(MIIM_DM9161_10BTCSR, MIIM_DM9161_10BTCSR_INIT),NULL}, + /* Configure some basic stuff */ + { mk_mii_write(MII_BMCR, 0x1000), NULL}, + { mk_mii_read(MII_BMCR), mii_parse_cr }, + { mk_mii_read(MII_ADVERTISE), mii_parse_anar }, + { mk_mii_end,} + }, + (const phy_cmd_t[]) { /* startup */ + /* Restart Auto Negotiation */ + { mk_mii_write(MII_BMCR, MIIM_DM9161_CR_RSTAN), NULL}, + /* Status is read once to clear old link state */ + { mk_mii_read(MII_BMSR), mii_dm9161_wait}, + /* Auto-negotiate */ + { mk_mii_read(MII_BMSR), mii_parse_dm9161_sr}, + /* Read the status */ + { mk_mii_read(MIIM_DM9161_SCSR), mii_parse_dm9161_scsr}, + /* Clear any pending interrupts */ + { mk_mii_read(MIIM_DM9161_INTR), NULL}, + /* Enable Interrupts */ + { mk_mii_write(MIIM_DM9161_INTR, MIIM_DM9161_INTR_INIT), NULL}, + { mk_mii_end,} + }, + (const phy_cmd_t[]) { /* ack_int */ + { mk_mii_read(MIIM_DM9161_INTR), NULL}, +#if 0 + { mk_mii_read(MII_BMSR), NULL}, + { mk_mii_read(MII_BMSR), mii_parse_dm9161_sr}, + { mk_mii_read(MIIM_DM9161_SCSR), mii_parse_dm9161_scsr}, +#endif + { mk_mii_end,} + }, + (const phy_cmd_t[]) { /* shutdown */ + { mk_mii_read(MIIM_DM9161_INTR),NULL}, + { mk_mii_write(MIIM_DM9161_INTR, MIIM_DM9161_INTR_STOP), NULL}, + { mk_mii_end,} + }, +}; +#endif /* CONFIG_FCC_DM9161 */ + +static phy_info_t *phy_info[] = { + +#ifdef CONFIG_FCC_LXT970 + &phy_info_lxt970, +#endif /* CONFIG_FEC_LXT970 */ + +#ifdef CONFIG_FCC_LXT971 + &phy_info_lxt971, +#endif /* CONFIG_FEC_LXT971 */ + +#ifdef CONFIG_FCC_QS6612 + &phy_info_qs6612, +#endif /* CONFIG_FEC_QS6612 */ + +#ifdef CONFIG_FCC_DM9131 + &phy_info_dm9131, +#endif /* CONFIG_FEC_DM9131 */ + +#ifdef CONFIG_FCC_DM9161 + &phy_info_dm9161, +#endif /* CONFIG_FCC_DM9161 */ + +#ifdef CONFIG_FCC_GENERIC_PHY + /* Generic PHY support. This must be the last PHY in the table. + * It will be used to support any PHY that doesn't match a previous + * entry in the table. + */ + &phy_info_generic, +#endif /* CONFIG_FCC_GENERIC_PHY */ + + NULL +}; + +static void mii_display_status(void *data) +{ + struct net_device *dev = data; + volatile struct fcc_enet_private *fep = dev->priv; + uint s = fep->phy_status; + + if (!fep->link && !fep->old_link) { + /* Link is still down - don't print anything */ + return; + } + + printk("%s: status: ", dev->name); + + if (!fep->link) { + printk("link down"); + } else { + printk("link up"); + + switch(s & PHY_STAT_SPMASK) { + case PHY_STAT_100FDX: printk(", 100 Mbps Full Duplex"); break; + case PHY_STAT_100HDX: printk(", 100 Mbps Half Duplex"); break; + case PHY_STAT_10FDX: printk(", 10 Mbps Full Duplex"); break; + case PHY_STAT_10HDX: printk(", 10 Mbps Half Duplex"); break; + default: + printk(", Unknown speed/duplex"); + } + + if (s & PHY_STAT_ANC) + printk(", auto-negotiation complete"); + } + + if (s & PHY_STAT_FAULT) + printk(", remote fault"); + + printk(".\n"); +} + +static void mii_display_config(void *data) +{ + struct net_device *dev = data; + volatile struct fcc_enet_private *fep = dev->priv; + uint s = fep->phy_status; + + printk("%s: config: auto-negotiation ", dev->name); + + if (s & PHY_CONF_ANE) + printk("on"); + else + printk("off"); + + if (s & PHY_CONF_100FDX) + printk(", 100FDX"); + if (s & PHY_CONF_100HDX) + printk(", 100HDX"); + if (s & PHY_CONF_10FDX) + printk(", 10FDX"); + if (s & PHY_CONF_10HDX) + printk(", 10HDX"); + if (!(s & PHY_CONF_SPMASK)) + printk(", No speed/duplex selected?"); + + if (s & PHY_CONF_LOOP) + printk(", loopback enabled"); + + printk(".\n"); + + fep->sequence_done = 1; +} + +static void mii_relink(struct net_device *dev) +{ + struct fcc_enet_private *fep = dev->priv; + int duplex = 0; + + fep->old_link = fep->link; + fep->link = (fep->phy_status & PHY_STAT_LINK) ? 1 : 0; + +#ifdef MDIO_DEBUG + printk(" mii_relink: link=%d\n", fep->link); +#endif + + if (fep->link) { + if (fep->phy_status + & (PHY_STAT_100FDX | PHY_STAT_10FDX)) + duplex = 1; + fcc_restart(dev, duplex); +#ifdef MDIO_DEBUG + printk(" mii_relink: duplex=%d\n", duplex); +#endif + } +} + +static void mii_queue_relink(uint mii_reg, struct net_device *dev) +{ + struct fcc_enet_private *fep = dev->priv; + + mii_relink(dev); + + schedule_work(&fep->phy_relink); +} + +static void mii_queue_config(uint mii_reg, struct net_device *dev) +{ + struct fcc_enet_private *fep = dev->priv; + + schedule_work(&fep->phy_display_config); +} + +phy_cmd_t phy_cmd_relink[] = { { mk_mii_read(MII_BMCR), mii_queue_relink }, + { mk_mii_end, } }; +phy_cmd_t phy_cmd_config[] = { { mk_mii_read(MII_BMCR), mii_queue_config }, + { mk_mii_end, } }; + + +/* Read remainder of PHY ID. +*/ +static void +mii_discover_phy3(uint mii_reg, struct net_device *dev) +{ + struct fcc_enet_private *fep; + int i; + + fep = dev->priv; + printk("mii_reg: %08x\n", mii_reg); + fep->phy_id |= (mii_reg & 0xffff); + + for(i = 0; phy_info[i]; i++) + if((phy_info[i]->id == (fep->phy_id >> 4)) || !phy_info[i]->id) + break; + + if(!phy_info[i]) + panic("%s: PHY id 0x%08x is not supported!\n", + dev->name, fep->phy_id); + + fep->phy = phy_info[i]; + fep->phy_id_done = 1; + + printk("%s: Phy @ 0x%x, type %s (0x%08x)\n", + dev->name, fep->phy_addr, fep->phy->name, fep->phy_id); +} + +/* Scan all of the MII PHY addresses looking for someone to respond + * with a valid ID. This usually happens quickly. + */ +static void +mii_discover_phy(uint mii_reg, struct net_device *dev) +{ + struct fcc_enet_private *fep; + uint phytype; + + fep = dev->priv; + + if ((phytype = (mii_reg & 0xffff)) != 0xffff) { + + /* Got first part of ID, now get remainder. */ + fep->phy_id = phytype << 16; + mii_queue(dev, mk_mii_read(MII_PHYSID2), mii_discover_phy3); + } else { + fep->phy_addr++; + if (fep->phy_addr < 32) { + mii_queue(dev, mk_mii_read(MII_PHYSID1), + mii_discover_phy); + } else { + printk("fec: No PHY device found.\n"); + } + } +} +#endif /* CONFIG_USE_MDIO */ + +#ifdef PHY_INTERRUPT +/* This interrupt occurs when the PHY detects a link change. */ +static irqreturn_t +mii_link_interrupt(int irq, void * dev_id, struct pt_regs * regs) +{ + struct net_device *dev = dev_id; + struct fcc_enet_private *fep = dev->priv; + fcc_info_t *fip = fep->fip; + + if (fep->phy) { + /* We don't want to be interrupted by an FCC + * interrupt here. + */ + disable_irq_nosync(fip->fc_interrupt); + + mii_do_cmd(dev, fep->phy->ack_int); + /* restart and display status */ + mii_do_cmd(dev, phy_cmd_relink); + + enable_irq(fip->fc_interrupt); + } + return IRQ_HANDLED; +} +#endif /* ifdef PHY_INTERRUPT */ + +#if 0 /* This should be fixed someday */ +/* Set or clear the multicast filter for this adaptor. + * Skeleton taken from sunlance driver. + * The CPM Ethernet implementation allows Multicast as well as individual + * MAC address filtering. Some of the drivers check to make sure it is + * a group multicast address, and discard those that are not. I guess I + * will do the same for now, but just remove the test if you want + * individual filtering as well (do the upper net layers want or support + * this kind of feature?). + */ +static void +set_multicast_list(struct net_device *dev) +{ + struct fcc_enet_private *cep; + struct dev_mc_list *dmi; + u_char *mcptr, *tdptr; + volatile fcc_enet_t *ep; + int i, j; + + cep = (struct fcc_enet_private *)dev->priv; + +return; + /* Get pointer to FCC area in parameter RAM. + */ + ep = (fcc_enet_t *)dev->base_addr; + + if (dev->flags&IFF_PROMISC) { + + /* Log any net taps. */ + printk("%s: Promiscuous mode enabled.\n", dev->name); + cep->fccp->fcc_fpsmr |= FCC_PSMR_PRO; + } else { + + cep->fccp->fcc_fpsmr &= ~FCC_PSMR_PRO; + + if (dev->flags & IFF_ALLMULTI) { + /* Catch all multicast addresses, so set the + * filter to all 1's. + */ + ep->fen_gaddrh = 0xffffffff; + ep->fen_gaddrl = 0xffffffff; + } + else { + /* Clear filter and add the addresses in the list. + */ + ep->fen_gaddrh = 0; + ep->fen_gaddrl = 0; + + dmi = dev->mc_list; + + for (i=0; imc_count; i++, dmi = dmi->next) { + + /* Only support group multicast for now. + */ + if (!(dmi->dmi_addr[0] & 1)) + continue; + + /* The address in dmi_addr is LSB first, + * and taddr is MSB first. We have to + * copy bytes MSB first from dmi_addr. + */ + mcptr = (u_char *)dmi->dmi_addr + 5; + tdptr = (u_char *)&ep->fen_taddrh; + for (j=0; j<6; j++) + *tdptr++ = *mcptr--; + + /* Ask CPM to run CRC and set bit in + * filter mask. + */ + cpmp->cp_cpcr = mk_cr_cmd(cep->fip->fc_cpmpage, + cep->fip->fc_cpmblock, 0x0c, + CPM_CR_SET_GADDR) | CPM_CR_FLG; + udelay(10); + while (cpmp->cp_cpcr & CPM_CR_FLG); + } + } + } +} +#endif /* if 0 */ + + +/* Set the individual MAC address. + */ +int fcc_enet_set_mac_address(struct net_device *dev, void *p) +{ + struct sockaddr *addr= (struct sockaddr *) p; + struct fcc_enet_private *cep; + volatile fcc_enet_t *ep; + unsigned char *eap; + int i; + + cep = (struct fcc_enet_private *)(dev->priv); + ep = cep->ep; + + if (netif_running(dev)) + return -EBUSY; + + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + + eap = (unsigned char *) &(ep->fen_paddrh); + for (i=5; i>=0; i--) + *eap++ = addr->sa_data[i]; + + return 0; +} + + +/* Initialize the CPM Ethernet on FCC. + */ +static int __init fec_enet_init(void) +{ + struct net_device *dev; + struct fcc_enet_private *cep; + fcc_info_t *fip; + int i, np, err; + volatile cpm2_map_t *immap; + volatile iop_cpm2_t *io; + + immap = (cpm2_map_t *)CPM_MAP_ADDR; /* and to internal registers */ + io = &immap->im_ioport; + + np = sizeof(fcc_ports) / sizeof(fcc_info_t); + fip = fcc_ports; + + while (np-- > 0) { + /* Create an Ethernet device instance. + */ + dev = alloc_etherdev(sizeof(*cep)); + if (!dev) + return -ENOMEM; + + cep = dev->priv; + spin_lock_init(&cep->lock); + cep->fip = fip; + + init_fcc_shutdown(fip, cep, immap); + init_fcc_ioports(fip, io, immap); + init_fcc_param(fip, dev, immap); + + dev->base_addr = (unsigned long)(cep->ep); + + /* The CPM Ethernet specific entries in the device + * structure. + */ + dev->open = fcc_enet_open; + dev->hard_start_xmit = fcc_enet_start_xmit; + dev->tx_timeout = fcc_enet_timeout; + dev->watchdog_timeo = TX_TIMEOUT; + dev->stop = fcc_enet_close; + dev->get_stats = fcc_enet_get_stats; + /* dev->set_multicast_list = set_multicast_list; */ + dev->set_mac_address = fcc_enet_set_mac_address; + + init_fcc_startup(fip, dev); + + err = register_netdev(dev); + if (err) { + free_netdev(dev); + return err; + } + + printk("%s: FCC ENET Version 0.3, ", dev->name); + for (i=0; i<5; i++) + printk("%02x:", dev->dev_addr[i]); + printk("%02x\n", dev->dev_addr[5]); + +#ifdef CONFIG_USE_MDIO + /* Queue up command to detect the PHY and initialize the + * remainder of the interface. + */ + cep->phy_id_done = 0; + cep->phy_addr = fip->fc_phyaddr; + mii_queue(dev, mk_mii_read(MII_PHYSID1), mii_discover_phy); + INIT_WORK(&cep->phy_relink, mii_display_status, dev); + INIT_WORK(&cep->phy_display_config, mii_display_config, dev); +#endif /* CONFIG_USE_MDIO */ + + fip++; + } + + return 0; +} +module_init(fec_enet_init); + +/* Make sure the device is shut down during initialization. +*/ +static void __init +init_fcc_shutdown(fcc_info_t *fip, struct fcc_enet_private *cep, + volatile cpm2_map_t *immap) +{ + volatile fcc_enet_t *ep; + volatile fcc_t *fccp; + + /* Get pointer to FCC area in parameter RAM. + */ + ep = (fcc_enet_t *)(&immap->im_dprambase[fip->fc_proff]); + + /* And another to the FCC register area. + */ + fccp = (volatile fcc_t *)(&immap->im_fcc[fip->fc_fccnum]); + cep->fccp = fccp; /* Keep the pointers handy */ + cep->ep = ep; + + /* Disable receive and transmit in case someone left it running. + */ + fccp->fcc_gfmr &= ~(FCC_GFMR_ENR | FCC_GFMR_ENT); +} + +/* Initialize the I/O pins for the FCC Ethernet. +*/ +static void __init +init_fcc_ioports(fcc_info_t *fip, volatile iop_cpm2_t *io, + volatile cpm2_map_t *immap) +{ + + /* FCC1 pins are on port A/C. FCC2/3 are port B/C. + */ + if (fip->fc_proff == PROFF_FCC1) { + /* Configure port A and C pins for FCC1 Ethernet. + */ + io->iop_pdira &= ~PA1_DIRA_BOUT; + io->iop_pdira |= PA1_DIRA_BIN; + io->iop_psora &= ~PA1_PSORA_BOUT; + io->iop_psora |= PA1_PSORA_BIN; + io->iop_ppara |= (PA1_DIRA_BOUT | PA1_DIRA_BIN); + } + if (fip->fc_proff == PROFF_FCC2) { + /* Configure port B and C pins for FCC Ethernet. + */ + io->iop_pdirb &= ~PB2_DIRB_BOUT; + io->iop_pdirb |= PB2_DIRB_BIN; + io->iop_psorb &= ~PB2_PSORB_BOUT; + io->iop_psorb |= PB2_PSORB_BIN; + io->iop_pparb |= (PB2_DIRB_BOUT | PB2_DIRB_BIN); + } + if (fip->fc_proff == PROFF_FCC3) { + /* Configure port B and C pins for FCC Ethernet. + */ + io->iop_pdirb &= ~PB3_DIRB_BOUT; + io->iop_pdirb |= PB3_DIRB_BIN; + io->iop_psorb &= ~PB3_PSORB_BOUT; + io->iop_psorb |= PB3_PSORB_BIN; + io->iop_pparb |= (PB3_DIRB_BOUT | PB3_DIRB_BIN); + + io->iop_pdirc &= ~PC3_DIRC_BOUT; + io->iop_pdirc |= PC3_DIRC_BIN; + io->iop_psorc &= ~PC3_PSORC_BOUT; + io->iop_psorc |= PC3_PSORC_BIN; + io->iop_pparc |= (PC3_DIRC_BOUT | PC3_DIRC_BIN); + + } + + /* Port C has clocks...... + */ + io->iop_psorc &= ~(fip->fc_trxclocks); + io->iop_pdirc &= ~(fip->fc_trxclocks); + io->iop_pparc |= fip->fc_trxclocks; + +#ifdef CONFIG_USE_MDIO + /* ....and the MII serial clock/data. + */ + io->iop_pdatc |= (fip->fc_mdio | fip->fc_mdck); + io->iop_podrc &= ~(fip->fc_mdio | fip->fc_mdck); + io->iop_pdirc |= (fip->fc_mdio | fip->fc_mdck); + io->iop_pparc &= ~(fip->fc_mdio | fip->fc_mdck); +#endif /* CONFIG_USE_MDIO */ + + /* Configure Serial Interface clock routing. + * First, clear all FCC bits to zero, + * then set the ones we want. + */ + immap->im_cpmux.cmx_fcr &= ~(fip->fc_clockmask); + immap->im_cpmux.cmx_fcr |= fip->fc_clockroute; +} + +static void __init +init_fcc_param(fcc_info_t *fip, struct net_device *dev, + volatile cpm2_map_t *immap) +{ + unsigned char *eap; + unsigned long mem_addr; + bd_t *bd; + int i, j; + struct fcc_enet_private *cep; + volatile fcc_enet_t *ep; + volatile cbd_t *bdp; + volatile cpm_cpm2_t *cp; + + cep = (struct fcc_enet_private *)(dev->priv); + ep = cep->ep; + cp = cpmp; + + bd = (bd_t *)__res; + + /* Zero the whole thing.....I must have missed some individually. + * It works when I do this. + */ + memset((char *)ep, 0, sizeof(fcc_enet_t)); + + /* Allocate space for the buffer descriptors from regular memory. + * Initialize base addresses for the buffer descriptors. + */ + cep->rx_bd_base = (cbd_t *)kmalloc(sizeof(cbd_t) * RX_RING_SIZE, + GFP_KERNEL | GFP_DMA); + ep->fen_genfcc.fcc_rbase = __pa(cep->rx_bd_base); + cep->tx_bd_base = (cbd_t *)kmalloc(sizeof(cbd_t) * TX_RING_SIZE, + GFP_KERNEL | GFP_DMA); + ep->fen_genfcc.fcc_tbase = __pa(cep->tx_bd_base); + + cep->dirty_tx = cep->cur_tx = cep->tx_bd_base; + cep->cur_rx = cep->rx_bd_base; + + ep->fen_genfcc.fcc_rstate = (CPMFCR_GBL | CPMFCR_EB) << 24; + ep->fen_genfcc.fcc_tstate = (CPMFCR_GBL | CPMFCR_EB) << 24; + + /* Set maximum bytes per receive buffer. + * It must be a multiple of 32. + */ + ep->fen_genfcc.fcc_mrblr = PKT_MAXBLR_SIZE; + + /* Allocate space in the reserved FCC area of DPRAM for the + * internal buffers. No one uses this space (yet), so we + * can do this. Later, we will add resource management for + * this area. + */ + mem_addr = CPM_FCC_SPECIAL_BASE + (fip->fc_fccnum * 128); + ep->fen_genfcc.fcc_riptr = mem_addr; + ep->fen_genfcc.fcc_tiptr = mem_addr+32; + ep->fen_padptr = mem_addr+64; + memset((char *)(&(immap->im_dprambase[(mem_addr+64)])), 0x88, 32); + + ep->fen_genfcc.fcc_rbptr = 0; + ep->fen_genfcc.fcc_tbptr = 0; + ep->fen_genfcc.fcc_rcrc = 0; + ep->fen_genfcc.fcc_tcrc = 0; + ep->fen_genfcc.fcc_res1 = 0; + ep->fen_genfcc.fcc_res2 = 0; + + ep->fen_camptr = 0; /* CAM isn't used in this driver */ + + /* Set CRC preset and mask. + */ + ep->fen_cmask = 0xdebb20e3; + ep->fen_cpres = 0xffffffff; + + ep->fen_crcec = 0; /* CRC Error counter */ + ep->fen_alec = 0; /* alignment error counter */ + ep->fen_disfc = 0; /* discard frame counter */ + ep->fen_retlim = 15; /* Retry limit threshold */ + ep->fen_pper = 0; /* Normal persistence */ + + /* Clear hash filter tables. + */ + ep->fen_gaddrh = 0; + ep->fen_gaddrl = 0; + ep->fen_iaddrh = 0; + ep->fen_iaddrl = 0; + + /* Clear the Out-of-sequence TxBD. + */ + ep->fen_tfcstat = 0; + ep->fen_tfclen = 0; + ep->fen_tfcptr = 0; + + ep->fen_mflr = PKT_MAXBUF_SIZE; /* maximum frame length register */ + ep->fen_minflr = PKT_MINBUF_SIZE; /* minimum frame length register */ + + /* Set Ethernet station address. + * + * This is supplied in the board information structure, so we + * copy that into the controller. + * So, far we have only been given one Ethernet address. We make + * it unique by setting a few bits in the upper byte of the + * non-static part of the address. + */ + eap = (unsigned char *)&(ep->fen_paddrh); + for (i=5; i>=0; i--) { + +/* + * The EP8260 only uses FCC3, so we can safely give it the real + * MAC address. + */ +#ifdef CONFIG_SBC82xx + if (i == 5) { + /* bd->bi_enetaddr holds the SCC0 address; the FCC + devices count up from there */ + dev->dev_addr[i] = bd->bi_enetaddr[i] & ~3; + dev->dev_addr[i] += 1 + fip->fc_fccnum; + *eap++ = dev->dev_addr[i]; + } +#else +#ifndef CONFIG_RPX8260 + if (i == 3) { + dev->dev_addr[i] = bd->bi_enetaddr[i]; + dev->dev_addr[i] |= (1 << (7 - fip->fc_fccnum)); + *eap++ = dev->dev_addr[i]; + } else +#endif + { + *eap++ = dev->dev_addr[i] = bd->bi_enetaddr[i]; + } +#endif + } + + ep->fen_taddrh = 0; + ep->fen_taddrm = 0; + ep->fen_taddrl = 0; + + ep->fen_maxd1 = PKT_MAXDMA_SIZE; /* maximum DMA1 length */ + ep->fen_maxd2 = PKT_MAXDMA_SIZE; /* maximum DMA2 length */ + + /* Clear stat counters, in case we ever enable RMON. + */ + ep->fen_octc = 0; + ep->fen_colc = 0; + ep->fen_broc = 0; + ep->fen_mulc = 0; + ep->fen_uspc = 0; + ep->fen_frgc = 0; + ep->fen_ospc = 0; + ep->fen_jbrc = 0; + ep->fen_p64c = 0; + ep->fen_p65c = 0; + ep->fen_p128c = 0; + ep->fen_p256c = 0; + ep->fen_p512c = 0; + ep->fen_p1024c = 0; + + ep->fen_rfthr = 0; /* Suggested by manual */ + ep->fen_rfcnt = 0; + ep->fen_cftype = 0; + + /* Now allocate the host memory pages and initialize the + * buffer descriptors. + */ + bdp = cep->tx_bd_base; + for (i=0; icbd_sc = 0; + bdp->cbd_datlen = 0; + bdp->cbd_bufaddr = 0; + bdp++; + } + + /* Set the last buffer to wrap. + */ + bdp--; + bdp->cbd_sc |= BD_SC_WRAP; + + bdp = cep->rx_bd_base; + for (i=0; icbd_sc = BD_ENET_RX_EMPTY | BD_ENET_RX_INTR; + bdp->cbd_datlen = 0; + bdp->cbd_bufaddr = __pa(mem_addr); + mem_addr += FCC_ENET_RX_FRSIZE; + bdp++; + } + } + + /* Set the last buffer to wrap. + */ + bdp--; + bdp->cbd_sc |= BD_SC_WRAP; + + /* Let's re-initialize the channel now. We have to do it later + * than the manual describes because we have just now finished + * the BD initialization. + */ + cp->cp_cpcr = mk_cr_cmd(fip->fc_cpmpage, fip->fc_cpmblock, 0x0c, + CPM_CR_INIT_TRX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + + cep->skb_cur = cep->skb_dirty = 0; +} + +/* Let 'er rip. +*/ +static void __init +init_fcc_startup(fcc_info_t *fip, struct net_device *dev) +{ + volatile fcc_t *fccp; + struct fcc_enet_private *cep; + + cep = (struct fcc_enet_private *)(dev->priv); + fccp = cep->fccp; + +#ifdef CONFIG_RPX8260 +#ifdef PHY_INTERRUPT + /* Route PHY interrupt to IRQ. The following code only works for + * IRQ1 - IRQ7. It does not work for Port C interrupts. + */ + *((volatile u_char *) (RPX_CSR_ADDR + 13)) &= ~BCSR13_FETH_IRQMASK; + *((volatile u_char *) (RPX_CSR_ADDR + 13)) |= + ((PHY_INTERRUPT - SIU_INT_IRQ1 + 1) << 4); +#endif + /* Initialize MDIO pins. */ + *((volatile u_char *) (RPX_CSR_ADDR + 4)) &= ~BCSR4_MII_MDC; + *((volatile u_char *) (RPX_CSR_ADDR + 4)) |= + BCSR4_MII_READ | BCSR4_MII_MDIO; + /* Enable external LXT971 PHY. */ + *((volatile u_char *) (RPX_CSR_ADDR + 4)) |= BCSR4_EN_PHY; + udelay(1000); + *((volatile u_char *) (RPX_CSR_ADDR+ 4)) |= BCSR4_EN_MII; + udelay(1000); +#endif /* ifdef CONFIG_RPX8260 */ + + fccp->fcc_fcce = 0xffff; /* Clear any pending events */ + + /* Leave FCC interrupts masked for now. Will be unmasked by + * fcc_restart(). + */ + fccp->fcc_fccm = 0; + + /* Install our interrupt handler. + */ + if (request_irq(fip->fc_interrupt, fcc_enet_interrupt, 0, "fenet", + dev) < 0) + printk("Can't get FCC IRQ %d\n", fip->fc_interrupt); + +#ifdef PHY_INTERRUPT +#ifdef CONFIG_ADS8272 + if (request_irq(PHY_INTERRUPT, mii_link_interrupt, SA_SHIRQ, + "mii", dev) < 0) + printk(KERN_CRIT "Can't get MII IRQ %d\n", PHY_INTERRUPT); +#else + /* Make IRQn edge triggered. This does not work if PHY_INTERRUPT is + * on Port C. + */ + ((volatile cpm2_map_t *) CPM_MAP_ADDR)->im_intctl.ic_siexr |= + (1 << (14 - (PHY_INTERRUPT - SIU_INT_IRQ1))); + + if (request_irq(PHY_INTERRUPT, mii_link_interrupt, 0, + "mii", dev) < 0) + printk(KERN_CRIT "Can't get MII IRQ %d\n", PHY_INTERRUPT); +#endif +#endif /* PHY_INTERRUPT */ + + /* Set GFMR to enable Ethernet operating mode. + */ + fccp->fcc_gfmr = (FCC_GFMR_TCI | FCC_GFMR_MODE_ENET); + + /* Set sync/delimiters. + */ + fccp->fcc_fdsr = 0xd555; + + /* Set protocol specific processing mode for Ethernet. + * This has to be adjusted for Full Duplex operation after we can + * determine how to detect that. + */ + fccp->fcc_fpsmr = FCC_PSMR_ENCRC; + +#ifdef CONFIG_PQ2ADS + /* Enable the PHY. */ + *(volatile uint *)(BCSR_ADDR + 4) &= ~BCSR1_FETHIEN; + *(volatile uint *)(BCSR_ADDR + 4) |= BCSR1_FETH_RST; +#endif +#if defined(CONFIG_PQ2ADS) || defined(CONFIG_PQ2FADS) + /* Enable the 2nd PHY. */ + *(volatile uint *)(BCSR_ADDR + 12) &= ~BCSR3_FETHIEN2; + *(volatile uint *)(BCSR_ADDR + 12) |= BCSR3_FETH2_RST; +#endif + +#if defined(CONFIG_USE_MDIO) || defined(CONFIG_TQM8260) + /* start in full duplex mode, and negotiate speed + */ + fcc_restart (dev, 1); +#else + /* start in half duplex mode + */ + fcc_restart (dev, 0); +#endif +} + +#ifdef CONFIG_USE_MDIO +/* MII command/status interface. + * I'm not going to describe all of the details. You can find the + * protocol definition in many other places, including the data sheet + * of most PHY parts. + * I wonder what "they" were thinking (maybe weren't) when they leave + * the I2C in the CPM but I have to toggle these bits...... + */ +#ifdef CONFIG_RPX8260 + /* The EP8260 has the MDIO pins in a BCSR instead of on Port C + * like most other boards. + */ +#define MDIO_ADDR ((volatile u_char *)(RPX_CSR_ADDR + 4)) +#define MAKE_MDIO_OUTPUT *MDIO_ADDR &= ~BCSR4_MII_READ +#define MAKE_MDIO_INPUT *MDIO_ADDR |= BCSR4_MII_READ | BCSR4_MII_MDIO +#define OUT_MDIO(bit) \ + if (bit) \ + *MDIO_ADDR |= BCSR4_MII_MDIO; \ + else \ + *MDIO_ADDR &= ~BCSR4_MII_MDIO; +#define IN_MDIO (*MDIO_ADDR & BCSR4_MII_MDIO) +#define OUT_MDC(bit) \ + if (bit) \ + *MDIO_ADDR |= BCSR4_MII_MDC; \ + else \ + *MDIO_ADDR &= ~BCSR4_MII_MDC; +#else /* ifdef CONFIG_RPX8260 */ + /* This is for the usual case where the MDIO pins are on Port C. + */ +#define MDIO_ADDR (((volatile cpm2_map_t *)CPM_MAP_ADDR)->im_ioport) +#define MAKE_MDIO_OUTPUT MDIO_ADDR.iop_pdirc |= fip->fc_mdio +#define MAKE_MDIO_INPUT MDIO_ADDR.iop_pdirc &= ~fip->fc_mdio +#define OUT_MDIO(bit) \ + if (bit) \ + MDIO_ADDR.iop_pdatc |= fip->fc_mdio; \ + else \ + MDIO_ADDR.iop_pdatc &= ~fip->fc_mdio; +#define IN_MDIO ((MDIO_ADDR.iop_pdatc) & fip->fc_mdio) +#define OUT_MDC(bit) \ + if (bit) \ + MDIO_ADDR.iop_pdatc |= fip->fc_mdck; \ + else \ + MDIO_ADDR.iop_pdatc &= ~fip->fc_mdck; +#endif /* ifdef CONFIG_RPX8260 */ + +static uint +mii_send_receive(fcc_info_t *fip, uint cmd) +{ + uint retval; + int read_op, i, off; + const int us = 1; + + read_op = ((cmd & 0xf0000000) == 0x60000000); + + /* Write preamble + */ + OUT_MDIO(1); + MAKE_MDIO_OUTPUT; + OUT_MDIO(1); + for (i = 0; i < 32; i++) + { + udelay(us); + OUT_MDC(1); + udelay(us); + OUT_MDC(0); + } + + /* Write data + */ + for (i = 0, off = 31; i < (read_op ? 14 : 32); i++, --off) + { + OUT_MDIO((cmd >> off) & 0x00000001); + udelay(us); + OUT_MDC(1); + udelay(us); + OUT_MDC(0); + } + + retval = cmd; + + if (read_op) + { + retval >>= 16; + + MAKE_MDIO_INPUT; + udelay(us); + OUT_MDC(1); + udelay(us); + OUT_MDC(0); + + for (i = 0; i < 16; i++) + { + udelay(us); + OUT_MDC(1); + udelay(us); + retval <<= 1; + if (IN_MDIO) + retval++; + OUT_MDC(0); + } + } + + MAKE_MDIO_INPUT; + udelay(us); + OUT_MDC(1); + udelay(us); + OUT_MDC(0); + + return retval; +} +#endif /* CONFIG_USE_MDIO */ + +static void +fcc_stop(struct net_device *dev) +{ + struct fcc_enet_private *fep= (struct fcc_enet_private *)(dev->priv); + volatile fcc_t *fccp = fep->fccp; + fcc_info_t *fip = fep->fip; + volatile fcc_enet_t *ep = fep->ep; + volatile cpm_cpm2_t *cp = cpmp; + volatile cbd_t *bdp; + int i; + + if ((fccp->fcc_gfmr & (FCC_GFMR_ENR | FCC_GFMR_ENT)) == 0) + return; /* already down */ + + fccp->fcc_fccm = 0; + + /* issue the graceful stop tx command */ + while (cp->cp_cpcr & CPM_CR_FLG); + cp->cp_cpcr = mk_cr_cmd(fip->fc_cpmpage, fip->fc_cpmblock, + 0x0c, CPM_CR_GRA_STOP_TX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + + /* Disable transmit/receive */ + fccp->fcc_gfmr &= ~(FCC_GFMR_ENR | FCC_GFMR_ENT); + + /* issue the restart tx command */ + fccp->fcc_fcce = FCC_ENET_GRA; + while (cp->cp_cpcr & CPM_CR_FLG); + cp->cp_cpcr = mk_cr_cmd(fip->fc_cpmpage, fip->fc_cpmblock, + 0x0c, CPM_CR_RESTART_TX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + + /* free tx buffers */ + fep->skb_cur = fep->skb_dirty = 0; + for (i=0; i<=TX_RING_MOD_MASK; i++) { + if (fep->tx_skbuff[i] != NULL) { + dev_kfree_skb(fep->tx_skbuff[i]); + fep->tx_skbuff[i] = NULL; + } + } + fep->dirty_tx = fep->cur_tx = fep->tx_bd_base; + fep->tx_free = TX_RING_SIZE; + ep->fen_genfcc.fcc_tbptr = ep->fen_genfcc.fcc_tbase; + + /* Initialize the tx buffer descriptors. */ + bdp = fep->tx_bd_base; + for (i=0; icbd_sc = 0; + bdp->cbd_datlen = 0; + bdp->cbd_bufaddr = 0; + bdp++; + } + /* Set the last buffer to wrap. */ + bdp--; + bdp->cbd_sc |= BD_SC_WRAP; +} + +static void +fcc_restart(struct net_device *dev, int duplex) +{ + struct fcc_enet_private *fep = (struct fcc_enet_private *)(dev->priv); + volatile fcc_t *fccp = fep->fccp; + + /* stop any transmissions in progress */ + fcc_stop(dev); + + if (duplex) + fccp->fcc_fpsmr |= FCC_PSMR_FDE | FCC_PSMR_LPB; + else + fccp->fcc_fpsmr &= ~(FCC_PSMR_FDE | FCC_PSMR_LPB); + + /* Enable interrupts for transmit error, complete frame + * received, and any transmit buffer we have also set the + * interrupt flag. + */ + fccp->fcc_fccm = (FCC_ENET_TXE | FCC_ENET_RXF | FCC_ENET_TXB); + + /* Enable transmit/receive */ + fccp->fcc_gfmr |= FCC_GFMR_ENR | FCC_GFMR_ENT; +} + +static int +fcc_enet_open(struct net_device *dev) +{ + struct fcc_enet_private *fep = dev->priv; + +#ifdef CONFIG_USE_MDIO + fep->sequence_done = 0; + fep->link = 0; + + if (fep->phy) { + fcc_restart(dev, 0); /* always start in half-duplex */ + mii_do_cmd(dev, fep->phy->ack_int); + mii_do_cmd(dev, fep->phy->config); + mii_do_cmd(dev, phy_cmd_config); /* display configuration */ + while(!fep->sequence_done) + schedule(); + + mii_do_cmd(dev, fep->phy->startup); + netif_start_queue(dev); + return 0; /* Success */ + } + return -ENODEV; /* No PHY we understand */ +#else + fep->link = 1; + fcc_restart(dev, 0); /* always start in half-duplex */ + netif_start_queue(dev); + return 0; /* Always succeed */ +#endif /* CONFIG_USE_MDIO */ +} + diff --git a/arch/ppc/8xx_io/Kconfig b/arch/ppc/8xx_io/Kconfig new file mode 100644 index 00000000000..9e2227ec3b3 --- /dev/null +++ b/arch/ppc/8xx_io/Kconfig @@ -0,0 +1,138 @@ +# +# MPC8xx Communication options +# + +menu "MPC8xx CPM Options" + depends on 8xx + +config SCC_ENET + bool "CPM SCC Ethernet" + depends on NET_ETHERNET + help + Enable Ethernet support via the Motorola MPC8xx serial + communications controller. + +choice + prompt "SCC used for Ethernet" + depends on SCC_ENET + default SCC1_ENET + +config SCC1_ENET + bool "SCC1" + help + Use MPC8xx serial communications controller 1 to drive Ethernet + (default). + +config SCC2_ENET + bool "SCC2" + help + Use MPC8xx serial communications controller 2 to drive Ethernet. + +config SCC3_ENET + bool "SCC3" + help + Use MPC8xx serial communications controller 3 to drive Ethernet. + +endchoice + +config FEC_ENET + bool "860T FEC Ethernet" + depends on NET_ETHERNET + help + Enable Ethernet support via the Fast Ethernet Controller (FCC) on + the Motorola MPC8260. + +config USE_MDIO + bool "Use MDIO for PHY configuration" + depends on FEC_ENET + help + On some boards the hardware configuration of the ethernet PHY can be + used without any software interaction over the MDIO interface, so + all MII code can be omitted. Say N here if unsure or if you don't + need link status reports. + +config FEC_AM79C874 + bool "Support AMD79C874 PHY" + depends on USE_MDIO + +config FEC_LXT970 + bool "Support LXT970 PHY" + depends on USE_MDIO + +config FEC_LXT971 + bool "Support LXT971 PHY" + depends on USE_MDIO + +config FEC_QS6612 + bool "Support QS6612 PHY" + depends on USE_MDIO + +config ENET_BIG_BUFFERS + bool "Use Big CPM Ethernet Buffers" + depends on NET_ETHERNET + help + Allocate large buffers for MPC8xx Etherenet. Increases throughput + and decreases the likelihood of dropped packets, but costs memory. + +config HTDMSOUND + bool "Embedded Planet HIOX Audio" + depends on SOUND=y + +# This doesn't really belong here, but it is convenient to ask +# 8xx specific questions. +comment "Generic MPC8xx Options" + +config 8xx_COPYBACK + bool "Copy-Back Data Cache (else Writethrough)" + help + Saying Y here will cause the cache on an MPC8xx processor to be used + in Copy-Back mode. If you say N here, it is used in Writethrough + mode. + + If in doubt, say Y here. + +config 8xx_CPU6 + bool "CPU6 Silicon Errata (860 Pre Rev. C)" + help + MPC860 CPUs, prior to Rev C have some bugs in the silicon, which + require workarounds for Linux (and most other OSes to work). If you + get a BUG() very early in boot, this might fix the problem. For + more details read the document entitled "MPC860 Family Device Errata + Reference" on Motorola's website. This option also incurs a + performance hit. + + If in doubt, say N here. + +choice + prompt "Microcode patch selection" + default NO_UCODE_PATCH + help + Help not implemented yet, coming soon. + +config NO_UCODE_PATCH + bool "None" + +config USB_SOF_UCODE_PATCH + bool "USB SOF patch" + help + Help not implemented yet, coming soon. + +config I2C_SPI_UCODE_PATCH + bool "I2C/SPI relocation patch" + help + Help not implemented yet, coming soon. + +config I2C_SPI_SMC1_UCODE_PATCH + bool "I2C/SPI/SMC1 relocation patch" + help + Help not implemented yet, coming soon. + +endchoice + +config UCODE_PATCH + bool + default y + depends on !NO_UCODE_PATCH + +endmenu + diff --git a/arch/ppc/8xx_io/Makefile b/arch/ppc/8xx_io/Makefile new file mode 100644 index 00000000000..d8760181fe9 --- /dev/null +++ b/arch/ppc/8xx_io/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for the linux MPC8xx ppc-specific parts of comm processor +# + +obj-y := commproc.o + +obj-$(CONFIG_FEC_ENET) += fec.o +obj-$(CONFIG_SCC_ENET) += enet.o +obj-$(CONFIG_UCODE_PATCH) += micropatch.o +obj-$(CONFIG_HTDMSOUND) += cs4218_tdm.o diff --git a/arch/ppc/8xx_io/commproc.c b/arch/ppc/8xx_io/commproc.c new file mode 100644 index 00000000000..0cc2e7a9cb1 --- /dev/null +++ b/arch/ppc/8xx_io/commproc.c @@ -0,0 +1,464 @@ +/* + * General Purpose functions for the global management of the + * Communication Processor Module. + * Copyright (c) 1997 Dan Malek (dmalek@jlc.net) + * + * In addition to the individual control of the communication + * channels, there are a few functions that globally affect the + * communication processor. + * + * Buffer descriptors must be allocated from the dual ported memory + * space. The allocator for that is here. When the communication + * process is reset, we reclaim the memory available. There is + * currently no deallocator for this memory. + * The amount of space available is platform dependent. On the + * MBX, the EPPC software loads additional microcode into the + * communication processor, and uses some of the DP ram for this + * purpose. Current, the first 512 bytes and the last 256 bytes of + * memory are used. Right now I am conservative and only use the + * memory that can never be used for microcode. If there are + * applications that require more DP ram, we can expand the boundaries + * but then we have to be careful of any downloaded microcode. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern int get_pteptr(struct mm_struct *mm, unsigned long addr, pte_t **ptep); + +static void m8xx_cpm_dpinit(void); +static uint host_buffer; /* One page of host buffer */ +static uint host_end; /* end + 1 */ +cpm8xx_t *cpmp; /* Pointer to comm processor space */ + +/* CPM interrupt vector functions. +*/ +struct cpm_action { + void (*handler)(void *, struct pt_regs * regs); + void *dev_id; +}; +static struct cpm_action cpm_vecs[CPMVEC_NR]; +static irqreturn_t cpm_interrupt(int irq, void * dev, struct pt_regs * regs); +static irqreturn_t cpm_error_interrupt(int irq, void *dev, struct pt_regs * regs); +static void alloc_host_memory(void); +/* Define a table of names to identify CPM interrupt handlers in + * /proc/interrupts. + */ +const char *cpm_int_name[] = + { "error", "PC4", "PC5", "SMC2", + "SMC1", "SPI", "PC6", "Timer 4", + "", "PC7", "PC8", "PC9", + "Timer 3", "", "PC10", "PC11", + "I2C", "RISC Timer", "Timer 2", "", + "IDMA2", "IDMA1", "SDMA error", "PC12", + "PC13", "Timer 1", "PC14", "SCC4", + "SCC3", "SCC2", "SCC1", "PC15" + }; + +static void +cpm_mask_irq(unsigned int irq) +{ + int cpm_vec = irq - CPM_IRQ_OFFSET; + + ((immap_t *)IMAP_ADDR)->im_cpic.cpic_cimr &= ~(1 << cpm_vec); +} + +static void +cpm_unmask_irq(unsigned int irq) +{ + int cpm_vec = irq - CPM_IRQ_OFFSET; + + ((immap_t *)IMAP_ADDR)->im_cpic.cpic_cimr |= (1 << cpm_vec); +} + +static void +cpm_ack(unsigned int irq) +{ + /* We do not need to do anything here. */ +} + +static void +cpm_eoi(unsigned int irq) +{ + int cpm_vec = irq - CPM_IRQ_OFFSET; + + ((immap_t *)IMAP_ADDR)->im_cpic.cpic_cisr = (1 << cpm_vec); +} + +struct hw_interrupt_type cpm_pic = { + .typename = " CPM ", + .enable = cpm_unmask_irq, + .disable = cpm_mask_irq, + .ack = cpm_ack, + .end = cpm_eoi, +}; + +extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr); + +void +m8xx_cpm_reset(uint bootpage) +{ + volatile immap_t *imp; + volatile cpm8xx_t *commproc; + pte_t *pte; + + imp = (immap_t *)IMAP_ADDR; + commproc = (cpm8xx_t *)&imp->im_cpm; + +#ifdef CONFIG_UCODE_PATCH + /* Perform a reset. + */ + commproc->cp_cpcr = (CPM_CR_RST | CPM_CR_FLG); + + /* Wait for it. + */ + while (commproc->cp_cpcr & CPM_CR_FLG); + + cpm_load_patch(imp); +#endif + + /* Set SDMA Bus Request priority 5. + * On 860T, this also enables FEC priority 6. I am not sure + * this is what we realy want for some applications, but the + * manual recommends it. + * Bit 25, FAM can also be set to use FEC aggressive mode (860T). + */ + imp->im_siu_conf.sc_sdcr = 1; + + /* Reclaim the DP memory for our use. */ + m8xx_cpm_dpinit(); + + /* get the PTE for the bootpage */ + if (!get_pteptr(&init_mm, bootpage, &pte)) + panic("get_pteptr failed\n"); + + /* and make it uncachable */ + pte_val(*pte) |= _PAGE_NO_CACHE; + _tlbie(bootpage); + + host_buffer = bootpage; + host_end = host_buffer + PAGE_SIZE; + + /* Tell everyone where the comm processor resides. + */ + cpmp = (cpm8xx_t *)commproc; +} + +/* We used to do this earlier, but have to postpone as long as possible + * to ensure the kernel VM is now running. + */ +static void +alloc_host_memory(void) +{ + dma_addr_t physaddr; + + /* Set the host page for allocation. + */ + host_buffer = (uint)dma_alloc_coherent(NULL, PAGE_SIZE, &physaddr, + GFP_KERNEL); + host_end = host_buffer + PAGE_SIZE; +} + +/* This is called during init_IRQ. We used to do it above, but this + * was too early since init_IRQ was not yet called. + */ +static struct irqaction cpm_error_irqaction = { + .handler = cpm_error_interrupt, + .mask = CPU_MASK_NONE, +}; +static struct irqaction cpm_interrupt_irqaction = { + .handler = cpm_interrupt, + .mask = CPU_MASK_NONE, + .name = "CPM cascade", +}; + +void +cpm_interrupt_init(void) +{ + int i; + + /* Initialize the CPM interrupt controller. + */ + ((immap_t *)IMAP_ADDR)->im_cpic.cpic_cicr = + (CICR_SCD_SCC4 | CICR_SCC_SCC3 | CICR_SCB_SCC2 | CICR_SCA_SCC1) | + ((CPM_INTERRUPT/2) << 13) | CICR_HP_MASK; + ((immap_t *)IMAP_ADDR)->im_cpic.cpic_cimr = 0; + + /* install the CPM interrupt controller routines for the CPM + * interrupt vectors + */ + for ( i = CPM_IRQ_OFFSET ; i < CPM_IRQ_OFFSET + NR_CPM_INTS ; i++ ) + irq_desc[i].handler = &cpm_pic; + + /* Set our interrupt handler with the core CPU. */ + if (setup_irq(CPM_INTERRUPT, &cpm_interrupt_irqaction)) + panic("Could not allocate CPM IRQ!"); + + /* Install our own error handler. */ + cpm_error_irqaction.name = cpm_int_name[CPMVEC_ERROR]; + if (setup_irq(CPM_IRQ_OFFSET + CPMVEC_ERROR, &cpm_error_irqaction)) + panic("Could not allocate CPM error IRQ!"); + + ((immap_t *)IMAP_ADDR)->im_cpic.cpic_cicr |= CICR_IEN; +} + +/* + * Get the CPM interrupt vector. + */ +int +cpm_get_irq(struct pt_regs *regs) +{ + int cpm_vec; + + /* Get the vector by setting the ACK bit and then reading + * the register. + */ + ((volatile immap_t *)IMAP_ADDR)->im_cpic.cpic_civr = 1; + cpm_vec = ((volatile immap_t *)IMAP_ADDR)->im_cpic.cpic_civr; + cpm_vec >>= 11; + + return cpm_vec; +} + +/* CPM interrupt controller cascade interrupt. +*/ +static irqreturn_t +cpm_interrupt(int irq, void * dev, struct pt_regs * regs) +{ + /* This interrupt handler never actually gets called. It is + * installed only to unmask the CPM cascade interrupt in the SIU + * and to make the CPM cascade interrupt visible in /proc/interrupts. + */ + return IRQ_HANDLED; +} + +/* The CPM can generate the error interrupt when there is a race condition + * between generating and masking interrupts. All we have to do is ACK it + * and return. This is a no-op function so we don't need any special + * tests in the interrupt handler. + */ +static irqreturn_t +cpm_error_interrupt(int irq, void *dev, struct pt_regs *regs) +{ + return IRQ_HANDLED; +} + +/* A helper function to translate the handler prototype required by + * request_irq() to the handler prototype required by cpm_install_handler(). + */ +static irqreturn_t +cpm_handler_helper(int irq, void *dev_id, struct pt_regs *regs) +{ + int cpm_vec = irq - CPM_IRQ_OFFSET; + + (*cpm_vecs[cpm_vec].handler)(dev_id, regs); + + return IRQ_HANDLED; +} + +/* Install a CPM interrupt handler. + * This routine accepts a CPM interrupt vector in the range 0 to 31. + * This routine is retained for backward compatibility. Rather than using + * this routine to install a CPM interrupt handler, you can now use + * request_irq() with an IRQ in the range CPM_IRQ_OFFSET to + * CPM_IRQ_OFFSET + NR_CPM_INTS - 1 (16 to 47). + * + * Notice that the prototype of the interrupt handler function must be + * different depending on whether you install the handler with + * request_irq() or cpm_install_handler(). + */ +void +cpm_install_handler(int cpm_vec, void (*handler)(void *, struct pt_regs *regs), + void *dev_id) +{ + int err; + + /* If null handler, assume we are trying to free the IRQ. + */ + if (!handler) { + free_irq(CPM_IRQ_OFFSET + cpm_vec, dev_id); + return; + } + + if (cpm_vecs[cpm_vec].handler != 0) + printk(KERN_INFO "CPM interrupt %x replacing %x\n", + (uint)handler, (uint)cpm_vecs[cpm_vec].handler); + cpm_vecs[cpm_vec].handler = handler; + cpm_vecs[cpm_vec].dev_id = dev_id; + + if ((err = request_irq(CPM_IRQ_OFFSET + cpm_vec, cpm_handler_helper, + 0, cpm_int_name[cpm_vec], dev_id))) + printk(KERN_ERR "request_irq() returned %d for CPM vector %d\n", + err, cpm_vec); +} + +/* Free a CPM interrupt handler. + * This routine accepts a CPM interrupt vector in the range 0 to 31. + * This routine is retained for backward compatibility. + */ +void +cpm_free_handler(int cpm_vec) +{ + request_irq(CPM_IRQ_OFFSET + cpm_vec, NULL, 0, 0, + cpm_vecs[cpm_vec].dev_id); + + cpm_vecs[cpm_vec].handler = NULL; + cpm_vecs[cpm_vec].dev_id = NULL; +} + +/* We also own one page of host buffer space for the allocation of + * UART "fifos" and the like. + */ +uint +m8xx_cpm_hostalloc(uint size) +{ + uint retloc; + + if (host_buffer == 0) + alloc_host_memory(); + + if ((host_buffer + size) >= host_end) + return(0); + + retloc = host_buffer; + host_buffer += size; + + return(retloc); +} + +/* Set a baud rate generator. This needs lots of work. There are + * four BRGs, any of which can be wired to any channel. + * The internal baud rate clock is the system clock divided by 16. + * This assumes the baudrate is 16x oversampled by the uart. + */ +#define BRG_INT_CLK (((bd_t *)__res)->bi_intfreq) +#define BRG_UART_CLK (BRG_INT_CLK/16) +#define BRG_UART_CLK_DIV16 (BRG_UART_CLK/16) + +void +cpm_setbrg(uint brg, uint rate) +{ + volatile uint *bp; + + /* This is good enough to get SMCs running..... + */ + bp = (uint *)&cpmp->cp_brgc1; + bp += brg; + /* The BRG has a 12-bit counter. For really slow baud rates (or + * really fast processors), we may have to further divide by 16. + */ + if (((BRG_UART_CLK / rate) - 1) < 4096) + *bp = (((BRG_UART_CLK / rate) - 1) << 1) | CPM_BRG_EN; + else + *bp = (((BRG_UART_CLK_DIV16 / rate) - 1) << 1) | + CPM_BRG_EN | CPM_BRG_DIV16; +} + +/* + * dpalloc / dpfree bits. + */ +static spinlock_t cpm_dpmem_lock; +/* + * 16 blocks should be enough to satisfy all requests + * until the memory subsystem goes up... + */ +static rh_block_t cpm_boot_dpmem_rh_block[16]; +static rh_info_t cpm_dpmem_info; + +#define CPM_DPMEM_ALIGNMENT 8 + +void m8xx_cpm_dpinit(void) +{ + cpm8xx_t *cp = &((immap_t *)IMAP_ADDR)->im_cpm; + + spin_lock_init(&cpm_dpmem_lock); + + /* Initialize the info header */ + rh_init(&cpm_dpmem_info, CPM_DPMEM_ALIGNMENT, + sizeof(cpm_boot_dpmem_rh_block) / + sizeof(cpm_boot_dpmem_rh_block[0]), + cpm_boot_dpmem_rh_block); + + /* + * Attach the usable dpmem area. + * XXX: This is actually crap. CPM_DATAONLY_BASE and + * CPM_DATAONLY_SIZE are a subset of the available dparm. It varies + * with the processor and the microcode patches applied / activated. + * But the following should be at least safe. + */ + rh_attach_region(&cpm_dpmem_info, (void *)CPM_DATAONLY_BASE, CPM_DATAONLY_SIZE); +} + +/* + * Allocate the requested size worth of DP memory. + * This function used to return an index into the DPRAM area. + * Now it returns the actuall physical address of that area. + * use m8xx_cpm_dpram_offset() to get the index + */ +uint cpm_dpalloc(uint size, uint align) +{ + void *start; + unsigned long flags; + + spin_lock_irqsave(&cpm_dpmem_lock, flags); + cpm_dpmem_info.alignment = align; + start = rh_alloc(&cpm_dpmem_info, size, "commproc"); + spin_unlock_irqrestore(&cpm_dpmem_lock, flags); + + return (uint)start; +} +EXPORT_SYMBOL(cpm_dpalloc); + +int cpm_dpfree(uint offset) +{ + int ret; + unsigned long flags; + + spin_lock_irqsave(&cpm_dpmem_lock, flags); + ret = rh_free(&cpm_dpmem_info, (void *)offset); + spin_unlock_irqrestore(&cpm_dpmem_lock, flags); + + return ret; +} +EXPORT_SYMBOL(cpm_dpfree); + +uint cpm_dpalloc_fixed(uint offset, uint size, uint align) +{ + void *start; + unsigned long flags; + + spin_lock_irqsave(&cpm_dpmem_lock, flags); + cpm_dpmem_info.alignment = align; + start = rh_alloc_fixed(&cpm_dpmem_info, (void *)offset, size, "commproc"); + spin_unlock_irqrestore(&cpm_dpmem_lock, flags); + + return (uint)start; +} +EXPORT_SYMBOL(cpm_dpalloc_fixed); + +void cpm_dpdump(void) +{ + rh_dump(&cpm_dpmem_info); +} +EXPORT_SYMBOL(cpm_dpdump); + +void *cpm_dpram_addr(uint offset) +{ + return ((immap_t *)IMAP_ADDR)->im_cpm.cp_dpmem + offset; +} +EXPORT_SYMBOL(cpm_dpram_addr); diff --git a/arch/ppc/8xx_io/cs4218.h b/arch/ppc/8xx_io/cs4218.h new file mode 100644 index 00000000000..a3c38c5a5db --- /dev/null +++ b/arch/ppc/8xx_io/cs4218.h @@ -0,0 +1,167 @@ +#ifndef _cs4218_h_ +/* + * Hacked version of linux/drivers/sound/dmasound/dmasound.h + * + * + * Minor numbers for the sound driver. + * + * Unfortunately Creative called the codec chip of SB as a DSP. For this + * reason the /dev/dsp is reserved for digitized audio use. There is a + * device for true DSP processors but it will be called something else. + * In v3.0 it's /dev/sndproc but this could be a temporary solution. + */ +#define _cs4218_h_ + +#include +#include + +#define SND_NDEVS 256 /* Number of supported devices */ +#define SND_DEV_CTL 0 /* Control port /dev/mixer */ +#define SND_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM + synthesizer and MIDI output) */ +#define SND_DEV_MIDIN 2 /* Raw midi access */ +#define SND_DEV_DSP 3 /* Digitized voice /dev/dsp */ +#define SND_DEV_AUDIO 4 /* Sparc compatible /dev/audio */ +#define SND_DEV_DSP16 5 /* Like /dev/dsp but 16 bits/sample */ +#define SND_DEV_STATUS 6 /* /dev/sndstat */ +/* #7 not in use now. Was in 2.4. Free for use after v3.0. */ +#define SND_DEV_SEQ2 8 /* /dev/sequencer, level 2 interface */ +#define SND_DEV_SNDPROC 9 /* /dev/sndproc for programmable devices */ +#define SND_DEV_PSS SND_DEV_SNDPROC + +/* switch on various prinks */ +#define DEBUG_DMASOUND 1 + +#define MAX_AUDIO_DEV 5 +#define MAX_MIXER_DEV 4 +#define MAX_SYNTH_DEV 3 +#define MAX_MIDI_DEV 6 +#define MAX_TIMER_DEV 3 + +#define MAX_CATCH_RADIUS 10 + +#define le2be16(x) (((x)<<8 & 0xff00) | ((x)>>8 & 0x00ff)) +#define le2be16dbl(x) (((x)<<8 & 0xff00ff00) | ((x)>>8 & 0x00ff00ff)) + +#define IOCTL_IN(arg, ret) \ + do { int error = get_user(ret, (int *)(arg)); \ + if (error) return error; \ + } while (0) +#define IOCTL_OUT(arg, ret) ioctl_return((int *)(arg), ret) + +static inline int ioctl_return(int *addr, int value) +{ + return value < 0 ? value : put_user(value, addr); +} + +#define HAS_RECORD + + /* + * Initialization + */ + +/* description of the set-up applies to either hard or soft settings */ + +typedef struct { + int format; /* AFMT_* */ + int stereo; /* 0 = mono, 1 = stereo */ + int size; /* 8/16 bit*/ + int speed; /* speed */ +} SETTINGS; + + /* + * Machine definitions + */ + +typedef struct { + const char *name; + const char *name2; + void (*open)(void); + void (*release)(void); + void *(*dma_alloc)(unsigned int, int); + void (*dma_free)(void *, unsigned int); + int (*irqinit)(void); +#ifdef MODULE + void (*irqcleanup)(void); +#endif + void (*init)(void); + void (*silence)(void); + int (*setFormat)(int); + int (*setVolume)(int); + int (*setBass)(int); + int (*setTreble)(int); + int (*setGain)(int); + void (*play)(void); + void (*record)(void); /* optional */ + void (*mixer_init)(void); /* optional */ + int (*mixer_ioctl)(u_int, u_long); /* optional */ + int (*write_sq_setup)(void); /* optional */ + int (*read_sq_setup)(void); /* optional */ + int (*sq_open)(mode_t); /* optional */ + int (*state_info)(char *, size_t); /* optional */ + void (*abort_read)(void); /* optional */ + int min_dsp_speed; + int max_dsp_speed; + int version ; + int hardware_afmts ; /* OSS says we only return h'ware info */ + /* when queried via SNDCTL_DSP_GETFMTS */ + int capabilities ; /* low-level reply to SNDCTL_DSP_GETCAPS */ + SETTINGS default_hard ; /* open() or init() should set something valid */ + SETTINGS default_soft ; /* you can make it look like old OSS, if you want to */ +} MACHINE; + + /* + * Low level stuff + */ + +typedef struct { + ssize_t (*ct_ulaw)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_alaw)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_s8)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_u8)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_s16be)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_u16be)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_s16le)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_u16le)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); +} TRANS; + + + /* + * Sound queue stuff, the heart of the driver + */ + +struct sound_queue { + /* buffers allocated for this queue */ + int numBufs; /* real limits on what the user can have */ + int bufSize; /* in bytes */ + char **buffers; + + /* current parameters */ + int locked ; /* params cannot be modified when != 0 */ + int user_frags ; /* user requests this many */ + int user_frag_size ; /* of this size */ + int max_count; /* actual # fragments <= numBufs */ + int block_size; /* internal block size in bytes */ + int max_active; /* in-use fragments <= max_count */ + + /* it shouldn't be necessary to declare any of these volatile */ + int front, rear, count; + int rear_size; + /* + * The use of the playing field depends on the hardware + * + * Atari, PMac: The number of frames that are loaded/playing + * + * Amiga: Bit 0 is set: a frame is loaded + * Bit 1 is set: a frame is playing + */ + int active; + wait_queue_head_t action_queue, open_queue, sync_queue; + int open_mode; + int busy, syncing, xruns, died; +}; + +#define SLEEP(queue) interruptible_sleep_on_timeout(&queue, HZ) +#define WAKE_UP(queue) (wake_up_interruptible(&queue)) + +#endif /* _cs4218_h_ */ diff --git a/arch/ppc/8xx_io/cs4218_tdm.c b/arch/ppc/8xx_io/cs4218_tdm.c new file mode 100644 index 00000000000..89fe0ceeaa4 --- /dev/null +++ b/arch/ppc/8xx_io/cs4218_tdm.c @@ -0,0 +1,2836 @@ + +/* This is a modified version of linux/drivers/sound/dmasound.c to + * support the CS4218 codec on the 8xx TDM port. Thanks to everyone + * that contributed to the dmasound software (which includes me :-). + * + * The CS4218 is configured in Mode 4, sub-mode 0. This provides + * left/right data only on the TDM port, as a 32-bit word, per frame + * pulse. The control of the CS4218 is provided by some other means, + * like the SPI port. + * Dan Malek (dmalek@jlc.net) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* Should probably do something different with this path name..... + * Actually, I should just stop using it... + */ +#include "cs4218.h" +#include + +#include +#include +#include + +#define DMASND_CS4218 5 + +#define MAX_CATCH_RADIUS 10 +#define MIN_BUFFERS 4 +#define MIN_BUFSIZE 4 +#define MAX_BUFSIZE 128 + +#define HAS_8BIT_TABLES + +static int sq_unit = -1; +static int mixer_unit = -1; +static int state_unit = -1; +static int irq_installed = 0; +static char **sound_buffers = NULL; +static char **sound_read_buffers = NULL; + +static DEFINE_SPINLOCK(cs4218_lock); + +/* Local copies of things we put in the control register. Output + * volume, like most codecs is really attenuation. + */ +static int cs4218_rate_index; + +/* + * Stuff for outputting a beep. The values range from -327 to +327 + * so we can multiply by an amplitude in the range 0..100 to get a + * signed short value to put in the output buffer. + */ +static short beep_wform[256] = { + 0, 40, 79, 117, 153, 187, 218, 245, + 269, 288, 304, 316, 323, 327, 327, 324, + 318, 310, 299, 288, 275, 262, 249, 236, + 224, 213, 204, 196, 190, 186, 183, 182, + 182, 183, 186, 189, 192, 196, 200, 203, + 206, 208, 209, 209, 209, 207, 204, 201, + 197, 193, 188, 183, 179, 174, 170, 166, + 163, 161, 160, 159, 159, 160, 161, 162, + 164, 166, 168, 169, 171, 171, 171, 170, + 169, 167, 163, 159, 155, 150, 144, 139, + 133, 128, 122, 117, 113, 110, 107, 105, + 103, 103, 103, 103, 104, 104, 105, 105, + 105, 103, 101, 97, 92, 86, 78, 68, + 58, 45, 32, 18, 3, -11, -26, -41, + -55, -68, -79, -88, -95, -100, -102, -102, + -99, -93, -85, -75, -62, -48, -33, -16, + 0, 16, 33, 48, 62, 75, 85, 93, + 99, 102, 102, 100, 95, 88, 79, 68, + 55, 41, 26, 11, -3, -18, -32, -45, + -58, -68, -78, -86, -92, -97, -101, -103, + -105, -105, -105, -104, -104, -103, -103, -103, + -103, -105, -107, -110, -113, -117, -122, -128, + -133, -139, -144, -150, -155, -159, -163, -167, + -169, -170, -171, -171, -171, -169, -168, -166, + -164, -162, -161, -160, -159, -159, -160, -161, + -163, -166, -170, -174, -179, -183, -188, -193, + -197, -201, -204, -207, -209, -209, -209, -208, + -206, -203, -200, -196, -192, -189, -186, -183, + -182, -182, -183, -186, -190, -196, -204, -213, + -224, -236, -249, -262, -275, -288, -299, -310, + -318, -324, -327, -327, -323, -316, -304, -288, + -269, -245, -218, -187, -153, -117, -79, -40, +}; + +#define BEEP_SPEED 5 /* 22050 Hz sample rate */ +#define BEEP_BUFLEN 512 +#define BEEP_VOLUME 15 /* 0 - 100 */ + +static int beep_volume = BEEP_VOLUME; +static int beep_playing = 0; +static int beep_state = 0; +static short *beep_buf; +static void (*orig_mksound)(unsigned int, unsigned int); + +/* This is found someplace else......I guess in the keyboard driver + * we don't include. + */ +static void (*kd_mksound)(unsigned int, unsigned int); + +static int catchRadius = 0; +static int numBufs = 4, bufSize = 32; +static int numReadBufs = 4, readbufSize = 32; + + +/* TDM/Serial transmit and receive buffer descriptors. +*/ +static volatile cbd_t *rx_base, *rx_cur, *tx_base, *tx_cur; + +MODULE_PARM(catchRadius, "i"); +MODULE_PARM(numBufs, "i"); +MODULE_PARM(bufSize, "i"); +MODULE_PARM(numreadBufs, "i"); +MODULE_PARM(readbufSize, "i"); + +#define arraysize(x) (sizeof(x)/sizeof(*(x))) +#define le2be16(x) (((x)<<8 & 0xff00) | ((x)>>8 & 0x00ff)) +#define le2be16dbl(x) (((x)<<8 & 0xff00ff00) | ((x)>>8 & 0x00ff00ff)) + +#define IOCTL_IN(arg, ret) \ + do { int error = get_user(ret, (int *)(arg)); \ + if (error) return error; \ + } while (0) +#define IOCTL_OUT(arg, ret) ioctl_return((int *)(arg), ret) + +/* CS4218 serial port control in mode 4. +*/ +#define CS_INTMASK ((uint)0x40000000) +#define CS_DO1 ((uint)0x20000000) +#define CS_LATTEN ((uint)0x1f000000) +#define CS_RATTEN ((uint)0x00f80000) +#define CS_MUTE ((uint)0x00040000) +#define CS_ISL ((uint)0x00020000) +#define CS_ISR ((uint)0x00010000) +#define CS_LGAIN ((uint)0x0000f000) +#define CS_RGAIN ((uint)0x00000f00) + +#define CS_LATTEN_SET(X) (((X) & 0x1f) << 24) +#define CS_RATTEN_SET(X) (((X) & 0x1f) << 19) +#define CS_LGAIN_SET(X) (((X) & 0x0f) << 12) +#define CS_RGAIN_SET(X) (((X) & 0x0f) << 8) + +#define CS_LATTEN_GET(X) (((X) >> 24) & 0x1f) +#define CS_RATTEN_GET(X) (((X) >> 19) & 0x1f) +#define CS_LGAIN_GET(X) (((X) >> 12) & 0x0f) +#define CS_RGAIN_GET(X) (((X) >> 8) & 0x0f) + +/* The control register is effectively write only. We have to keep a copy + * of what we write. + */ +static uint cs4218_control; + +/* A place to store expanding information. +*/ +static int expand_bal; +static int expand_data; + +/* Since I can't make the microcode patch work for the SPI, I just + * clock the bits using software. + */ +static void sw_spi_init(void); +static void sw_spi_io(u_char *obuf, u_char *ibuf, uint bcnt); +static uint cs4218_ctl_write(uint ctlreg); + +/*** Some low level helpers **************************************************/ + +/* 16 bit mu-law */ + +static short ulaw2dma16[] = { + -32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956, + -23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764, + -15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412, + -11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316, + -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, + -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, + -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, + -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, + -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, + -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, + -876, -844, -812, -780, -748, -716, -684, -652, + -620, -588, -556, -524, -492, -460, -428, -396, + -372, -356, -340, -324, -308, -292, -276, -260, + -244, -228, -212, -196, -180, -164, -148, -132, + -120, -112, -104, -96, -88, -80, -72, -64, + -56, -48, -40, -32, -24, -16, -8, 0, + 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, + 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, + 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, + 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, + 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, + 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, + 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, + 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, + 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, + 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, + 876, 844, 812, 780, 748, 716, 684, 652, + 620, 588, 556, 524, 492, 460, 428, 396, + 372, 356, 340, 324, 308, 292, 276, 260, + 244, 228, 212, 196, 180, 164, 148, 132, + 120, 112, 104, 96, 88, 80, 72, 64, + 56, 48, 40, 32, 24, 16, 8, 0, +}; + +/* 16 bit A-law */ + +static short alaw2dma16[] = { + -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, + -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, + -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, + -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392, + -22016, -20992, -24064, -23040, -17920, -16896, -19968, -18944, + -30208, -29184, -32256, -31232, -26112, -25088, -28160, -27136, + -11008, -10496, -12032, -11520, -8960, -8448, -9984, -9472, + -15104, -14592, -16128, -15616, -13056, -12544, -14080, -13568, + -344, -328, -376, -360, -280, -264, -312, -296, + -472, -456, -504, -488, -408, -392, -440, -424, + -88, -72, -120, -104, -24, -8, -56, -40, + -216, -200, -248, -232, -152, -136, -184, -168, + -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184, + -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, + -688, -656, -752, -720, -560, -528, -624, -592, + -944, -912, -1008, -976, -816, -784, -880, -848, + 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, + 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, + 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368, + 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, + 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, + 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, + 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472, + 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, + 344, 328, 376, 360, 280, 264, 312, 296, + 472, 456, 504, 488, 408, 392, 440, 424, + 88, 72, 120, 104, 24, 8, 56, 40, + 216, 200, 248, 232, 152, 136, 184, 168, + 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184, + 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, + 688, 656, 752, 720, 560, 528, 624, 592, + 944, 912, 1008, 976, 816, 784, 880, 848, +}; + + +/*** Translations ************************************************************/ + + +static ssize_t cs4218_ct_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t cs4218_ct_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t cs4218_ct_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t cs4218_ct_s16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t cs4218_ct_u16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t cs4218_ctx_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t cs4218_ctx_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t cs4218_ctx_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t cs4218_ctx_s16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t cs4218_ctx_u16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t cs4218_ct_s16_read(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t cs4218_ct_u16_read(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); + + +/*** Low level stuff *********************************************************/ + +struct cs_sound_settings { + MACHINE mach; /* machine dependent things */ + SETTINGS hard; /* hardware settings */ + SETTINGS soft; /* software settings */ + SETTINGS dsp; /* /dev/dsp default settings */ + TRANS *trans_write; /* supported translations for playback */ + TRANS *trans_read; /* supported translations for record */ + int volume_left; /* volume (range is machine dependent) */ + int volume_right; + int bass; /* tone (range is machine dependent) */ + int treble; + int gain; + int minDev; /* minor device number currently open */ +}; + +static struct cs_sound_settings sound; + +static void *CS_Alloc(unsigned int size, int flags); +static void CS_Free(void *ptr, unsigned int size); +static int CS_IrqInit(void); +#ifdef MODULE +static void CS_IrqCleanup(void); +#endif /* MODULE */ +static void CS_Silence(void); +static void CS_Init(void); +static void CS_Play(void); +static void CS_Record(void); +static int CS_SetFormat(int format); +static int CS_SetVolume(int volume); +static void cs4218_tdm_tx_intr(void *devid); +static void cs4218_tdm_rx_intr(void *devid); +static void cs4218_intr(void *devid, struct pt_regs *regs); +static int cs_get_volume(uint reg); +static int cs_volume_setter(int volume, int mute); +static int cs_get_gain(uint reg); +static int cs_set_gain(int gain); +static void cs_mksound(unsigned int hz, unsigned int ticks); +static void cs_nosound(unsigned long xx); + +/*** Mid level stuff *********************************************************/ + + +static void sound_silence(void); +static void sound_init(void); +static int sound_set_format(int format); +static int sound_set_speed(int speed); +static int sound_set_stereo(int stereo); +static int sound_set_volume(int volume); + +static ssize_t sound_copy_translate(const u_char *userPtr, + size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t sound_copy_translate_read(const u_char *userPtr, + size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); + + +/* + * /dev/mixer abstraction + */ + +struct sound_mixer { + int busy; + int modify_counter; +}; + +static struct sound_mixer mixer; + +static struct sound_queue sq; +static struct sound_queue read_sq; + +#define sq_block_address(i) (sq.buffers[i]) +#define SIGNAL_RECEIVED (signal_pending(current)) +#define NON_BLOCKING(open_mode) (open_mode & O_NONBLOCK) +#define ONE_SECOND HZ /* in jiffies (100ths of a second) */ +#define NO_TIME_LIMIT 0xffffffff + +/* + * /dev/sndstat + */ + +struct sound_state { + int busy; + char buf[512]; + int len, ptr; +}; + +static struct sound_state state; + +/*** Common stuff ********************************************************/ + +static long long sound_lseek(struct file *file, long long offset, int orig); + +/*** Config & Setup **********************************************************/ + +void dmasound_setup(char *str, int *ints); + +/*** Translations ************************************************************/ + + +/* ++TeSche: radically changed for new expanding purposes... + * + * These two routines now deal with copying/expanding/translating the samples + * from user space into our buffer at the right frequency. They take care about + * how much data there's actually to read, how much buffer space there is and + * to convert samples into the right frequency/encoding. They will only work on + * complete samples so it may happen they leave some bytes in the input stream + * if the user didn't write a multiple of the current sample size. They both + * return the number of bytes they've used from both streams so you may detect + * such a situation. Luckily all programs should be able to cope with that. + * + * I think I've optimized anything as far as one can do in plain C, all + * variables should fit in registers and the loops are really short. There's + * one loop for every possible situation. Writing a more generalized and thus + * parameterized loop would only produce slower code. Feel free to optimize + * this in assembler if you like. :) + * + * I think these routines belong here because they're not yet really hardware + * independent, especially the fact that the Falcon can play 16bit samples + * only in stereo is hardcoded in both of them! + * + * ++geert: split in even more functions (one per format) + */ + +static ssize_t cs4218_ct_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + short *table = sound.soft.format == AFMT_MU_LAW ? ulaw2dma16: alaw2dma16; + ssize_t count, used; + short *p = (short *) &frame[*frameUsed]; + int val, stereo = sound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + used = count = min(userCount, frameLeft); + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + val = table[data]; + *p++ = val; + if (stereo) { + if (get_user(data, userPtr++)) + return -EFAULT; + val = table[data]; + } + *p++ = val; + count--; + } + *frameUsed += used * 4; + return stereo? used * 2: used; +} + + +static ssize_t cs4218_ct_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + short *p = (short *) &frame[*frameUsed]; + int val, stereo = sound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + used = count = min(userCount, frameLeft); + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + val = data << 8; + *p++ = val; + if (stereo) { + if (get_user(data, userPtr++)) + return -EFAULT; + val = data << 8; + } + *p++ = val; + count--; + } + *frameUsed += used * 4; + return stereo? used * 2: used; +} + + +static ssize_t cs4218_ct_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + short *p = (short *) &frame[*frameUsed]; + int val, stereo = sound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + used = count = min(userCount, frameLeft); + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + val = (data ^ 0x80) << 8; + *p++ = val; + if (stereo) { + if (get_user(data, userPtr++)) + return -EFAULT; + val = (data ^ 0x80) << 8; + } + *p++ = val; + count--; + } + *frameUsed += used * 4; + return stereo? used * 2: used; +} + + +/* This is the default format of the codec. Signed, 16-bit stereo + * generated by an application shouldn't have to be copied at all. + * We should just get the phsical address of the buffers and update + * the TDM BDs directly. + */ +static ssize_t cs4218_ct_s16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + int stereo = sound.soft.stereo; + short *fp = (short *) &frame[*frameUsed]; + + frameLeft >>= 2; + userCount >>= (stereo? 2: 1); + used = count = min(userCount, frameLeft); + if (!stereo) { + short *up = (short *) userPtr; + while (count > 0) { + short data; + if (get_user(data, up++)) + return -EFAULT; + *fp++ = data; + *fp++ = data; + count--; + } + } else { + if (copy_from_user(fp, userPtr, count * 4)) + return -EFAULT; + } + *frameUsed += used * 4; + return stereo? used * 4: used * 2; +} + +static ssize_t cs4218_ct_u16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + int mask = (sound.soft.format == AFMT_U16_LE? 0x0080: 0x8000); + int stereo = sound.soft.stereo; + short *fp = (short *) &frame[*frameUsed]; + short *up = (short *) userPtr; + + frameLeft >>= 2; + userCount >>= (stereo? 2: 1); + used = count = min(userCount, frameLeft); + while (count > 0) { + int data; + if (get_user(data, up++)) + return -EFAULT; + data ^= mask; + *fp++ = data; + if (stereo) { + if (get_user(data, up++)) + return -EFAULT; + data ^= mask; + } + *fp++ = data; + count--; + } + *frameUsed += used * 4; + return stereo? used * 4: used * 2; +} + + +static ssize_t cs4218_ctx_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + unsigned short *table = (unsigned short *) + (sound.soft.format == AFMT_MU_LAW ? ulaw2dma16: alaw2dma16); + unsigned int data = expand_data; + unsigned int *p = (unsigned int *) &frame[*frameUsed]; + int bal = expand_bal; + int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; + int utotal, ftotal; + int stereo = sound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + u_char c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(c, userPtr++)) + return -EFAULT; + data = table[c]; + if (stereo) { + if (get_user(c, userPtr++)) + return -EFAULT; + data = (data << 16) + table[c]; + } else + data = (data << 16) + data; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft) * 4; + utotal -= userCount; + return stereo? utotal * 2: utotal; +} + + +static ssize_t cs4218_ctx_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + unsigned int *p = (unsigned int *) &frame[*frameUsed]; + unsigned int data = expand_data; + int bal = expand_bal; + int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; + int stereo = sound.soft.stereo; + int utotal, ftotal; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + u_char c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(c, userPtr++)) + return -EFAULT; + data = c << 8; + if (stereo) { + if (get_user(c, userPtr++)) + return -EFAULT; + data = (data << 16) + (c << 8); + } else + data = (data << 16) + data; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft) * 4; + utotal -= userCount; + return stereo? utotal * 2: utotal; +} + + +static ssize_t cs4218_ctx_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + unsigned int *p = (unsigned int *) &frame[*frameUsed]; + unsigned int data = expand_data; + int bal = expand_bal; + int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; + int stereo = sound.soft.stereo; + int utotal, ftotal; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + u_char c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(c, userPtr++)) + return -EFAULT; + data = (c ^ 0x80) << 8; + if (stereo) { + if (get_user(c, userPtr++)) + return -EFAULT; + data = (data << 16) + ((c ^ 0x80) << 8); + } else + data = (data << 16) + data; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft) * 4; + utotal -= userCount; + return stereo? utotal * 2: utotal; +} + + +static ssize_t cs4218_ctx_s16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + unsigned int *p = (unsigned int *) &frame[*frameUsed]; + unsigned int data = expand_data; + unsigned short *up = (unsigned short *) userPtr; + int bal = expand_bal; + int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; + int stereo = sound.soft.stereo; + int utotal, ftotal; + + frameLeft >>= 2; + userCount >>= (stereo? 2: 1); + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + unsigned short c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(data, up++)) + return -EFAULT; + if (stereo) { + if (get_user(c, up++)) + return -EFAULT; + data = (data << 16) + c; + } else + data = (data << 16) + data; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft) * 4; + utotal -= userCount; + return stereo? utotal * 4: utotal * 2; +} + + +static ssize_t cs4218_ctx_u16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + int mask = (sound.soft.format == AFMT_U16_LE? 0x0080: 0x8000); + unsigned int *p = (unsigned int *) &frame[*frameUsed]; + unsigned int data = expand_data; + unsigned short *up = (unsigned short *) userPtr; + int bal = expand_bal; + int hSpeed = sound.hard.speed, sSpeed = sound.soft.speed; + int stereo = sound.soft.stereo; + int utotal, ftotal; + + frameLeft >>= 2; + userCount >>= (stereo? 2: 1); + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + unsigned short c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(data, up++)) + return -EFAULT; + data ^= mask; + if (stereo) { + if (get_user(c, up++)) + return -EFAULT; + data = (data << 16) + (c ^ mask); + } else + data = (data << 16) + data; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft) * 4; + utotal -= userCount; + return stereo? utotal * 4: utotal * 2; +} + +static ssize_t cs4218_ct_s8_read(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + short *p = (short *) &frame[*frameUsed]; + int val, stereo = sound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + used = count = min(userCount, frameLeft); + while (count > 0) { + u_char data; + + val = *p++; + data = val >> 8; + if (put_user(data, (u_char *)userPtr++)) + return -EFAULT; + if (stereo) { + val = *p; + data = val >> 8; + if (put_user(data, (u_char *)userPtr++)) + return -EFAULT; + } + p++; + count--; + } + *frameUsed += used * 4; + return stereo? used * 2: used; +} + + +static ssize_t cs4218_ct_u8_read(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + short *p = (short *) &frame[*frameUsed]; + int val, stereo = sound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + used = count = min(userCount, frameLeft); + while (count > 0) { + u_char data; + + val = *p++; + data = (val >> 8) ^ 0x80; + if (put_user(data, (u_char *)userPtr++)) + return -EFAULT; + if (stereo) { + val = *p; + data = (val >> 8) ^ 0x80; + if (put_user(data, (u_char *)userPtr++)) + return -EFAULT; + } + p++; + count--; + } + *frameUsed += used * 4; + return stereo? used * 2: used; +} + + +static ssize_t cs4218_ct_s16_read(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + int stereo = sound.soft.stereo; + short *fp = (short *) &frame[*frameUsed]; + + frameLeft >>= 2; + userCount >>= (stereo? 2: 1); + used = count = min(userCount, frameLeft); + if (!stereo) { + short *up = (short *) userPtr; + while (count > 0) { + short data; + data = *fp; + if (put_user(data, up++)) + return -EFAULT; + fp+=2; + count--; + } + } else { + if (copy_to_user((u_char *)userPtr, fp, count * 4)) + return -EFAULT; + } + *frameUsed += used * 4; + return stereo? used * 4: used * 2; +} + +static ssize_t cs4218_ct_u16_read(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + int mask = (sound.soft.format == AFMT_U16_LE? 0x0080: 0x8000); + int stereo = sound.soft.stereo; + short *fp = (short *) &frame[*frameUsed]; + short *up = (short *) userPtr; + + frameLeft >>= 2; + userCount >>= (stereo? 2: 1); + used = count = min(userCount, frameLeft); + while (count > 0) { + int data; + + data = *fp++; + data ^= mask; + if (put_user(data, up++)) + return -EFAULT; + if (stereo) { + data = *fp; + data ^= mask; + if (put_user(data, up++)) + return -EFAULT; + } + fp++; + count--; + } + *frameUsed += used * 4; + return stereo? used * 4: used * 2; +} + +static TRANS transCSNormal = { + cs4218_ct_law, cs4218_ct_law, cs4218_ct_s8, cs4218_ct_u8, + cs4218_ct_s16, cs4218_ct_u16, cs4218_ct_s16, cs4218_ct_u16 +}; + +static TRANS transCSExpand = { + cs4218_ctx_law, cs4218_ctx_law, cs4218_ctx_s8, cs4218_ctx_u8, + cs4218_ctx_s16, cs4218_ctx_u16, cs4218_ctx_s16, cs4218_ctx_u16 +}; + +static TRANS transCSNormalRead = { + NULL, NULL, cs4218_ct_s8_read, cs4218_ct_u8_read, + cs4218_ct_s16_read, cs4218_ct_u16_read, + cs4218_ct_s16_read, cs4218_ct_u16_read +}; + +/*** Low level stuff *********************************************************/ + +static void *CS_Alloc(unsigned int size, int flags) +{ + int order; + + size >>= 13; + for (order=0; order < 5; order++) { + if (size == 0) + break; + size >>= 1; + } + return (void *)__get_free_pages(flags, order); +} + +static void CS_Free(void *ptr, unsigned int size) +{ + int order; + + size >>= 13; + for (order=0; order < 5; order++) { + if (size == 0) + break; + size >>= 1; + } + free_pages((ulong)ptr, order); +} + +static int __init CS_IrqInit(void) +{ + cpm_install_handler(CPMVEC_SMC2, cs4218_intr, NULL); + return 1; +} + +#ifdef MODULE +static void CS_IrqCleanup(void) +{ + volatile smc_t *sp; + volatile cpm8xx_t *cp; + + /* First disable transmitter and receiver. + */ + sp = &cpmp->cp_smc[1]; + sp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); + + /* And now shut down the SMC. + */ + cp = cpmp; /* Get pointer to Communication Processor */ + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2, + CPM_CR_STOP_TX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + + /* Release the interrupt handler. + */ + cpm_free_handler(CPMVEC_SMC2); + + if (beep_buf) + kfree(beep_buf); + kd_mksound = orig_mksound; +} +#endif /* MODULE */ + +static void CS_Silence(void) +{ + volatile smc_t *sp; + + /* Disable transmitter. + */ + sp = &cpmp->cp_smc[1]; + sp->smc_smcmr &= ~SMCMR_TEN; +} + +/* Frequencies depend upon external oscillator. There are two + * choices, 12.288 and 11.2896 MHz. The RPCG audio supports both through + * and external control register selection bit. + */ +static int cs4218_freqs[] = { + /* 12.288 11.2896 */ + 48000, 44100, + 32000, 29400, + 24000, 22050, + 19200, 17640, + 16000, 14700, + 12000, 11025, + 9600, 8820, + 8000, 7350 +}; + +static void CS_Init(void) +{ + int i, tolerance; + + switch (sound.soft.format) { + case AFMT_S16_LE: + case AFMT_U16_LE: + sound.hard.format = AFMT_S16_LE; + break; + default: + sound.hard.format = AFMT_S16_BE; + break; + } + sound.hard.stereo = 1; + sound.hard.size = 16; + + /* + * If we have a sample rate which is within catchRadius percent + * of the requested value, we don't have to expand the samples. + * Otherwise choose the next higher rate. + */ + i = (sizeof(cs4218_freqs) / sizeof(int)); + do { + tolerance = catchRadius * cs4218_freqs[--i] / 100; + } while (sound.soft.speed > cs4218_freqs[i] + tolerance && i > 0); + if (sound.soft.speed >= cs4218_freqs[i] - tolerance) + sound.trans_write = &transCSNormal; + else + sound.trans_write = &transCSExpand; + sound.trans_read = &transCSNormalRead; + sound.hard.speed = cs4218_freqs[i]; + cs4218_rate_index = i; + + /* The CS4218 has seven selectable clock dividers for the sample + * clock. The HIOX then provides one of two external rates. + * An even numbered frequency table index uses the high external + * clock rate. + */ + *(uint *)HIOX_CSR4_ADDR &= ~(HIOX_CSR4_AUDCLKHI | HIOX_CSR4_AUDCLKSEL); + if ((i & 1) == 0) + *(uint *)HIOX_CSR4_ADDR |= HIOX_CSR4_AUDCLKHI; + i >>= 1; + *(uint *)HIOX_CSR4_ADDR |= (i & HIOX_CSR4_AUDCLKSEL); + + expand_bal = -sound.soft.speed; +} + +static int CS_SetFormat(int format) +{ + int size; + + switch (format) { + case AFMT_QUERY: + return sound.soft.format; + case AFMT_MU_LAW: + case AFMT_A_LAW: + case AFMT_U8: + case AFMT_S8: + size = 8; + break; + case AFMT_S16_BE: + case AFMT_U16_BE: + case AFMT_S16_LE: + case AFMT_U16_LE: + size = 16; + break; + default: /* :-) */ + printk(KERN_ERR "dmasound: unknown format 0x%x, using AFMT_U8\n", + format); + size = 8; + format = AFMT_U8; + } + + sound.soft.format = format; + sound.soft.size = size; + if (sound.minDev == SND_DEV_DSP) { + sound.dsp.format = format; + sound.dsp.size = size; + } + + CS_Init(); + + return format; +} + +/* Volume is the amount of attenuation we tell the codec to impose + * on the outputs. There are 32 levels, with 0 the "loudest". + */ +#define CS_VOLUME_TO_MASK(x) (31 - ((((x) - 1) * 31) / 99)) +#define CS_MASK_TO_VOLUME(y) (100 - ((y) * 99 / 31)) + +static int cs_get_volume(uint reg) +{ + int volume; + + volume = CS_MASK_TO_VOLUME(CS_LATTEN_GET(reg)); + volume |= CS_MASK_TO_VOLUME(CS_RATTEN_GET(reg)) << 8; + return volume; +} + +static int cs_volume_setter(int volume, int mute) +{ + uint tempctl; + + if (mute && volume == 0) { + tempctl = cs4218_control | CS_MUTE; + } else { + tempctl = cs4218_control & ~CS_MUTE; + tempctl = tempctl & ~(CS_LATTEN | CS_RATTEN); + tempctl |= CS_LATTEN_SET(CS_VOLUME_TO_MASK(volume & 0xff)); + tempctl |= CS_RATTEN_SET(CS_VOLUME_TO_MASK((volume >> 8) & 0xff)); + volume = cs_get_volume(tempctl); + } + if (tempctl != cs4218_control) { + cs4218_ctl_write(tempctl); + } + return volume; +} + + +/* Gain has 16 steps from 0 to 15. These are in 1.5dB increments from + * 0 (no gain) to 22.5 dB. + */ +#define CS_RECLEVEL_TO_GAIN(v) \ + ((v) < 0 ? 0 : (v) > 100 ? 15 : (v) * 3 / 20) +#define CS_GAIN_TO_RECLEVEL(v) (((v) * 20 + 2) / 3) + +static int cs_get_gain(uint reg) +{ + int gain; + + gain = CS_GAIN_TO_RECLEVEL(CS_LGAIN_GET(reg)); + gain |= CS_GAIN_TO_RECLEVEL(CS_RGAIN_GET(reg)) << 8; + return gain; +} + +static int cs_set_gain(int gain) +{ + uint tempctl; + + tempctl = cs4218_control & ~(CS_LGAIN | CS_RGAIN); + tempctl |= CS_LGAIN_SET(CS_RECLEVEL_TO_GAIN(gain & 0xff)); + tempctl |= CS_RGAIN_SET(CS_RECLEVEL_TO_GAIN((gain >> 8) & 0xff)); + gain = cs_get_gain(tempctl); + + if (tempctl != cs4218_control) { + cs4218_ctl_write(tempctl); + } + return gain; +} + +static int CS_SetVolume(int volume) +{ + return cs_volume_setter(volume, CS_MUTE); +} + +static void CS_Play(void) +{ + int i, count; + unsigned long flags; + volatile cbd_t *bdp; + volatile cpm8xx_t *cp; + + /* Protect buffer */ + spin_lock_irqsave(&cs4218_lock, flags); +#if 0 + if (awacs_beep_state) { + /* sound takes precedence over beeps */ + out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); + out_le32(&awacs->control, + (in_le32(&awacs->control) & ~0x1f00) + | (awacs_rate_index << 8)); + out_le32(&awacs->byteswap, sound.hard.format != AFMT_S16_BE); + out_le32(&awacs_txdma->cmdptr, virt_to_bus(&(awacs_tx_cmds[(sq.front+sq.active) % sq.max_count]))); + + beep_playing = 0; + awacs_beep_state = 0; + } +#endif + i = sq.front + sq.active; + if (i >= sq.max_count) + i -= sq.max_count; + while (sq.active < 2 && sq.active < sq.count) { + count = (sq.count == sq.active + 1)?sq.rear_size:sq.block_size; + if (count < sq.block_size && !sq.syncing) + /* last block not yet filled, and we're not syncing. */ + break; + + bdp = &tx_base[i]; + bdp->cbd_datlen = count; + + flush_dcache_range((ulong)sound_buffers[i], + (ulong)(sound_buffers[i] + count)); + + if (++i >= sq.max_count) + i = 0; + + if (sq.active == 0) { + /* The SMC does not load its fifo until the first + * TDM frame pulse, so the transmit data gets shifted + * by one word. To compensate for this, we incorrectly + * transmit the first buffer and shorten it by one + * word. Subsequent buffers are then aligned properly. + */ + bdp->cbd_datlen -= 2; + + /* Start up the SMC Transmitter. + */ + cp = cpmp; + cp->cp_smc[1].smc_smcmr |= SMCMR_TEN; + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2, + CPM_CR_RESTART_TX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + } + + /* Buffer is ready now. + */ + bdp->cbd_sc |= BD_SC_READY; + + ++sq.active; + } + spin_unlock_irqrestore(&cs4218_lock, flags); +} + + +static void CS_Record(void) +{ + unsigned long flags; + volatile smc_t *sp; + + if (read_sq.active) + return; + + /* Protect buffer */ + spin_lock_irqsave(&cs4218_lock, flags); + + /* This is all we have to do......Just start it up. + */ + sp = &cpmp->cp_smc[1]; + sp->smc_smcmr |= SMCMR_REN; + + read_sq.active = 1; + + spin_unlock_irqrestore(&cs4218_lock, flags); +} + + +static void +cs4218_tdm_tx_intr(void *devid) +{ + int i = sq.front; + volatile cbd_t *bdp; + + while (sq.active > 0) { + bdp = &tx_base[i]; + if (bdp->cbd_sc & BD_SC_READY) + break; /* this frame is still going */ + --sq.count; + --sq.active; + if (++i >= sq.max_count) + i = 0; + } + if (i != sq.front) + WAKE_UP(sq.action_queue); + sq.front = i; + + CS_Play(); + + if (!sq.active) + WAKE_UP(sq.sync_queue); +} + + +static void +cs4218_tdm_rx_intr(void *devid) +{ + + /* We want to blow 'em off when shutting down. + */ + if (read_sq.active == 0) + return; + + /* Check multiple buffers in case we were held off from + * interrupt processing for a long time. Geeze, I really hope + * this doesn't happen. + */ + while ((rx_base[read_sq.rear].cbd_sc & BD_SC_EMPTY) == 0) { + + /* Invalidate the data cache range for this buffer. + */ + invalidate_dcache_range( + (uint)(sound_read_buffers[read_sq.rear]), + (uint)(sound_read_buffers[read_sq.rear] + read_sq.block_size)); + + /* Make buffer available again and move on. + */ + rx_base[read_sq.rear].cbd_sc |= BD_SC_EMPTY; + read_sq.rear++; + + /* Wrap the buffer ring. + */ + if (read_sq.rear >= read_sq.max_active) + read_sq.rear = 0; + + /* If we have caught up to the front buffer, bump it. + * This will cause weird (but not fatal) results if the + * read loop is currently using this buffer. The user is + * behind in this case anyway, so weird things are going + * to happen. + */ + if (read_sq.rear == read_sq.front) { + read_sq.front++; + if (read_sq.front >= read_sq.max_active) + read_sq.front = 0; + } + } + + WAKE_UP(read_sq.action_queue); +} + +static void cs_nosound(unsigned long xx) +{ + unsigned long flags; + + /* not sure if this is needed, since hardware command is #if 0'd */ + spin_lock_irqsave(&cs4218_lock, flags); + if (beep_playing) { +#if 0 + st_le16(&beep_dbdma_cmd->command, DBDMA_STOP); +#endif + beep_playing = 0; + } + spin_unlock_irqrestore(&cs4218_lock, flags); +} + +static struct timer_list beep_timer = TIMER_INITIALIZER(cs_nosound, 0, 0); +}; + +static void cs_mksound(unsigned int hz, unsigned int ticks) +{ + unsigned long flags; + int beep_speed = BEEP_SPEED; + int srate = cs4218_freqs[beep_speed]; + int period, ncycles, nsamples; + int i, j, f; + short *p; + static int beep_hz_cache; + static int beep_nsamples_cache; + static int beep_volume_cache; + + if (hz <= srate / BEEP_BUFLEN || hz > srate / 2) { +#if 1 + /* this is a hack for broken X server code */ + hz = 750; + ticks = 12; +#else + /* cancel beep currently playing */ + awacs_nosound(0); + return; +#endif + } + /* lock while modifying beep_timer */ + spin_lock_irqsave(&cs4218_lock, flags); + del_timer(&beep_timer); + if (ticks) { + beep_timer.expires = jiffies + ticks; + add_timer(&beep_timer); + } + if (beep_playing || sq.active || beep_buf == NULL) { + spin_unlock_irqrestore(&cs4218_lock, flags); + return; /* too hard, sorry :-( */ + } + beep_playing = 1; +#if 0 + st_le16(&beep_dbdma_cmd->command, OUTPUT_MORE + BR_ALWAYS); +#endif + spin_unlock_irqrestore(&cs4218_lock, flags); + + if (hz == beep_hz_cache && beep_volume == beep_volume_cache) { + nsamples = beep_nsamples_cache; + } else { + period = srate * 256 / hz; /* fixed point */ + ncycles = BEEP_BUFLEN * 256 / period; + nsamples = (period * ncycles) >> 8; + f = ncycles * 65536 / nsamples; + j = 0; + p = beep_buf; + for (i = 0; i < nsamples; ++i, p += 2) { + p[0] = p[1] = beep_wform[j >> 8] * beep_volume; + j = (j + f) & 0xffff; + } + beep_hz_cache = hz; + beep_volume_cache = beep_volume; + beep_nsamples_cache = nsamples; + } + +#if 0 + st_le16(&beep_dbdma_cmd->req_count, nsamples*4); + st_le16(&beep_dbdma_cmd->xfer_status, 0); + st_le32(&beep_dbdma_cmd->cmd_dep, virt_to_bus(beep_dbdma_cmd)); + st_le32(&beep_dbdma_cmd->phy_addr, virt_to_bus(beep_buf)); + awacs_beep_state = 1; + + spin_lock_irqsave(&cs4218_lock, flags); + if (beep_playing) { /* i.e. haven't been terminated already */ + out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16); + out_le32(&awacs->control, + (in_le32(&awacs->control) & ~0x1f00) + | (beep_speed << 8)); + out_le32(&awacs->byteswap, 0); + out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd)); + out_le32(&awacs_txdma->control, RUN | (RUN << 16)); + } + spin_unlock_irqrestore(&cs4218_lock, flags); +#endif +} + +static MACHINE mach_cs4218 = { + .owner = THIS_MODULE, + .name = "HIOX CS4218", + .name2 = "Built-in Sound", + .dma_alloc = CS_Alloc, + .dma_free = CS_Free, + .irqinit = CS_IrqInit, +#ifdef MODULE + .irqcleanup = CS_IrqCleanup, +#endif /* MODULE */ + .init = CS_Init, + .silence = CS_Silence, + .setFormat = CS_SetFormat, + .setVolume = CS_SetVolume, + .play = CS_Play +}; + + +/*** Mid level stuff *********************************************************/ + + +static void sound_silence(void) +{ + /* update hardware settings one more */ + (*sound.mach.init)(); + + (*sound.mach.silence)(); +} + + +static void sound_init(void) +{ + (*sound.mach.init)(); +} + + +static int sound_set_format(int format) +{ + return(*sound.mach.setFormat)(format); +} + + +static int sound_set_speed(int speed) +{ + if (speed < 0) + return(sound.soft.speed); + + sound.soft.speed = speed; + (*sound.mach.init)(); + if (sound.minDev == SND_DEV_DSP) + sound.dsp.speed = sound.soft.speed; + + return(sound.soft.speed); +} + + +static int sound_set_stereo(int stereo) +{ + if (stereo < 0) + return(sound.soft.stereo); + + stereo = !!stereo; /* should be 0 or 1 now */ + + sound.soft.stereo = stereo; + if (sound.minDev == SND_DEV_DSP) + sound.dsp.stereo = stereo; + (*sound.mach.init)(); + + return(stereo); +} + + +static int sound_set_volume(int volume) +{ + return(*sound.mach.setVolume)(volume); +} + +static ssize_t sound_copy_translate(const u_char *userPtr, + size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t (*ct_func)(const u_char *, size_t, u_char *, ssize_t *, ssize_t) = NULL; + + switch (sound.soft.format) { + case AFMT_MU_LAW: + ct_func = sound.trans_write->ct_ulaw; + break; + case AFMT_A_LAW: + ct_func = sound.trans_write->ct_alaw; + break; + case AFMT_S8: + ct_func = sound.trans_write->ct_s8; + break; + case AFMT_U8: + ct_func = sound.trans_write->ct_u8; + break; + case AFMT_S16_BE: + ct_func = sound.trans_write->ct_s16be; + break; + case AFMT_U16_BE: + ct_func = sound.trans_write->ct_u16be; + break; + case AFMT_S16_LE: + ct_func = sound.trans_write->ct_s16le; + break; + case AFMT_U16_LE: + ct_func = sound.trans_write->ct_u16le; + break; + } + if (ct_func) + return ct_func(userPtr, userCount, frame, frameUsed, frameLeft); + else + return 0; +} + +static ssize_t sound_copy_translate_read(const u_char *userPtr, + size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t (*ct_func)(const u_char *, size_t, u_char *, ssize_t *, ssize_t) = NULL; + + switch (sound.soft.format) { + case AFMT_MU_LAW: + ct_func = sound.trans_read->ct_ulaw; + break; + case AFMT_A_LAW: + ct_func = sound.trans_read->ct_alaw; + break; + case AFMT_S8: + ct_func = sound.trans_read->ct_s8; + break; + case AFMT_U8: + ct_func = sound.trans_read->ct_u8; + break; + case AFMT_S16_BE: + ct_func = sound.trans_read->ct_s16be; + break; + case AFMT_U16_BE: + ct_func = sound.trans_read->ct_u16be; + break; + case AFMT_S16_LE: + ct_func = sound.trans_read->ct_s16le; + break; + case AFMT_U16_LE: + ct_func = sound.trans_read->ct_u16le; + break; + } + if (ct_func) + return ct_func(userPtr, userCount, frame, frameUsed, frameLeft); + else + return 0; +} + + +/* + * /dev/mixer abstraction + */ + +static int mixer_open(struct inode *inode, struct file *file) +{ + mixer.busy = 1; + return nonseekable_open(inode, file); +} + + +static int mixer_release(struct inode *inode, struct file *file) +{ + mixer.busy = 0; + return 0; +} + + +static int mixer_ioctl(struct inode *inode, struct file *file, u_int cmd, + u_long arg) +{ + int data; + uint tmpcs; + + if (_SIOC_DIR(cmd) & _SIOC_WRITE) + mixer.modify_counter++; + if (cmd == OSS_GETVERSION) + return IOCTL_OUT(arg, SOUND_VERSION); + switch (cmd) { + case SOUND_MIXER_INFO: { + mixer_info info; + strlcpy(info.id, "CS4218_TDM", sizeof(info.id)); + strlcpy(info.name, "CS4218_TDM", sizeof(info.name)); + info.name[sizeof(info.name)-1] = 0; + info.modify_counter = mixer.modify_counter; + if (copy_to_user((int *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + case SOUND_MIXER_READ_DEVMASK: + data = SOUND_MASK_VOLUME | SOUND_MASK_LINE + | SOUND_MASK_MIC | SOUND_MASK_RECLEV + | SOUND_MASK_ALTPCM; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECMASK: + data = SOUND_MASK_LINE | SOUND_MASK_MIC; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECSRC: + if (cs4218_control & CS_DO1) + data = SOUND_MASK_LINE; + else + data = SOUND_MASK_MIC; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_RECSRC: + IOCTL_IN(arg, data); + data &= (SOUND_MASK_LINE | SOUND_MASK_MIC); + if (data & SOUND_MASK_LINE) + tmpcs = cs4218_control | + (CS_ISL | CS_ISR | CS_DO1); + if (data & SOUND_MASK_MIC) + tmpcs = cs4218_control & + ~(CS_ISL | CS_ISR | CS_DO1); + if (tmpcs != cs4218_control) + cs4218_ctl_write(tmpcs); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_STEREODEVS: + data = SOUND_MASK_VOLUME | SOUND_MASK_RECLEV; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_CAPS: + return IOCTL_OUT(arg, 0); + case SOUND_MIXER_READ_VOLUME: + data = (cs4218_control & CS_MUTE)? 0: + cs_get_volume(cs4218_control); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_VOLUME: + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, sound_set_volume(data)); + case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */ + IOCTL_IN(arg, data); + beep_volume = data & 0xff; + /* fall through */ + case SOUND_MIXER_READ_ALTPCM: + return IOCTL_OUT(arg, beep_volume); + case SOUND_MIXER_WRITE_RECLEV: + IOCTL_IN(arg, data); + data = cs_set_gain(data); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECLEV: + data = cs_get_gain(cs4218_control); + return IOCTL_OUT(arg, data); + } + + return -EINVAL; +} + + +static struct file_operations mixer_fops = +{ + .owner = THIS_MODULE, + .llseek = sound_lseek, + .ioctl = mixer_ioctl, + .open = mixer_open, + .release = mixer_release, +}; + + +static void __init mixer_init(void) +{ + mixer_unit = register_sound_mixer(&mixer_fops, -1); + if (mixer_unit < 0) + return; + + mixer.busy = 0; + sound.treble = 0; + sound.bass = 0; + + /* Set Line input, no gain, no attenuation. + */ + cs4218_control = CS_ISL | CS_ISR | CS_DO1; + cs4218_control |= CS_LGAIN_SET(0) | CS_RGAIN_SET(0); + cs4218_control |= CS_LATTEN_SET(0) | CS_RATTEN_SET(0); + cs4218_ctl_write(cs4218_control); +} + + +/* + * Sound queue stuff, the heart of the driver + */ + + +static int sq_allocate_buffers(void) +{ + int i; + + if (sound_buffers) + return 0; + sound_buffers = kmalloc (numBufs * sizeof(char *), GFP_KERNEL); + if (!sound_buffers) + return -ENOMEM; + for (i = 0; i < numBufs; i++) { + sound_buffers[i] = sound.mach.dma_alloc (bufSize << 10, GFP_KERNEL); + if (!sound_buffers[i]) { + while (i--) + sound.mach.dma_free (sound_buffers[i], bufSize << 10); + kfree (sound_buffers); + sound_buffers = 0; + return -ENOMEM; + } + } + return 0; +} + + +static void sq_release_buffers(void) +{ + int i; + + if (sound_buffers) { + for (i = 0; i < numBufs; i++) + sound.mach.dma_free (sound_buffers[i], bufSize << 10); + kfree (sound_buffers); + sound_buffers = 0; + } +} + + +static int sq_allocate_read_buffers(void) +{ + int i; + + if (sound_read_buffers) + return 0; + sound_read_buffers = kmalloc(numReadBufs * sizeof(char *), GFP_KERNEL); + if (!sound_read_buffers) + return -ENOMEM; + for (i = 0; i < numBufs; i++) { + sound_read_buffers[i] = sound.mach.dma_alloc (readbufSize<<10, + GFP_KERNEL); + if (!sound_read_buffers[i]) { + while (i--) + sound.mach.dma_free (sound_read_buffers[i], + readbufSize << 10); + kfree (sound_read_buffers); + sound_read_buffers = 0; + return -ENOMEM; + } + } + return 0; +} + +static void sq_release_read_buffers(void) +{ + int i; + + if (sound_read_buffers) { + cpmp->cp_smc[1].smc_smcmr &= ~SMCMR_REN; + for (i = 0; i < numReadBufs; i++) + sound.mach.dma_free (sound_read_buffers[i], + bufSize << 10); + kfree (sound_read_buffers); + sound_read_buffers = 0; + } +} + + +static void sq_setup(int numBufs, int bufSize, char **write_buffers) +{ + int i; + volatile cbd_t *bdp; + volatile cpm8xx_t *cp; + volatile smc_t *sp; + + /* Make sure the SMC transmit is shut down. + */ + cp = cpmp; + sp = &cpmp->cp_smc[1]; + sp->smc_smcmr &= ~SMCMR_TEN; + + sq.max_count = numBufs; + sq.max_active = numBufs; + sq.block_size = bufSize; + sq.buffers = write_buffers; + + sq.front = sq.count = 0; + sq.rear = -1; + sq.syncing = 0; + sq.active = 0; + + bdp = tx_base; + for (i=0; icbd_bufaddr = virt_to_bus(write_buffers[i]); + bdp++; + } + + /* This causes the SMC to sync up with the first buffer again. + */ + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2, CPM_CR_INIT_TX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); +} + +static void read_sq_setup(int numBufs, int bufSize, char **read_buffers) +{ + int i; + volatile cbd_t *bdp; + volatile cpm8xx_t *cp; + volatile smc_t *sp; + + /* Make sure the SMC receive is shut down. + */ + cp = cpmp; + sp = &cpmp->cp_smc[1]; + sp->smc_smcmr &= ~SMCMR_REN; + + read_sq.max_count = numBufs; + read_sq.max_active = numBufs; + read_sq.block_size = bufSize; + read_sq.buffers = read_buffers; + + read_sq.front = read_sq.count = 0; + read_sq.rear = 0; + read_sq.rear_size = 0; + read_sq.syncing = 0; + read_sq.active = 0; + + bdp = rx_base; + for (i=0; icbd_bufaddr = virt_to_bus(read_buffers[i]); + bdp->cbd_datlen = read_sq.block_size; + bdp++; + } + + /* This causes the SMC to sync up with the first buffer again. + */ + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2, CPM_CR_INIT_RX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); +} + + +static void sq_play(void) +{ + (*sound.mach.play)(); +} + + +/* ++TeSche: radically changed this one too */ + +static ssize_t sq_write(struct file *file, const char *src, size_t uLeft, + loff_t *ppos) +{ + ssize_t uWritten = 0; + u_char *dest; + ssize_t uUsed, bUsed, bLeft; + + /* ++TeSche: Is something like this necessary? + * Hey, that's an honest question! Or does any other part of the + * filesystem already checks this situation? I really don't know. + */ + if (uLeft == 0) + return 0; + + /* The interrupt doesn't start to play the last, incomplete frame. + * Thus we can append to it without disabling the interrupts! (Note + * also that sq.rear isn't affected by the interrupt.) + */ + + if (sq.count > 0 && (bLeft = sq.block_size-sq.rear_size) > 0) { + dest = sq_block_address(sq.rear); + bUsed = sq.rear_size; + uUsed = sound_copy_translate(src, uLeft, dest, &bUsed, bLeft); + if (uUsed <= 0) + return uUsed; + src += uUsed; + uWritten += uUsed; + uLeft -= uUsed; + sq.rear_size = bUsed; + } + + do { + while (sq.count == sq.max_active) { + sq_play(); + if (NON_BLOCKING(sq.open_mode)) + return uWritten > 0 ? uWritten : -EAGAIN; + SLEEP(sq.action_queue); + if (SIGNAL_RECEIVED) + return uWritten > 0 ? uWritten : -EINTR; + } + + /* Here, we can avoid disabling the interrupt by first + * copying and translating the data, and then updating + * the sq variables. Until this is done, the interrupt + * won't see the new frame and we can work on it + * undisturbed. + */ + + dest = sq_block_address((sq.rear+1) % sq.max_count); + bUsed = 0; + bLeft = sq.block_size; + uUsed = sound_copy_translate(src, uLeft, dest, &bUsed, bLeft); + if (uUsed <= 0) + break; + src += uUsed; + uWritten += uUsed; + uLeft -= uUsed; + if (bUsed) { + sq.rear = (sq.rear+1) % sq.max_count; + sq.rear_size = bUsed; + sq.count++; + } + } while (bUsed); /* uUsed may have been 0 */ + + sq_play(); + + return uUsed < 0? uUsed: uWritten; +} + + +/***********/ + +/* Here is how the values are used for reading. + * The value 'active' simply indicates the DMA is running. This is + * done so the driver semantics are DMA starts when the first read is + * posted. The value 'front' indicates the buffer we should next + * send to the user. The value 'rear' indicates the buffer the DMA is + * currently filling. When 'front' == 'rear' the buffer "ring" is + * empty (we always have an empty available). The 'rear_size' is used + * to track partial offsets into the current buffer. Right now, I just keep + * The DMA running. If the reader can't keep up, the interrupt tosses + * the oldest buffer. We could also shut down the DMA in this case. + */ +static ssize_t sq_read(struct file *file, char *dst, size_t uLeft, + loff_t *ppos) +{ + + ssize_t uRead, bLeft, bUsed, uUsed; + + if (uLeft == 0) + return 0; + + if (!read_sq.active) + CS_Record(); /* Kick off the record process. */ + + uRead = 0; + + /* Move what the user requests, depending upon other options. + */ + while (uLeft > 0) { + + /* When front == rear, the DMA is not done yet. + */ + while (read_sq.front == read_sq.rear) { + if (NON_BLOCKING(read_sq.open_mode)) { + return uRead > 0 ? uRead : -EAGAIN; + } + SLEEP(read_sq.action_queue); + if (SIGNAL_RECEIVED) + return uRead > 0 ? uRead : -EINTR; + } + + /* The amount we move is either what is left in the + * current buffer or what the user wants. + */ + bLeft = read_sq.block_size - read_sq.rear_size; + bUsed = read_sq.rear_size; + uUsed = sound_copy_translate_read(dst, uLeft, + read_sq.buffers[read_sq.front], &bUsed, bLeft); + if (uUsed <= 0) + return uUsed; + dst += uUsed; + uRead += uUsed; + uLeft -= uUsed; + read_sq.rear_size += bUsed; + if (read_sq.rear_size >= read_sq.block_size) { + read_sq.rear_size = 0; + read_sq.front++; + if (read_sq.front >= read_sq.max_active) + read_sq.front = 0; + } + } + return uRead; +} + +static int sq_open(struct inode *inode, struct file *file) +{ + int rc = 0; + + if (file->f_mode & FMODE_WRITE) { + if (sq.busy) { + rc = -EBUSY; + if (NON_BLOCKING(file->f_flags)) + goto err_out; + rc = -EINTR; + while (sq.busy) { + SLEEP(sq.open_queue); + if (SIGNAL_RECEIVED) + goto err_out; + } + } + sq.busy = 1; /* Let's play spot-the-race-condition */ + + if (sq_allocate_buffers()) goto err_out_nobusy; + + sq_setup(numBufs, bufSize<<10,sound_buffers); + sq.open_mode = file->f_mode; + } + + + if (file->f_mode & FMODE_READ) { + if (read_sq.busy) { + rc = -EBUSY; + if (NON_BLOCKING(file->f_flags)) + goto err_out; + rc = -EINTR; + while (read_sq.busy) { + SLEEP(read_sq.open_queue); + if (SIGNAL_RECEIVED) + goto err_out; + } + rc = 0; + } + read_sq.busy = 1; + if (sq_allocate_read_buffers()) goto err_out_nobusy; + + read_sq_setup(numReadBufs,readbufSize<<10, sound_read_buffers); + read_sq.open_mode = file->f_mode; + } + + /* Start up the 4218 by: + * Reset. + * Enable, unreset. + */ + *((volatile uint *)HIOX_CSR4_ADDR) &= ~HIOX_CSR4_RSTAUDIO; + eieio(); + *((volatile uint *)HIOX_CSR4_ADDR) |= HIOX_CSR4_ENAUDIO; + mdelay(50); + *((volatile uint *)HIOX_CSR4_ADDR) |= HIOX_CSR4_RSTAUDIO; + + /* We need to send the current control word in case someone + * opened /dev/mixer and changed things while we were shut + * down. Chances are good the initialization that follows + * would have done this, but it is still possible it wouldn't. + */ + cs4218_ctl_write(cs4218_control); + + sound.minDev = iminor(inode) & 0x0f; + sound.soft = sound.dsp; + sound.hard = sound.dsp; + sound_init(); + if ((iminor(inode) & 0x0f) == SND_DEV_AUDIO) { + sound_set_speed(8000); + sound_set_stereo(0); + sound_set_format(AFMT_MU_LAW); + } + + return nonseekable_open(inode, file); + +err_out_nobusy: + if (file->f_mode & FMODE_WRITE) { + sq.busy = 0; + WAKE_UP(sq.open_queue); + } + if (file->f_mode & FMODE_READ) { + read_sq.busy = 0; + WAKE_UP(read_sq.open_queue); + } +err_out: + return rc; +} + + +static void sq_reset(void) +{ + sound_silence(); + sq.active = 0; + sq.count = 0; + sq.front = (sq.rear+1) % sq.max_count; +#if 0 + init_tdm_buffers(); +#endif +} + + +static int sq_fsync(struct file *filp, struct dentry *dentry) +{ + int rc = 0; + + sq.syncing = 1; + sq_play(); /* there may be an incomplete frame waiting */ + + while (sq.active) { + SLEEP(sq.sync_queue); + if (SIGNAL_RECEIVED) { + /* While waiting for audio output to drain, an + * interrupt occurred. Stop audio output immediately + * and clear the queue. */ + sq_reset(); + rc = -EINTR; + break; + } + } + + sq.syncing = 0; + return rc; +} + +static int sq_release(struct inode *inode, struct file *file) +{ + int rc = 0; + + if (sq.busy) + rc = sq_fsync(file, file->f_dentry); + sound.soft = sound.dsp; + sound.hard = sound.dsp; + sound_silence(); + + sq_release_read_buffers(); + sq_release_buffers(); + + if (file->f_mode & FMODE_READ) { + read_sq.busy = 0; + WAKE_UP(read_sq.open_queue); + } + + if (file->f_mode & FMODE_WRITE) { + sq.busy = 0; + WAKE_UP(sq.open_queue); + } + + /* Shut down the SMC. + */ + cpmp->cp_smc[1].smc_smcmr &= ~(SMCMR_TEN | SMCMR_REN); + + /* Shut down the codec. + */ + *((volatile uint *)HIOX_CSR4_ADDR) |= HIOX_CSR4_RSTAUDIO; + eieio(); + *((volatile uint *)HIOX_CSR4_ADDR) &= ~HIOX_CSR4_ENAUDIO; + + /* Wake up a process waiting for the queue being released. + * Note: There may be several processes waiting for a call + * to open() returning. */ + + return rc; +} + + +static int sq_ioctl(struct inode *inode, struct file *file, u_int cmd, + u_long arg) +{ + u_long fmt; + int data; +#if 0 + int size, nbufs; +#else + int size; +#endif + + switch (cmd) { + case SNDCTL_DSP_RESET: + sq_reset(); + return 0; + case SNDCTL_DSP_POST: + case SNDCTL_DSP_SYNC: + return sq_fsync(file, file->f_dentry); + + /* ++TeSche: before changing any of these it's + * probably wise to wait until sound playing has + * settled down. */ + case SNDCTL_DSP_SPEED: + sq_fsync(file, file->f_dentry); + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, sound_set_speed(data)); + case SNDCTL_DSP_STEREO: + sq_fsync(file, file->f_dentry); + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, sound_set_stereo(data)); + case SOUND_PCM_WRITE_CHANNELS: + sq_fsync(file, file->f_dentry); + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, sound_set_stereo(data-1)+1); + case SNDCTL_DSP_SETFMT: + sq_fsync(file, file->f_dentry); + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, sound_set_format(data)); + case SNDCTL_DSP_GETFMTS: + fmt = 0; + if (sound.trans_write) { + if (sound.trans_write->ct_ulaw) + fmt |= AFMT_MU_LAW; + if (sound.trans_write->ct_alaw) + fmt |= AFMT_A_LAW; + if (sound.trans_write->ct_s8) + fmt |= AFMT_S8; + if (sound.trans_write->ct_u8) + fmt |= AFMT_U8; + if (sound.trans_write->ct_s16be) + fmt |= AFMT_S16_BE; + if (sound.trans_write->ct_u16be) + fmt |= AFMT_U16_BE; + if (sound.trans_write->ct_s16le) + fmt |= AFMT_S16_LE; + if (sound.trans_write->ct_u16le) + fmt |= AFMT_U16_LE; + } + return IOCTL_OUT(arg, fmt); + case SNDCTL_DSP_GETBLKSIZE: + size = sq.block_size + * sound.soft.size * (sound.soft.stereo + 1) + / (sound.hard.size * (sound.hard.stereo + 1)); + return IOCTL_OUT(arg, size); + case SNDCTL_DSP_SUBDIVIDE: + break; +#if 0 /* Sorry can't do this at the moment. The CPM allocated buffers + * long ago that can't be changed. + */ + case SNDCTL_DSP_SETFRAGMENT: + if (sq.count || sq.active || sq.syncing) + return -EINVAL; + IOCTL_IN(arg, size); + nbufs = size >> 16; + if (nbufs < 2 || nbufs > numBufs) + nbufs = numBufs; + size &= 0xffff; + if (size >= 8 && size <= 30) { + size = 1 << size; + size *= sound.hard.size * (sound.hard.stereo + 1); + size /= sound.soft.size * (sound.soft.stereo + 1); + if (size > (bufSize << 10)) + size = bufSize << 10; + } else + size = bufSize << 10; + sq_setup(numBufs, size, sound_buffers); + sq.max_active = nbufs; + return 0; +#endif + + default: + return mixer_ioctl(inode, file, cmd, arg); + } + return -EINVAL; +} + + + +static struct file_operations sq_fops = +{ + .owner = THIS_MODULE, + .llseek = sound_lseek, + .read = sq_read, /* sq_read */ + .write = sq_write, + .ioctl = sq_ioctl, + .open = sq_open, + .release = sq_release, +}; + + +static void __init sq_init(void) +{ + sq_unit = register_sound_dsp(&sq_fops, -1); + if (sq_unit < 0) + return; + + init_waitqueue_head(&sq.action_queue); + init_waitqueue_head(&sq.open_queue); + init_waitqueue_head(&sq.sync_queue); + init_waitqueue_head(&read_sq.action_queue); + init_waitqueue_head(&read_sq.open_queue); + init_waitqueue_head(&read_sq.sync_queue); + + sq.busy = 0; + read_sq.busy = 0; + + /* whatever you like as startup mode for /dev/dsp, + * (/dev/audio hasn't got a startup mode). note that + * once changed a new open() will *not* restore these! + */ + sound.dsp.format = AFMT_S16_BE; + sound.dsp.stereo = 1; + sound.dsp.size = 16; + + /* set minimum rate possible without expanding */ + sound.dsp.speed = 8000; + + /* before the first open to /dev/dsp this wouldn't be set */ + sound.soft = sound.dsp; + sound.hard = sound.dsp; + + sound_silence(); +} + +/* + * /dev/sndstat + */ + + +/* state.buf should not overflow! */ + +static int state_open(struct inode *inode, struct file *file) +{ + char *buffer = state.buf, *mach = "", cs4218_buf[50]; + int len = 0; + + if (state.busy) + return -EBUSY; + + state.ptr = 0; + state.busy = 1; + + sprintf(cs4218_buf, "Crystal CS4218 on TDM, "); + mach = cs4218_buf; + + len += sprintf(buffer+len, "%sDMA sound driver:\n", mach); + + len += sprintf(buffer+len, "\tsound.format = 0x%x", sound.soft.format); + switch (sound.soft.format) { + case AFMT_MU_LAW: + len += sprintf(buffer+len, " (mu-law)"); + break; + case AFMT_A_LAW: + len += sprintf(buffer+len, " (A-law)"); + break; + case AFMT_U8: + len += sprintf(buffer+len, " (unsigned 8 bit)"); + break; + case AFMT_S8: + len += sprintf(buffer+len, " (signed 8 bit)"); + break; + case AFMT_S16_BE: + len += sprintf(buffer+len, " (signed 16 bit big)"); + break; + case AFMT_U16_BE: + len += sprintf(buffer+len, " (unsigned 16 bit big)"); + break; + case AFMT_S16_LE: + len += sprintf(buffer+len, " (signed 16 bit little)"); + break; + case AFMT_U16_LE: + len += sprintf(buffer+len, " (unsigned 16 bit little)"); + break; + } + len += sprintf(buffer+len, "\n"); + len += sprintf(buffer+len, "\tsound.speed = %dHz (phys. %dHz)\n", + sound.soft.speed, sound.hard.speed); + len += sprintf(buffer+len, "\tsound.stereo = 0x%x (%s)\n", + sound.soft.stereo, sound.soft.stereo ? "stereo" : "mono"); + len += sprintf(buffer+len, "\tsq.block_size = %d sq.max_count = %d" + " sq.max_active = %d\n", + sq.block_size, sq.max_count, sq.max_active); + len += sprintf(buffer+len, "\tsq.count = %d sq.rear_size = %d\n", sq.count, + sq.rear_size); + len += sprintf(buffer+len, "\tsq.active = %d sq.syncing = %d\n", + sq.active, sq.syncing); + state.len = len; + return nonseekable_open(inode, file); +} + + +static int state_release(struct inode *inode, struct file *file) +{ + state.busy = 0; + return 0; +} + + +static ssize_t state_read(struct file *file, char *buf, size_t count, + loff_t *ppos) +{ + int n = state.len - state.ptr; + if (n > count) + n = count; + if (n <= 0) + return 0; + if (copy_to_user(buf, &state.buf[state.ptr], n)) + return -EFAULT; + state.ptr += n; + return n; +} + + +static struct file_operations state_fops = +{ + .owner = THIS_MODULE, + .llseek = sound_lseek, + .read = state_read, + .open = state_open, + .release = state_release, +}; + + +static void __init state_init(void) +{ + state_unit = register_sound_special(&state_fops, SND_DEV_STATUS); + if (state_unit < 0) + return; + state.busy = 0; +} + + +/*** Common stuff ********************************************************/ + +static long long sound_lseek(struct file *file, long long offset, int orig) +{ + return -ESPIPE; +} + + +/*** Config & Setup **********************************************************/ + + +int __init tdm8xx_sound_init(void) +{ + int i, has_sound; + uint dp_offset; + volatile uint *sirp; + volatile cbd_t *bdp; + volatile cpm8xx_t *cp; + volatile smc_t *sp; + volatile smc_uart_t *up; + volatile immap_t *immap; + + has_sound = 0; + + /* Program the SI/TSA to use TDMa, connected to SMC2, for 4 bytes. + */ + cp = cpmp; /* Get pointer to Communication Processor */ + immap = (immap_t *)IMAP_ADDR; /* and to internal registers */ + + /* Set all TDMa control bits to zero. This enables most features + * we want. + */ + cp->cp_simode &= ~0x00000fff; + + /* Enable common receive/transmit clock pins, use IDL format. + * Sync on falling edge, transmit rising clock, receive falling + * clock, delay 1 bit on both Tx and Rx. Common Tx/Rx clocks and + * sync. + * Connect SMC2 to TSA. + */ + cp->cp_simode |= 0x80000141; + + /* Configure port A pins for TDMa operation. + * The RPX-Lite (MPC850/823) loses SMC2 when TDM is used. + */ + immap->im_ioport.iop_papar |= 0x01c0; /* Enable TDMa functions */ + immap->im_ioport.iop_padir |= 0x00c0; /* Enable TDMa Tx/Rx */ + immap->im_ioport.iop_padir &= ~0x0100; /* Enable L1RCLKa */ + + immap->im_ioport.iop_pcpar |= 0x0800; /* Enable L1RSYNCa */ + immap->im_ioport.iop_pcdir &= ~0x0800; + + /* Initialize the SI TDM routing table. We use TDMa only. + * The receive table and transmit table each have only one + * entry, to capture/send four bytes after each frame pulse. + * The 16-bit ram entry is 0000 0001 1000 1111. (SMC2) + */ + cp->cp_sigmr = 0; + sirp = (uint *)cp->cp_siram; + + *sirp = 0x018f0000; /* Receive entry */ + sirp += 64; + *sirp = 0x018f0000; /* Tramsmit entry */ + + /* Enable single TDMa routing. + */ + cp->cp_sigmr = 0x04; + + /* Initialize the SMC for transparent operation. + */ + sp = &cpmp->cp_smc[1]; + up = (smc_uart_t *)&cp->cp_dparam[PROFF_SMC2]; + + /* We need to allocate a transmit and receive buffer + * descriptors from dual port ram. + */ + dp_addr = cpm_dpalloc(sizeof(cbd_t) * numReadBufs, 8); + + /* Set the physical address of the host memory + * buffers in the buffer descriptors, and the + * virtual address for us to work with. + */ + bdp = (cbd_t *)&cp->cp_dpmem[dp_addr]; + up->smc_rbase = dp_offset; + rx_cur = rx_base = (cbd_t *)bdp; + + for (i=0; i<(numReadBufs-1); i++) { + bdp->cbd_bufaddr = 0; + bdp->cbd_datlen = 0; + bdp->cbd_sc = BD_SC_EMPTY | BD_SC_INTRPT; + bdp++; + } + bdp->cbd_bufaddr = 0; + bdp->cbd_datlen = 0; + bdp->cbd_sc = BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT; + + /* Now, do the same for the transmit buffers. + */ + dp_offset = cpm_dpalloc(sizeof(cbd_t) * numBufs, 8); + + bdp = (cbd_t *)&cp->cp_dpmem[dp_addr]; + up->smc_tbase = dp_offset; + tx_cur = tx_base = (cbd_t *)bdp; + + for (i=0; i<(numBufs-1); i++) { + bdp->cbd_bufaddr = 0; + bdp->cbd_datlen = 0; + bdp->cbd_sc = BD_SC_INTRPT; + bdp++; + } + bdp->cbd_bufaddr = 0; + bdp->cbd_datlen = 0; + bdp->cbd_sc = (BD_SC_WRAP | BD_SC_INTRPT); + + /* Set transparent SMC mode. + * A few things are specific to our application. The codec interface + * is MSB first, hence the REVD selection. The CD/CTS pulse are + * used by the TSA to indicate the frame start to the SMC. + */ + up->smc_rfcr = SCC_EB; + up->smc_tfcr = SCC_EB; + up->smc_mrblr = readbufSize * 1024; + + /* Set 16-bit reversed data, transparent mode. + */ + sp->smc_smcmr = smcr_mk_clen(15) | + SMCMR_SM_TRANS | SMCMR_REVD | SMCMR_BS; + + /* Enable and clear events. + * Because of FIFO delays, all we need is the receive interrupt + * and we can process both the current receive and current + * transmit interrupt within a few microseconds of the transmit. + */ + sp->smc_smce = 0xff; + sp->smc_smcm = SMCM_TXE | SMCM_TX | SMCM_RX; + + /* Send the CPM an initialize command. + */ + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2, + CPM_CR_INIT_TRX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + + sound.mach = mach_cs4218; + has_sound = 1; + + /* Initialize beep stuff */ + orig_mksound = kd_mksound; + kd_mksound = cs_mksound; + beep_buf = (short *) kmalloc(BEEP_BUFLEN * 4, GFP_KERNEL); + if (beep_buf == NULL) + printk(KERN_WARNING "dmasound: no memory for " + "beep buffer\n"); + + if (!has_sound) + return -ENODEV; + + /* Initialize the software SPI. + */ + sw_spi_init(); + + /* Set up sound queue, /dev/audio and /dev/dsp. */ + + /* Set default settings. */ + sq_init(); + + /* Set up /dev/sndstat. */ + state_init(); + + /* Set up /dev/mixer. */ + mixer_init(); + + if (!sound.mach.irqinit()) { + printk(KERN_ERR "DMA sound driver: Interrupt initialization failed\n"); + return -ENODEV; + } +#ifdef MODULE + irq_installed = 1; +#endif + + printk(KERN_INFO "DMA sound driver installed, using %d buffers of %dk.\n", + numBufs, bufSize); + + return 0; +} + +/* Due to FIFOs and bit delays, the transmit interrupt occurs a few + * microseconds ahead of the receive interrupt. + * When we get an interrupt, we service the transmit first, then + * check for a receive to prevent the overhead of returning through + * the interrupt handler only to get back here right away during + * full duplex operation. + */ +static void +cs4218_intr(void *dev_id, struct pt_regs *regs) +{ + volatile smc_t *sp; + volatile cpm8xx_t *cp; + + sp = &cpmp->cp_smc[1]; + + if (sp->smc_smce & SCCM_TX) { + sp->smc_smce = SCCM_TX; + cs4218_tdm_tx_intr((void *)sp); + } + + if (sp->smc_smce & SCCM_RX) { + sp->smc_smce = SCCM_RX; + cs4218_tdm_rx_intr((void *)sp); + } + + if (sp->smc_smce & SCCM_TXE) { + /* Transmit underrun. This happens with the application + * didn't keep up sending buffers. We tell the SMC to + * restart, which will cause it to poll the current (next) + * BD. If the user supplied data since this occurred, + * we just start running again. If they didn't, the SMC + * will poll the descriptor until data is placed there. + */ + sp->smc_smce = SCCM_TXE; + cp = cpmp; /* Get pointer to Communication Processor */ + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2, + CPM_CR_RESTART_TX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + } +} + + +#define MAXARGS 8 /* Should be sufficient for now */ + +void __init dmasound_setup(char *str, int *ints) +{ + /* check the bootstrap parameter for "dmasound=" */ + + switch (ints[0]) { + case 3: + if ((ints[3] < 0) || (ints[3] > MAX_CATCH_RADIUS)) + printk("dmasound_setup: invalid catch radius, using default = %d\n", catchRadius); + else + catchRadius = ints[3]; + /* fall through */ + case 2: + if (ints[1] < MIN_BUFFERS) + printk("dmasound_setup: invalid number of buffers, using default = %d\n", numBufs); + else + numBufs = ints[1]; + if (ints[2] < MIN_BUFSIZE || ints[2] > MAX_BUFSIZE) + printk("dmasound_setup: invalid buffer size, using default = %d\n", bufSize); + else + bufSize = ints[2]; + break; + case 0: + break; + default: + printk("dmasound_setup: invalid number of arguments\n"); + } +} + +/* Software SPI functions. + * These are on Port B. + */ +#define PB_SPICLK ((uint)0x00000002) +#define PB_SPIMOSI ((uint)0x00000004) +#define PB_SPIMISO ((uint)0x00000008) + +static +void sw_spi_init(void) +{ + volatile cpm8xx_t *cp; + volatile uint *hcsr4; + + hcsr4 = (volatile uint *)HIOX_CSR4_ADDR; + cp = cpmp; /* Get pointer to Communication Processor */ + + *hcsr4 &= ~HIOX_CSR4_AUDSPISEL; /* Disable SPI select */ + + /* Make these Port B signals general purpose I/O. + * First, make sure the clock is low. + */ + cp->cp_pbdat &= ~PB_SPICLK; + cp->cp_pbpar &= ~(PB_SPICLK | PB_SPIMOSI | PB_SPIMISO); + + /* Clock and Master Output are outputs. + */ + cp->cp_pbdir |= (PB_SPICLK | PB_SPIMOSI); + + /* Master Input. + */ + cp->cp_pbdir &= ~PB_SPIMISO; + +} + +/* Write the CS4218 control word out the SPI port. While the + * the control word is going out, the status word is arriving. + */ +static +uint cs4218_ctl_write(uint ctlreg) +{ + uint status; + + sw_spi_io((u_char *)&ctlreg, (u_char *)&status, 4); + + /* Shadow the control register.....I guess we could do + * the same for the status, but for now we just return it + * and let the caller decide. + */ + cs4218_control = ctlreg; + return status; +} + +static +void sw_spi_io(u_char *obuf, u_char *ibuf, uint bcnt) +{ + int bits, i; + u_char outbyte, inbyte; + volatile cpm8xx_t *cp; + volatile uint *hcsr4; + + hcsr4 = (volatile uint *)HIOX_CSR4_ADDR; + cp = cpmp; /* Get pointer to Communication Processor */ + + /* The timing on the bus is pretty slow. Code inefficiency + * and eieio() is our friend here :-). + */ + cp->cp_pbdat &= ~PB_SPICLK; + *hcsr4 |= HIOX_CSR4_AUDSPISEL; /* Enable SPI select */ + eieio(); + + /* Clock in/out the bytes. Data is valid on the falling edge + * of the clock. Data is MSB first. + */ + for (i=0; icp_pbdat |= PB_SPICLK; + eieio(); + if (outbyte & 0x80) + cp->cp_pbdat |= PB_SPIMOSI; + else + cp->cp_pbdat &= ~PB_SPIMOSI; + eieio(); + cp->cp_pbdat &= ~PB_SPICLK; + eieio(); + outbyte <<= 1; + inbyte <<= 1; + if (cp->cp_pbdat & PB_SPIMISO) + inbyte |= 1; + } + *ibuf++ = inbyte; + } + + *hcsr4 &= ~HIOX_CSR4_AUDSPISEL; /* Disable SPI select */ + eieio(); +} + +void cleanup_module(void) +{ + if (irq_installed) { + sound_silence(); +#ifdef MODULE + sound.mach.irqcleanup(); +#endif + } + + sq_release_read_buffers(); + sq_release_buffers(); + + if (mixer_unit >= 0) + unregister_sound_mixer(mixer_unit); + if (state_unit >= 0) + unregister_sound_special(state_unit); + if (sq_unit >= 0) + unregister_sound_dsp(sq_unit); +} + +module_init(tdm8xx_sound_init); +module_exit(cleanup_module); + diff --git a/arch/ppc/8xx_io/enet.c b/arch/ppc/8xx_io/enet.c new file mode 100644 index 00000000000..4ea7158e506 --- /dev/null +++ b/arch/ppc/8xx_io/enet.c @@ -0,0 +1,971 @@ +/* + * Ethernet driver for Motorola MPC8xx. + * Copyright (c) 1997 Dan Malek (dmalek@jlc.net) + * + * I copied the basic skeleton from the lance driver, because I did not + * know how to write the Linux driver, but I did know how the LANCE worked. + * + * This version of the driver is somewhat selectable for the different + * processor/board combinations. It works for the boards I know about + * now, and should be easily modified to include others. Some of the + * configuration information is contained in and the + * remainder is here. + * + * Buffer descriptors are kept in the CPM dual port RAM, and the frame + * buffers are in the host memory. + * + * Right now, I am very watseful with the buffers. I allocate memory + * pages and then divide them into 2K frame buffers. This way I know I + * have buffers large enough to hold one frame within one buffer descriptor. + * Once I get this working, I will use 64 or 128 byte CPM buffers, which + * will be much more memory efficient and will easily handle lots of + * small packets. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* + * Theory of Operation + * + * The MPC8xx CPM performs the Ethernet processing on SCC1. It can use + * an aribtrary number of buffers on byte boundaries, but must have at + * least two receive buffers to prevent constant overrun conditions. + * + * The buffer descriptors are allocated from the CPM dual port memory + * with the data buffers allocated from host memory, just like all other + * serial communication protocols. The host memory buffers are allocated + * from the free page pool, and then divided into smaller receive and + * transmit buffers. The size of the buffers should be a power of two, + * since that nicely divides the page. This creates a ring buffer + * structure similar to the LANCE and other controllers. + * + * Like the LANCE driver: + * The driver runs as two independent, single-threaded flows of control. One + * is the send-packet routine, which enforces single-threaded use by the + * cep->tx_busy flag. The other thread is the interrupt handler, which is + * single threaded by the hardware and other software. + * + * The send packet thread has partial control over the Tx ring and the + * 'cep->tx_busy' flag. It sets the tx_busy flag whenever it's queuing a Tx + * packet. If the next queue slot is empty, it clears the tx_busy flag when + * finished otherwise it sets the 'lp->tx_full' flag. + * + * The MBX has a control register external to the MPC8xx that has some + * control of the Ethernet interface. Information is in the manual for + * your board. + * + * The RPX boards have an external control/status register. Consult the + * programming documents for details unique to your board. + * + * For the TQM8xx(L) modules, there is no control register interface. + * All functions are directly controlled using I/O pins. See . + */ + +/* The transmitter timeout + */ +#define TX_TIMEOUT (2*HZ) + +/* The number of Tx and Rx buffers. These are allocated from the page + * pool. The code may assume these are power of two, so it is best + * to keep them that size. + * We don't need to allocate pages for the transmitter. We just use + * the skbuffer directly. + */ +#ifdef CONFIG_ENET_BIG_BUFFERS +#define CPM_ENET_RX_PAGES 32 +#define CPM_ENET_RX_FRSIZE 2048 +#define CPM_ENET_RX_FRPPG (PAGE_SIZE / CPM_ENET_RX_FRSIZE) +#define RX_RING_SIZE (CPM_ENET_RX_FRPPG * CPM_ENET_RX_PAGES) +#define TX_RING_SIZE 64 /* Must be power of two */ +#define TX_RING_MOD_MASK 63 /* for this to work */ +#else +#define CPM_ENET_RX_PAGES 4 +#define CPM_ENET_RX_FRSIZE 2048 +#define CPM_ENET_RX_FRPPG (PAGE_SIZE / CPM_ENET_RX_FRSIZE) +#define RX_RING_SIZE (CPM_ENET_RX_FRPPG * CPM_ENET_RX_PAGES) +#define TX_RING_SIZE 8 /* Must be power of two */ +#define TX_RING_MOD_MASK 7 /* for this to work */ +#endif + +/* The CPM stores dest/src/type, data, and checksum for receive packets. + */ +#define PKT_MAXBUF_SIZE 1518 +#define PKT_MINBUF_SIZE 64 +#define PKT_MAXBLR_SIZE 1520 + +/* The CPM buffer descriptors track the ring buffers. The rx_bd_base and + * tx_bd_base always point to the base of the buffer descriptors. The + * cur_rx and cur_tx point to the currently available buffer. + * The dirty_tx tracks the current buffer that is being sent by the + * controller. The cur_tx and dirty_tx are equal under both completely + * empty and completely full conditions. The empty/ready indicator in + * the buffer descriptor determines the actual condition. + */ +struct scc_enet_private { + /* The saved address of a sent-in-place packet/buffer, for skfree(). */ + struct sk_buff* tx_skbuff[TX_RING_SIZE]; + ushort skb_cur; + ushort skb_dirty; + + /* CPM dual port RAM relative addresses. + */ + cbd_t *rx_bd_base; /* Address of Rx and Tx buffers. */ + cbd_t *tx_bd_base; + cbd_t *cur_rx, *cur_tx; /* The next free ring entry */ + cbd_t *dirty_tx; /* The ring entries to be free()ed. */ + scc_t *sccp; + + /* Virtual addresses for the receive buffers because we can't + * do a __va() on them anymore. + */ + unsigned char *rx_vaddr[RX_RING_SIZE]; + struct net_device_stats stats; + uint tx_full; + spinlock_t lock; +}; + +static int scc_enet_open(struct net_device *dev); +static int scc_enet_start_xmit(struct sk_buff *skb, struct net_device *dev); +static int scc_enet_rx(struct net_device *dev); +static void scc_enet_interrupt(void *dev_id, struct pt_regs *regs); +static int scc_enet_close(struct net_device *dev); +static struct net_device_stats *scc_enet_get_stats(struct net_device *dev); +static void set_multicast_list(struct net_device *dev); + +/* Get this from various configuration locations (depends on board). +*/ +/*static ushort my_enet_addr[] = { 0x0800, 0x3e26, 0x1559 };*/ + +/* Typically, 860(T) boards use SCC1 for Ethernet, and other 8xx boards + * use SCC2. Some even may use SCC3. + * This is easily extended if necessary. + */ +#if defined(CONFIG_SCC3_ENET) +#define CPM_CR_ENET CPM_CR_CH_SCC3 +#define PROFF_ENET PROFF_SCC3 +#define SCC_ENET 2 /* Index, not number! */ +#define CPMVEC_ENET CPMVEC_SCC3 +#elif defined(CONFIG_SCC2_ENET) +#define CPM_CR_ENET CPM_CR_CH_SCC2 +#define PROFF_ENET PROFF_SCC2 +#define SCC_ENET 1 /* Index, not number! */ +#define CPMVEC_ENET CPMVEC_SCC2 +#elif defined(CONFIG_SCC1_ENET) +#define CPM_CR_ENET CPM_CR_CH_SCC1 +#define PROFF_ENET PROFF_SCC1 +#define SCC_ENET 0 /* Index, not number! */ +#define CPMVEC_ENET CPMVEC_SCC1 +#else +#error CONFIG_SCCx_ENET not defined +#endif + +static int +scc_enet_open(struct net_device *dev) +{ + + /* I should reset the ring buffers here, but I don't yet know + * a simple way to do that. + */ + + netif_start_queue(dev); + return 0; /* Always succeed */ +} + +static int +scc_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct scc_enet_private *cep = (struct scc_enet_private *)dev->priv; + volatile cbd_t *bdp; + + /* Fill in a Tx ring entry */ + bdp = cep->cur_tx; + +#ifndef final_version + if (bdp->cbd_sc & BD_ENET_TX_READY) { + /* Ooops. All transmit buffers are full. Bail out. + * This should not happen, since cep->tx_busy should be set. + */ + printk("%s: tx queue full!.\n", dev->name); + return 1; + } +#endif + + /* Clear all of the status flags. + */ + bdp->cbd_sc &= ~BD_ENET_TX_STATS; + + /* If the frame is short, tell CPM to pad it. + */ + if (skb->len <= ETH_ZLEN) + bdp->cbd_sc |= BD_ENET_TX_PAD; + else + bdp->cbd_sc &= ~BD_ENET_TX_PAD; + + /* Set buffer length and buffer pointer. + */ + bdp->cbd_datlen = skb->len; + bdp->cbd_bufaddr = __pa(skb->data); + + /* Save skb pointer. + */ + cep->tx_skbuff[cep->skb_cur] = skb; + + cep->stats.tx_bytes += skb->len; + cep->skb_cur = (cep->skb_cur+1) & TX_RING_MOD_MASK; + + /* Push the data cache so the CPM does not get stale memory + * data. + */ + flush_dcache_range((unsigned long)(skb->data), + (unsigned long)(skb->data + skb->len)); + + spin_lock_irq(&cep->lock); + + /* Send it on its way. Tell CPM its ready, interrupt when done, + * its the last BD of the frame, and to put the CRC on the end. + */ + bdp->cbd_sc |= (BD_ENET_TX_READY | BD_ENET_TX_INTR | BD_ENET_TX_LAST | BD_ENET_TX_TC); + + dev->trans_start = jiffies; + + /* If this was the last BD in the ring, start at the beginning again. + */ + if (bdp->cbd_sc & BD_ENET_TX_WRAP) + bdp = cep->tx_bd_base; + else + bdp++; + + if (bdp->cbd_sc & BD_ENET_TX_READY) { + netif_stop_queue(dev); + cep->tx_full = 1; + } + + cep->cur_tx = (cbd_t *)bdp; + + spin_unlock_irq(&cep->lock); + + return 0; +} + +static void +scc_enet_timeout(struct net_device *dev) +{ + struct scc_enet_private *cep = (struct scc_enet_private *)dev->priv; + + printk("%s: transmit timed out.\n", dev->name); + cep->stats.tx_errors++; +#ifndef final_version + { + int i; + cbd_t *bdp; + printk(" Ring data dump: cur_tx %p%s cur_rx %p.\n", + cep->cur_tx, cep->tx_full ? " (full)" : "", + cep->cur_rx); + bdp = cep->tx_bd_base; + for (i = 0 ; i < TX_RING_SIZE; i++, bdp++) + printk("%04x %04x %08x\n", + bdp->cbd_sc, + bdp->cbd_datlen, + bdp->cbd_bufaddr); + bdp = cep->rx_bd_base; + for (i = 0 ; i < RX_RING_SIZE; i++, bdp++) + printk("%04x %04x %08x\n", + bdp->cbd_sc, + bdp->cbd_datlen, + bdp->cbd_bufaddr); + } +#endif + if (!cep->tx_full) + netif_wake_queue(dev); +} + +/* The interrupt handler. + * This is called from the CPM handler, not the MPC core interrupt. + */ +static void +scc_enet_interrupt(void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = dev_id; + volatile struct scc_enet_private *cep; + volatile cbd_t *bdp; + ushort int_events; + int must_restart; + + cep = (struct scc_enet_private *)dev->priv; + + /* Get the interrupt events that caused us to be here. + */ + int_events = cep->sccp->scc_scce; + cep->sccp->scc_scce = int_events; + must_restart = 0; + + /* Handle receive event in its own function. + */ + if (int_events & SCCE_ENET_RXF) + scc_enet_rx(dev_id); + + /* Check for a transmit error. The manual is a little unclear + * about this, so the debug code until I get it figured out. It + * appears that if TXE is set, then TXB is not set. However, + * if carrier sense is lost during frame transmission, the TXE + * bit is set, "and continues the buffer transmission normally." + * I don't know if "normally" implies TXB is set when the buffer + * descriptor is closed.....trial and error :-). + */ + + /* Transmit OK, or non-fatal error. Update the buffer descriptors. + */ + if (int_events & (SCCE_ENET_TXE | SCCE_ENET_TXB)) { + spin_lock(&cep->lock); + bdp = cep->dirty_tx; + while ((bdp->cbd_sc&BD_ENET_TX_READY)==0) { + if ((bdp==cep->cur_tx) && (cep->tx_full == 0)) + break; + + if (bdp->cbd_sc & BD_ENET_TX_HB) /* No heartbeat */ + cep->stats.tx_heartbeat_errors++; + if (bdp->cbd_sc & BD_ENET_TX_LC) /* Late collision */ + cep->stats.tx_window_errors++; + if (bdp->cbd_sc & BD_ENET_TX_RL) /* Retrans limit */ + cep->stats.tx_aborted_errors++; + if (bdp->cbd_sc & BD_ENET_TX_UN) /* Underrun */ + cep->stats.tx_fifo_errors++; + if (bdp->cbd_sc & BD_ENET_TX_CSL) /* Carrier lost */ + cep->stats.tx_carrier_errors++; + + + /* No heartbeat or Lost carrier are not really bad errors. + * The others require a restart transmit command. + */ + if (bdp->cbd_sc & + (BD_ENET_TX_LC | BD_ENET_TX_RL | BD_ENET_TX_UN)) { + must_restart = 1; + cep->stats.tx_errors++; + } + + cep->stats.tx_packets++; + + /* Deferred means some collisions occurred during transmit, + * but we eventually sent the packet OK. + */ + if (bdp->cbd_sc & BD_ENET_TX_DEF) + cep->stats.collisions++; + + /* Free the sk buffer associated with this last transmit. + */ + dev_kfree_skb_irq(cep->tx_skbuff[cep->skb_dirty]); + cep->skb_dirty = (cep->skb_dirty + 1) & TX_RING_MOD_MASK; + + /* Update pointer to next buffer descriptor to be transmitted. + */ + if (bdp->cbd_sc & BD_ENET_TX_WRAP) + bdp = cep->tx_bd_base; + else + bdp++; + + /* I don't know if we can be held off from processing these + * interrupts for more than one frame time. I really hope + * not. In such a case, we would now want to check the + * currently available BD (cur_tx) and determine if any + * buffers between the dirty_tx and cur_tx have also been + * sent. We would want to process anything in between that + * does not have BD_ENET_TX_READY set. + */ + + /* Since we have freed up a buffer, the ring is no longer + * full. + */ + if (cep->tx_full) { + cep->tx_full = 0; + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + } + + cep->dirty_tx = (cbd_t *)bdp; + } + + if (must_restart) { + volatile cpm8xx_t *cp; + + /* Some transmit errors cause the transmitter to shut + * down. We now issue a restart transmit. Since the + * errors close the BD and update the pointers, the restart + * _should_ pick up without having to reset any of our + * pointers either. + */ + cp = cpmp; + cp->cp_cpcr = + mk_cr_cmd(CPM_CR_ENET, CPM_CR_RESTART_TX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + } + spin_unlock(&cep->lock); + } + + /* Check for receive busy, i.e. packets coming but no place to + * put them. This "can't happen" because the receive interrupt + * is tossing previous frames. + */ + if (int_events & SCCE_ENET_BSY) { + cep->stats.rx_dropped++; + printk("CPM ENET: BSY can't happen.\n"); + } + + return; +} + +/* During a receive, the cur_rx points to the current incoming buffer. + * When we update through the ring, if the next incoming buffer has + * not been given to the system, we just set the empty indicator, + * effectively tossing the packet. + */ +static int +scc_enet_rx(struct net_device *dev) +{ + struct scc_enet_private *cep; + volatile cbd_t *bdp; + struct sk_buff *skb; + ushort pkt_len; + + cep = (struct scc_enet_private *)dev->priv; + + /* First, grab all of the stats for the incoming packet. + * These get messed up if we get called due to a busy condition. + */ + bdp = cep->cur_rx; + +for (;;) { + if (bdp->cbd_sc & BD_ENET_RX_EMPTY) + break; + +#ifndef final_version + /* Since we have allocated space to hold a complete frame, both + * the first and last indicators should be set. + */ + if ((bdp->cbd_sc & (BD_ENET_RX_FIRST | BD_ENET_RX_LAST)) != + (BD_ENET_RX_FIRST | BD_ENET_RX_LAST)) + printk("CPM ENET: rcv is not first+last\n"); +#endif + + /* Frame too long or too short. + */ + if (bdp->cbd_sc & (BD_ENET_RX_LG | BD_ENET_RX_SH)) + cep->stats.rx_length_errors++; + if (bdp->cbd_sc & BD_ENET_RX_NO) /* Frame alignment */ + cep->stats.rx_frame_errors++; + if (bdp->cbd_sc & BD_ENET_RX_CR) /* CRC Error */ + cep->stats.rx_crc_errors++; + if (bdp->cbd_sc & BD_ENET_RX_OV) /* FIFO overrun */ + cep->stats.rx_crc_errors++; + + /* Report late collisions as a frame error. + * On this error, the BD is closed, but we don't know what we + * have in the buffer. So, just drop this frame on the floor. + */ + if (bdp->cbd_sc & BD_ENET_RX_CL) { + cep->stats.rx_frame_errors++; + } + else { + + /* Process the incoming frame. + */ + cep->stats.rx_packets++; + pkt_len = bdp->cbd_datlen; + cep->stats.rx_bytes += pkt_len; + + /* This does 16 byte alignment, much more than we need. + * The packet length includes FCS, but we don't want to + * include that when passing upstream as it messes up + * bridging applications. + */ + skb = dev_alloc_skb(pkt_len-4); + + if (skb == NULL) { + printk("%s: Memory squeeze, dropping packet.\n", dev->name); + cep->stats.rx_dropped++; + } + else { + skb->dev = dev; + skb_put(skb,pkt_len-4); /* Make room */ + eth_copy_and_sum(skb, + cep->rx_vaddr[bdp - cep->rx_bd_base], + pkt_len-4, 0); + skb->protocol=eth_type_trans(skb,dev); + netif_rx(skb); + } + } + + /* Clear the status flags for this buffer. + */ + bdp->cbd_sc &= ~BD_ENET_RX_STATS; + + /* Mark the buffer empty. + */ + bdp->cbd_sc |= BD_ENET_RX_EMPTY; + + /* Update BD pointer to next entry. + */ + if (bdp->cbd_sc & BD_ENET_RX_WRAP) + bdp = cep->rx_bd_base; + else + bdp++; + + } + cep->cur_rx = (cbd_t *)bdp; + + return 0; +} + +static int +scc_enet_close(struct net_device *dev) +{ + /* Don't know what to do yet. + */ + netif_stop_queue(dev); + + return 0; +} + +static struct net_device_stats *scc_enet_get_stats(struct net_device *dev) +{ + struct scc_enet_private *cep = (struct scc_enet_private *)dev->priv; + + return &cep->stats; +} + +/* Set or clear the multicast filter for this adaptor. + * Skeleton taken from sunlance driver. + * The CPM Ethernet implementation allows Multicast as well as individual + * MAC address filtering. Some of the drivers check to make sure it is + * a group multicast address, and discard those that are not. I guess I + * will do the same for now, but just remove the test if you want + * individual filtering as well (do the upper net layers want or support + * this kind of feature?). + */ + +static void set_multicast_list(struct net_device *dev) +{ + struct scc_enet_private *cep; + struct dev_mc_list *dmi; + u_char *mcptr, *tdptr; + volatile scc_enet_t *ep; + int i, j; + cep = (struct scc_enet_private *)dev->priv; + + /* Get pointer to SCC area in parameter RAM. + */ + ep = (scc_enet_t *)dev->base_addr; + + if (dev->flags&IFF_PROMISC) { + + /* Log any net taps. */ + printk("%s: Promiscuous mode enabled.\n", dev->name); + cep->sccp->scc_psmr |= SCC_PSMR_PRO; + } else { + + cep->sccp->scc_psmr &= ~SCC_PSMR_PRO; + + if (dev->flags & IFF_ALLMULTI) { + /* Catch all multicast addresses, so set the + * filter to all 1's. + */ + ep->sen_gaddr1 = 0xffff; + ep->sen_gaddr2 = 0xffff; + ep->sen_gaddr3 = 0xffff; + ep->sen_gaddr4 = 0xffff; + } + else { + /* Clear filter and add the addresses in the list. + */ + ep->sen_gaddr1 = 0; + ep->sen_gaddr2 = 0; + ep->sen_gaddr3 = 0; + ep->sen_gaddr4 = 0; + + dmi = dev->mc_list; + + for (i=0; imc_count; i++) { + + /* Only support group multicast for now. + */ + if (!(dmi->dmi_addr[0] & 1)) + continue; + + /* The address in dmi_addr is LSB first, + * and taddr is MSB first. We have to + * copy bytes MSB first from dmi_addr. + */ + mcptr = (u_char *)dmi->dmi_addr + 5; + tdptr = (u_char *)&ep->sen_taddrh; + for (j=0; j<6; j++) + *tdptr++ = *mcptr--; + + /* Ask CPM to run CRC and set bit in + * filter mask. + */ + cpmp->cp_cpcr = mk_cr_cmd(CPM_CR_ENET, CPM_CR_SET_GADDR) | CPM_CR_FLG; + /* this delay is necessary here -- Cort */ + udelay(10); + while (cpmp->cp_cpcr & CPM_CR_FLG); + } + } + } +} + +/* Initialize the CPM Ethernet on SCC. If EPPC-Bug loaded us, or performed + * some other network I/O, a whole bunch of this has already been set up. + * It is no big deal if we do it again, we just have to disable the + * transmit and receive to make sure we don't catch the CPM with some + * inconsistent control information. + */ +static int __init scc_enet_init(void) +{ + struct net_device *dev; + struct scc_enet_private *cep; + int i, j, k, err; + uint dp_offset; + unsigned char *eap, *ba; + dma_addr_t mem_addr; + bd_t *bd; + volatile cbd_t *bdp; + volatile cpm8xx_t *cp; + volatile scc_t *sccp; + volatile scc_enet_t *ep; + volatile immap_t *immap; + + cp = cpmp; /* Get pointer to Communication Processor */ + + immap = (immap_t *)(mfspr(SPRN_IMMR) & 0xFFFF0000); /* and to internal registers */ + + bd = (bd_t *)__res; + + dev = alloc_etherdev(sizeof(*cep)); + if (!dev) + return -ENOMEM; + + cep = dev->priv; + spin_lock_init(&cep->lock); + + /* Get pointer to SCC area in parameter RAM. + */ + ep = (scc_enet_t *)(&cp->cp_dparam[PROFF_ENET]); + + /* And another to the SCC register area. + */ + sccp = (volatile scc_t *)(&cp->cp_scc[SCC_ENET]); + cep->sccp = (scc_t *)sccp; /* Keep the pointer handy */ + + /* Disable receive and transmit in case EPPC-Bug started it. + */ + sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); + + /* Cookbook style from the MPC860 manual..... + * Not all of this is necessary if EPPC-Bug has initialized + * the network. + * So far we are lucky, all board configurations use the same + * pins, or at least the same I/O Port for these functions..... + * It can't last though...... + */ + +#if (defined(PA_ENET_RXD) && defined(PA_ENET_TXD)) + /* Configure port A pins for Txd and Rxd. + */ + immap->im_ioport.iop_papar |= (PA_ENET_RXD | PA_ENET_TXD); + immap->im_ioport.iop_padir &= ~(PA_ENET_RXD | PA_ENET_TXD); + immap->im_ioport.iop_paodr &= ~PA_ENET_TXD; +#elif (defined(PB_ENET_RXD) && defined(PB_ENET_TXD)) + /* Configure port B pins for Txd and Rxd. + */ + immap->im_cpm.cp_pbpar |= (PB_ENET_RXD | PB_ENET_TXD); + immap->im_cpm.cp_pbdir &= ~(PB_ENET_RXD | PB_ENET_TXD); + immap->im_cpm.cp_pbodr &= ~PB_ENET_TXD; +#else +#error Exactly ONE pair of PA_ENET_[RT]XD, PB_ENET_[RT]XD must be defined +#endif + +#if defined(PC_ENET_LBK) + /* Configure port C pins to disable External Loopback + */ + immap->im_ioport.iop_pcpar &= ~PC_ENET_LBK; + immap->im_ioport.iop_pcdir |= PC_ENET_LBK; + immap->im_ioport.iop_pcso &= ~PC_ENET_LBK; + immap->im_ioport.iop_pcdat &= ~PC_ENET_LBK; /* Disable Loopback */ +#endif /* PC_ENET_LBK */ + + /* Configure port C pins to enable CLSN and RENA. + */ + immap->im_ioport.iop_pcpar &= ~(PC_ENET_CLSN | PC_ENET_RENA); + immap->im_ioport.iop_pcdir &= ~(PC_ENET_CLSN | PC_ENET_RENA); + immap->im_ioport.iop_pcso |= (PC_ENET_CLSN | PC_ENET_RENA); + + /* Configure port A for TCLK and RCLK. + */ + immap->im_ioport.iop_papar |= (PA_ENET_TCLK | PA_ENET_RCLK); + immap->im_ioport.iop_padir &= ~(PA_ENET_TCLK | PA_ENET_RCLK); + + /* Configure Serial Interface clock routing. + * First, clear all SCC bits to zero, then set the ones we want. + */ + cp->cp_sicr &= ~SICR_ENET_MASK; + cp->cp_sicr |= SICR_ENET_CLKRT; + + /* Manual says set SDDR, but I can't find anything with that + * name. I think it is a misprint, and should be SDCR. This + * has already been set by the communication processor initialization. + */ + + /* Allocate space for the buffer descriptors in the DP ram. + * These are relative offsets in the DP ram address space. + * Initialize base addresses for the buffer descriptors. + */ + dp_offset = cpm_dpalloc(sizeof(cbd_t) * RX_RING_SIZE, 8); + ep->sen_genscc.scc_rbase = dp_offset; + cep->rx_bd_base = cpm_dpram_addr(dp_offset); + + dp_offset = cpm_dpalloc(sizeof(cbd_t) * TX_RING_SIZE, 8); + ep->sen_genscc.scc_tbase = dp_offset; + cep->tx_bd_base = cpm_dpram_addr(dp_offset); + + cep->dirty_tx = cep->cur_tx = cep->tx_bd_base; + cep->cur_rx = cep->rx_bd_base; + + /* Issue init Rx BD command for SCC. + * Manual says to perform an Init Rx parameters here. We have + * to perform both Rx and Tx because the SCC may have been + * already running. + * In addition, we have to do it later because we don't yet have + * all of the BD control/status set properly. + cp->cp_cpcr = mk_cr_cmd(CPM_CR_ENET, CPM_CR_INIT_RX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + */ + + /* Initialize function code registers for big-endian. + */ + ep->sen_genscc.scc_rfcr = SCC_EB; + ep->sen_genscc.scc_tfcr = SCC_EB; + + /* Set maximum bytes per receive buffer. + * This appears to be an Ethernet frame size, not the buffer + * fragment size. It must be a multiple of four. + */ + ep->sen_genscc.scc_mrblr = PKT_MAXBLR_SIZE; + + /* Set CRC preset and mask. + */ + ep->sen_cpres = 0xffffffff; + ep->sen_cmask = 0xdebb20e3; + + ep->sen_crcec = 0; /* CRC Error counter */ + ep->sen_alec = 0; /* alignment error counter */ + ep->sen_disfc = 0; /* discard frame counter */ + + ep->sen_pads = 0x8888; /* Tx short frame pad character */ + ep->sen_retlim = 15; /* Retry limit threshold */ + + ep->sen_maxflr = PKT_MAXBUF_SIZE; /* maximum frame length register */ + ep->sen_minflr = PKT_MINBUF_SIZE; /* minimum frame length register */ + + ep->sen_maxd1 = PKT_MAXBLR_SIZE; /* maximum DMA1 length */ + ep->sen_maxd2 = PKT_MAXBLR_SIZE; /* maximum DMA2 length */ + + /* Clear hash tables. + */ + ep->sen_gaddr1 = 0; + ep->sen_gaddr2 = 0; + ep->sen_gaddr3 = 0; + ep->sen_gaddr4 = 0; + ep->sen_iaddr1 = 0; + ep->sen_iaddr2 = 0; + ep->sen_iaddr3 = 0; + ep->sen_iaddr4 = 0; + + /* Set Ethernet station address. + */ + eap = (unsigned char *)&(ep->sen_paddrh); + for (i=5; i>=0; i--) + *eap++ = dev->dev_addr[i] = bd->bi_enetaddr[i]; + + ep->sen_pper = 0; /* 'cause the book says so */ + ep->sen_taddrl = 0; /* temp address (LSB) */ + ep->sen_taddrm = 0; + ep->sen_taddrh = 0; /* temp address (MSB) */ + + /* Now allocate the host memory pages and initialize the + * buffer descriptors. + */ + bdp = cep->tx_bd_base; + for (i=0; icbd_sc = 0; + bdp->cbd_bufaddr = 0; + bdp++; + } + + /* Set the last buffer to wrap. + */ + bdp--; + bdp->cbd_sc |= BD_SC_WRAP; + + bdp = cep->rx_bd_base; + k = 0; + for (i=0; icbd_sc = BD_ENET_RX_EMPTY | BD_ENET_RX_INTR; + bdp->cbd_bufaddr = mem_addr; + cep->rx_vaddr[k++] = ba; + mem_addr += CPM_ENET_RX_FRSIZE; + ba += CPM_ENET_RX_FRSIZE; + bdp++; + } + } + + /* Set the last buffer to wrap. + */ + bdp--; + bdp->cbd_sc |= BD_SC_WRAP; + + /* Let's re-initialize the channel now. We have to do it later + * than the manual describes because we have just now finished + * the BD initialization. + */ + cp->cp_cpcr = mk_cr_cmd(CPM_CR_ENET, CPM_CR_INIT_TRX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + + cep->skb_cur = cep->skb_dirty = 0; + + sccp->scc_scce = 0xffff; /* Clear any pending events */ + + /* Enable interrupts for transmit error, complete frame + * received, and any transmit buffer we have also set the + * interrupt flag. + */ + sccp->scc_sccm = (SCCE_ENET_TXE | SCCE_ENET_RXF | SCCE_ENET_TXB); + + /* Install our interrupt handler. + */ + cpm_install_handler(CPMVEC_ENET, scc_enet_interrupt, dev); + + /* Set GSMR_H to enable all normal operating modes. + * Set GSMR_L to enable Ethernet to MC68160. + */ + sccp->scc_gsmrh = 0; + sccp->scc_gsmrl = (SCC_GSMRL_TCI | SCC_GSMRL_TPL_48 | SCC_GSMRL_TPP_10 | SCC_GSMRL_MODE_ENET); + + /* Set sync/delimiters. + */ + sccp->scc_dsr = 0xd555; + + /* Set processing mode. Use Ethernet CRC, catch broadcast, and + * start frame search 22 bit times after RENA. + */ + sccp->scc_psmr = (SCC_PSMR_ENCRC | SCC_PSMR_NIB22); + + /* It is now OK to enable the Ethernet transmitter. + * Unfortunately, there are board implementation differences here. + */ +#if (!defined (PB_ENET_TENA) && defined (PC_ENET_TENA)) + immap->im_ioport.iop_pcpar |= PC_ENET_TENA; + immap->im_ioport.iop_pcdir &= ~PC_ENET_TENA; +#elif ( defined (PB_ENET_TENA) && !defined (PC_ENET_TENA)) + cp->cp_pbpar |= PB_ENET_TENA; + cp->cp_pbdir |= PB_ENET_TENA; +#else +#error Configuration Error: define exactly ONE of PB_ENET_TENA, PC_ENET_TENA +#endif + +#if defined(CONFIG_RPXLITE) || defined(CONFIG_RPXCLASSIC) + /* And while we are here, set the configuration to enable ethernet. + */ + *((volatile uint *)RPX_CSR_ADDR) &= ~BCSR0_ETHLPBK; + *((volatile uint *)RPX_CSR_ADDR) |= + (BCSR0_ETHEN | BCSR0_COLTESTDIS | BCSR0_FULLDPLXDIS); +#endif + +#ifdef CONFIG_BSEIP + /* BSE uses port B and C for PHY control. + */ + cp->cp_pbpar &= ~(PB_BSE_POWERUP | PB_BSE_FDXDIS); + cp->cp_pbdir |= (PB_BSE_POWERUP | PB_BSE_FDXDIS); + cp->cp_pbdat |= (PB_BSE_POWERUP | PB_BSE_FDXDIS); + + immap->im_ioport.iop_pcpar &= ~PC_BSE_LOOPBACK; + immap->im_ioport.iop_pcdir |= PC_BSE_LOOPBACK; + immap->im_ioport.iop_pcso &= ~PC_BSE_LOOPBACK; + immap->im_ioport.iop_pcdat &= ~PC_BSE_LOOPBACK; +#endif + +#ifdef CONFIG_FADS + cp->cp_pbpar |= PB_ENET_TENA; + cp->cp_pbdir |= PB_ENET_TENA; + + /* Enable the EEST PHY. + */ + *((volatile uint *)BCSR1) &= ~BCSR1_ETHEN; +#endif + + dev->base_addr = (unsigned long)ep; +#if 0 + dev->name = "CPM_ENET"; +#endif + + /* The CPM Ethernet specific entries in the device structure. */ + dev->open = scc_enet_open; + dev->hard_start_xmit = scc_enet_start_xmit; + dev->tx_timeout = scc_enet_timeout; + dev->watchdog_timeo = TX_TIMEOUT; + dev->stop = scc_enet_close; + dev->get_stats = scc_enet_get_stats; + dev->set_multicast_list = set_multicast_list; + + err = register_netdev(dev); + if (err) { + free_netdev(dev); + return err; + } + + /* And last, enable the transmit and receive processing. + */ + sccp->scc_gsmrl |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT); + + printk("%s: CPM ENET Version 0.2 on SCC%d, ", dev->name, SCC_ENET+1); + for (i=0; i<5; i++) + printk("%02x:", dev->dev_addr[i]); + printk("%02x\n", dev->dev_addr[5]); + + return 0; +} + +module_init(scc_enet_init); diff --git a/arch/ppc/8xx_io/fec.c b/arch/ppc/8xx_io/fec.c new file mode 100644 index 00000000000..0730392dcc2 --- /dev/null +++ b/arch/ppc/8xx_io/fec.c @@ -0,0 +1,1973 @@ +/* + * Fast Ethernet Controller (FEC) driver for Motorola MPC8xx. + * Copyright (c) 1997 Dan Malek (dmalek@jlc.net) + * + * This version of the driver is specific to the FADS implementation, + * since the board contains control registers external to the processor + * for the control of the LevelOne LXT970 transceiver. The MPC860T manual + * describes connections using the internal parallel port I/O, which + * is basically all of Port D. + * + * Includes support for the following PHYs: QS6612, LXT970, LXT971/2. + * + * Right now, I am very wasteful with the buffers. I allocate memory + * pages and then divide them into 2K frame buffers. This way I know I + * have buffers large enough to hold one frame within one buffer descriptor. + * Once I get this working, I will use 64 or 128 byte CPM buffers, which + * will be much more memory efficient and will easily handle lots of + * small packets. + * + * Much better multiple PHY support by Magnus Damm. + * Copyright (c) 2000 Ericsson Radio Systems AB. + * + * Make use of MII for PHY control configurable. + * Some fixes. + * Copyright (c) 2000-2002 Wolfgang Denk, DENX Software Engineering. + * + * Support for AMD AM79C874 added. + * Thomas Lange, thomas@corelatus.com + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_FEC_PACKETHOOK +#include +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_USE_MDIO +/* Forward declarations of some structures to support different PHYs +*/ + +typedef struct { + uint mii_data; + void (*funct)(uint mii_reg, struct net_device *dev); +} phy_cmd_t; + +typedef struct { + uint id; + char *name; + + const phy_cmd_t *config; + const phy_cmd_t *startup; + const phy_cmd_t *ack_int; + const phy_cmd_t *shutdown; +} phy_info_t; +#endif /* CONFIG_USE_MDIO */ + +/* The number of Tx and Rx buffers. These are allocated from the page + * pool. The code may assume these are power of two, so it is best + * to keep them that size. + * We don't need to allocate pages for the transmitter. We just use + * the skbuffer directly. + */ +#ifdef CONFIG_ENET_BIG_BUFFERS +#define FEC_ENET_RX_PAGES 16 +#define FEC_ENET_RX_FRSIZE 2048 +#define FEC_ENET_RX_FRPPG (PAGE_SIZE / FEC_ENET_RX_FRSIZE) +#define RX_RING_SIZE (FEC_ENET_RX_FRPPG * FEC_ENET_RX_PAGES) +#define TX_RING_SIZE 16 /* Must be power of two */ +#define TX_RING_MOD_MASK 15 /* for this to work */ +#else +#define FEC_ENET_RX_PAGES 4 +#define FEC_ENET_RX_FRSIZE 2048 +#define FEC_ENET_RX_FRPPG (PAGE_SIZE / FEC_ENET_RX_FRSIZE) +#define RX_RING_SIZE (FEC_ENET_RX_FRPPG * FEC_ENET_RX_PAGES) +#define TX_RING_SIZE 8 /* Must be power of two */ +#define TX_RING_MOD_MASK 7 /* for this to work */ +#endif + +/* Interrupt events/masks. +*/ +#define FEC_ENET_HBERR ((uint)0x80000000) /* Heartbeat error */ +#define FEC_ENET_BABR ((uint)0x40000000) /* Babbling receiver */ +#define FEC_ENET_BABT ((uint)0x20000000) /* Babbling transmitter */ +#define FEC_ENET_GRA ((uint)0x10000000) /* Graceful stop complete */ +#define FEC_ENET_TXF ((uint)0x08000000) /* Full frame transmitted */ +#define FEC_ENET_TXB ((uint)0x04000000) /* A buffer was transmitted */ +#define FEC_ENET_RXF ((uint)0x02000000) /* Full frame received */ +#define FEC_ENET_RXB ((uint)0x01000000) /* A buffer was received */ +#define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */ +#define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */ + +/* +*/ +#define FEC_ECNTRL_PINMUX 0x00000004 +#define FEC_ECNTRL_ETHER_EN 0x00000002 +#define FEC_ECNTRL_RESET 0x00000001 + +#define FEC_RCNTRL_BC_REJ 0x00000010 +#define FEC_RCNTRL_PROM 0x00000008 +#define FEC_RCNTRL_MII_MODE 0x00000004 +#define FEC_RCNTRL_DRT 0x00000002 +#define FEC_RCNTRL_LOOP 0x00000001 + +#define FEC_TCNTRL_FDEN 0x00000004 +#define FEC_TCNTRL_HBC 0x00000002 +#define FEC_TCNTRL_GTS 0x00000001 + +/* Delay to wait for FEC reset command to complete (in us) +*/ +#define FEC_RESET_DELAY 50 + +/* The FEC stores dest/src/type, data, and checksum for receive packets. + */ +#define PKT_MAXBUF_SIZE 1518 +#define PKT_MINBUF_SIZE 64 +#define PKT_MAXBLR_SIZE 1520 + +/* The FEC buffer descriptors track the ring buffers. The rx_bd_base and + * tx_bd_base always point to the base of the buffer descriptors. The + * cur_rx and cur_tx point to the currently available buffer. + * The dirty_tx tracks the current buffer that is being sent by the + * controller. The cur_tx and dirty_tx are equal under both completely + * empty and completely full conditions. The empty/ready indicator in + * the buffer descriptor determines the actual condition. + */ +struct fec_enet_private { + /* The saved address of a sent-in-place packet/buffer, for skfree(). */ + struct sk_buff* tx_skbuff[TX_RING_SIZE]; + ushort skb_cur; + ushort skb_dirty; + + /* CPM dual port RAM relative addresses. + */ + cbd_t *rx_bd_base; /* Address of Rx and Tx buffers. */ + cbd_t *tx_bd_base; + cbd_t *cur_rx, *cur_tx; /* The next free ring entry */ + cbd_t *dirty_tx; /* The ring entries to be free()ed. */ + + /* Virtual addresses for the receive buffers because we can't + * do a __va() on them anymore. + */ + unsigned char *rx_vaddr[RX_RING_SIZE]; + + struct net_device_stats stats; + uint tx_full; + spinlock_t lock; + +#ifdef CONFIG_USE_MDIO + uint phy_id; + uint phy_id_done; + uint phy_status; + uint phy_speed; + phy_info_t *phy; + struct tq_struct phy_task; + + uint sequence_done; + + uint phy_addr; +#endif /* CONFIG_USE_MDIO */ + + int link; + int old_link; + int full_duplex; + +#ifdef CONFIG_FEC_PACKETHOOK + unsigned long ph_lock; + fec_ph_func *ph_rxhandler; + fec_ph_func *ph_txhandler; + __u16 ph_proto; + volatile __u32 *ph_regaddr; + void *ph_priv; +#endif +}; + +static int fec_enet_open(struct net_device *dev); +static int fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev); +#ifdef CONFIG_USE_MDIO +static void fec_enet_mii(struct net_device *dev); +#endif /* CONFIG_USE_MDIO */ +static void fec_enet_interrupt(int irq, void * dev_id, struct pt_regs * regs); +#ifdef CONFIG_FEC_PACKETHOOK +static void fec_enet_tx(struct net_device *dev, __u32 regval); +static void fec_enet_rx(struct net_device *dev, __u32 regval); +#else +static void fec_enet_tx(struct net_device *dev); +static void fec_enet_rx(struct net_device *dev); +#endif +static int fec_enet_close(struct net_device *dev); +static struct net_device_stats *fec_enet_get_stats(struct net_device *dev); +static void set_multicast_list(struct net_device *dev); +static void fec_restart(struct net_device *dev, int duplex); +static void fec_stop(struct net_device *dev); +static ushort my_enet_addr[3]; + +#ifdef CONFIG_USE_MDIO +/* MII processing. We keep this as simple as possible. Requests are + * placed on the list (if there is room). When the request is finished + * by the MII, an optional function may be called. + */ +typedef struct mii_list { + uint mii_regval; + void (*mii_func)(uint val, struct net_device *dev); + struct mii_list *mii_next; +} mii_list_t; + +#define NMII 20 +mii_list_t mii_cmds[NMII]; +mii_list_t *mii_free; +mii_list_t *mii_head; +mii_list_t *mii_tail; + +static int mii_queue(struct net_device *dev, int request, + void (*func)(uint, struct net_device *)); + +/* Make MII read/write commands for the FEC. +*/ +#define mk_mii_read(REG) (0x60020000 | ((REG & 0x1f) << 18)) +#define mk_mii_write(REG, VAL) (0x50020000 | ((REG & 0x1f) << 18) | \ + (VAL & 0xffff)) +#define mk_mii_end 0 +#endif /* CONFIG_USE_MDIO */ + +/* Transmitter timeout. +*/ +#define TX_TIMEOUT (2*HZ) + +#ifdef CONFIG_USE_MDIO +/* Register definitions for the PHY. +*/ + +#define MII_REG_CR 0 /* Control Register */ +#define MII_REG_SR 1 /* Status Register */ +#define MII_REG_PHYIR1 2 /* PHY Identification Register 1 */ +#define MII_REG_PHYIR2 3 /* PHY Identification Register 2 */ +#define MII_REG_ANAR 4 /* A-N Advertisement Register */ +#define MII_REG_ANLPAR 5 /* A-N Link Partner Ability Register */ +#define MII_REG_ANER 6 /* A-N Expansion Register */ +#define MII_REG_ANNPTR 7 /* A-N Next Page Transmit Register */ +#define MII_REG_ANLPRNPR 8 /* A-N Link Partner Received Next Page Reg. */ + +/* values for phy_status */ + +#define PHY_CONF_ANE 0x0001 /* 1 auto-negotiation enabled */ +#define PHY_CONF_LOOP 0x0002 /* 1 loopback mode enabled */ +#define PHY_CONF_SPMASK 0x00f0 /* mask for speed */ +#define PHY_CONF_10HDX 0x0010 /* 10 Mbit half duplex supported */ +#define PHY_CONF_10FDX 0x0020 /* 10 Mbit full duplex supported */ +#define PHY_CONF_100HDX 0x0040 /* 100 Mbit half duplex supported */ +#define PHY_CONF_100FDX 0x0080 /* 100 Mbit full duplex supported */ + +#define PHY_STAT_LINK 0x0100 /* 1 up - 0 down */ +#define PHY_STAT_FAULT 0x0200 /* 1 remote fault */ +#define PHY_STAT_ANC 0x0400 /* 1 auto-negotiation complete */ +#define PHY_STAT_SPMASK 0xf000 /* mask for speed */ +#define PHY_STAT_10HDX 0x1000 /* 10 Mbit half duplex selected */ +#define PHY_STAT_10FDX 0x2000 /* 10 Mbit full duplex selected */ +#define PHY_STAT_100HDX 0x4000 /* 100 Mbit half duplex selected */ +#define PHY_STAT_100FDX 0x8000 /* 100 Mbit full duplex selected */ +#endif /* CONFIG_USE_MDIO */ + +#ifdef CONFIG_FEC_PACKETHOOK +int +fec_register_ph(struct net_device *dev, fec_ph_func *rxfun, fec_ph_func *txfun, + __u16 proto, volatile __u32 *regaddr, void *priv) +{ + struct fec_enet_private *fep; + int retval = 0; + + fep = dev->priv; + + if (test_and_set_bit(0, (void*)&fep->ph_lock) != 0) { + /* Someone is messing with the packet hook */ + return -EAGAIN; + } + if (fep->ph_rxhandler != NULL || fep->ph_txhandler != NULL) { + retval = -EBUSY; + goto out; + } + fep->ph_rxhandler = rxfun; + fep->ph_txhandler = txfun; + fep->ph_proto = proto; + fep->ph_regaddr = regaddr; + fep->ph_priv = priv; + + out: + fep->ph_lock = 0; + + return retval; +} + + +int +fec_unregister_ph(struct net_device *dev) +{ + struct fec_enet_private *fep; + int retval = 0; + + fep = dev->priv; + + if (test_and_set_bit(0, (void*)&fep->ph_lock) != 0) { + /* Someone is messing with the packet hook */ + return -EAGAIN; + } + + fep->ph_rxhandler = fep->ph_txhandler = NULL; + fep->ph_proto = 0; + fep->ph_regaddr = NULL; + fep->ph_priv = NULL; + + fep->ph_lock = 0; + + return retval; +} + +EXPORT_SYMBOL(fec_register_ph); +EXPORT_SYMBOL(fec_unregister_ph); + +#endif /* CONFIG_FEC_PACKETHOOK */ + +static int +fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct fec_enet_private *fep; + volatile fec_t *fecp; + volatile cbd_t *bdp; + + fep = dev->priv; + fecp = (volatile fec_t*)dev->base_addr; + + if (!fep->link) { + /* Link is down or autonegotiation is in progress. */ + return 1; + } + + /* Fill in a Tx ring entry */ + bdp = fep->cur_tx; + +#ifndef final_version + if (bdp->cbd_sc & BD_ENET_TX_READY) { + /* Ooops. All transmit buffers are full. Bail out. + * This should not happen, since dev->tbusy should be set. + */ + printk("%s: tx queue full!.\n", dev->name); + return 1; + } +#endif + + /* Clear all of the status flags. + */ + bdp->cbd_sc &= ~BD_ENET_TX_STATS; + + /* Set buffer length and buffer pointer. + */ + bdp->cbd_bufaddr = __pa(skb->data); + bdp->cbd_datlen = skb->len; + + /* Save skb pointer. + */ + fep->tx_skbuff[fep->skb_cur] = skb; + + fep->stats.tx_bytes += skb->len; + fep->skb_cur = (fep->skb_cur+1) & TX_RING_MOD_MASK; + + /* Push the data cache so the CPM does not get stale memory + * data. + */ + flush_dcache_range((unsigned long)skb->data, + (unsigned long)skb->data + skb->len); + + /* disable interrupts while triggering transmit */ + spin_lock_irq(&fep->lock); + + /* Send it on its way. Tell FEC its ready, interrupt when done, + * its the last BD of the frame, and to put the CRC on the end. + */ + + bdp->cbd_sc |= (BD_ENET_TX_READY | BD_ENET_TX_INTR + | BD_ENET_TX_LAST | BD_ENET_TX_TC); + + dev->trans_start = jiffies; + + /* Trigger transmission start */ + fecp->fec_x_des_active = 0x01000000; + + /* If this was the last BD in the ring, start at the beginning again. + */ + if (bdp->cbd_sc & BD_ENET_TX_WRAP) { + bdp = fep->tx_bd_base; + } else { + bdp++; + } + + if (bdp->cbd_sc & BD_ENET_TX_READY) { + netif_stop_queue(dev); + fep->tx_full = 1; + } + + fep->cur_tx = (cbd_t *)bdp; + + spin_unlock_irq(&fep->lock); + + return 0; +} + +static void +fec_timeout(struct net_device *dev) +{ + struct fec_enet_private *fep = dev->priv; + + printk("%s: transmit timed out.\n", dev->name); + fep->stats.tx_errors++; +#ifndef final_version + { + int i; + cbd_t *bdp; + + printk("Ring data dump: cur_tx %lx%s, dirty_tx %lx cur_rx: %lx\n", + (unsigned long)fep->cur_tx, fep->tx_full ? " (full)" : "", + (unsigned long)fep->dirty_tx, + (unsigned long)fep->cur_rx); + + bdp = fep->tx_bd_base; + printk(" tx: %u buffers\n", TX_RING_SIZE); + for (i = 0 ; i < TX_RING_SIZE; i++) { + printk(" %08x: %04x %04x %08x\n", + (uint) bdp, + bdp->cbd_sc, + bdp->cbd_datlen, + bdp->cbd_bufaddr); + bdp++; + } + + bdp = fep->rx_bd_base; + printk(" rx: %lu buffers\n", RX_RING_SIZE); + for (i = 0 ; i < RX_RING_SIZE; i++) { + printk(" %08x: %04x %04x %08x\n", + (uint) bdp, + bdp->cbd_sc, + bdp->cbd_datlen, + bdp->cbd_bufaddr); + bdp++; + } + } +#endif + if (!fep->tx_full) + netif_wake_queue(dev); +} + +/* The interrupt handler. + * This is called from the MPC core interrupt. + */ +static void +fec_enet_interrupt(int irq, void * dev_id, struct pt_regs * regs) +{ + struct net_device *dev = dev_id; + volatile fec_t *fecp; + uint int_events; +#ifdef CONFIG_FEC_PACKETHOOK + struct fec_enet_private *fep = dev->priv; + __u32 regval; + + if (fep->ph_regaddr) regval = *fep->ph_regaddr; +#endif + fecp = (volatile fec_t*)dev->base_addr; + + /* Get the interrupt events that caused us to be here. + */ + while ((int_events = fecp->fec_ievent) != 0) { + fecp->fec_ievent = int_events; + if ((int_events & (FEC_ENET_HBERR | FEC_ENET_BABR | + FEC_ENET_BABT | FEC_ENET_EBERR)) != 0) { + printk("FEC ERROR %x\n", int_events); + } + + /* Handle receive event in its own function. + */ + if (int_events & FEC_ENET_RXF) { +#ifdef CONFIG_FEC_PACKETHOOK + fec_enet_rx(dev, regval); +#else + fec_enet_rx(dev); +#endif + } + + /* Transmit OK, or non-fatal error. Update the buffer + descriptors. FEC handles all errors, we just discover + them as part of the transmit process. + */ + if (int_events & FEC_ENET_TXF) { +#ifdef CONFIG_FEC_PACKETHOOK + fec_enet_tx(dev, regval); +#else + fec_enet_tx(dev); +#endif + } + + if (int_events & FEC_ENET_MII) { +#ifdef CONFIG_USE_MDIO + fec_enet_mii(dev); +#else +printk("%s[%d] %s: unexpected FEC_ENET_MII event\n", __FILE__,__LINE__,__FUNCTION__); +#endif /* CONFIG_USE_MDIO */ + } + + } +} + + +static void +#ifdef CONFIG_FEC_PACKETHOOK +fec_enet_tx(struct net_device *dev, __u32 regval) +#else +fec_enet_tx(struct net_device *dev) +#endif +{ + struct fec_enet_private *fep; + volatile cbd_t *bdp; + struct sk_buff *skb; + + fep = dev->priv; + /* lock while transmitting */ + spin_lock(&fep->lock); + bdp = fep->dirty_tx; + + while ((bdp->cbd_sc&BD_ENET_TX_READY) == 0) { + if (bdp == fep->cur_tx && fep->tx_full == 0) break; + + skb = fep->tx_skbuff[fep->skb_dirty]; + /* Check for errors. */ + if (bdp->cbd_sc & (BD_ENET_TX_HB | BD_ENET_TX_LC | + BD_ENET_TX_RL | BD_ENET_TX_UN | + BD_ENET_TX_CSL)) { + fep->stats.tx_errors++; + if (bdp->cbd_sc & BD_ENET_TX_HB) /* No heartbeat */ + fep->stats.tx_heartbeat_errors++; + if (bdp->cbd_sc & BD_ENET_TX_LC) /* Late collision */ + fep->stats.tx_window_errors++; + if (bdp->cbd_sc & BD_ENET_TX_RL) /* Retrans limit */ + fep->stats.tx_aborted_errors++; + if (bdp->cbd_sc & BD_ENET_TX_UN) /* Underrun */ + fep->stats.tx_fifo_errors++; + if (bdp->cbd_sc & BD_ENET_TX_CSL) /* Carrier lost */ + fep->stats.tx_carrier_errors++; + } else { +#ifdef CONFIG_FEC_PACKETHOOK + /* Packet hook ... */ + if (fep->ph_txhandler && + ((struct ethhdr *)skb->data)->h_proto + == fep->ph_proto) { + fep->ph_txhandler((__u8*)skb->data, skb->len, + regval, fep->ph_priv); + } +#endif + fep->stats.tx_packets++; + } + +#ifndef final_version + if (bdp->cbd_sc & BD_ENET_TX_READY) + printk("HEY! Enet xmit interrupt and TX_READY.\n"); +#endif + /* Deferred means some collisions occurred during transmit, + * but we eventually sent the packet OK. + */ + if (bdp->cbd_sc & BD_ENET_TX_DEF) + fep->stats.collisions++; + + /* Free the sk buffer associated with this last transmit. + */ +#if 0 +printk("TXI: %x %x %x\n", bdp, skb, fep->skb_dirty); +#endif + dev_kfree_skb_irq (skb/*, FREE_WRITE*/); + fep->tx_skbuff[fep->skb_dirty] = NULL; + fep->skb_dirty = (fep->skb_dirty + 1) & TX_RING_MOD_MASK; + + /* Update pointer to next buffer descriptor to be transmitted. + */ + if (bdp->cbd_sc & BD_ENET_TX_WRAP) + bdp = fep->tx_bd_base; + else + bdp++; + + /* Since we have freed up a buffer, the ring is no longer + * full. + */ + if (fep->tx_full) { + fep->tx_full = 0; + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + } +#ifdef CONFIG_FEC_PACKETHOOK + /* Re-read register. Not exactly guaranteed to be correct, + but... */ + if (fep->ph_regaddr) regval = *fep->ph_regaddr; +#endif + } + fep->dirty_tx = (cbd_t *)bdp; + spin_unlock(&fep->lock); +} + + +/* During a receive, the cur_rx points to the current incoming buffer. + * When we update through the ring, if the next incoming buffer has + * not been given to the system, we just set the empty indicator, + * effectively tossing the packet. + */ +static void +#ifdef CONFIG_FEC_PACKETHOOK +fec_enet_rx(struct net_device *dev, __u32 regval) +#else +fec_enet_rx(struct net_device *dev) +#endif +{ + struct fec_enet_private *fep; + volatile fec_t *fecp; + volatile cbd_t *bdp; + struct sk_buff *skb; + ushort pkt_len; + __u8 *data; + + fep = dev->priv; + fecp = (volatile fec_t*)dev->base_addr; + + /* First, grab all of the stats for the incoming packet. + * These get messed up if we get called due to a busy condition. + */ + bdp = fep->cur_rx; + +while (!(bdp->cbd_sc & BD_ENET_RX_EMPTY)) { + +#ifndef final_version + /* Since we have allocated space to hold a complete frame, + * the last indicator should be set. + */ + if ((bdp->cbd_sc & BD_ENET_RX_LAST) == 0) + printk("FEC ENET: rcv is not +last\n"); +#endif + + /* Check for errors. */ + if (bdp->cbd_sc & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO | + BD_ENET_RX_CR | BD_ENET_RX_OV)) { + fep->stats.rx_errors++; + if (bdp->cbd_sc & (BD_ENET_RX_LG | BD_ENET_RX_SH)) { + /* Frame too long or too short. */ + fep->stats.rx_length_errors++; + } + if (bdp->cbd_sc & BD_ENET_RX_NO) /* Frame alignment */ + fep->stats.rx_frame_errors++; + if (bdp->cbd_sc & BD_ENET_RX_CR) /* CRC Error */ + fep->stats.rx_crc_errors++; + if (bdp->cbd_sc & BD_ENET_RX_OV) /* FIFO overrun */ + fep->stats.rx_crc_errors++; + } + + /* Report late collisions as a frame error. + * On this error, the BD is closed, but we don't know what we + * have in the buffer. So, just drop this frame on the floor. + */ + if (bdp->cbd_sc & BD_ENET_RX_CL) { + fep->stats.rx_errors++; + fep->stats.rx_frame_errors++; + goto rx_processing_done; + } + + /* Process the incoming frame. + */ + fep->stats.rx_packets++; + pkt_len = bdp->cbd_datlen; + fep->stats.rx_bytes += pkt_len; + data = fep->rx_vaddr[bdp - fep->rx_bd_base]; + +#ifdef CONFIG_FEC_PACKETHOOK + /* Packet hook ... */ + if (fep->ph_rxhandler) { + if (((struct ethhdr *)data)->h_proto == fep->ph_proto) { + switch (fep->ph_rxhandler(data, pkt_len, regval, + fep->ph_priv)) { + case 1: + goto rx_processing_done; + break; + case 0: + break; + default: + fep->stats.rx_errors++; + goto rx_processing_done; + } + } + } + + /* If it wasn't filtered - copy it to an sk buffer. */ +#endif + + /* This does 16 byte alignment, exactly what we need. + * The packet length includes FCS, but we don't want to + * include that when passing upstream as it messes up + * bridging applications. + */ + skb = dev_alloc_skb(pkt_len-4); + + if (skb == NULL) { + printk("%s: Memory squeeze, dropping packet.\n", dev->name); + fep->stats.rx_dropped++; + } else { + skb->dev = dev; + skb_put(skb,pkt_len-4); /* Make room */ + eth_copy_and_sum(skb, data, pkt_len-4, 0); + skb->protocol=eth_type_trans(skb,dev); + netif_rx(skb); + } + rx_processing_done: + + /* Clear the status flags for this buffer. + */ + bdp->cbd_sc &= ~BD_ENET_RX_STATS; + + /* Mark the buffer empty. + */ + bdp->cbd_sc |= BD_ENET_RX_EMPTY; + + /* Update BD pointer to next entry. + */ + if (bdp->cbd_sc & BD_ENET_RX_WRAP) + bdp = fep->rx_bd_base; + else + bdp++; + +#if 1 + /* Doing this here will keep the FEC running while we process + * incoming frames. On a heavily loaded network, we should be + * able to keep up at the expense of system resources. + */ + fecp->fec_r_des_active = 0x01000000; +#endif +#ifdef CONFIG_FEC_PACKETHOOK + /* Re-read register. Not exactly guaranteed to be correct, + but... */ + if (fep->ph_regaddr) regval = *fep->ph_regaddr; +#endif + } /* while (!(bdp->cbd_sc & BD_ENET_RX_EMPTY)) */ + fep->cur_rx = (cbd_t *)bdp; + +#if 0 + /* Doing this here will allow us to process all frames in the + * ring before the FEC is allowed to put more there. On a heavily + * loaded network, some frames may be lost. Unfortunately, this + * increases the interrupt overhead since we can potentially work + * our way back to the interrupt return only to come right back + * here. + */ + fecp->fec_r_des_active = 0x01000000; +#endif +} + + +#ifdef CONFIG_USE_MDIO +static void +fec_enet_mii(struct net_device *dev) +{ + struct fec_enet_private *fep; + volatile fec_t *ep; + mii_list_t *mip; + uint mii_reg; + + fep = (struct fec_enet_private *)dev->priv; + ep = &(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec); + mii_reg = ep->fec_mii_data; + + if ((mip = mii_head) == NULL) { + printk("MII and no head!\n"); + return; + } + + if (mip->mii_func != NULL) + (*(mip->mii_func))(mii_reg, dev); + + mii_head = mip->mii_next; + mip->mii_next = mii_free; + mii_free = mip; + + if ((mip = mii_head) != NULL) { + ep->fec_mii_data = mip->mii_regval; + + } +} + +static int +mii_queue(struct net_device *dev, int regval, void (*func)(uint, struct net_device *)) +{ + struct fec_enet_private *fep; + unsigned long flags; + mii_list_t *mip; + int retval; + + /* Add PHY address to register command. + */ + fep = dev->priv; + regval |= fep->phy_addr << 23; + + retval = 0; + + /* lock while modifying mii_list */ + spin_lock_irqsave(&fep->lock, flags); + + if ((mip = mii_free) != NULL) { + mii_free = mip->mii_next; + mip->mii_regval = regval; + mip->mii_func = func; + mip->mii_next = NULL; + if (mii_head) { + mii_tail->mii_next = mip; + mii_tail = mip; + } else { + mii_head = mii_tail = mip; + (&(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec))->fec_mii_data = regval; + } + } else { + retval = 1; + } + + spin_unlock_irqrestore(&fep->lock, flags); + + return(retval); +} + +static void mii_do_cmd(struct net_device *dev, const phy_cmd_t *c) +{ + int k; + + if(!c) + return; + + for(k = 0; (c+k)->mii_data != mk_mii_end; k++) + mii_queue(dev, (c+k)->mii_data, (c+k)->funct); +} + +static void mii_parse_sr(uint mii_reg, struct net_device *dev) +{ + struct fec_enet_private *fep = dev->priv; + volatile uint *s = &(fep->phy_status); + + *s &= ~(PHY_STAT_LINK | PHY_STAT_FAULT | PHY_STAT_ANC); + + if (mii_reg & 0x0004) + *s |= PHY_STAT_LINK; + if (mii_reg & 0x0010) + *s |= PHY_STAT_FAULT; + if (mii_reg & 0x0020) + *s |= PHY_STAT_ANC; + + fep->link = (*s & PHY_STAT_LINK) ? 1 : 0; +} + +static void mii_parse_cr(uint mii_reg, struct net_device *dev) +{ + struct fec_enet_private *fep = dev->priv; + volatile uint *s = &(fep->phy_status); + + *s &= ~(PHY_CONF_ANE | PHY_CONF_LOOP); + + if (mii_reg & 0x1000) + *s |= PHY_CONF_ANE; + if (mii_reg & 0x4000) + *s |= PHY_CONF_LOOP; +} + +static void mii_parse_anar(uint mii_reg, struct net_device *dev) +{ + struct fec_enet_private *fep = dev->priv; + volatile uint *s = &(fep->phy_status); + + *s &= ~(PHY_CONF_SPMASK); + + if (mii_reg & 0x0020) + *s |= PHY_CONF_10HDX; + if (mii_reg & 0x0040) + *s |= PHY_CONF_10FDX; + if (mii_reg & 0x0080) + *s |= PHY_CONF_100HDX; + if (mii_reg & 0x00100) + *s |= PHY_CONF_100FDX; +} +#if 0 +static void mii_disp_reg(uint mii_reg, struct net_device *dev) +{ + printk("reg %u = 0x%04x\n", (mii_reg >> 18) & 0x1f, mii_reg & 0xffff); +} +#endif + +/* ------------------------------------------------------------------------- */ +/* The Level one LXT970 is used by many boards */ + +#ifdef CONFIG_FEC_LXT970 + +#define MII_LXT970_MIRROR 16 /* Mirror register */ +#define MII_LXT970_IER 17 /* Interrupt Enable Register */ +#define MII_LXT970_ISR 18 /* Interrupt Status Register */ +#define MII_LXT970_CONFIG 19 /* Configuration Register */ +#define MII_LXT970_CSR 20 /* Chip Status Register */ + +static void mii_parse_lxt970_csr(uint mii_reg, struct net_device *dev) +{ + struct fec_enet_private *fep = dev->priv; + volatile uint *s = &(fep->phy_status); + + *s &= ~(PHY_STAT_SPMASK); + + if (mii_reg & 0x0800) { + if (mii_reg & 0x1000) + *s |= PHY_STAT_100FDX; + else + *s |= PHY_STAT_100HDX; + } + else { + if (mii_reg & 0x1000) + *s |= PHY_STAT_10FDX; + else + *s |= PHY_STAT_10HDX; + } +} + +static phy_info_t phy_info_lxt970 = { + 0x07810000, + "LXT970", + + (const phy_cmd_t []) { /* config */ +#if 0 +// { mk_mii_write(MII_REG_ANAR, 0x0021), NULL }, + + /* Set default operation of 100-TX....for some reason + * some of these bits are set on power up, which is wrong. + */ + { mk_mii_write(MII_LXT970_CONFIG, 0), NULL }, +#endif + { mk_mii_read(MII_REG_CR), mii_parse_cr }, + { mk_mii_read(MII_REG_ANAR), mii_parse_anar }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* startup - enable interrupts */ + { mk_mii_write(MII_LXT970_IER, 0x0002), NULL }, + { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */ + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* ack_int */ + /* read SR and ISR to acknowledge */ + + { mk_mii_read(MII_REG_SR), mii_parse_sr }, + { mk_mii_read(MII_LXT970_ISR), NULL }, + + /* find out the current status */ + + { mk_mii_read(MII_LXT970_CSR), mii_parse_lxt970_csr }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* shutdown - disable interrupts */ + { mk_mii_write(MII_LXT970_IER, 0x0000), NULL }, + { mk_mii_end, } + }, +}; + +#endif /* CONFIG_FEC_LXT970 */ + +/* ------------------------------------------------------------------------- */ +/* The Level one LXT971 is used on some of my custom boards */ + +#ifdef CONFIG_FEC_LXT971 + +/* register definitions for the 971 */ + +#define MII_LXT971_PCR 16 /* Port Control Register */ +#define MII_LXT971_SR2 17 /* Status Register 2 */ +#define MII_LXT971_IER 18 /* Interrupt Enable Register */ +#define MII_LXT971_ISR 19 /* Interrupt Status Register */ +#define MII_LXT971_LCR 20 /* LED Control Register */ +#define MII_LXT971_TCR 30 /* Transmit Control Register */ + +/* + * I had some nice ideas of running the MDIO faster... + * The 971 should support 8MHz and I tried it, but things acted really + * weird, so 2.5 MHz ought to be enough for anyone... + */ + +static void mii_parse_lxt971_sr2(uint mii_reg, struct net_device *dev) +{ + struct fec_enet_private *fep = dev->priv; + volatile uint *s = &(fep->phy_status); + + *s &= ~(PHY_STAT_SPMASK); + + if (mii_reg & 0x4000) { + if (mii_reg & 0x0200) + *s |= PHY_STAT_100FDX; + else + *s |= PHY_STAT_100HDX; + } + else { + if (mii_reg & 0x0200) + *s |= PHY_STAT_10FDX; + else + *s |= PHY_STAT_10HDX; + } + if (mii_reg & 0x0008) + *s |= PHY_STAT_FAULT; +} + +static phy_info_t phy_info_lxt971 = { + 0x0001378e, + "LXT971", + + (const phy_cmd_t []) { /* config */ +// { mk_mii_write(MII_REG_ANAR, 0x021), NULL }, /* 10 Mbps, HD */ + { mk_mii_read(MII_REG_CR), mii_parse_cr }, + { mk_mii_read(MII_REG_ANAR), mii_parse_anar }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* startup - enable interrupts */ + { mk_mii_write(MII_LXT971_IER, 0x00f2), NULL }, + { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */ + + /* Somehow does the 971 tell me that the link is down + * the first read after power-up. + * read here to get a valid value in ack_int */ + + { mk_mii_read(MII_REG_SR), mii_parse_sr }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* ack_int */ + /* find out the current status */ + + { mk_mii_read(MII_REG_SR), mii_parse_sr }, + { mk_mii_read(MII_LXT971_SR2), mii_parse_lxt971_sr2 }, + + /* we only need to read ISR to acknowledge */ + + { mk_mii_read(MII_LXT971_ISR), NULL }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* shutdown - disable interrupts */ + { mk_mii_write(MII_LXT971_IER, 0x0000), NULL }, + { mk_mii_end, } + }, +}; + +#endif /* CONFIG_FEC_LXT970 */ + + +/* ------------------------------------------------------------------------- */ +/* The Quality Semiconductor QS6612 is used on the RPX CLLF */ + +#ifdef CONFIG_FEC_QS6612 + +/* register definitions */ + +#define MII_QS6612_MCR 17 /* Mode Control Register */ +#define MII_QS6612_FTR 27 /* Factory Test Register */ +#define MII_QS6612_MCO 28 /* Misc. Control Register */ +#define MII_QS6612_ISR 29 /* Interrupt Source Register */ +#define MII_QS6612_IMR 30 /* Interrupt Mask Register */ +#define MII_QS6612_PCR 31 /* 100BaseTx PHY Control Reg. */ + +static void mii_parse_qs6612_pcr(uint mii_reg, struct net_device *dev) +{ + struct fec_enet_private *fep = dev->priv; + volatile uint *s = &(fep->phy_status); + + *s &= ~(PHY_STAT_SPMASK); + + switch((mii_reg >> 2) & 7) { + case 1: *s |= PHY_STAT_10HDX; break; + case 2: *s |= PHY_STAT_100HDX; break; + case 5: *s |= PHY_STAT_10FDX; break; + case 6: *s |= PHY_STAT_100FDX; break; + } +} + +static phy_info_t phy_info_qs6612 = { + 0x00181440, + "QS6612", + + (const phy_cmd_t []) { /* config */ +// { mk_mii_write(MII_REG_ANAR, 0x061), NULL }, /* 10 Mbps */ + + /* The PHY powers up isolated on the RPX, + * so send a command to allow operation. + */ + + { mk_mii_write(MII_QS6612_PCR, 0x0dc0), NULL }, + + /* parse cr and anar to get some info */ + + { mk_mii_read(MII_REG_CR), mii_parse_cr }, + { mk_mii_read(MII_REG_ANAR), mii_parse_anar }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* startup - enable interrupts */ + { mk_mii_write(MII_QS6612_IMR, 0x003a), NULL }, + { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */ + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* ack_int */ + + /* we need to read ISR, SR and ANER to acknowledge */ + + { mk_mii_read(MII_QS6612_ISR), NULL }, + { mk_mii_read(MII_REG_SR), mii_parse_sr }, + { mk_mii_read(MII_REG_ANER), NULL }, + + /* read pcr to get info */ + + { mk_mii_read(MII_QS6612_PCR), mii_parse_qs6612_pcr }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* shutdown - disable interrupts */ + { mk_mii_write(MII_QS6612_IMR, 0x0000), NULL }, + { mk_mii_end, } + }, +}; + +#endif /* CONFIG_FEC_QS6612 */ + +/* ------------------------------------------------------------------------- */ +/* The Advanced Micro Devices AM79C874 is used on the ICU862 */ + +#ifdef CONFIG_FEC_AM79C874 + +/* register definitions for the 79C874 */ + +#define MII_AM79C874_MFR 16 /* Miscellaneous Features Register */ +#define MII_AM79C874_ICSR 17 /* Interrupt Control/Status Register */ +#define MII_AM79C874_DR 18 /* Diagnostic Register */ +#define MII_AM79C874_PMLR 19 /* Power Management & Loopback Register */ +#define MII_AM79C874_MCR 21 /* Mode Control Register */ +#define MII_AM79C874_DC 23 /* Disconnect Counter */ +#define MII_AM79C874_REC 24 /* Receiver Error Counter */ + +static void mii_parse_amd79c874_dr(uint mii_reg, struct net_device *dev, uint data) +{ + volatile struct fec_enet_private *fep = dev->priv; + uint s = fep->phy_status; + + s &= ~(PHY_STAT_SPMASK); + + /* Register 18: Bit 10 is data rate, 11 is Duplex */ + switch ((mii_reg >> 10) & 3) { + case 0: s |= PHY_STAT_10HDX; break; + case 1: s |= PHY_STAT_100HDX; break; + case 2: s |= PHY_STAT_10FDX; break; + case 3: s |= PHY_STAT_100FDX; break; + } + + fep->phy_status = s; +} + +static phy_info_t phy_info_amd79c874 = { + 0x00022561, + "AM79C874", + + (const phy_cmd_t []) { /* config */ +// { mk_mii_write(MII_REG_ANAR, 0x021), NULL }, /* 10 Mbps, HD */ + { mk_mii_read(MII_REG_CR), mii_parse_cr }, + { mk_mii_read(MII_REG_ANAR), mii_parse_anar }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* startup - enable interrupts */ + { mk_mii_write(MII_AM79C874_ICSR, 0xff00), NULL }, + { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */ + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* ack_int */ + /* find out the current status */ + + { mk_mii_read(MII_REG_SR), mii_parse_sr }, + { mk_mii_read(MII_AM79C874_DR), mii_parse_amd79c874_dr }, + + /* we only need to read ICSR to acknowledge */ + + { mk_mii_read(MII_AM79C874_ICSR), NULL }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* shutdown - disable interrupts */ + { mk_mii_write(MII_AM79C874_ICSR, 0x0000), NULL }, + { mk_mii_end, } + }, +}; + +#endif /* CONFIG_FEC_AM79C874 */ + +static phy_info_t *phy_info[] = { + +#ifdef CONFIG_FEC_LXT970 + &phy_info_lxt970, +#endif /* CONFIG_FEC_LXT970 */ + +#ifdef CONFIG_FEC_LXT971 + &phy_info_lxt971, +#endif /* CONFIG_FEC_LXT971 */ + +#ifdef CONFIG_FEC_QS6612 + &phy_info_qs6612, +#endif /* CONFIG_FEC_QS6612 */ + +#ifdef CONFIG_FEC_AM79C874 + &phy_info_amd79c874, +#endif /* CONFIG_FEC_AM79C874 */ + + NULL +}; + +static void mii_display_status(struct net_device *dev) +{ + struct fec_enet_private *fep = dev->priv; + volatile uint *s = &(fep->phy_status); + + if (!fep->link && !fep->old_link) { + /* Link is still down - don't print anything */ + return; + } + + printk("%s: status: ", dev->name); + + if (!fep->link) { + printk("link down"); + } else { + printk("link up"); + + switch(*s & PHY_STAT_SPMASK) { + case PHY_STAT_100FDX: printk(", 100 Mbps Full Duplex"); break; + case PHY_STAT_100HDX: printk(", 100 Mbps Half Duplex"); break; + case PHY_STAT_10FDX: printk(", 10 Mbps Full Duplex"); break; + case PHY_STAT_10HDX: printk(", 10 Mbps Half Duplex"); break; + default: + printk(", Unknown speed/duplex"); + } + + if (*s & PHY_STAT_ANC) + printk(", auto-negotiation complete"); + } + + if (*s & PHY_STAT_FAULT) + printk(", remote fault"); + + printk(".\n"); +} + +static void mii_display_config(struct net_device *dev) +{ + struct fec_enet_private *fep = dev->priv; + volatile uint *s = &(fep->phy_status); + + printk("%s: config: auto-negotiation ", dev->name); + + if (*s & PHY_CONF_ANE) + printk("on"); + else + printk("off"); + + if (*s & PHY_CONF_100FDX) + printk(", 100FDX"); + if (*s & PHY_CONF_100HDX) + printk(", 100HDX"); + if (*s & PHY_CONF_10FDX) + printk(", 10FDX"); + if (*s & PHY_CONF_10HDX) + printk(", 10HDX"); + if (!(*s & PHY_CONF_SPMASK)) + printk(", No speed/duplex selected?"); + + if (*s & PHY_CONF_LOOP) + printk(", loopback enabled"); + + printk(".\n"); + + fep->sequence_done = 1; +} + +static void mii_relink(struct net_device *dev) +{ + struct fec_enet_private *fep = dev->priv; + int duplex; + + fep->link = (fep->phy_status & PHY_STAT_LINK) ? 1 : 0; + mii_display_status(dev); + fep->old_link = fep->link; + + if (fep->link) { + duplex = 0; + if (fep->phy_status + & (PHY_STAT_100FDX | PHY_STAT_10FDX)) + duplex = 1; + fec_restart(dev, duplex); + } + else + fec_stop(dev); + +#if 0 + enable_irq(fep->mii_irq); +#endif + +} + +static void mii_queue_relink(uint mii_reg, struct net_device *dev) +{ + struct fec_enet_private *fep = dev->priv; + + fep->phy_task.routine = (void *)mii_relink; + fep->phy_task.data = dev; + schedule_task(&fep->phy_task); +} + +static void mii_queue_config(uint mii_reg, struct net_device *dev) +{ + struct fec_enet_private *fep = dev->priv; + + fep->phy_task.routine = (void *)mii_display_config; + fep->phy_task.data = dev; + schedule_task(&fep->phy_task); +} + + + +phy_cmd_t phy_cmd_relink[] = { { mk_mii_read(MII_REG_CR), mii_queue_relink }, + { mk_mii_end, } }; +phy_cmd_t phy_cmd_config[] = { { mk_mii_read(MII_REG_CR), mii_queue_config }, + { mk_mii_end, } }; + + + +/* Read remainder of PHY ID. +*/ +static void +mii_discover_phy3(uint mii_reg, struct net_device *dev) +{ + struct fec_enet_private *fep; + int i; + + fep = dev->priv; + fep->phy_id |= (mii_reg & 0xffff); + + for(i = 0; phy_info[i]; i++) + if(phy_info[i]->id == (fep->phy_id >> 4)) + break; + + if(!phy_info[i]) + panic("%s: PHY id 0x%08x is not supported!\n", + dev->name, fep->phy_id); + + fep->phy = phy_info[i]; + fep->phy_id_done = 1; + + printk("%s: Phy @ 0x%x, type %s (0x%08x)\n", + dev->name, fep->phy_addr, fep->phy->name, fep->phy_id); +} + +/* Scan all of the MII PHY addresses looking for someone to respond + * with a valid ID. This usually happens quickly. + */ +static void +mii_discover_phy(uint mii_reg, struct net_device *dev) +{ + struct fec_enet_private *fep; + uint phytype; + + fep = dev->priv; + + if ((phytype = (mii_reg & 0xffff)) != 0xffff) { + + /* Got first part of ID, now get remainder. + */ + fep->phy_id = phytype << 16; + mii_queue(dev, mk_mii_read(MII_REG_PHYIR2), mii_discover_phy3); + } else { + fep->phy_addr++; + if (fep->phy_addr < 32) { + mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), + mii_discover_phy); + } else { + printk("fec: No PHY device found.\n"); + } + } +} +#endif /* CONFIG_USE_MDIO */ + +/* This interrupt occurs when the PHY detects a link change. +*/ +static void +#ifdef CONFIG_RPXCLASSIC +mii_link_interrupt(void *dev_id) +#else +mii_link_interrupt(int irq, void * dev_id, struct pt_regs * regs) +#endif +{ +#ifdef CONFIG_USE_MDIO + struct net_device *dev = dev_id; + struct fec_enet_private *fep = dev->priv; + volatile immap_t *immap = (immap_t *)IMAP_ADDR; + volatile fec_t *fecp = &(immap->im_cpm.cp_fec); + unsigned int ecntrl = fecp->fec_ecntrl; + + /* We need the FEC enabled to access the MII + */ + if ((ecntrl & FEC_ECNTRL_ETHER_EN) == 0) { + fecp->fec_ecntrl |= FEC_ECNTRL_ETHER_EN; + } +#endif /* CONFIG_USE_MDIO */ + +#if 0 + disable_irq(fep->mii_irq); /* disable now, enable later */ +#endif + + +#ifdef CONFIG_USE_MDIO + mii_do_cmd(dev, fep->phy->ack_int); + mii_do_cmd(dev, phy_cmd_relink); /* restart and display status */ + + if ((ecntrl & FEC_ECNTRL_ETHER_EN) == 0) { + fecp->fec_ecntrl = ecntrl; /* restore old settings */ + } +#else +printk("%s[%d] %s: unexpected Link interrupt\n", __FILE__,__LINE__,__FUNCTION__); +#endif /* CONFIG_USE_MDIO */ + +} + +static int +fec_enet_open(struct net_device *dev) +{ + struct fec_enet_private *fep = dev->priv; + + /* I should reset the ring buffers here, but I don't yet know + * a simple way to do that. + */ + +#ifdef CONFIG_USE_MDIO + fep->sequence_done = 0; + fep->link = 0; + + if (fep->phy) { + mii_do_cmd(dev, fep->phy->ack_int); + mii_do_cmd(dev, fep->phy->config); + mii_do_cmd(dev, phy_cmd_config); /* display configuration */ + while(!fep->sequence_done) + schedule(); + + mii_do_cmd(dev, fep->phy->startup); + netif_start_queue(dev); + return 0; /* Success */ + } + return -ENODEV; /* No PHY we understand */ +#else + fep->link = 1; + netif_start_queue(dev); + return 0; /* Success */ +#endif /* CONFIG_USE_MDIO */ + +} + +static int +fec_enet_close(struct net_device *dev) +{ + /* Don't know what to do yet. + */ + netif_stop_queue(dev); + fec_stop(dev); + + return 0; +} + +static struct net_device_stats *fec_enet_get_stats(struct net_device *dev) +{ + struct fec_enet_private *fep = (struct fec_enet_private *)dev->priv; + + return &fep->stats; +} + +/* Set or clear the multicast filter for this adaptor. + * Skeleton taken from sunlance driver. + * The CPM Ethernet implementation allows Multicast as well as individual + * MAC address filtering. Some of the drivers check to make sure it is + * a group multicast address, and discard those that are not. I guess I + * will do the same for now, but just remove the test if you want + * individual filtering as well (do the upper net layers want or support + * this kind of feature?). + */ + +static void set_multicast_list(struct net_device *dev) +{ + struct fec_enet_private *fep; + volatile fec_t *ep; + + fep = (struct fec_enet_private *)dev->priv; + ep = &(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec); + + if (dev->flags&IFF_PROMISC) { + + /* Log any net taps. */ + printk("%s: Promiscuous mode enabled.\n", dev->name); + ep->fec_r_cntrl |= FEC_RCNTRL_PROM; + } else { + + ep->fec_r_cntrl &= ~FEC_RCNTRL_PROM; + + if (dev->flags & IFF_ALLMULTI) { + /* Catch all multicast addresses, so set the + * filter to all 1's. + */ + ep->fec_hash_table_high = 0xffffffff; + ep->fec_hash_table_low = 0xffffffff; + } +#if 0 + else { + /* Clear filter and add the addresses in the list. + */ + ep->sen_gaddr1 = 0; + ep->sen_gaddr2 = 0; + ep->sen_gaddr3 = 0; + ep->sen_gaddr4 = 0; + + dmi = dev->mc_list; + + for (i=0; imc_count; i++) { + + /* Only support group multicast for now. + */ + if (!(dmi->dmi_addr[0] & 1)) + continue; + + /* The address in dmi_addr is LSB first, + * and taddr is MSB first. We have to + * copy bytes MSB first from dmi_addr. + */ + mcptr = (u_char *)dmi->dmi_addr + 5; + tdptr = (u_char *)&ep->sen_taddrh; + for (j=0; j<6; j++) + *tdptr++ = *mcptr--; + + /* Ask CPM to run CRC and set bit in + * filter mask. + */ + cpmp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SCC1, CPM_CR_SET_GADDR) | CPM_CR_FLG; + /* this delay is necessary here -- Cort */ + udelay(10); + while (cpmp->cp_cpcr & CPM_CR_FLG); + } + } +#endif + } +} + +/* Initialize the FEC Ethernet on 860T. + */ +static int __init fec_enet_init(void) +{ + struct net_device *dev; + struct fec_enet_private *fep; + int i, j, k, err; + unsigned char *eap, *iap, *ba; + unsigned long mem_addr; + volatile cbd_t *bdp; + cbd_t *cbd_base; + volatile immap_t *immap; + volatile fec_t *fecp; + bd_t *bd; +#ifdef CONFIG_SCC_ENET + unsigned char tmpaddr[6]; +#endif + + immap = (immap_t *)IMAP_ADDR; /* pointer to internal registers */ + + bd = (bd_t *)__res; + + dev = alloc_etherdev(sizeof(*fep)); + if (!dev) + return -ENOMEM; + + fep = dev->priv; + + fecp = &(immap->im_cpm.cp_fec); + + /* Whack a reset. We should wait for this. + */ + fecp->fec_ecntrl = FEC_ECNTRL_PINMUX | FEC_ECNTRL_RESET; + for (i = 0; + (fecp->fec_ecntrl & FEC_ECNTRL_RESET) && (i < FEC_RESET_DELAY); + ++i) { + udelay(1); + } + if (i == FEC_RESET_DELAY) { + printk ("FEC Reset timeout!\n"); + } + + /* Set the Ethernet address. If using multiple Enets on the 8xx, + * this needs some work to get unique addresses. + */ + eap = (unsigned char *)my_enet_addr; + iap = bd->bi_enetaddr; + +#ifdef CONFIG_SCC_ENET + /* + * If a board has Ethernet configured both on a SCC and the + * FEC, it needs (at least) 2 MAC addresses (we know that Sun + * disagrees, but anyway). For the FEC port, we create + * another address by setting one of the address bits above + * something that would have (up to now) been allocated. + */ + for (i=0; i<6; i++) + tmpaddr[i] = *iap++; + tmpaddr[3] |= 0x80; + iap = tmpaddr; +#endif + + for (i=0; i<6; i++) { + dev->dev_addr[i] = *eap++ = *iap++; + } + + /* Allocate memory for buffer descriptors. + */ + if (((RX_RING_SIZE + TX_RING_SIZE) * sizeof(cbd_t)) > PAGE_SIZE) { + printk("FEC init error. Need more space.\n"); + printk("FEC initialization failed.\n"); + return 1; + } + cbd_base = (cbd_t *)consistent_alloc(GFP_KERNEL, PAGE_SIZE, &mem_addr); + + /* Set receive and transmit descriptor base. + */ + fep->rx_bd_base = cbd_base; + fep->tx_bd_base = cbd_base + RX_RING_SIZE; + + fep->skb_cur = fep->skb_dirty = 0; + + /* Initialize the receive buffer descriptors. + */ + bdp = fep->rx_bd_base; + k = 0; + for (i=0; icbd_sc = BD_ENET_RX_EMPTY; + bdp->cbd_bufaddr = mem_addr; + fep->rx_vaddr[k++] = ba; + mem_addr += FEC_ENET_RX_FRSIZE; + ba += FEC_ENET_RX_FRSIZE; + bdp++; + } + } + + /* Set the last buffer to wrap. + */ + bdp--; + bdp->cbd_sc |= BD_SC_WRAP; + +#ifdef CONFIG_FEC_PACKETHOOK + fep->ph_lock = 0; + fep->ph_rxhandler = fep->ph_txhandler = NULL; + fep->ph_proto = 0; + fep->ph_regaddr = NULL; + fep->ph_priv = NULL; +#endif + + /* Install our interrupt handler. + */ + if (request_irq(FEC_INTERRUPT, fec_enet_interrupt, 0, "fec", dev) != 0) + panic("Could not allocate FEC IRQ!"); + +#ifdef CONFIG_RPXCLASSIC + /* Make Port C, bit 15 an input that causes interrupts. + */ + immap->im_ioport.iop_pcpar &= ~0x0001; + immap->im_ioport.iop_pcdir &= ~0x0001; + immap->im_ioport.iop_pcso &= ~0x0001; + immap->im_ioport.iop_pcint |= 0x0001; + cpm_install_handler(CPMVEC_PIO_PC15, mii_link_interrupt, dev); + + /* Make LEDS reflect Link status. + */ + *((uint *) RPX_CSR_ADDR) &= ~BCSR2_FETHLEDMODE; +#endif + +#ifdef PHY_INTERRUPT + ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_siel |= + (0x80000000 >> PHY_INTERRUPT); + + if (request_irq(PHY_INTERRUPT, mii_link_interrupt, 0, "mii", dev) != 0) + panic("Could not allocate MII IRQ!"); +#endif + + dev->base_addr = (unsigned long)fecp; + + /* The FEC Ethernet specific entries in the device structure. */ + dev->open = fec_enet_open; + dev->hard_start_xmit = fec_enet_start_xmit; + dev->tx_timeout = fec_timeout; + dev->watchdog_timeo = TX_TIMEOUT; + dev->stop = fec_enet_close; + dev->get_stats = fec_enet_get_stats; + dev->set_multicast_list = set_multicast_list; + +#ifdef CONFIG_USE_MDIO + for (i=0; iim_ioport.iop_pdpar = 0x1fff; + + /* Bits moved from Rev. D onward. + */ + if ((mfspr(SPRN_IMMR) & 0xffff) < 0x0501) + immap->im_ioport.iop_pddir = 0x1c58; /* Pre rev. D */ + else + immap->im_ioport.iop_pddir = 0x1fff; /* Rev. D and later */ + +#ifdef CONFIG_USE_MDIO + /* Set MII speed to 2.5 MHz + */ + fecp->fec_mii_speed = fep->phy_speed = + (( (bd->bi_intfreq + 500000) / 2500000 / 2 ) & 0x3F ) << 1; +#else + fecp->fec_mii_speed = 0; /* turn off MDIO */ +#endif /* CONFIG_USE_MDIO */ + + err = register_netdev(dev); + if (err) { + free_netdev(dev); + return err; + } + + printk ("%s: FEC ENET Version 0.2, FEC irq %d" +#ifdef PHY_INTERRUPT + ", MII irq %d" +#endif + ", addr ", + dev->name, FEC_INTERRUPT +#ifdef PHY_INTERRUPT + , PHY_INTERRUPT +#endif + ); + for (i=0; i<6; i++) + printk("%02x%c", dev->dev_addr[i], (i==5) ? '\n' : ':'); + +#ifdef CONFIG_USE_MDIO /* start in full duplex mode, and negotiate speed */ + fec_restart (dev, 1); +#else /* always use half duplex mode only */ + fec_restart (dev, 0); +#endif + +#ifdef CONFIG_USE_MDIO + /* Queue up command to detect the PHY and initialize the + * remainder of the interface. + */ + fep->phy_id_done = 0; + fep->phy_addr = 0; + mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), mii_discover_phy); +#endif /* CONFIG_USE_MDIO */ + + return 0; +} +module_init(fec_enet_init); + +/* This function is called to start or restart the FEC during a link + * change. This only happens when switching between half and full + * duplex. + */ +static void +fec_restart(struct net_device *dev, int duplex) +{ + struct fec_enet_private *fep; + int i; + volatile cbd_t *bdp; + volatile immap_t *immap; + volatile fec_t *fecp; + + immap = (immap_t *)IMAP_ADDR; /* pointer to internal registers */ + + fecp = &(immap->im_cpm.cp_fec); + + fep = dev->priv; + + /* Whack a reset. We should wait for this. + */ + fecp->fec_ecntrl = FEC_ECNTRL_PINMUX | FEC_ECNTRL_RESET; + for (i = 0; + (fecp->fec_ecntrl & FEC_ECNTRL_RESET) && (i < FEC_RESET_DELAY); + ++i) { + udelay(1); + } + if (i == FEC_RESET_DELAY) { + printk ("FEC Reset timeout!\n"); + } + + /* Set station address. + */ + fecp->fec_addr_low = (my_enet_addr[0] << 16) | my_enet_addr[1]; + fecp->fec_addr_high = my_enet_addr[2]; + + /* Reset all multicast. + */ + fecp->fec_hash_table_high = 0; + fecp->fec_hash_table_low = 0; + + /* Set maximum receive buffer size. + */ + fecp->fec_r_buff_size = PKT_MAXBLR_SIZE; + fecp->fec_r_hash = PKT_MAXBUF_SIZE; + + /* Set receive and transmit descriptor base. + */ + fecp->fec_r_des_start = iopa((uint)(fep->rx_bd_base)); + fecp->fec_x_des_start = iopa((uint)(fep->tx_bd_base)); + + fep->dirty_tx = fep->cur_tx = fep->tx_bd_base; + fep->cur_rx = fep->rx_bd_base; + + /* Reset SKB transmit buffers. + */ + fep->skb_cur = fep->skb_dirty = 0; + for (i=0; i<=TX_RING_MOD_MASK; i++) { + if (fep->tx_skbuff[i] != NULL) { + dev_kfree_skb(fep->tx_skbuff[i]); + fep->tx_skbuff[i] = NULL; + } + } + + /* Initialize the receive buffer descriptors. + */ + bdp = fep->rx_bd_base; + for (i=0; icbd_sc = BD_ENET_RX_EMPTY; + bdp++; + } + + /* Set the last buffer to wrap. + */ + bdp--; + bdp->cbd_sc |= BD_SC_WRAP; + + /* ...and the same for transmmit. + */ + bdp = fep->tx_bd_base; + for (i=0; icbd_sc = 0; + bdp->cbd_bufaddr = 0; + bdp++; + } + + /* Set the last buffer to wrap. + */ + bdp--; + bdp->cbd_sc |= BD_SC_WRAP; + + /* Enable MII mode. + */ + if (duplex) { + fecp->fec_r_cntrl = FEC_RCNTRL_MII_MODE; /* MII enable */ + fecp->fec_x_cntrl = FEC_TCNTRL_FDEN; /* FD enable */ + } + else { + fecp->fec_r_cntrl = FEC_RCNTRL_MII_MODE | FEC_RCNTRL_DRT; + fecp->fec_x_cntrl = 0; + } + fep->full_duplex = duplex; + + /* Enable big endian and don't care about SDMA FC. + */ + fecp->fec_fun_code = 0x78000000; + +#ifdef CONFIG_USE_MDIO + /* Set MII speed. + */ + fecp->fec_mii_speed = fep->phy_speed; +#endif /* CONFIG_USE_MDIO */ + + /* Clear any outstanding interrupt. + */ + fecp->fec_ievent = 0xffc0; + + fecp->fec_ivec = (FEC_INTERRUPT/2) << 29; + + /* Enable interrupts we wish to service. + */ + fecp->fec_imask = ( FEC_ENET_TXF | FEC_ENET_TXB | + FEC_ENET_RXF | FEC_ENET_RXB | FEC_ENET_MII ); + + /* And last, enable the transmit and receive processing. + */ + fecp->fec_ecntrl = FEC_ECNTRL_PINMUX | FEC_ECNTRL_ETHER_EN; + fecp->fec_r_des_active = 0x01000000; +} + +static void +fec_stop(struct net_device *dev) +{ + volatile immap_t *immap; + volatile fec_t *fecp; + struct fec_enet_private *fep; + int i; + + immap = (immap_t *)IMAP_ADDR; /* pointer to internal registers */ + + fecp = &(immap->im_cpm.cp_fec); + + if ((fecp->fec_ecntrl & FEC_ECNTRL_ETHER_EN) == 0) + return; /* already down */ + + fep = dev->priv; + + + fecp->fec_x_cntrl = 0x01; /* Graceful transmit stop */ + + for (i = 0; + ((fecp->fec_ievent & 0x10000000) == 0) && (i < FEC_RESET_DELAY); + ++i) { + udelay(1); + } + if (i == FEC_RESET_DELAY) { + printk ("FEC timeout on graceful transmit stop\n"); + } + + /* Clear outstanding MII command interrupts. + */ + fecp->fec_ievent = FEC_ENET_MII; + + /* Enable MII command finished interrupt + */ + fecp->fec_ivec = (FEC_INTERRUPT/2) << 29; + fecp->fec_imask = FEC_ENET_MII; + +#ifdef CONFIG_USE_MDIO + /* Set MII speed. + */ + fecp->fec_mii_speed = fep->phy_speed; +#endif /* CONFIG_USE_MDIO */ + + /* Disable FEC + */ + fecp->fec_ecntrl &= ~(FEC_ECNTRL_ETHER_EN); +} diff --git a/arch/ppc/8xx_io/micropatch.c b/arch/ppc/8xx_io/micropatch.c new file mode 100644 index 00000000000..312af0776c3 --- /dev/null +++ b/arch/ppc/8xx_io/micropatch.c @@ -0,0 +1,744 @@ + +/* Microcode patches for the CPM as supplied by Motorola. + * This is the one for IIC/SPI. There is a newer one that + * also relocates SMC2, but this would require additional changes + * to uart.c, so I am holding off on that for a moment. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * I2C/SPI relocation patch arrays. + */ + +#ifdef CONFIG_I2C_SPI_UCODE_PATCH + +uint patch_2000[] = { + 0x7FFFEFD9, + 0x3FFD0000, + 0x7FFB49F7, + 0x7FF90000, + 0x5FEFADF7, + 0x5F89ADF7, + 0x5FEFAFF7, + 0x5F89AFF7, + 0x3A9CFBC8, + 0xE7C0EDF0, + 0x77C1E1BB, + 0xF4DC7F1D, + 0xABAD932F, + 0x4E08FDCF, + 0x6E0FAFF8, + 0x7CCF76CF, + 0xFD1FF9CF, + 0xABF88DC6, + 0xAB5679F7, + 0xB0937383, + 0xDFCE79F7, + 0xB091E6BB, + 0xE5BBE74F, + 0xB3FA6F0F, + 0x6FFB76CE, + 0xEE0DF9CF, + 0x2BFBEFEF, + 0xCFEEF9CF, + 0x76CEAD24, + 0x90B2DF9A, + 0x7FDDD0BF, + 0x4BF847FD, + 0x7CCF76CE, + 0xCFEF7E1F, + 0x7F1D7DFD, + 0xF0B6EF71, + 0x7FC177C1, + 0xFBC86079, + 0xE722FBC8, + 0x5FFFDFFF, + 0x5FB2FFFB, + 0xFBC8F3C8, + 0x94A67F01, + 0x7F1D5F39, + 0xAFE85F5E, + 0xFFDFDF96, + 0xCB9FAF7D, + 0x5FC1AFED, + 0x8C1C5FC1, + 0xAFDD5FC3, + 0xDF9A7EFD, + 0xB0B25FB2, + 0xFFFEABAD, + 0x5FB2FFFE, + 0x5FCE600B, + 0xE6BB600B, + 0x5FCEDFC6, + 0x27FBEFDF, + 0x5FC8CFDE, + 0x3A9CE7C0, + 0xEDF0F3C8, + 0x7F0154CD, + 0x7F1D2D3D, + 0x363A7570, + 0x7E0AF1CE, + 0x37EF2E68, + 0x7FEE10EC, + 0xADF8EFDE, + 0xCFEAE52F, + 0x7D0FE12B, + 0xF1CE5F65, + 0x7E0A4DF8, + 0xCFEA5F72, + 0x7D0BEFEE, + 0xCFEA5F74, + 0xE522EFDE, + 0x5F74CFDA, + 0x0B627385, + 0xDF627E0A, + 0x30D8145B, + 0xBFFFF3C8, + 0x5FFFDFFF, + 0xA7F85F5E, + 0xBFFE7F7D, + 0x10D31450, + 0x5F36BFFF, + 0xAF785F5E, + 0xBFFDA7F8, + 0x5F36BFFE, + 0x77FD30C0, + 0x4E08FDCF, + 0xE5FF6E0F, + 0xAFF87E1F, + 0x7E0FFD1F, + 0xF1CF5F1B, + 0xABF80D5E, + 0x5F5EFFEF, + 0x79F730A2, + 0xAFDD5F34, + 0x47F85F34, + 0xAFED7FDD, + 0x50B24978, + 0x47FD7F1D, + 0x7DFD70AD, + 0xEF717EC1, + 0x6BA47F01, + 0x2D267EFD, + 0x30DE5F5E, + 0xFFFD5F5E, + 0xFFEF5F5E, + 0xFFDF0CA0, + 0xAFED0A9E, + 0xAFDD0C3A, + 0x5F3AAFBD, + 0x7FBDB082, + 0x5F8247F8 +}; + +uint patch_2f00[] = { + 0x3E303430, + 0x34343737, + 0xABF7BF9B, + 0x994B4FBD, + 0xBD599493, + 0x349FFF37, + 0xFB9B177D, + 0xD9936956, + 0xBBFDD697, + 0xBDD2FD11, + 0x31DB9BB3, + 0x63139637, + 0x93733693, + 0x193137F7, + 0x331737AF, + 0x7BB9B999, + 0xBB197957, + 0x7FDFD3D5, + 0x73B773F7, + 0x37933B99, + 0x1D115316, + 0x99315315, + 0x31694BF4, + 0xFBDBD359, + 0x31497353, + 0x76956D69, + 0x7B9D9693, + 0x13131979, + 0x79376935 +}; +#endif + +/* + * I2C/SPI/SMC1 relocation patch arrays. + */ + +#ifdef CONFIG_I2C_SPI_SMC1_UCODE_PATCH + +uint patch_2000[] = { + 0x3fff0000, + 0x3ffd0000, + 0x3ffb0000, + 0x3ff90000, + 0x5f13eff8, + 0x5eb5eff8, + 0x5f88adf7, + 0x5fefadf7, + 0x3a9cfbc8, + 0x77cae1bb, + 0xf4de7fad, + 0xabae9330, + 0x4e08fdcf, + 0x6e0faff8, + 0x7ccf76cf, + 0xfdaff9cf, + 0xabf88dc8, + 0xab5879f7, + 0xb0925d8d, + 0xdfd079f7, + 0xb090e6bb, + 0xe5bbe74f, + 0x9e046f0f, + 0x6ffb76ce, + 0xee0cf9cf, + 0x2bfbefef, + 0xcfeef9cf, + 0x76cead23, + 0x90b3df99, + 0x7fddd0c1, + 0x4bf847fd, + 0x7ccf76ce, + 0xcfef77ca, + 0x7eaf7fad, + 0x7dfdf0b7, + 0xef7a7fca, + 0x77cafbc8, + 0x6079e722, + 0xfbc85fff, + 0xdfff5fb3, + 0xfffbfbc8, + 0xf3c894a5, + 0xe7c9edf9, + 0x7f9a7fad, + 0x5f36afe8, + 0x5f5bffdf, + 0xdf95cb9e, + 0xaf7d5fc3, + 0xafed8c1b, + 0x5fc3afdd, + 0x5fc5df99, + 0x7efdb0b3, + 0x5fb3fffe, + 0xabae5fb3, + 0xfffe5fd0, + 0x600be6bb, + 0x600b5fd0, + 0xdfc827fb, + 0xefdf5fca, + 0xcfde3a9c, + 0xe7c9edf9, + 0xf3c87f9e, + 0x54ca7fed, + 0x2d3a3637, + 0x756f7e9a, + 0xf1ce37ef, + 0x2e677fee, + 0x10ebadf8, + 0xefdecfea, + 0xe52f7d9f, + 0xe12bf1ce, + 0x5f647e9a, + 0x4df8cfea, + 0x5f717d9b, + 0xefeecfea, + 0x5f73e522, + 0xefde5f73, + 0xcfda0b61, + 0x5d8fdf61, + 0xe7c9edf9, + 0x7e9a30d5, + 0x1458bfff, + 0xf3c85fff, + 0xdfffa7f8, + 0x5f5bbffe, + 0x7f7d10d0, + 0x144d5f33, + 0xbfffaf78, + 0x5f5bbffd, + 0xa7f85f33, + 0xbffe77fd, + 0x30bd4e08, + 0xfdcfe5ff, + 0x6e0faff8, + 0x7eef7e9f, + 0xfdeff1cf, + 0x5f17abf8, + 0x0d5b5f5b, + 0xffef79f7, + 0x309eafdd, + 0x5f3147f8, + 0x5f31afed, + 0x7fdd50af, + 0x497847fd, + 0x7f9e7fed, + 0x7dfd70a9, + 0xef7e7ece, + 0x6ba07f9e, + 0x2d227efd, + 0x30db5f5b, + 0xfffd5f5b, + 0xffef5f5b, + 0xffdf0c9c, + 0xafed0a9a, + 0xafdd0c37, + 0x5f37afbd, + 0x7fbdb081, + 0x5f8147f8, + 0x3a11e710, + 0xedf0ccdd, + 0xf3186d0a, + 0x7f0e5f06, + 0x7fedbb38, + 0x3afe7468, + 0x7fedf4fc, + 0x8ffbb951, + 0xb85f77fd, + 0xb0df5ddd, + 0xdefe7fed, + 0x90e1e74d, + 0x6f0dcbf7, + 0xe7decfed, + 0xcb74cfed, + 0xcfeddf6d, + 0x91714f74, + 0x5dd2deef, + 0x9e04e7df, + 0xefbb6ffb, + 0xe7ef7f0e, + 0x9e097fed, + 0xebdbeffa, + 0xeb54affb, + 0x7fea90d7, + 0x7e0cf0c3, + 0xbffff318, + 0x5fffdfff, + 0xac59efea, + 0x7fce1ee5, + 0xe2ff5ee1, + 0xaffbe2ff, + 0x5ee3affb, + 0xf9cc7d0f, + 0xaef8770f, + 0x7d0fb0c6, + 0xeffbbfff, + 0xcfef5ede, + 0x7d0fbfff, + 0x5ede4cf8, + 0x7fddd0bf, + 0x49f847fd, + 0x7efdf0bb, + 0x7fedfffd, + 0x7dfdf0b7, + 0xef7e7e1e, + 0x5ede7f0e, + 0x3a11e710, + 0xedf0ccab, + 0xfb18ad2e, + 0x1ea9bbb8, + 0x74283b7e, + 0x73c2e4bb, + 0x2ada4fb8, + 0xdc21e4bb, + 0xb2a1ffbf, + 0x5e2c43f8, + 0xfc87e1bb, + 0xe74ffd91, + 0x6f0f4fe8, + 0xc7ba32e2, + 0xf396efeb, + 0x600b4f78, + 0xe5bb760b, + 0x53acaef8, + 0x4ef88b0e, + 0xcfef9e09, + 0xabf8751f, + 0xefef5bac, + 0x741f4fe8, + 0x751e760d, + 0x7fdbf081, + 0x741cafce, + 0xefcc7fce, + 0x751e70ac, + 0x741ce7bb, + 0x3372cfed, + 0xafdbefeb, + 0xe5bb760b, + 0x53f2aef8, + 0xafe8e7eb, + 0x4bf8771e, + 0x7e247fed, + 0x4fcbe2cc, + 0x7fbc30a9, + 0x7b0f7a0f, + 0x34d577fd, + 0x308b5db7, + 0xde553e5f, + 0xaf78741f, + 0x741f30f0, + 0xcfef5e2c, + 0x741f3eac, + 0xafb8771e, + 0x5e677fed, + 0x0bd3e2cc, + 0x741ccfec, + 0xe5ca53cd, + 0x6fcb4f74, + 0x5dadde4b, + 0x2ab63d38, + 0x4bb3de30, + 0x751f741c, + 0x6c42effa, + 0xefea7fce, + 0x6ffc30be, + 0xefec3fca, + 0x30b3de2e, + 0xadf85d9e, + 0xaf7daefd, + 0x5d9ede2e, + 0x5d9eafdd, + 0x761f10ac, + 0x1da07efd, + 0x30adfffe, + 0x4908fb18, + 0x5fffdfff, + 0xafbb709b, + 0x4ef85e67, + 0xadf814ad, + 0x7a0f70ad, + 0xcfef50ad, + 0x7a0fde30, + 0x5da0afed, + 0x3c12780f, + 0xefef780f, + 0xefef790f, + 0xa7f85e0f, + 0xffef790f, + 0xefef790f, + 0x14adde2e, + 0x5d9eadfd, + 0x5e2dfffb, + 0xe79addfd, + 0xeff96079, + 0x607ae79a, + 0xddfceff9, + 0x60795dff, + 0x607acfef, + 0xefefefdf, + 0xefbfef7f, + 0xeeffedff, + 0xebffe7ff, + 0xafefafdf, + 0xafbfaf7f, + 0xaeffadff, + 0xabffa7ff, + 0x6fef6fdf, + 0x6fbf6f7f, + 0x6eff6dff, + 0x6bff67ff, + 0x2fef2fdf, + 0x2fbf2f7f, + 0x2eff2dff, + 0x2bff27ff, + 0x4e08fd1f, + 0xe5ff6e0f, + 0xaff87eef, + 0x7e0ffdef, + 0xf11f6079, + 0xabf8f542, + 0x7e0af11c, + 0x37cfae3a, + 0x7fec90be, + 0xadf8efdc, + 0xcfeae52f, + 0x7d0fe12b, + 0xf11c6079, + 0x7e0a4df8, + 0xcfea5dc4, + 0x7d0befec, + 0xcfea5dc6, + 0xe522efdc, + 0x5dc6cfda, + 0x4e08fd1f, + 0x6e0faff8, + 0x7c1f761f, + 0xfdeff91f, + 0x6079abf8, + 0x761cee24, + 0xf91f2bfb, + 0xefefcfec, + 0xf91f6079, + 0x761c27fb, + 0xefdf5da7, + 0xcfdc7fdd, + 0xd09c4bf8, + 0x47fd7c1f, + 0x761ccfcf, + 0x7eef7fed, + 0x7dfdf093, + 0xef7e7f1e, + 0x771efb18, + 0x6079e722, + 0xe6bbe5bb, + 0xae0ae5bb, + 0x600bae85, + 0xe2bbe2bb, + 0xe2bbe2bb, + 0xaf02e2bb, + 0xe2bb2ff9, + 0x6079e2bb +}; + +uint patch_2f00[] = { + 0x30303030, + 0x3e3e3434, + 0xabbf9b99, + 0x4b4fbdbd, + 0x59949334, + 0x9fff37fb, + 0x9b177dd9, + 0x936956bb, + 0xfbdd697b, + 0xdd2fd113, + 0x1db9f7bb, + 0x36313963, + 0x79373369, + 0x3193137f, + 0x7331737a, + 0xf7bb9b99, + 0x9bb19795, + 0x77fdfd3d, + 0x573b773f, + 0x737933f7, + 0xb991d115, + 0x31699315, + 0x31531694, + 0xbf4fbdbd, + 0x35931497, + 0x35376956, + 0xbd697b9d, + 0x96931313, + 0x19797937, + 0x6935af78, + 0xb9b3baa3, + 0xb8788683, + 0x368f78f7, + 0x87778733, + 0x3ffffb3b, + 0x8e8f78b8, + 0x1d118e13, + 0xf3ff3f8b, + 0x6bd8e173, + 0xd1366856, + 0x68d1687b, + 0x3daf78b8, + 0x3a3a3f87, + 0x8f81378f, + 0xf876f887, + 0x77fd8778, + 0x737de8d6, + 0xbbf8bfff, + 0xd8df87f7, + 0xfd876f7b, + 0x8bfff8bd, + 0x8683387d, + 0xb873d87b, + 0x3b8fd7f8, + 0xf7338883, + 0xbb8ee1f8, + 0xef837377, + 0x3337b836, + 0x817d11f8, + 0x7378b878, + 0xd3368b7d, + 0xed731b7d, + 0x833731f3, + 0xf22f3f23 +}; + +uint patch_2e00[] = { + 0x27eeeeee, + 0xeeeeeeee, + 0xeeeeeeee, + 0xeeeeeeee, + 0xee4bf4fb, + 0xdbd259bb, + 0x1979577f, + 0xdfd2d573, + 0xb773f737, + 0x4b4fbdbd, + 0x25b9b177, + 0xd2d17376, + 0x956bbfdd, + 0x697bdd2f, + 0xff9f79ff, + 0xff9ff22f +}; +#endif + +/* + * USB SOF patch arrays. + */ + +#ifdef CONFIG_USB_SOF_UCODE_PATCH + +uint patch_2000[] = { + 0x7fff0000, + 0x7ffd0000, + 0x7ffb0000, + 0x49f7ba5b, + 0xba383ffb, + 0xf9b8b46d, + 0xe5ab4e07, + 0xaf77bffe, + 0x3f7bbf79, + 0xba5bba38, + 0xe7676076, + 0x60750000 +}; + +uint patch_2f00[] = { + 0x3030304c, + 0xcab9e441, + 0xa1aaf220 +}; +#endif + +void +cpm_load_patch(volatile immap_t *immr) +{ + volatile uint *dp; /* Dual-ported RAM. */ + volatile cpm8xx_t *commproc; + volatile iic_t *iip; + volatile spi_t *spp; + volatile smc_uart_t *smp; + int i; + + commproc = (cpm8xx_t *)&immr->im_cpm; + +#ifdef CONFIG_USB_SOF_UCODE_PATCH + commproc->cp_rccr = 0; + + dp = (uint *)(commproc->cp_dpmem); + for (i=0; i<(sizeof(patch_2000)/4); i++) + *dp++ = patch_2000[i]; + + dp = (uint *)&(commproc->cp_dpmem[0x0f00]); + for (i=0; i<(sizeof(patch_2f00)/4); i++) + *dp++ = patch_2f00[i]; + + commproc->cp_rccr = 0x0009; + + printk("USB SOF microcode patch installed\n"); +#endif /* CONFIG_USB_SOF_UCODE_PATCH */ + +#if defined(CONFIG_I2C_SPI_UCODE_PATCH) || \ + defined(CONFIG_I2C_SPI_SMC1_UCODE_PATCH) + + commproc->cp_rccr = 0; + + dp = (uint *)(commproc->cp_dpmem); + for (i=0; i<(sizeof(patch_2000)/4); i++) + *dp++ = patch_2000[i]; + + dp = (uint *)&(commproc->cp_dpmem[0x0f00]); + for (i=0; i<(sizeof(patch_2f00)/4); i++) + *dp++ = patch_2f00[i]; + + iip = (iic_t *)&commproc->cp_dparam[PROFF_IIC]; +# define RPBASE 0x0500 + iip->iic_rpbase = RPBASE; + + /* Put SPI above the IIC, also 32-byte aligned. + */ + i = (RPBASE + sizeof(iic_t) + 31) & ~31; + spp = (spi_t *)&commproc->cp_dparam[PROFF_SPI]; + spp->spi_rpbase = i; + +# if defined(CONFIG_I2C_SPI_UCODE_PATCH) + commproc->cp_cpmcr1 = 0x802a; + commproc->cp_cpmcr2 = 0x8028; + commproc->cp_cpmcr3 = 0x802e; + commproc->cp_cpmcr4 = 0x802c; + commproc->cp_rccr = 1; + + printk("I2C/SPI microcode patch installed.\n"); +# endif /* CONFIG_I2C_SPI_UCODE_PATCH */ + +# if defined(CONFIG_I2C_SPI_SMC1_UCODE_PATCH) + + dp = (uint *)&(commproc->cp_dpmem[0x0e00]); + for (i=0; i<(sizeof(patch_2e00)/4); i++) + *dp++ = patch_2e00[i]; + + commproc->cp_cpmcr1 = 0x8080; + commproc->cp_cpmcr2 = 0x808a; + commproc->cp_cpmcr3 = 0x8028; + commproc->cp_cpmcr4 = 0x802a; + commproc->cp_rccr = 3; + + smp = (smc_uart_t *)&commproc->cp_dparam[PROFF_SMC1]; + smp->smc_rpbase = 0x1FC0; + + printk("I2C/SPI/SMC1 microcode patch installed.\n"); +# endif /* CONFIG_I2C_SPI_SMC1_UCODE_PATCH) */ + +#endif /* some variation of the I2C/SPI patch was selected */ +} + +/* + * Take this entire routine out, since no one calls it and its + * logic is suspect. + */ + +#if 0 +void +verify_patch(volatile immap_t *immr) +{ + volatile uint *dp; + volatile cpm8xx_t *commproc; + int i; + + commproc = (cpm8xx_t *)&immr->im_cpm; + + printk("cp_rccr %x\n", commproc->cp_rccr); + commproc->cp_rccr = 0; + + dp = (uint *)(commproc->cp_dpmem); + for (i=0; i<(sizeof(patch_2000)/4); i++) + if (*dp++ != patch_2000[i]) { + printk("patch_2000 bad at %d\n", i); + dp--; + printk("found 0x%X, wanted 0x%X\n", *dp, patch_2000[i]); + break; + } + + dp = (uint *)&(commproc->cp_dpmem[0x0f00]); + for (i=0; i<(sizeof(patch_2f00)/4); i++) + if (*dp++ != patch_2f00[i]) { + printk("patch_2f00 bad at %d\n", i); + dp--; + printk("found 0x%X, wanted 0x%X\n", *dp, patch_2f00[i]); + break; + } + + commproc->cp_rccr = 0x0009; +} +#endif diff --git a/arch/ppc/Kconfig b/arch/ppc/Kconfig new file mode 100644 index 00000000000..813c6c9a6d9 --- /dev/null +++ b/arch/ppc/Kconfig @@ -0,0 +1,1317 @@ +# For a description of the syntax of this configuration file, +# see Documentation/kbuild/kconfig-language.txt. +# + +mainmenu "Linux/PowerPC Kernel Configuration" + +config MMU + bool + default y + +config UID16 + bool + +config GENERIC_HARDIRQS + bool + default y + +config RWSEM_GENERIC_SPINLOCK + bool + +config RWSEM_XCHGADD_ALGORITHM + bool + default y + +config GENERIC_CALIBRATE_DELAY + bool + default y + +config HAVE_DEC_LOCK + bool + default y + +config PPC + bool + default y + +config PPC32 + bool + default y + +# All PPCs use generic nvram driver through ppc_md +config GENERIC_NVRAM + bool + default y + +source "init/Kconfig" + +menu "Processor" + +choice + prompt "Processor Type" + default 6xx + +config 6xx + bool "6xx/7xx/74xx/52xx/82xx/83xx" + help + There are four types of PowerPC chips supported. The more common + types (601, 603, 604, 740, 750, 7400), the Motorola embedded + versions (821, 823, 850, 855, 860, 52xx, 82xx, 83xx), the IBM embedded + versions (403 and 405) and the high end 64 bit Power processors + (POWER 3, POWER4, and IBM 970 also known as G5) + Unless you are building a kernel for one of the embedded processor + systems, 64 bit IBM RS/6000 or an Apple G5, choose 6xx. + Note that the kernel runs in 32-bit mode even on 64-bit chips. + Also note that because the 52xx, 82xx, & 83xx family has a 603e core, + specific support for that chipset is asked later on. + +config 40x + bool "40x" + +config 44x + bool "44x" + +config POWER3 + bool "POWER3" + +config POWER4 + bool "POWER4 and 970 (G5)" + +config 8xx + depends on BROKEN + bool "8xx" + +config E500 + bool "e500" + +endchoice + +config BOOKE + bool + depends on E500 + default y + +config FSL_BOOKE + bool + depends on E500 + default y + +config PTE_64BIT + bool + depends on 44x + default y + +config PHYS_64BIT + bool + depends on 44x + default y + +config ALTIVEC + bool "AltiVec Support" + depends on 6xx || POWER4 + depends on !8260 && !83xx + ---help--- + This option enables kernel support for the Altivec extensions to the + PowerPC processor. The kernel currently supports saving and restoring + altivec registers, and turning on the 'altivec enable' bit so user + processes can execute altivec instructions. + + This option is only usefully if you have a processor that supports + altivec (G4, otherwise known as 74xx series), but does not have + any affect on a non-altivec cpu (it does, however add code to the + kernel). + + If in doubt, say Y here. + +config SPE + bool "SPE Support" + depends on E500 + ---help--- + This option enables kernel support for the Signal Processing + Extensions (SPE) to the PowerPC processor. The kernel currently + supports saving and restoring SPE registers, and turning on the + 'spe enable' bit so user processes can execute SPE instructions. + + This option is only usefully if you have a processor that supports + SPE (e500, otherwise known as 85xx series), but does not have any + affect on a non-spe cpu (it does, however add code to the kernel). + + If in doubt, say Y here. + +config TAU + bool "Thermal Management Support" + depends on 6xx && !8260 && !83xx + help + G3 and G4 processors have an on-chip temperature sensor called the + 'Thermal Assist Unit (TAU)', which, in theory, can measure the on-die + temperature within 2-4 degrees Celsius. This option shows the current + on-die temperature in /proc/cpuinfo if the cpu supports it. + + Unfortunately, on some chip revisions, this sensor is very inaccurate + and in some cases, does not work at all, so don't assume the cpu + temp is actually what /proc/cpuinfo says it is. + +config TAU_INT + bool "Interrupt driven TAU driver (DANGEROUS)" + depends on TAU + ---help--- + The TAU supports an interrupt driven mode which causes an interrupt + whenever the temperature goes out of range. This is the fastest way + to get notified the temp has exceeded a range. With this option off, + a timer is used to re-check the temperature periodically. + + However, on some cpus it appears that the TAU interrupt hardware + is buggy and can cause a situation which would lead unexplained hard + lockups. + + Unless you are extending the TAU driver, or enjoy kernel/hardware + debugging, leave this option off. + +config TAU_AVERAGE + bool "Average high and low temp" + depends on TAU + ---help--- + The TAU hardware can compare the temperature to an upper and lower + bound. The default behavior is to show both the upper and lower + bound in /proc/cpuinfo. If the range is large, the temperature is + either changing a lot, or the TAU hardware is broken (likely on some + G4's). If the range is small (around 4 degrees), the temperature is + relatively stable. If you say Y here, a single temperature value, + halfway between the upper and lower bounds, will be reported in + /proc/cpuinfo. + + If in doubt, say N here. + +config MATH_EMULATION + bool "Math emulation" + depends on 4xx || 8xx || E500 + ---help--- + Some PowerPC chips designed for embedded applications do not have + a floating-point unit and therefore do not implement the + floating-point instructions in the PowerPC instruction set. If you + say Y here, the kernel will include code to emulate a floating-point + unit, which will allow programs that use floating-point + instructions to run. + + If you have an Apple machine or an IBM RS/6000 or pSeries machine, + or any machine with a 6xx, 7xx or 7xxx series processor, say N + here. Saying Y here will not hurt performance (on any machine) but + will increase the size of the kernel. + +source "drivers/cpufreq/Kconfig" + +config CPU_FREQ_PMAC + bool "Support for Apple PowerBooks" + depends on CPU_FREQ && ADB_PMU + select CPU_FREQ_TABLE + help + This adds support for frequency switching on Apple PowerBooks, + this currently includes some models of iBook & Titanium + PowerBook. + +config PPC601_SYNC_FIX + bool "Workarounds for PPC601 bugs" + depends on 6xx && (PPC_PREP || PPC_PMAC) + help + Some versions of the PPC601 (the first PowerPC chip) have bugs which + mean that extra synchronization instructions are required near + certain instructions, typically those that make major changes to the + CPU state. These extra instructions reduce performance slightly. + If you say N here, these extra instructions will not be included, + resulting in a kernel which will run faster but may not run at all + on some systems with the PPC601 chip. + + If in doubt, say Y here. + +source arch/ppc/platforms/4xx/Kconfig +source arch/ppc/platforms/85xx/Kconfig + +config PPC64BRIDGE + bool + depends on POWER3 || POWER4 + default y + +config PPC_STD_MMU + bool + depends on 6xx || POWER3 || POWER4 + default y + +config NOT_COHERENT_CACHE + bool + depends on 4xx || 8xx + default y + +endmenu + +menu "Platform options" + +choice + prompt "8xx Machine Type" + depends on 8xx + default RPXLITE + +config RPXLITE + bool "RPX-Lite" + ---help--- + Single-board computers based around the PowerPC MPC8xx chips and + intended for embedded applications. The following types are + supported: + + RPX-Lite: + Embedded Planet RPX Lite. PC104 form-factor SBC based on the MPC823. + + RPX-Classic: + Embedded Planet RPX Classic Low-fat. Credit-card-size SBC based on + the MPC 860 + + BSE-IP: + Bright Star Engineering ip-Engine. + + TQM823L: + TQM850L: + TQM855L: + TQM860L: + MPC8xx based family of mini modules, half credit card size, + up to 64 MB of RAM, 8 MB Flash, (Fast) Ethernet, 2 x serial ports, + 2 x CAN bus interface, ... + Manufacturer: TQ Components, www.tq-group.de + Date of Release: October (?) 1999 + End of Life: not yet :-) + URL: + - module: + - starter kit: + - images: + + FPS850L: + FingerPrint Sensor System (based on TQM850L) + Manufacturer: IKENDI AG, + Date of Release: November 1999 + End of life: end 2000 ? + URL: see TQM850L + + SPD823TS: + MPC823 based board used in the "Tele Server" product + Manufacturer: Speech Design, + Date of Release: Mid 2000 (?) + End of life: - + URL: + select "English", then "Teleteam Solutions", then "TeleServer" + + IVMS8: + MPC860 based board used in the "Integrated Voice Mail System", + Small Version (8 voice channels) + Manufacturer: Speech Design, + Date of Release: December 2000 (?) + End of life: - + URL: + + IVML24: + MPC860 based board used in the "Integrated Voice Mail System", + Large Version (24 voice channels) + Manufacturer: Speech Design, + Date of Release: March 2001 (?) + End of life: - + URL: + + SM850: + Service Module (based on TQM850L) + Manufacturer: Dependable Computer Systems, + Date of Release: end 2000 (?) + End of life: mid 2001 (?) + URL: + + HERMES: + Hermes-Pro ISDN/LAN router with integrated 8 x hub + Manufacturer: Multidata Gesellschaft fur Datentechnik und Informatik + + Date of Release: 2000 (?) + End of life: - + URL: + + IP860: + VMEBus IP (Industry Pack) carrier board with MPC860 + Manufacturer: MicroSys GmbH, + Date of Release: ? + End of life: - + URL: + + PCU_E: + PCU = Peripheral Controller Unit, Extended + Manufacturer: Siemens AG, ICN (Information and Communication Networks) + + Date of Release: April 2001 + End of life: August 2001 + URL: n. a. + +config RPXCLASSIC + bool "RPX-Classic" + help + The RPX-Classic is a single-board computer based on the Motorola + MPC860. It features 16MB of DRAM and a variable amount of flash, + I2C EEPROM, thermal monitoring, a PCMCIA slot, a DIP switch and two + LEDs. Variants with Ethernet ports exist. Say Y here to support it + directly. + +config BSEIP + bool "BSE-IP" + help + Say Y here to support the Bright Star Engineering ipEngine SBC. + This is a credit-card-sized device featuring a MPC823 processor, + 26MB DRAM, 4MB flash, Ethernet, a 16K-gate FPGA, USB, an LCD/video + controller, and two RS232 ports. + +config FADS + bool "FADS" + +config TQM823L + bool "TQM823L" + help + Say Y here to support the TQM823L, one of an MPC8xx-based family of + mini SBCs (half credit-card size) from TQ Components first released + in late 1999. Technical references are at + , and + , and an image at + . + +config TQM850L + bool "TQM850L" + help + Say Y here to support the TQM850L, one of an MPC8xx-based family of + mini SBCs (half credit-card size) from TQ Components first released + in late 1999. Technical references are at + , and + , and an image at + . + +config TQM855L + bool "TQM855L" + help + Say Y here to support the TQM855L, one of an MPC8xx-based family of + mini SBCs (half credit-card size) from TQ Components first released + in late 1999. Technical references are at + , and + , and an image at + . + +config TQM860L + bool "TQM860L" + help + Say Y here to support the TQM860L, one of an MPC8xx-based family of + mini SBCs (half credit-card size) from TQ Components first released + in late 1999. Technical references are at + , and + , and an image at + . + +config FPS850L + bool "FPS850L" + +config SPD823TS + bool "SPD823TS" + help + Say Y here to support the Speech Design 823 Tele-Server from Speech + Design, released in 2000. The manufacturer's website is at + . + +config IVMS8 + bool "IVMS8" + help + Say Y here to support the Integrated Voice-Mail Small 8-channel SBC + from Speech Design, released March 2001. The manufacturer's website + is at . + +config IVML24 + bool "IVML24" + help + Say Y here to support the Integrated Voice-Mail Large 24-channel SBC + from Speech Design, released March 2001. The manufacturer's website + is at . + +config SM850 + bool "SM850" + help + Say Y here to support the Service Module 850 from Dependable + Computer Systems, an SBC based on the TQM850L module by TQ + Components. This board is no longer in production. The + manufacturer's website is at . + +config HERMES_PRO + bool "HERMES" + +config IP860 + bool "IP860" + +config LWMON + bool "LWMON" + +config PCU_E + bool "PCU_E" + +config CCM + bool "CCM" + +config LANTEC + bool "LANTEC" + +config MBX + bool "MBX" + help + MBX is a line of Motorola single-board computer based around the + MPC821 and MPC860 processors, and intended for embedded-controller + applications. Say Y here to support these boards directly. + +config WINCEPT + bool "WinCept" + help + The Wincept 100/110 is a Motorola single-board computer based on the + MPC821 PowerPC, introduced in 1998 and designed to be used in + thin-client machines. Say Y to support it directly. + +endchoice + +choice + prompt "Machine Type" + depends on 6xx || POWER3 || POWER4 + default PPC_MULTIPLATFORM + ---help--- + Linux currently supports several different kinds of PowerPC-based + machines: Apple Power Macintoshes and clones (such as the Motorola + Starmax series), PReP (PowerPC Reference Platform) machines (such + as the Motorola PowerStacks, Motorola cPCI/VME embedded systems, + and some IBM RS/6000 systems), CHRP (Common Hardware Reference + Platform) machines (including all of the recent IBM RS/6000 and + pSeries machines), and several embedded PowerPC systems containing + 4xx, 6xx, 7xx, 8xx, 74xx, and 82xx processors. Currently, the + default option is to build a kernel which works on the first three. + + Select CHRP/PowerMac/PReP if configuring for an IBM RS/6000 or + pSeries machine, a Power Macintosh (including iMacs, iBooks and + Powerbooks), or a PReP machine. + + Select Gemini if configuring for a Synergy Microsystems' Gemini + series Single Board Computer. More information is available at: + . + + Select APUS if configuring for a PowerUP Amiga. More information is + available at: . + +config PPC_MULTIPLATFORM + bool "CHRP/PowerMac/PReP" + +config APUS + bool "Amiga-APUS" + help + Select APUS if configuring for a PowerUP Amiga. + More information is available at: + . + +config KATANA + bool "Artesyn-Katana" + help + Select KATANA if configuring an Artesyn KATANA 750i or 3750 + cPCI board. + +config WILLOW + bool "Cogent-Willow" + +config CPCI690 + bool "Force-CPCI690" + help + Select CPCI690 if configuring a Force CPCI690 cPCI board. + +config PCORE + bool "Force-PowerCore" + +config POWERPMC250 + bool "Force-PowerPMC250" + +config CHESTNUT + bool "IBM 750FX Eval board or 750GX Eval board" + help + Select CHESTNUT if configuring an IBM 750FX Eval Board or a + IBM 750GX Eval board. + +config SPRUCE + bool "IBM-Spruce" + +config HDPU + bool "Sky-HDPU" + help + Select HDPU if configuring a Sky Computers Compute Blade. + +config HDPU_FEATURES + depends HDPU + tristate "HDPU-Features" + help + Select to enable HDPU enhanced features. + +config EV64260 + bool "Marvell-EV64260BP" + help + Select EV64260 if configuring a Marvell (formerly Galileo) + EV64260BP Evaluation platform. + +config LOPEC + bool "Motorola-LoPEC" + +config MCPN765 + bool "Motorola-MCPN765" + +config MVME5100 + bool "Motorola-MVME5100" + +config PPLUS + bool "Motorola-PowerPlus" + +config PRPMC750 + bool "Motorola-PrPMC750" + +config PRPMC800 + bool "Motorola-PrPMC800" + +config SANDPOINT + bool "Motorola-Sandpoint" + help + Select SANDPOINT if configuring for a Motorola Sandpoint X3 + (any flavor). + +config RADSTONE_PPC7D + bool "Radstone Technology PPC7D board" + +config ADIR + bool "SBS-Adirondack" + +config K2 + bool "SBS-K2" + +config PAL4 + bool "SBS-Palomar4" + +config GEMINI + bool "Synergy-Gemini" + help + Select Gemini if configuring for a Synergy Microsystems' Gemini + series Single Board Computer. More information is available at: + . + +config EST8260 + bool "EST8260" + ---help--- + The EST8260 is a single-board computer manufactured by Wind River + Systems, Inc. (formerly Embedded Support Tools Corp.) and based on + the MPC8260. Wind River Systems has a website at + , but the EST8260 cannot be found on it + and has probably been discontinued or rebadged. + +config SBC82xx + bool "SBC82xx" + ---help--- + SBC PowerQUICC II, single-board computer with MPC82xx CPU + Manufacturer: Wind River Systems, Inc. + Date of Release: May 2003 + End of Life: - + URL: + +config SBS8260 + bool "SBS8260" + +config RPX8260 + bool "RPXSUPER" + +config TQM8260 + bool "TQM8260" + ---help--- + MPC8260 based module, little larger than credit card, + up to 128 MB global + 64 MB local RAM, 32 MB Flash, + 32 kB EEPROM, 256 kB L@ Cache, 10baseT + 100baseT Ethernet, + 2 x serial ports, ... + Manufacturer: TQ Components, www.tq-group.de + Date of Release: June 2001 + End of Life: not yet :-) + URL: + +config ADS8272 + bool "ADS8272" + +config PQ2FADS + bool "Freescale-PQ2FADS" + help + Select PQ2FADS if you wish to configure for a Freescale + PQ2FADS board (-VR or -ZU). + +config LITE5200 + bool "Freescale LITE5200 / (IceCube)" + select PPC_MPC52xx + help + Support for the LITE5200 dev board for the MPC5200 from Freescale. + This is for the LITE5200 version 2.0 board. Don't know if it changes + much but it's only been tested on this board version. I think this + board is also known as IceCube. + +config MPC834x_SYS + bool "Freescale MPC834x SYS" + help + This option enables support for the MPC 834x SYS evaluation board. + +endchoice + +config PQ2ADS + bool + depends on ADS8272 + default y + +config TQM8xxL + bool + depends on 8xx && (TQM823L || TQM850L || FPS850L || TQM855L || TQM860L || SM850) + default y + +config EMBEDDEDBOOT + bool + depends on 8xx || 8260 + default y + +config PPC_MPC52xx + bool + +config 8260 + bool "CPM2 Support" if WILLOW + depends on 6xx + default y if TQM8260 || RPX8260 || EST8260 || SBS8260 || SBC82xx || PQ2FADS + help + The MPC8260 is a typical embedded CPU made by Motorola. Selecting + this option means that you wish to build a kernel for a machine with + an 8260 class CPU. + +config 8272 + bool + depends on 6xx + default y if ADS8272 + select 8260 + help + The MPC8272 CPM has a different internal dpram setup than other CPM2 + devices + +config 83xx + bool + default y if MPC834x_SYS + +config MPC834x + bool + default y if MPC834x_SYS + +config CPM2 + bool + depends on 8260 || MPC8560 || MPC8555 + default y + help + The CPM2 (Communications Processor Module) is a coprocessor on + embedded CPUs made by Motorola. Selecting this option means that + you wish to build a kernel for a machine with a CPM2 coprocessor + on it (826x, 827x, 8560). + +config PPC_CHRP + bool + depends on PPC_MULTIPLATFORM + default y + +config PPC_PMAC + bool + depends on PPC_MULTIPLATFORM + default y + +config PPC_PMAC64 + bool + depends on PPC_PMAC && POWER4 + default y + +config PPC_PREP + bool + depends on PPC_MULTIPLATFORM + default y + +config PPC_OF + bool + depends on PPC_PMAC || PPC_CHRP + default y + +config PPC_GEN550 + bool + depends on SANDPOINT || MCPN765 || SPRUCE || PPLUS || PCORE || \ + PRPMC750 || K2 || PRPMC800 || LOPEC || \ + (EV64260 && !SERIAL_MPSC) || CHESTNUT || RADSTONE_PPC7D || \ + 83xx + default y + +config FORCE + bool + depends on 6xx && (PCORE || POWERPMC250) + default y + +config GT64260 + bool + depends on EV64260 || CPCI690 + default y + +config MV64360 # Really MV64360 & MV64460 + bool + depends on CHESTNUT || KATANA || RADSTONE_PPC7D || HDPU + default y + +config MV64X60 + bool + depends on (GT64260 || MV64360) + default y + +menu "Set bridge options" + depends on MV64X60 + +config NOT_COHERENT_CACHE + bool "Turn off Cache Coherency" + default n + help + Some 64x60 bridges lock up when trying to enforce cache coherency. + When this option is selected, cache coherency will be turned off. + Note that this can cause other problems (e.g., stale data being + speculatively loaded via a cached mapping). Use at your own risk. + +config MV64X60_BASE + hex "Set bridge base used by firmware" + default "0xf1000000" + help + A firmware can leave the base address of the bridge's registers at + a non-standard location. If so, set this value to reflect the + address of that non-standard location. + +config MV64X60_NEW_BASE + hex "Set bridge base used by kernel" + default "0xf1000000" + help + If the current base address of the bridge's registers is not where + you want it, set this value to the address that you want it moved to. + +endmenu + +config NONMONARCH_SUPPORT + bool "Enable Non-Monarch Support" + depends on PRPMC800 + +config HARRIER + bool + depends on PRPMC800 + default y + +config EPIC_SERIAL_MODE + bool + depends on 6xx && (LOPEC || SANDPOINT) + default y + +config MPC10X_BRIDGE + bool + depends on PCORE || POWERPMC250 || LOPEC || SANDPOINT + default y + +config FSL_OCP + bool + depends on MPC10X_BRIDGE + default y + +config MPC10X_OPENPIC + bool + depends on POWERPMC250 || LOPEC || SANDPOINT + default y + +config MPC10X_STORE_GATHERING + bool "Enable MPC10x store gathering" + depends on MPC10X_BRIDGE + +config CPC710_DATA_GATHERING + bool "Enable CPC710 data gathering" + depends on K2 + +config HARRIER_STORE_GATHERING + bool "Enable Harrier store gathering" + depends on HARRIER + +config MVME5100_IPMC761_PRESENT + bool "MVME5100 configured with an IPMC761" + depends on MVME5100 + +config SPRUCE_BAUD_33M + bool "Spruce baud clock support" + depends on SPRUCE + +config PC_KEYBOARD + bool "PC PS/2 style Keyboard" + depends on 4xx || CPM2 + +config PPCBUG_NVRAM + bool "Enable reading PPCBUG NVRAM during boot" if PPLUS || LOPEC + default y if PPC_PREP + +config SMP + bool "Symmetric multi-processing support" + ---help--- + This enables support for systems with more than one CPU. If you have + a system with only one CPU, say N. If you have a system with more + than one CPU, say Y. Note that the kernel does not currently + support SMP machines with 603/603e/603ev or PPC750 ("G3") processors + since they have inadequate hardware support for multiprocessor + operation. + + If you say N here, the kernel will run on single and multiprocessor + machines, but will use only one CPU of a multiprocessor machine. If + you say Y here, the kernel will run on single-processor machines. + On a single-processor machine, the kernel will run faster if you say + N here. + + If you don't know what to do here, say N. + +config IRQ_ALL_CPUS + bool "Distribute interrupts on all CPUs by default" + depends on SMP + help + This option gives the kernel permission to distribute IRQs across + multiple CPUs. Saying N here will route all IRQs to the first + CPU. Generally saying Y is safe, although some problems have been + reported with SMP Power Macintoshes with this option enabled. + +config NR_CPUS + int "Maximum number of CPUs (2-32)" + range 2 32 + depends on SMP + default "4" + +config PREEMPT + bool "Preemptible Kernel" + help + This option reduces the latency of the kernel when reacting to + real-time or interactive events by allowing a low priority process to + be preempted even if it is in kernel mode executing a system call. + + Say Y here if you are building a kernel for a desktop, embedded + or real-time system. Say N if you are unsure. + +config HIGHMEM + bool "High memory support" + +source "fs/Kconfig.binfmt" + +config PROC_DEVICETREE + bool "Support for Open Firmware device tree in /proc" + depends on PPC_OF && PROC_FS + help + This option adds a device-tree directory under /proc which contains + an image of the device tree that the kernel copies from Open + Firmware. If unsure, say Y here. + +config PREP_RESIDUAL + bool "Support for PReP Residual Data" + depends on PPC_PREP + help + Some PReP systems have residual data passed to the kernel by the + firmware. This allows detection of memory size, devices present and + other useful pieces of information. Sometimes this information is + not present or incorrect, in which case it could lead to the machine + behaving incorrectly. If this happens, either disable PREP_RESIDUAL + or pass the 'noresidual' option to the kernel. + + If you are running a PReP system, say Y here, otherwise say N. + +config PROC_PREPRESIDUAL + bool "Support for reading of PReP Residual Data in /proc" + depends on PREP_RESIDUAL && PROC_FS + help + Enabling this option will create a /proc/residual file which allows + you to get at the residual data on PReP systems. You will need a tool + (lsresidual) to parse it. If you aren't on a PReP system, you don't + want this. + +config CMDLINE_BOOL + bool "Default bootloader kernel arguments" + +config CMDLINE + string "Initial kernel command string" + depends on CMDLINE_BOOL + default "console=ttyS0,9600 console=tty0 root=/dev/sda2" + help + On some platforms, there is currently no way for the boot loader to + pass arguments to the kernel. For these platforms, you can supply + some command-line options at build time by entering them here. In + most cases you will need to specify the root device here. + +config AMIGA + bool + depends on APUS + default y + help + This option enables support for the Amiga series of computers. + +config ZORRO + bool + depends on APUS + default y + help + This enables support for the Zorro bus in the Amiga. If you have + expansion cards in your Amiga that conform to the Amiga + AutoConfig(tm) specification, say Y, otherwise N. Note that even + expansion cards that do not fit in the Zorro slots but fit in e.g. + the CPU slot may fall in this category, so you have to say Y to let + Linux use these. + +config ABSTRACT_CONSOLE + bool + depends on APUS + default y + +config APUS_FAST_EXCEPT + bool + depends on APUS + default y + +config AMIGA_PCMCIA + bool "Amiga 1200/600 PCMCIA support" + depends on APUS && EXPERIMENTAL + help + Include support in the kernel for pcmcia on Amiga 1200 and Amiga + 600. If you intend to use pcmcia cards say Y; otherwise say N. + +config AMIGA_BUILTIN_SERIAL + tristate "Amiga builtin serial support" + depends on APUS + help + If you want to use your Amiga's built-in serial port in Linux, + answer Y. + + To compile this driver as a module, choose M here. + +config GVPIOEXT + tristate "GVP IO-Extender support" + depends on APUS + help + If you want to use a GVP IO-Extender serial card in Linux, say Y. + Otherwise, say N. + +config GVPIOEXT_LP + tristate "GVP IO-Extender parallel printer support" + depends on GVPIOEXT + help + Say Y to enable driving a printer from the parallel port on your + GVP IO-Extender card, N otherwise. + +config GVPIOEXT_PLIP + tristate "GVP IO-Extender PLIP support" + depends on GVPIOEXT + help + Say Y to enable doing IP over the parallel port on your GVP + IO-Extender card, N otherwise. + +config MULTIFACE_III_TTY + tristate "Multiface Card III serial support" + depends on APUS + help + If you want to use a Multiface III card's serial port in Linux, + answer Y. + + To compile this driver as a module, choose M here. + +config A2232 + tristate "Commodore A2232 serial support (EXPERIMENTAL)" + depends on EXPERIMENTAL && APUS + ---help--- + This option supports the 2232 7-port serial card shipped with the + Amiga 2000 and other Zorro-bus machines, dating from 1989. At + a max of 19,200 bps, the ports are served by a 6551 ACIA UART chip + each, plus a 8520 CIA, and a master 6502 CPU and buffer as well. The + ports were connected with 8 pin DIN connectors on the card bracket, + for which 8 pin to DB25 adapters were supplied. The card also had + jumpers internally to toggle various pinning configurations. + + This driver can be built as a module; but then "generic_serial" + will also be built as a module. This has to be loaded before + "ser_a2232". If you want to do this, answer M here. + +config WHIPPET_SERIAL + tristate "Hisoft Whippet PCMCIA serial support" + depends on AMIGA_PCMCIA + help + HiSoft has a web page at , but there + is no listing for the Whippet in their Amiga section. + +config APNE + tristate "PCMCIA NE2000 support" + depends on AMIGA_PCMCIA + help + If you have a PCMCIA NE2000 compatible adapter, say Y. Otherwise, + say N. + + To compile this driver as a module, choose M here: the + module will be called apne. + +config SERIAL_CONSOLE + bool "Support for serial port console" + depends on APUS && (AMIGA_BUILTIN_SERIAL=y || GVPIOEXT=y || MULTIFACE_III_TTY=y) + +config HEARTBEAT + bool "Use power LED as a heartbeat" + depends on APUS + help + Use the power-on LED on your machine as a load meter. The exact + behavior is platform-dependent, but normally the flash frequency is + a hyperbolic function of the 5-minute load average. + +config PROC_HARDWARE + bool "/proc/hardware support" + depends on APUS + +source "drivers/zorro/Kconfig" + +source kernel/power/Kconfig + +endmenu + +menu "Bus options" + +config ISA + bool "Support for ISA-bus hardware" + depends on PPC_PREP || PPC_CHRP + help + Find out whether you have ISA slots on your motherboard. ISA is the + name of a bus system, i.e. the way the CPU talks to the other stuff + inside your box. If you have an Apple machine, say N here; if you + have an IBM RS/6000 or pSeries machine or a PReP machine, say Y. If + you have an embedded board, consult your board documentation. + +config GENERIC_ISA_DMA + bool + depends on POWER3 || POWER4 || 6xx && !CPM2 + default y + +config EISA + bool + help + The Extended Industry Standard Architecture (EISA) bus is a bus + architecture used on some older intel-based PCs. + +config SBUS + bool + +# Yes MCA RS/6000s exist but Linux-PPC does not currently support any +config MCA + bool + +config PCI + bool "PCI support" if 40x || CPM2 || 83xx || 85xx || PPC_MPC52xx + default y if !40x && !CPM2 && !8xx && !APUS && !83xx && !85xx + default PCI_PERMEDIA if !4xx && !CPM2 && !8xx && APUS + default PCI_QSPAN if !4xx && !CPM2 && 8xx + help + Find out whether your system includes a PCI bus. PCI is the name of + a bus system, i.e. the way the CPU talks to the other stuff inside + your box. If you say Y here, the kernel will include drivers and + infrastructure code to support PCI bus devices. + +config PCI_DOMAINS + bool + default PCI + +config PCI_QSPAN + bool "QSpan PCI" + depends on !4xx && !CPM2 && 8xx + help + Say Y here if you have a system based on a Motorola 8xx-series + embedded processor with a QSPAN PCI interface, otherwise say N. + +config PCI_8260 + bool + depends on PCI && 8260 && !8272 + default y + +config 8260_PCI9 + bool " Enable workaround for MPC826x erratum PCI 9" + depends on PCI_8260 + default y + +choice + prompt " IDMA channel for PCI 9 workaround" + depends on 8260_PCI9 + +config 8260_PCI9_IDMA1 + bool "IDMA1" + +config 8260_PCI9_IDMA2 + bool "IDMA2" + +config 8260_PCI9_IDMA3 + bool "IDMA3" + +config 8260_PCI9_IDMA4 + bool "IDMA4" + +endchoice + +config PCI_PERMEDIA + bool "PCI for Permedia2" + depends on !4xx && !8xx && APUS + +source "drivers/pci/Kconfig" + +source "drivers/pcmcia/Kconfig" + +endmenu + +menu "Advanced setup" + +config ADVANCED_OPTIONS + bool "Prompt for advanced kernel configuration options" + help + This option will enable prompting for a variety of advanced kernel + configuration options. These options can cause the kernel to not + work if they are set incorrectly, but can be used to optimize certain + aspects of kernel memory management. + + Unless you know what you are doing, say N here. + +comment "Default settings for advanced configuration options are used" + depends on !ADVANCED_OPTIONS + +config HIGHMEM_START_BOOL + bool "Set high memory pool address" + depends on ADVANCED_OPTIONS && HIGHMEM + help + This option allows you to set the base address of the kernel virtual + area used to map high memory pages. This can be useful in + optimizing the layout of kernel virtual memory. + + Say N here unless you know what you are doing. + +config HIGHMEM_START + hex "Virtual start address of high memory pool" if HIGHMEM_START_BOOL + default "0xfe000000" + +config LOWMEM_SIZE_BOOL + bool "Set maximum low memory" + depends on ADVANCED_OPTIONS + help + This option allows you to set the maximum amount of memory which + will be used as "low memory", that is, memory which the kernel can + access directly, without having to set up a kernel virtual mapping. + This can be useful in optimizing the layout of kernel virtual + memory. + + Say N here unless you know what you are doing. + +config LOWMEM_SIZE + hex "Maximum low memory size (in bytes)" if LOWMEM_SIZE_BOOL + default "0x30000000" + +config KERNEL_START_BOOL + bool "Set custom kernel base address" + depends on ADVANCED_OPTIONS + help + This option allows you to set the kernel virtual address at which + the kernel will map low memory (the kernel image will be linked at + this address). This can be useful in optimizing the virtual memory + layout of the system. + + Say N here unless you know what you are doing. + +config KERNEL_START + hex "Virtual address of kernel base" if KERNEL_START_BOOL + default "0xc0000000" + +config TASK_SIZE_BOOL + bool "Set custom user task size" + depends on ADVANCED_OPTIONS + help + This option allows you to set the amount of virtual address space + allocated to user tasks. This can be useful in optimizing the + virtual memory layout of the system. + + Say N here unless you know what you are doing. + +config TASK_SIZE + hex "Size of user task space" if TASK_SIZE_BOOL + default "0x80000000" + +config CONSISTENT_START_BOOL + bool "Set custom consistent memory pool address" + depends on ADVANCED_OPTIONS && NOT_COHERENT_CACHE + help + This option allows you to set the base virtual address + of the the consistent memory pool. This pool of virtual + memory is used to make consistent memory allocations. + +config CONSISTENT_START + hex "Base virtual address of consistent memory pool" if CONSISTENT_START_BOOL + default "0xff100000" if NOT_COHERENT_CACHE + +config CONSISTENT_SIZE_BOOL + bool "Set custom consistent memory pool size" + depends on ADVANCED_OPTIONS && NOT_COHERENT_CACHE + help + This option allows you to set the size of the the + consistent memory pool. This pool of virtual memory + is used to make consistent memory allocations. + +config CONSISTENT_SIZE + hex "Size of consistent memory pool" if CONSISTENT_SIZE_BOOL + default "0x00200000" if NOT_COHERENT_CACHE + +config BOOT_LOAD_BOOL + bool "Set the boot link/load address" + depends on ADVANCED_OPTIONS && !PPC_MULTIPLATFORM + help + This option allows you to set the initial load address of the zImage + or zImage.initrd file. This can be useful if you are on a board + which has a small amount of memory. + + Say N here unless you know what you are doing. + +config BOOT_LOAD + hex "Link/load address for booting" if BOOT_LOAD_BOOL + default "0x00400000" if 40x || 8xx || 8260 + default "0x01000000" if 44x + default "0x00800000" + +config PIN_TLB + bool "Pinned Kernel TLBs (860 ONLY)" + depends on ADVANCED_OPTIONS && 8xx +endmenu + +source "drivers/Kconfig" + +source "fs/Kconfig" + +source "arch/ppc/8xx_io/Kconfig" + +source "arch/ppc/8260_io/Kconfig" + + +menu "IBM 40x options" + depends on 40x + +config SERIAL_SICC + bool "SICC Serial port" + depends on STB03xxx + +config UART1_DFLT_CONSOLE + bool + depends on SERIAL_SICC && UART0_TTYS1 + default y + +config SERIAL_SICC_CONSOLE + bool + depends on SERIAL_SICC && UART0_TTYS1 + default y + +endmenu + +source "lib/Kconfig" + +source "arch/ppc/oprofile/Kconfig" + +source "arch/ppc/Kconfig.debug" + +source "security/Kconfig" + +source "crypto/Kconfig" diff --git a/arch/ppc/Kconfig.debug b/arch/ppc/Kconfig.debug new file mode 100644 index 00000000000..d2e1eea8e8e --- /dev/null +++ b/arch/ppc/Kconfig.debug @@ -0,0 +1,72 @@ +menu "Kernel hacking" + +source "lib/Kconfig.debug" + +config KGDB + bool "Include kgdb kernel debugger" + depends on DEBUG_KERNEL && (BROKEN || PPC_GEN550 || 4xx) + select DEBUG_INFO + help + Include in-kernel hooks for kgdb, the Linux kernel source level + debugger. See for more information. + Unless you are intending to debug the kernel, say N here. + +choice + prompt "Serial Port" + depends on KGDB + default KGDB_TTYS1 + +config KGDB_TTYS0 + bool "ttyS0" + +config KGDB_TTYS1 + bool "ttyS1" + +config KGDB_TTYS2 + bool "ttyS2" + +config KGDB_TTYS3 + bool "ttyS3" + +endchoice + +config KGDB_CONSOLE + bool "Enable serial console thru kgdb port" + depends on KGDB && 8xx || CPM2 + help + If you enable this, all serial console messages will be sent + over the gdb stub. + If unsure, say N. + +config XMON + bool "Include xmon kernel debugger" + depends on DEBUG_KERNEL + help + Include in-kernel hooks for the xmon kernel monitor/debugger. + Unless you are intending to debug the kernel, say N here. + +config BDI_SWITCH + bool "Include BDI-2000 user context switcher" + depends on DEBUG_KERNEL + help + Include in-kernel support for the Abatron BDI2000 debugger. + Unless you are intending to debug the kernel with one of these + machines, say N here. + +config BOOTX_TEXT + bool "Support for early boot text console (BootX or OpenFirmware only)" + depends PPC_OF + help + Say Y here to see progress messages from the boot firmware in text + mode. Requires either BootX or Open Firmware. + +config SERIAL_TEXT_DEBUG + bool "Support for early boot texts over serial port" + depends on 4xx || GT64260 || LOPEC || PPLUS || PRPMC800 || PPC_GEN550 || PPC_MPC52xx + +config PPC_OCP + bool + depends on IBM_OCP || FSL_OCP || XILINX_OCP + default y + +endmenu diff --git a/arch/ppc/Makefile b/arch/ppc/Makefile new file mode 100644 index 00000000000..73cbdda5b59 --- /dev/null +++ b/arch/ppc/Makefile @@ -0,0 +1,138 @@ +# This file is included by the global makefile so that you can add your own +# architecture-specific flags and dependencies. +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (C) 1994 by Linus Torvalds +# Changes for PPC by Gary Thomas +# Rewritten by Cort Dougan and Paul Mackerras +# + +# This must match PAGE_OFFSET in include/asm-ppc/page.h. +KERNELLOAD := $(CONFIG_KERNEL_START) + +HAS_BIARCH := $(call cc-option-yn, -m32) +ifeq ($(HAS_BIARCH),y) +AS := $(AS) -a32 +LD := $(LD) -m elf32ppc +CC := $(CC) -m32 +endif + +LDFLAGS_vmlinux := -Ttext $(KERNELLOAD) -Bstatic +CPPFLAGS += -Iarch/$(ARCH) +AFLAGS += -Iarch/$(ARCH) +CFLAGS += -Iarch/$(ARCH) -msoft-float -pipe \ + -ffixed-r2 -mmultiple +CPP = $(CC) -E $(CFLAGS) + +CHECKFLAGS += -D__powerpc__ + +ifndef CONFIG_E500 +CFLAGS += -mstring +endif + +cpu-as-$(CONFIG_PPC64BRIDGE) += -Wa,-mppc64bridge +cpu-as-$(CONFIG_4xx) += -Wa,-m405 +cpu-as-$(CONFIG_6xx) += -Wa,-maltivec +cpu-as-$(CONFIG_POWER4) += -Wa,-maltivec +cpu-as-$(CONFIG_E500) += -Wa,-me500 + +AFLAGS += $(cpu-as-y) +CFLAGS += $(cpu-as-y) + +# Default to the common case. +KBUILD_DEFCONFIG := common_defconfig + +head-y := arch/ppc/kernel/head.o +head-$(CONFIG_8xx) := arch/ppc/kernel/head_8xx.o +head-$(CONFIG_4xx) := arch/ppc/kernel/head_4xx.o +head-$(CONFIG_44x) := arch/ppc/kernel/head_44x.o +head-$(CONFIG_FSL_BOOKE) := arch/ppc/kernel/head_fsl_booke.o + +head-$(CONFIG_6xx) += arch/ppc/kernel/idle_6xx.o +head-$(CONFIG_POWER4) += arch/ppc/kernel/idle_power4.o + +core-y += arch/ppc/kernel/ arch/ppc/platforms/ \ + arch/ppc/mm/ arch/ppc/lib/ arch/ppc/syslib/ +core-$(CONFIG_4xx) += arch/ppc/platforms/4xx/ +core-$(CONFIG_83xx) += arch/ppc/platforms/83xx/ +core-$(CONFIG_85xx) += arch/ppc/platforms/85xx/ +core-$(CONFIG_MATH_EMULATION) += arch/ppc/math-emu/ +core-$(CONFIG_XMON) += arch/ppc/xmon/ +core-$(CONFIG_APUS) += arch/ppc/amiga/ +drivers-$(CONFIG_8xx) += arch/ppc/8xx_io/ +drivers-$(CONFIG_4xx) += arch/ppc/4xx_io/ +drivers-$(CONFIG_CPM2) += arch/ppc/8260_io/ + +drivers-$(CONFIG_OPROFILE) += arch/ppc/oprofile/ + +BOOT_TARGETS = zImage zImage.initrd znetboot znetboot.initrd vmlinux.sm + +.PHONY: $(BOOT_TARGETS) + +all: uImage zImage + +CPPFLAGS_vmlinux.lds := -Upowerpc + +# All the instructions talk about "make bzImage". +bzImage: zImage + +boot := arch/$(ARCH)/boot + +$(BOOT_TARGETS): vmlinux + $(Q)$(MAKE) $(build)=$(boot) $@ + +uImage: vmlinux + $(Q)$(MAKE) $(build)=$(boot)/images $(boot)/images/$@ + +define archhelp + @echo '* zImage - Compressed kernel image (arch/$(ARCH)/boot/images/zImage.*)' + @echo ' uImage - Create a bootable image for U-Boot / PPCBoot' + @echo ' install - Install kernel using' + @echo ' (your) ~/bin/installkernel or' + @echo ' (distribution) /sbin/installkernel or' + @echo ' install to $$(INSTALL_PATH) and run lilo' + @echo ' *_defconfig - Select default config from arch/$(ARCH)/ppc/configs' +endef + +archclean: + $(Q)$(MAKE) $(clean)=arch/ppc/boot + +prepare: include/asm-$(ARCH)/offsets.h checkbin + +arch/$(ARCH)/kernel/asm-offsets.s: include/asm include/linux/version.h \ + include/config/MARKER + +include/asm-$(ARCH)/offsets.h: arch/$(ARCH)/kernel/asm-offsets.s + $(call filechk,gen-asm-offsets) + +# Use the file '.tmp_gas_check' for binutils tests, as gas won't output +# to stdout and these checks are run even on install targets. +TOUT := .tmp_gas_check +# Ensure this is binutils 2.12.1 (or 2.12.90.0.7) or later for altivec +# instructions. +# gcc-3.4 and binutils-2.14 are a fatal combination. +GCC_VERSION := $(call cc-version) + +checkbin: + @if test "$(GCC_VERSION)" = "0304" ; then \ + if ! /bin/echo mftb 5 | $(AS) -v -mppc -many -o $(TOUT) >/dev/null 2>&1 ; then \ + echo -n '*** ${VERSION}.${PATCHLEVEL} kernels no longer build '; \ + echo 'correctly with gcc-3.4 and your version of binutils.'; \ + echo '*** Please upgrade your binutils or downgrade your gcc'; \ + false; \ + fi ; \ + fi + @if ! /bin/echo dssall | $(AS) -many -o $(TOUT) >/dev/null 2>&1 ; then \ + echo -n '*** ${VERSION}.${PATCHLEVEL} kernels no longer build ' ; \ + echo 'correctly with old versions of binutils.' ; \ + echo '*** Please upgrade your binutils to 2.12.1 or newer' ; \ + false ; \ + fi + +CLEAN_FILES += include/asm-$(ARCH)/offsets.h \ + arch/$(ARCH)/kernel/asm-offsets.s \ + $(TOUT) + diff --git a/arch/ppc/amiga/Makefile b/arch/ppc/amiga/Makefile new file mode 100644 index 00000000000..59fec0a3ac8 --- /dev/null +++ b/arch/ppc/amiga/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for Linux arch/m68k/amiga source directory +# + +obj-y := config.o amiints.o cia.o time.o bootinfo.o amisound.o \ + chipram.o amiga_ksyms.o + +obj-$(CONFIG_AMIGA_PCMCIA) += pcmcia.o diff --git a/arch/ppc/amiga/amiga_ksyms.c b/arch/ppc/amiga/amiga_ksyms.c new file mode 100644 index 00000000000..ec74e5b7a1c --- /dev/null +++ b/arch/ppc/amiga/amiga_ksyms.c @@ -0,0 +1 @@ +#include "../../m68k/amiga/amiga_ksyms.c" diff --git a/arch/ppc/amiga/amiints.c b/arch/ppc/amiga/amiints.c new file mode 100644 index 00000000000..91195e2ce38 --- /dev/null +++ b/arch/ppc/amiga/amiints.c @@ -0,0 +1,323 @@ +/* + * arch/ppc/amiga/amiints.c -- Amiga Linux interrupt handling code + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + * + * 11/07/96: rewritten interrupt handling, irq lists are exists now only for + * this sources where it makes sense (VERTB/PORTS/EXTER) and you must + * be careful that dev_id for this sources is unique since this the + * only possibility to distinguish between different handlers for + * free_irq. irq lists also have different irq flags: + * - IRQ_FLG_FAST: handler is inserted at top of list (after other + * fast handlers) + * - IRQ_FLG_SLOW: handler is inserted at bottom of list and before + * they're executed irq level is set to the previous + * one, but handlers don't need to be reentrant, if + * reentrance occurred, slow handlers will be just + * called again. + * The whole interrupt handling for CIAs is moved to cia.c + * /Roman Zippel + * + * 07/08/99: rewamp of the interrupt handling - we now have two types of + * interrupts, normal and fast handlers, fast handlers being + * marked with SA_INTERRUPT and runs with all other interrupts + * disabled. Normal interrupts disable their own source but + * run with all other interrupt sources enabled. + * PORTS and EXTER interrupts are always shared even if the + * drivers do not explicitly mark this when calling + * request_irq which they really should do. + * This is similar to the way interrupts are handled on all + * other architectures and makes a ton of sense besides + * having the advantage of making it easier to share + * drivers. + * /Jes + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_APUS +#include +#endif + +extern void cia_init_IRQ(struct ciabase *base); + +unsigned short ami_intena_vals[AMI_STD_IRQS] = { + IF_VERTB, IF_COPER, IF_AUD0, IF_AUD1, IF_AUD2, IF_AUD3, IF_BLIT, + IF_DSKSYN, IF_DSKBLK, IF_RBF, IF_TBE, IF_SOFT, IF_PORTS, IF_EXTER +}; +static const unsigned char ami_servers[AMI_STD_IRQS] = { + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 +}; + +static short ami_ablecount[AMI_IRQS]; + +static void ami_badint(int irq, void *dev_id, struct pt_regs *fp) +{ +/* num_spurious += 1;*/ +} + +/* + * void amiga_init_IRQ(void) + * + * Parameters: None + * + * Returns: Nothing + * + * This function should be called during kernel startup to initialize + * the amiga IRQ handling routines. + */ + +__init +void amiga_init_IRQ(void) +{ + int i; + + for (i = 0; i < AMI_IRQS; i++) + ami_ablecount[i] = 0; + + /* turn off PCMCIA interrupts */ + if (AMIGAHW_PRESENT(PCMCIA)) + gayle.inten = GAYLE_IRQ_IDE; + + /* turn off all interrupts... */ + custom.intena = 0x7fff; + custom.intreq = 0x7fff; + +#ifdef CONFIG_APUS + /* Clear any inter-CPU interrupt requests. Circumvents bug in + Blizzard IPL emulation HW (or so it appears). */ + APUS_WRITE(APUS_INT_LVL, INTLVL_SETRESET | INTLVL_MASK); + + /* Init IPL emulation. */ + APUS_WRITE(APUS_REG_INT, REGINT_INTMASTER | REGINT_ENABLEIPL); + APUS_WRITE(APUS_IPL_EMU, IPLEMU_DISABLEINT); + APUS_WRITE(APUS_IPL_EMU, IPLEMU_SETRESET | IPLEMU_IPLMASK); +#endif + /* ... and enable the master interrupt bit */ + custom.intena = IF_SETCLR | IF_INTEN; + + cia_init_IRQ(&ciaa_base); + cia_init_IRQ(&ciab_base); +} + +/* + * Enable/disable a particular machine specific interrupt source. + * Note that this may affect other interrupts in case of a shared interrupt. + * This function should only be called for a _very_ short time to change some + * internal data, that may not be changed by the interrupt at the same time. + * ami_(enable|disable)_irq calls may also be nested. + */ + +void amiga_enable_irq(unsigned int irq) +{ + if (irq >= AMI_IRQS) { + printk("%s: Unknown IRQ %d\n", __FUNCTION__, irq); + return; + } + + ami_ablecount[irq]--; + if (ami_ablecount[irq]<0) + ami_ablecount[irq]=0; + else if (ami_ablecount[irq]) + return; + + /* No action for auto-vector interrupts */ + if (irq >= IRQ_AMIGA_AUTO){ + printk("%s: Trying to enable auto-vector IRQ %i\n", + __FUNCTION__, irq - IRQ_AMIGA_AUTO); + return; + } + + if (irq >= IRQ_AMIGA_CIAA) { + cia_set_irq(irq, 0); + cia_able_irq(irq, 1); + return; + } + + /* enable the interrupt */ + custom.intena = IF_SETCLR | ami_intena_vals[irq]; +} + +void amiga_disable_irq(unsigned int irq) +{ + if (irq >= AMI_IRQS) { + printk("%s: Unknown IRQ %d\n", __FUNCTION__, irq); + return; + } + + if (ami_ablecount[irq]++) + return; + + /* No action for auto-vector interrupts */ + if (irq >= IRQ_AMIGA_AUTO) { + printk("%s: Trying to disable auto-vector IRQ %i\n", + __FUNCTION__, irq - IRQ_AMIGA_AUTO); + return; + } + + if (irq >= IRQ_AMIGA_CIAA) { + cia_able_irq(irq, 0); + return; + } + + /* disable the interrupt */ + custom.intena = ami_intena_vals[irq]; +} + +inline void amiga_do_irq(int irq, struct pt_regs *fp) +{ + irq_desc_t *desc = irq_desc + irq; + struct irqaction *action = desc->action; + + kstat_cpu(0).irqs[irq]++; + action->handler(irq, action->dev_id, fp); +} + +void amiga_do_irq_list(int irq, struct pt_regs *fp) +{ + irq_desc_t *desc = irq_desc + irq; + struct irqaction *action; + + kstat_cpu(0).irqs[irq]++; + + custom.intreq = ami_intena_vals[irq]; + + for (action = desc->action; action; action = action->next) + action->handler(irq, action->dev_id, fp); +} + +/* + * The builtin Amiga hardware interrupt handlers. + */ + +static void ami_int1(int irq, void *dev_id, struct pt_regs *fp) +{ + unsigned short ints = custom.intreqr & custom.intenar; + + /* if serial transmit buffer empty, interrupt */ + if (ints & IF_TBE) { + custom.intreq = IF_TBE; + amiga_do_irq(IRQ_AMIGA_TBE, fp); + } + + /* if floppy disk transfer complete, interrupt */ + if (ints & IF_DSKBLK) { + custom.intreq = IF_DSKBLK; + amiga_do_irq(IRQ_AMIGA_DSKBLK, fp); + } + + /* if software interrupt set, interrupt */ + if (ints & IF_SOFT) { + custom.intreq = IF_SOFT; + amiga_do_irq(IRQ_AMIGA_SOFT, fp); + } +} + +static void ami_int3(int irq, void *dev_id, struct pt_regs *fp) +{ + unsigned short ints = custom.intreqr & custom.intenar; + + /* if a blitter interrupt */ + if (ints & IF_BLIT) { + custom.intreq = IF_BLIT; + amiga_do_irq(IRQ_AMIGA_BLIT, fp); + } + + /* if a copper interrupt */ + if (ints & IF_COPER) { + custom.intreq = IF_COPER; + amiga_do_irq(IRQ_AMIGA_COPPER, fp); + } + + /* if a vertical blank interrupt */ + if (ints & IF_VERTB) + amiga_do_irq_list(IRQ_AMIGA_VERTB, fp); +} + +static void ami_int4(int irq, void *dev_id, struct pt_regs *fp) +{ + unsigned short ints = custom.intreqr & custom.intenar; + + /* if audio 0 interrupt */ + if (ints & IF_AUD0) { + custom.intreq = IF_AUD0; + amiga_do_irq(IRQ_AMIGA_AUD0, fp); + } + + /* if audio 1 interrupt */ + if (ints & IF_AUD1) { + custom.intreq = IF_AUD1; + amiga_do_irq(IRQ_AMIGA_AUD1, fp); + } + + /* if audio 2 interrupt */ + if (ints & IF_AUD2) { + custom.intreq = IF_AUD2; + amiga_do_irq(IRQ_AMIGA_AUD2, fp); + } + + /* if audio 3 interrupt */ + if (ints & IF_AUD3) { + custom.intreq = IF_AUD3; + amiga_do_irq(IRQ_AMIGA_AUD3, fp); + } +} + +static void ami_int5(int irq, void *dev_id, struct pt_regs *fp) +{ + unsigned short ints = custom.intreqr & custom.intenar; + + /* if serial receive buffer full interrupt */ + if (ints & IF_RBF) { + /* acknowledge of IF_RBF must be done by the serial interrupt */ + amiga_do_irq(IRQ_AMIGA_RBF, fp); + } + + /* if a disk sync interrupt */ + if (ints & IF_DSKSYN) { + custom.intreq = IF_DSKSYN; + amiga_do_irq(IRQ_AMIGA_DSKSYN, fp); + } +} + +static void ami_int7(int irq, void *dev_id, struct pt_regs *fp) +{ + panic ("level 7 interrupt received\n"); +} + +#ifdef CONFIG_APUS +/* The PPC irq handling links all handlers requested on the same vector + and executes them in a loop. Having ami_badint at the end of the chain + is a bad idea. */ +struct irqaction amiga_sys_irqaction[AUTO_IRQS] = { + { .handler = ami_badint, .name = "spurious int" }, + { .handler = ami_int1, .name = "int1 handler" }, + { 0, /* CIAA */ }, + { .handler = ami_int3, .name = "int3 handler" }, + { .handler = ami_int4, .name = "int4 handler" }, + { .handler = ami_int5, .name = "int5 handler" }, + { 0, /* CIAB */ }, + { .handler = ami_int7, .name = "int7 handler" }, +}; +#else +void (*amiga_default_handler[SYS_IRQS])(int, void *, struct pt_regs *) = { + ami_badint, ami_int1, ami_badint, ami_int3, + ami_int4, ami_int5, ami_badint, ami_int7 +}; +#endif diff --git a/arch/ppc/amiga/amisound.c b/arch/ppc/amiga/amisound.c new file mode 100644 index 00000000000..2b86cbef79f --- /dev/null +++ b/arch/ppc/amiga/amisound.c @@ -0,0 +1 @@ +#include "../../m68k/amiga/amisound.c" diff --git a/arch/ppc/amiga/bootinfo.c b/arch/ppc/amiga/bootinfo.c new file mode 100644 index 00000000000..e2e965661d0 --- /dev/null +++ b/arch/ppc/amiga/bootinfo.c @@ -0,0 +1,80 @@ +/* + * arch/ppc/amiga/bootinfo.c + * + * Extracted from arch/m68k/kernel/setup.c. + * Should be properly generalized and put somewhere else. + * Jesper + */ + +#include +#include +#include +#include + +#include +#include + +extern char cmd_line[CL_SIZE]; + +extern int num_memory; +extern int m68k_realnum_memory; +extern struct mem_info memory[NUM_MEMINFO]; +extern struct mem_info m68k_memory[NUM_MEMINFO]; +extern struct mem_info ramdisk; + +extern int amiga_parse_bootinfo(const struct bi_record *); +extern int atari_parse_bootinfo(const struct bi_record *); +extern int mac_parse_bootinfo(const struct bi_record *); + +void __init parse_bootinfo(const struct bi_record *record) +{ + while (record->tag != BI_LAST) { + int unknown = 0; + const u_long *data = record->data; + switch (record->tag) { + case BI_MACHTYPE: + case BI_CPUTYPE: + case BI_FPUTYPE: + case BI_MMUTYPE: + /* Already set up by head.S */ + break; + + case BI_MEMCHUNK: + if (num_memory < NUM_MEMINFO) { + memory[num_memory].addr = data[0]; + memory[num_memory].size = data[1]; + num_memory++; + + /* FIXME: duplicate for m68k drivers. */ + m68k_memory[m68k_realnum_memory].addr = data[0]; + m68k_memory[m68k_realnum_memory].size = data[1]; + m68k_realnum_memory++; + } else + printk("parse_bootinfo: too many memory chunks\n"); + break; + + case BI_RAMDISK: + ramdisk.addr = data[0]; + ramdisk.size = data[1]; + break; + + case BI_COMMAND_LINE: + strlcpy(cmd_line, (const char *)data, sizeof(cmd_line)); + break; + + default: + if (MACH_IS_AMIGA) + unknown = amiga_parse_bootinfo(record); + else if (MACH_IS_ATARI) + unknown = atari_parse_bootinfo(record); + else if (MACH_IS_MAC) + unknown = mac_parse_bootinfo(record); + else + unknown = 1; + } + if (unknown) + printk("parse_bootinfo: unknown tag 0x%04x ignored\n", + record->tag); + record = (struct bi_record *)((u_long)record+record->size); + } +} diff --git a/arch/ppc/amiga/chipram.c b/arch/ppc/amiga/chipram.c new file mode 100644 index 00000000000..e6ab3c6b223 --- /dev/null +++ b/arch/ppc/amiga/chipram.c @@ -0,0 +1 @@ +#include "../../m68k/amiga/chipram.c" diff --git a/arch/ppc/amiga/cia.c b/arch/ppc/amiga/cia.c new file mode 100644 index 00000000000..ad961465b6c --- /dev/null +++ b/arch/ppc/amiga/cia.c @@ -0,0 +1,178 @@ +/* + * arch/ppc/amiga/cia.c - CIA support + * + * Copyright (C) 1996 Roman Zippel + * + * The concept of some functions bases on the original Amiga OS function + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +struct ciabase { + volatile struct CIA *cia; + u_char icr_mask, icr_data; + u_short int_mask; + int handler_irq, cia_irq, server_irq; + char *name; +} ciaa_base = { + &ciaa, 0, 0, IF_PORTS, + IRQ_AMIGA_AUTO_2, IRQ_AMIGA_CIAA, + IRQ_AMIGA_PORTS, + "CIAA handler" +}, ciab_base = { + &ciab, 0, 0, IF_EXTER, + IRQ_AMIGA_AUTO_6, IRQ_AMIGA_CIAB, + IRQ_AMIGA_EXTER, + "CIAB handler" +}; + +#define CIA_SET_BASE_ADJUST_IRQ(base, irq) \ +do { \ + if (irq >= IRQ_AMIGA_CIAB) { \ + base = &ciab_base; \ + irq -= IRQ_AMIGA_CIAB; \ + } else { \ + base = &ciaa_base; \ + irq -= IRQ_AMIGA_CIAA; \ + } \ +} while (0) + +/* + * Cause or clear CIA interrupts, return old interrupt status. + */ + +static unsigned char cia_set_irq_private(struct ciabase *base, + unsigned char mask) +{ + u_char old; + + old = (base->icr_data |= base->cia->icr); + if (mask & CIA_ICR_SETCLR) + base->icr_data |= mask; + else + base->icr_data &= ~mask; + if (base->icr_data & base->icr_mask) + custom.intreq = IF_SETCLR | base->int_mask; + return old & base->icr_mask; +} + +unsigned char cia_set_irq(unsigned int irq, int set) +{ + struct ciabase *base; + unsigned char mask; + + if (irq >= IRQ_AMIGA_CIAB) + mask = (1 << (irq - IRQ_AMIGA_CIAB)); + else + mask = (1 << (irq - IRQ_AMIGA_CIAA)); + mask |= (set) ? CIA_ICR_SETCLR : 0; + + CIA_SET_BASE_ADJUST_IRQ(base, irq); + + return cia_set_irq_private(base, mask); +} + +unsigned char cia_get_irq_mask(unsigned int irq) +{ + struct ciabase *base; + + CIA_SET_BASE_ADJUST_IRQ(base, irq); + + return base->cia->icr; +} + +/* + * Enable or disable CIA interrupts, return old interrupt mask. + */ + +static unsigned char cia_able_irq_private(struct ciabase *base, + unsigned char mask) +{ + u_char old; + + old = base->icr_mask; + base->icr_data |= base->cia->icr; + base->cia->icr = mask; + if (mask & CIA_ICR_SETCLR) + base->icr_mask |= mask; + else + base->icr_mask &= ~mask; + base->icr_mask &= CIA_ICR_ALL; + + if (base->icr_data & base->icr_mask) + custom.intreq = IF_SETCLR | base->int_mask; + return old; +} + +unsigned char cia_able_irq(unsigned int irq, int enable) +{ + struct ciabase *base; + unsigned char mask; + + if (irq >= IRQ_AMIGA_CIAB) + mask = (1 << (irq - IRQ_AMIGA_CIAB)); + else + mask = (1 << (irq - IRQ_AMIGA_CIAA)); + mask |= (enable) ? CIA_ICR_SETCLR : 0; + + CIA_SET_BASE_ADJUST_IRQ(base, irq); + + return cia_able_irq_private(base, mask); +} + +static void cia_handler(int irq, void *dev_id, struct pt_regs *fp) +{ + struct ciabase *base = (struct ciabase *)dev_id; + irq_desc_t *desc; + struct irqaction *action; + int i; + unsigned char ints; + + irq = base->cia_irq; + desc = irq_desc + irq; + ints = cia_set_irq_private(base, CIA_ICR_ALL); + custom.intreq = base->int_mask; + for (i = 0; i < CIA_IRQS; i++, irq++) { + if (ints & 1) { + kstat_cpu(0).irqs[irq]++; + action = desc->action; + action->handler(irq, action->dev_id, fp); + } + ints >>= 1; + desc++; + } + amiga_do_irq_list(base->server_irq, fp); +} + +void __init cia_init_IRQ(struct ciabase *base) +{ + extern struct irqaction amiga_sys_irqaction[AUTO_IRQS]; + struct irqaction *action; + + /* clear any pending interrupt and turn off all interrupts */ + cia_set_irq_private(base, CIA_ICR_ALL); + cia_able_irq_private(base, CIA_ICR_ALL); + + /* install CIA handler */ + action = &amiga_sys_irqaction[base->handler_irq-IRQ_AMIGA_AUTO]; + action->handler = cia_handler; + action->dev_id = base; + action->name = base->name; + setup_irq(base->handler_irq, &amiga_sys_irqaction[base->handler_irq-IRQ_AMIGA_AUTO]); + + custom.intena = IF_SETCLR | base->int_mask; +} diff --git a/arch/ppc/amiga/config.c b/arch/ppc/amiga/config.c new file mode 100644 index 00000000000..af881d7454d --- /dev/null +++ b/arch/ppc/amiga/config.c @@ -0,0 +1,962 @@ +#define m68k_debug_device debug_device + +/* + * arch/ppc/amiga/config.c + * + * Copyright (C) 1993 Hamish Macdonald + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + +/* + * Miscellaneous Amiga stuff + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_ZORRO +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +unsigned long powerup_PCI_present; +unsigned long powerup_BPPCPLUS_present; +unsigned long amiga_model; +unsigned long amiga_eclock; +unsigned long amiga_masterclock; +unsigned long amiga_colorclock; +unsigned long amiga_chipset; +unsigned char amiga_vblank; +unsigned char amiga_psfreq; +struct amiga_hw_present amiga_hw_present; + +static char s_a500[] __initdata = "A500"; +static char s_a500p[] __initdata = "A500+"; +static char s_a600[] __initdata = "A600"; +static char s_a1000[] __initdata = "A1000"; +static char s_a1200[] __initdata = "A1200"; +static char s_a2000[] __initdata = "A2000"; +static char s_a2500[] __initdata = "A2500"; +static char s_a3000[] __initdata = "A3000"; +static char s_a3000t[] __initdata = "A3000T"; +static char s_a3000p[] __initdata = "A3000+"; +static char s_a4000[] __initdata = "A4000"; +static char s_a4000t[] __initdata = "A4000T"; +static char s_cdtv[] __initdata = "CDTV"; +static char s_cd32[] __initdata = "CD32"; +static char s_draco[] __initdata = "Draco"; +static char *amiga_models[] __initdata = { + s_a500, s_a500p, s_a600, s_a1000, s_a1200, s_a2000, s_a2500, s_a3000, + s_a3000t, s_a3000p, s_a4000, s_a4000t, s_cdtv, s_cd32, s_draco, +}; + +static char amiga_model_name[13] = "Amiga "; + +extern char m68k_debug_device[]; + +static void amiga_sched_init(irqreturn_t (*handler)(int, void *, struct pt_regs *)); +/* amiga specific irq functions */ +extern void amiga_init_IRQ (void); +extern void (*amiga_default_handler[]) (int, void *, struct pt_regs *); +extern int amiga_request_irq (unsigned int irq, + void (*handler)(int, void *, struct pt_regs *), + unsigned long flags, const char *devname, + void *dev_id); +extern void amiga_free_irq (unsigned int irq, void *dev_id); +extern void amiga_enable_irq (unsigned int); +extern void amiga_disable_irq (unsigned int); +static void amiga_get_model(char *model); +static int amiga_get_hardware_list(char *buffer); +/* amiga specific timer functions */ +static unsigned long amiga_gettimeoffset (void); +static void a3000_gettod (int *, int *, int *, int *, int *, int *); +static void a2000_gettod (int *, int *, int *, int *, int *, int *); +static int amiga_hwclk (int, struct hwclk_time *); +static int amiga_set_clock_mmss (unsigned long); +#ifdef CONFIG_AMIGA_FLOPPY +extern void amiga_floppy_setup(char *, int *); +#endif +static void amiga_reset (void); +extern void amiga_init_sound(void); +static void amiga_savekmsg_init(void); +static void amiga_mem_console_write(struct console *co, const char *b, + unsigned int count); +void amiga_serial_console_write(struct console *co, const char *s, + unsigned int count); +static void amiga_debug_init(void); +#ifdef CONFIG_HEARTBEAT +static void amiga_heartbeat(int on); +#endif + +static struct console amiga_console_driver = { + .name = "debug", + .flags = CON_PRINTBUFFER, + .index = -1, +}; + + + /* + * Motherboard Resources present in all Amiga models + */ + +static struct { + struct resource _ciab, _ciaa, _custom, _kickstart; +} mb_resources = { +// { "Ranger Memory", 0x00c00000, 0x00c7ffff }, + ._ciab = { "CIA B", 0x00bfd000, 0x00bfdfff }, + ._ciaa = { "CIA A", 0x00bfe000, 0x00bfefff }, + ._custom = { "Custom I/O", 0x00dff000, 0x00dfffff }, + ._kickstart = { "Kickstart ROM", 0x00f80000, 0x00ffffff } +}; + +static struct resource rtc_resource = { + NULL, 0x00dc0000, 0x00dcffff +}; + +static struct resource ram_resource[NUM_MEMINFO]; + + + /* + * Parse an Amiga-specific record in the bootinfo + */ + +int amiga_parse_bootinfo(const struct bi_record *record) +{ + int unknown = 0; + const unsigned long *data = record->data; + + switch (record->tag) { + case BI_AMIGA_MODEL: + { + unsigned long d = *data; + + powerup_PCI_present = d & 0x100; + amiga_model = d & 0xff; + } + break; + + case BI_AMIGA_ECLOCK: + amiga_eclock = *data; + break; + + case BI_AMIGA_CHIPSET: + amiga_chipset = *data; + break; + + case BI_AMIGA_CHIP_SIZE: + amiga_chip_size = *(const int *)data; + break; + + case BI_AMIGA_VBLANK: + amiga_vblank = *(const unsigned char *)data; + break; + + case BI_AMIGA_PSFREQ: + amiga_psfreq = *(const unsigned char *)data; + break; + + case BI_AMIGA_AUTOCON: +#ifdef CONFIG_ZORRO + if (zorro_num_autocon < ZORRO_NUM_AUTO) { + const struct ConfigDev *cd = (struct ConfigDev *)data; + struct zorro_dev *dev = &zorro_autocon[zorro_num_autocon++]; + dev->rom = cd->cd_Rom; + dev->slotaddr = cd->cd_SlotAddr; + dev->slotsize = cd->cd_SlotSize; + dev->resource.start = (unsigned long)cd->cd_BoardAddr; + dev->resource.end = dev->resource.start+cd->cd_BoardSize-1; + } else + printk("amiga_parse_bootinfo: too many AutoConfig devices\n"); +#endif /* CONFIG_ZORRO */ + break; + + case BI_AMIGA_SERPER: + /* serial port period: ignored here */ + break; + + case BI_AMIGA_PUP_BRIDGE: + powerup_PCI_present = *(const unsigned short *)data; + break; + + case BI_AMIGA_BPPC_SCSI: + powerup_BPPCPLUS_present = *(const unsigned short *)data; + break; + + default: + unknown = 1; + } + return(unknown); +} + + /* + * Identify builtin hardware + */ + +static void __init amiga_identify(void) +{ + /* Fill in some default values, if necessary */ + if (amiga_eclock == 0) + amiga_eclock = 709379; + + memset(&amiga_hw_present, 0, sizeof(amiga_hw_present)); + + printk("Amiga hardware found: "); + if (amiga_model >= AMI_500 && amiga_model <= AMI_DRACO) { + printk("[%s] ", amiga_models[amiga_model-AMI_500]); + strcat(amiga_model_name, amiga_models[amiga_model-AMI_500]); + } + + switch(amiga_model) { + case AMI_UNKNOWN: + goto Generic; + + case AMI_600: + case AMI_1200: + AMIGAHW_SET(A1200_IDE); + AMIGAHW_SET(PCMCIA); + case AMI_500: + case AMI_500PLUS: + case AMI_1000: + case AMI_2000: + case AMI_2500: + AMIGAHW_SET(A2000_CLK); /* Is this correct for all models? */ + goto Generic; + + case AMI_3000: + case AMI_3000T: + AMIGAHW_SET(AMBER_FF); + AMIGAHW_SET(MAGIC_REKICK); + /* fall through */ + case AMI_3000PLUS: + AMIGAHW_SET(A3000_SCSI); + AMIGAHW_SET(A3000_CLK); + AMIGAHW_SET(ZORRO3); + goto Generic; + + case AMI_4000T: + AMIGAHW_SET(A4000_SCSI); + /* fall through */ + case AMI_4000: + AMIGAHW_SET(A4000_IDE); + AMIGAHW_SET(A3000_CLK); + AMIGAHW_SET(ZORRO3); + goto Generic; + + case AMI_CDTV: + case AMI_CD32: + AMIGAHW_SET(CD_ROM); + AMIGAHW_SET(A2000_CLK); /* Is this correct? */ + goto Generic; + + Generic: + AMIGAHW_SET(AMI_VIDEO); + AMIGAHW_SET(AMI_BLITTER); + AMIGAHW_SET(AMI_AUDIO); + AMIGAHW_SET(AMI_FLOPPY); + AMIGAHW_SET(AMI_KEYBOARD); + AMIGAHW_SET(AMI_MOUSE); + AMIGAHW_SET(AMI_SERIAL); + AMIGAHW_SET(AMI_PARALLEL); + AMIGAHW_SET(CHIP_RAM); + AMIGAHW_SET(PAULA); + + switch(amiga_chipset) { + case CS_OCS: + case CS_ECS: + case CS_AGA: + switch (custom.deniseid & 0xf) { + case 0x0c: + AMIGAHW_SET(DENISE_HR); + break; + case 0x08: + AMIGAHW_SET(LISA); + break; + } + break; + default: + AMIGAHW_SET(DENISE); + break; + } + switch ((custom.vposr>>8) & 0x7f) { + case 0x00: + AMIGAHW_SET(AGNUS_PAL); + break; + case 0x10: + AMIGAHW_SET(AGNUS_NTSC); + break; + case 0x20: + case 0x21: + AMIGAHW_SET(AGNUS_HR_PAL); + break; + case 0x30: + case 0x31: + AMIGAHW_SET(AGNUS_HR_NTSC); + break; + case 0x22: + case 0x23: + AMIGAHW_SET(ALICE_PAL); + break; + case 0x32: + case 0x33: + AMIGAHW_SET(ALICE_NTSC); + break; + } + AMIGAHW_SET(ZORRO); + break; + + case AMI_DRACO: + panic("No support for Draco yet"); + + default: + panic("Unknown Amiga Model"); + } + +#define AMIGAHW_ANNOUNCE(name, str) \ + if (AMIGAHW_PRESENT(name)) \ + printk(str) + + AMIGAHW_ANNOUNCE(AMI_VIDEO, "VIDEO "); + AMIGAHW_ANNOUNCE(AMI_BLITTER, "BLITTER "); + AMIGAHW_ANNOUNCE(AMBER_FF, "AMBER_FF "); + AMIGAHW_ANNOUNCE(AMI_AUDIO, "AUDIO "); + AMIGAHW_ANNOUNCE(AMI_FLOPPY, "FLOPPY "); + AMIGAHW_ANNOUNCE(A3000_SCSI, "A3000_SCSI "); + AMIGAHW_ANNOUNCE(A4000_SCSI, "A4000_SCSI "); + AMIGAHW_ANNOUNCE(A1200_IDE, "A1200_IDE "); + AMIGAHW_ANNOUNCE(A4000_IDE, "A4000_IDE "); + AMIGAHW_ANNOUNCE(CD_ROM, "CD_ROM "); + AMIGAHW_ANNOUNCE(AMI_KEYBOARD, "KEYBOARD "); + AMIGAHW_ANNOUNCE(AMI_MOUSE, "MOUSE "); + AMIGAHW_ANNOUNCE(AMI_SERIAL, "SERIAL "); + AMIGAHW_ANNOUNCE(AMI_PARALLEL, "PARALLEL "); + AMIGAHW_ANNOUNCE(A2000_CLK, "A2000_CLK "); + AMIGAHW_ANNOUNCE(A3000_CLK, "A3000_CLK "); + AMIGAHW_ANNOUNCE(CHIP_RAM, "CHIP_RAM "); + AMIGAHW_ANNOUNCE(PAULA, "PAULA "); + AMIGAHW_ANNOUNCE(DENISE, "DENISE "); + AMIGAHW_ANNOUNCE(DENISE_HR, "DENISE_HR "); + AMIGAHW_ANNOUNCE(LISA, "LISA "); + AMIGAHW_ANNOUNCE(AGNUS_PAL, "AGNUS_PAL "); + AMIGAHW_ANNOUNCE(AGNUS_NTSC, "AGNUS_NTSC "); + AMIGAHW_ANNOUNCE(AGNUS_HR_PAL, "AGNUS_HR_PAL "); + AMIGAHW_ANNOUNCE(AGNUS_HR_NTSC, "AGNUS_HR_NTSC "); + AMIGAHW_ANNOUNCE(ALICE_PAL, "ALICE_PAL "); + AMIGAHW_ANNOUNCE(ALICE_NTSC, "ALICE_NTSC "); + AMIGAHW_ANNOUNCE(MAGIC_REKICK, "MAGIC_REKICK "); + AMIGAHW_ANNOUNCE(PCMCIA, "PCMCIA "); + if (AMIGAHW_PRESENT(ZORRO)) + printk("ZORRO%s ", AMIGAHW_PRESENT(ZORRO3) ? "3" : ""); + printk("\n"); + +#undef AMIGAHW_ANNOUNCE +} + + /* + * Setup the Amiga configuration info + */ + +void __init config_amiga(void) +{ + int i; + + amiga_debug_init(); + amiga_identify(); + + /* Some APUS boxes may have PCI memory, but ... */ + iomem_resource.name = "Memory"; + for (i = 0; i < 4; i++) + request_resource(&iomem_resource, &((struct resource *)&mb_resources)[i]); + + mach_sched_init = amiga_sched_init; + mach_init_IRQ = amiga_init_IRQ; +#ifndef CONFIG_APUS + mach_default_handler = &amiga_default_handler; + mach_request_irq = amiga_request_irq; + mach_free_irq = amiga_free_irq; + enable_irq = amiga_enable_irq; + disable_irq = amiga_disable_irq; +#endif + mach_get_model = amiga_get_model; + mach_get_hardware_list = amiga_get_hardware_list; + mach_gettimeoffset = amiga_gettimeoffset; + if (AMIGAHW_PRESENT(A3000_CLK)){ + mach_gettod = a3000_gettod; + rtc_resource.name = "A3000 RTC"; + request_resource(&iomem_resource, &rtc_resource); + } + else{ /* if (AMIGAHW_PRESENT(A2000_CLK)) */ + mach_gettod = a2000_gettod; + rtc_resource.name = "A2000 RTC"; + request_resource(&iomem_resource, &rtc_resource); + } + + mach_max_dma_address = 0xffffffff; /* + * default MAX_DMA=0xffffffff + * on all machines. If we don't + * do so, the SCSI code will not + * be able to allocate any mem + * for transfers, unless we are + * dealing with a Z2 mem only + * system. /Jes + */ + + mach_hwclk = amiga_hwclk; + mach_set_clock_mmss = amiga_set_clock_mmss; +#ifdef CONFIG_AMIGA_FLOPPY + mach_floppy_setup = amiga_floppy_setup; +#endif + mach_reset = amiga_reset; +#ifdef CONFIG_HEARTBEAT + mach_heartbeat = amiga_heartbeat; +#endif + + /* Fill in the clock values (based on the 700 kHz E-Clock) */ + amiga_masterclock = 40*amiga_eclock; /* 28 MHz */ + amiga_colorclock = 5*amiga_eclock; /* 3.5 MHz */ + + /* clear all DMA bits */ + custom.dmacon = DMAF_ALL; + /* ensure that the DMA master bit is set */ + custom.dmacon = DMAF_SETCLR | DMAF_MASTER; + + /* request all RAM */ + for (i = 0; i < m68k_num_memory; i++) { + ram_resource[i].name = + (m68k_memory[i].addr >= 0x01000000) ? "32-bit Fast RAM" : + (m68k_memory[i].addr < 0x00c00000) ? "16-bit Fast RAM" : + "16-bit Slow RAM"; + ram_resource[i].start = m68k_memory[i].addr; + ram_resource[i].end = m68k_memory[i].addr+m68k_memory[i].size-1; + request_resource(&iomem_resource, &ram_resource[i]); + } + + /* initialize chipram allocator */ + amiga_chip_init (); + + /* debugging using chipram */ + if (!strcmp( m68k_debug_device, "mem" )){ + if (!AMIGAHW_PRESENT(CHIP_RAM)) + printk("Warning: no chipram present for debugging\n"); + else { + amiga_savekmsg_init(); + amiga_console_driver.write = amiga_mem_console_write; + register_console(&amiga_console_driver); + } + } + + /* our beloved beeper */ + if (AMIGAHW_PRESENT(AMI_AUDIO)) + amiga_init_sound(); + + /* + * if it is an A3000, set the magic bit that forces + * a hard rekick + */ + if (AMIGAHW_PRESENT(MAGIC_REKICK)) + *(unsigned char *)ZTWO_VADDR(0xde0002) |= 0x80; +} + +static unsigned short jiffy_ticks; + +static void __init amiga_sched_init(irqreturn_t (*timer_routine)(int, void *, + struct pt_regs *)) +{ + static struct resource sched_res = { + "timer", 0x00bfd400, 0x00bfd5ff, + }; + jiffy_ticks = (amiga_eclock+HZ/2)/HZ; + + if (request_resource(&mb_resources._ciab, &sched_res)) + printk("Cannot allocate ciab.ta{lo,hi}\n"); + ciab.cra &= 0xC0; /* turn off timer A, continuous mode, from Eclk */ + ciab.talo = jiffy_ticks % 256; + ciab.tahi = jiffy_ticks / 256; + + /* install interrupt service routine for CIAB Timer A + * + * Please don't change this to use ciaa, as it interferes with the + * SCSI code. We'll have to take a look at this later + */ + request_irq(IRQ_AMIGA_CIAB_TA, timer_routine, 0, "timer", NULL); + /* start timer */ + ciab.cra |= 0x11; +} + +#define TICK_SIZE 10000 + +extern unsigned char cia_get_irq_mask(unsigned int irq); + +/* This is always executed with interrupts disabled. */ +static unsigned long amiga_gettimeoffset (void) +{ + unsigned short hi, lo, hi2; + unsigned long ticks, offset = 0; + + /* read CIA B timer A current value */ + hi = ciab.tahi; + lo = ciab.talo; + hi2 = ciab.tahi; + + if (hi != hi2) { + lo = ciab.talo; + hi = hi2; + } + + ticks = hi << 8 | lo; + + if (ticks > jiffy_ticks / 2) + /* check for pending interrupt */ + if (cia_get_irq_mask(IRQ_AMIGA_CIAB) & CIA_ICR_TA) + offset = 10000; + + ticks = jiffy_ticks - ticks; + ticks = (10000 * ticks) / jiffy_ticks; + + return ticks + offset; +} + +static void a3000_gettod (int *yearp, int *monp, int *dayp, + int *hourp, int *minp, int *secp) +{ + volatile struct tod3000 *tod = TOD_3000; + + tod->cntrl1 = TOD3000_CNTRL1_HOLD; + + *secp = tod->second1 * 10 + tod->second2; + *minp = tod->minute1 * 10 + tod->minute2; + *hourp = tod->hour1 * 10 + tod->hour2; + *dayp = tod->day1 * 10 + tod->day2; + *monp = tod->month1 * 10 + tod->month2; + *yearp = tod->year1 * 10 + tod->year2; + + tod->cntrl1 = TOD3000_CNTRL1_FREE; +} + +static void a2000_gettod (int *yearp, int *monp, int *dayp, + int *hourp, int *minp, int *secp) +{ + volatile struct tod2000 *tod = TOD_2000; + + tod->cntrl1 = TOD2000_CNTRL1_HOLD; + + while (tod->cntrl1 & TOD2000_CNTRL1_BUSY) + ; + + *secp = tod->second1 * 10 + tod->second2; + *minp = tod->minute1 * 10 + tod->minute2; + *hourp = (tod->hour1 & 3) * 10 + tod->hour2; + *dayp = tod->day1 * 10 + tod->day2; + *monp = tod->month1 * 10 + tod->month2; + *yearp = tod->year1 * 10 + tod->year2; + + if (!(tod->cntrl3 & TOD2000_CNTRL3_24HMODE)){ + if (!(tod->hour1 & TOD2000_HOUR1_PM) && *hourp == 12) + *hourp = 0; + else if ((tod->hour1 & TOD2000_HOUR1_PM) && *hourp != 12) + *hourp += 12; + } + + tod->cntrl1 &= ~TOD2000_CNTRL1_HOLD; +} + +static int amiga_hwclk(int op, struct hwclk_time *t) +{ + if (AMIGAHW_PRESENT(A3000_CLK)) { + volatile struct tod3000 *tod = TOD_3000; + + tod->cntrl1 = TOD3000_CNTRL1_HOLD; + + if (!op) { /* read */ + t->sec = tod->second1 * 10 + tod->second2; + t->min = tod->minute1 * 10 + tod->minute2; + t->hour = tod->hour1 * 10 + tod->hour2; + t->day = tod->day1 * 10 + tod->day2; + t->wday = tod->weekday; + t->mon = tod->month1 * 10 + tod->month2 - 1; + t->year = tod->year1 * 10 + tod->year2; + if (t->year <= 69) + t->year += 100; + } else { + tod->second1 = t->sec / 10; + tod->second2 = t->sec % 10; + tod->minute1 = t->min / 10; + tod->minute2 = t->min % 10; + tod->hour1 = t->hour / 10; + tod->hour2 = t->hour % 10; + tod->day1 = t->day / 10; + tod->day2 = t->day % 10; + if (t->wday != -1) + tod->weekday = t->wday; + tod->month1 = (t->mon + 1) / 10; + tod->month2 = (t->mon + 1) % 10; + if (t->year >= 100) + t->year -= 100; + tod->year1 = t->year / 10; + tod->year2 = t->year % 10; + } + + tod->cntrl1 = TOD3000_CNTRL1_FREE; + } else /* if (AMIGAHW_PRESENT(A2000_CLK)) */ { + volatile struct tod2000 *tod = TOD_2000; + + tod->cntrl1 = TOD2000_CNTRL1_HOLD; + + while (tod->cntrl1 & TOD2000_CNTRL1_BUSY) + ; + + if (!op) { /* read */ + t->sec = tod->second1 * 10 + tod->second2; + t->min = tod->minute1 * 10 + tod->minute2; + t->hour = (tod->hour1 & 3) * 10 + tod->hour2; + t->day = tod->day1 * 10 + tod->day2; + t->wday = tod->weekday; + t->mon = tod->month1 * 10 + tod->month2 - 1; + t->year = tod->year1 * 10 + tod->year2; + if (t->year <= 69) + t->year += 100; + + if (!(tod->cntrl3 & TOD2000_CNTRL3_24HMODE)){ + if (!(tod->hour1 & TOD2000_HOUR1_PM) && t->hour == 12) + t->hour = 0; + else if ((tod->hour1 & TOD2000_HOUR1_PM) && t->hour != 12) + t->hour += 12; + } + } else { + tod->second1 = t->sec / 10; + tod->second2 = t->sec % 10; + tod->minute1 = t->min / 10; + tod->minute2 = t->min % 10; + if (tod->cntrl3 & TOD2000_CNTRL3_24HMODE) + tod->hour1 = t->hour / 10; + else if (t->hour >= 12) + tod->hour1 = TOD2000_HOUR1_PM + + (t->hour - 12) / 10; + else + tod->hour1 = t->hour / 10; + tod->hour2 = t->hour % 10; + tod->day1 = t->day / 10; + tod->day2 = t->day % 10; + if (t->wday != -1) + tod->weekday = t->wday; + tod->month1 = (t->mon + 1) / 10; + tod->month2 = (t->mon + 1) % 10; + if (t->year >= 100) + t->year -= 100; + tod->year1 = t->year / 10; + tod->year2 = t->year % 10; + } + + tod->cntrl1 &= ~TOD2000_CNTRL1_HOLD; + } + + return 0; +} + +static int amiga_set_clock_mmss (unsigned long nowtime) +{ + short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60; + + if (AMIGAHW_PRESENT(A3000_CLK)) { + volatile struct tod3000 *tod = TOD_3000; + + tod->cntrl1 = TOD3000_CNTRL1_HOLD; + + tod->second1 = real_seconds / 10; + tod->second2 = real_seconds % 10; + tod->minute1 = real_minutes / 10; + tod->minute2 = real_minutes % 10; + + tod->cntrl1 = TOD3000_CNTRL1_FREE; + } else /* if (AMIGAHW_PRESENT(A2000_CLK)) */ { + volatile struct tod2000 *tod = TOD_2000; + + tod->cntrl1 = TOD2000_CNTRL1_HOLD; + + while (tod->cntrl1 & TOD2000_CNTRL1_BUSY) + ; + + tod->second1 = real_seconds / 10; + tod->second2 = real_seconds % 10; + tod->minute1 = real_minutes / 10; + tod->minute2 = real_minutes % 10; + + tod->cntrl1 &= ~TOD2000_CNTRL1_HOLD; + } + + return 0; +} + +static NORET_TYPE void amiga_reset( void ) + ATTRIB_NORET; + +static void amiga_reset (void) +{ + for (;;); +} + + + /* + * Debugging + */ + +#define SAVEKMSG_MAXMEM 128*1024 + +#define SAVEKMSG_MAGIC1 0x53415645 /* 'SAVE' */ +#define SAVEKMSG_MAGIC2 0x4B4D5347 /* 'KMSG' */ + +struct savekmsg { + unsigned long magic1; /* SAVEKMSG_MAGIC1 */ + unsigned long magic2; /* SAVEKMSG_MAGIC2 */ + unsigned long magicptr; /* address of magic1 */ + unsigned long size; + char data[0]; +}; + +static struct savekmsg *savekmsg = NULL; + +static void amiga_mem_console_write(struct console *co, const char *s, + unsigned int count) +{ + if (savekmsg->size+count <= SAVEKMSG_MAXMEM-sizeof(struct savekmsg)) { + memcpy(savekmsg->data+savekmsg->size, s, count); + savekmsg->size += count; + } +} + +static void amiga_savekmsg_init(void) +{ + static struct resource debug_res = { "Debug" }; + + savekmsg = amiga_chip_alloc_res(SAVEKMSG_MAXMEM, &debug_res); + savekmsg->magic1 = SAVEKMSG_MAGIC1; + savekmsg->magic2 = SAVEKMSG_MAGIC2; + savekmsg->magicptr = virt_to_phys(savekmsg); + savekmsg->size = 0; +} + +static void amiga_serial_putc(char c) +{ + custom.serdat = (unsigned char)c | 0x100; + mb(); + while (!(custom.serdatr & 0x2000)) + ; +} + +void amiga_serial_console_write(struct console *co, const char *s, + unsigned int count) +{ +#if 0 /* def CONFIG_KGDB */ + /* FIXME:APUS GDB doesn't seem to like O-packages before it is + properly connected with the target. */ + __gdb_output_string (s, count); +#else + while (count--) { + if (*s == '\n') + amiga_serial_putc('\r'); + amiga_serial_putc(*s++); + } +#endif +} + +#ifdef CONFIG_SERIAL_CONSOLE +void amiga_serial_puts(const char *s) +{ + amiga_serial_console_write(NULL, s, strlen(s)); +} + +int amiga_serial_console_wait_key(struct console *co) +{ + int ch; + + while (!(custom.intreqr & IF_RBF)) + barrier(); + ch = custom.serdatr & 0xff; + /* clear the interrupt, so that another character can be read */ + custom.intreq = IF_RBF; + return ch; +} + +void amiga_serial_gets(struct console *co, char *s, int len) +{ + int ch, cnt = 0; + + while (1) { + ch = amiga_serial_console_wait_key(co); + + /* Check for backspace. */ + if (ch == 8 || ch == 127) { + if (cnt == 0) { + amiga_serial_putc('\007'); + continue; + } + cnt--; + amiga_serial_puts("\010 \010"); + continue; + } + + /* Check for enter. */ + if (ch == 10 || ch == 13) + break; + + /* See if line is too long. */ + if (cnt >= len + 1) { + amiga_serial_putc(7); + cnt--; + continue; + } + + /* Store and echo character. */ + s[cnt++] = ch; + amiga_serial_putc(ch); + } + /* Print enter. */ + amiga_serial_puts("\r\n"); + s[cnt] = 0; +} +#endif + +static void __init amiga_debug_init(void) +{ + if (!strcmp( m68k_debug_device, "ser" )) { + /* no initialization required (?) */ + amiga_console_driver.write = amiga_serial_console_write; + register_console(&amiga_console_driver); + } +} + +#ifdef CONFIG_HEARTBEAT +static void amiga_heartbeat(int on) +{ + if (on) + ciaa.pra &= ~2; + else + ciaa.pra |= 2; +} +#endif + + /* + * Amiga specific parts of /proc + */ + +static void amiga_get_model(char *model) +{ + strcpy(model, amiga_model_name); +} + + +static int amiga_get_hardware_list(char *buffer) +{ + int len = 0; + + if (AMIGAHW_PRESENT(CHIP_RAM)) + len += sprintf(buffer+len, "Chip RAM:\t%ldK\n", amiga_chip_size>>10); + len += sprintf(buffer+len, "PS Freq:\t%dHz\nEClock Freq:\t%ldHz\n", + amiga_psfreq, amiga_eclock); + if (AMIGAHW_PRESENT(AMI_VIDEO)) { + char *type; + switch(amiga_chipset) { + case CS_OCS: + type = "OCS"; + break; + case CS_ECS: + type = "ECS"; + break; + case CS_AGA: + type = "AGA"; + break; + default: + type = "Old or Unknown"; + break; + } + len += sprintf(buffer+len, "Graphics:\t%s\n", type); + } + +#define AMIGAHW_ANNOUNCE(name, str) \ + if (AMIGAHW_PRESENT(name)) \ + len += sprintf (buffer+len, "\t%s\n", str) + + len += sprintf (buffer + len, "Detected hardware:\n"); + + AMIGAHW_ANNOUNCE(AMI_VIDEO, "Amiga Video"); + AMIGAHW_ANNOUNCE(AMI_BLITTER, "Blitter"); + AMIGAHW_ANNOUNCE(AMBER_FF, "Amber Flicker Fixer"); + AMIGAHW_ANNOUNCE(AMI_AUDIO, "Amiga Audio"); + AMIGAHW_ANNOUNCE(AMI_FLOPPY, "Floppy Controller"); + AMIGAHW_ANNOUNCE(A3000_SCSI, "SCSI Controller WD33C93 (A3000 style)"); + AMIGAHW_ANNOUNCE(A4000_SCSI, "SCSI Controller NCR53C710 (A4000T style)"); + AMIGAHW_ANNOUNCE(A1200_IDE, "IDE Interface (A1200 style)"); + AMIGAHW_ANNOUNCE(A4000_IDE, "IDE Interface (A4000 style)"); + AMIGAHW_ANNOUNCE(CD_ROM, "Internal CD ROM drive"); + AMIGAHW_ANNOUNCE(AMI_KEYBOARD, "Keyboard"); + AMIGAHW_ANNOUNCE(AMI_MOUSE, "Mouse Port"); + AMIGAHW_ANNOUNCE(AMI_SERIAL, "Serial Port"); + AMIGAHW_ANNOUNCE(AMI_PARALLEL, "Parallel Port"); + AMIGAHW_ANNOUNCE(A2000_CLK, "Hardware Clock (A2000 style)"); + AMIGAHW_ANNOUNCE(A3000_CLK, "Hardware Clock (A3000 style)"); + AMIGAHW_ANNOUNCE(CHIP_RAM, "Chip RAM"); + AMIGAHW_ANNOUNCE(PAULA, "Paula 8364"); + AMIGAHW_ANNOUNCE(DENISE, "Denise 8362"); + AMIGAHW_ANNOUNCE(DENISE_HR, "Denise 8373"); + AMIGAHW_ANNOUNCE(LISA, "Lisa 8375"); + AMIGAHW_ANNOUNCE(AGNUS_PAL, "Normal/Fat PAL Agnus 8367/8371"); + AMIGAHW_ANNOUNCE(AGNUS_NTSC, "Normal/Fat NTSC Agnus 8361/8370"); + AMIGAHW_ANNOUNCE(AGNUS_HR_PAL, "Fat Hires PAL Agnus 8372"); + AMIGAHW_ANNOUNCE(AGNUS_HR_NTSC, "Fat Hires NTSC Agnus 8372"); + AMIGAHW_ANNOUNCE(ALICE_PAL, "PAL Alice 8374"); + AMIGAHW_ANNOUNCE(ALICE_NTSC, "NTSC Alice 8374"); + AMIGAHW_ANNOUNCE(MAGIC_REKICK, "Magic Hard Rekick"); + AMIGAHW_ANNOUNCE(PCMCIA, "PCMCIA Slot"); + if (AMIGAHW_PRESENT(ZORRO)) + len += sprintf(buffer+len, "\tZorro II%s AutoConfig: %d Expansion " + "Device%s\n", + AMIGAHW_PRESENT(ZORRO3) ? "I" : "", + zorro_num_autocon, zorro_num_autocon == 1 ? "" : "s"); + +#undef AMIGAHW_ANNOUNCE + + return(len); +} + +#ifdef CONFIG_APUS +int get_hardware_list(char *buffer) +{ + extern int get_cpuinfo(char *buffer); + int len = 0; + char model[80]; + u_long mem; + int i; + + if (mach_get_model) + mach_get_model(model); + else + strcpy(model, "Unknown PowerPC"); + + len += sprintf(buffer+len, "Model:\t\t%s\n", model); + len += get_cpuinfo(buffer+len); + for (mem = 0, i = 0; i < m68k_realnum_memory; i++) + mem += m68k_memory[i].size; + len += sprintf(buffer+len, "System Memory:\t%ldK\n", mem>>10); + + if (mach_get_hardware_list) + len += mach_get_hardware_list(buffer+len); + + return(len); +} +#endif diff --git a/arch/ppc/amiga/ints.c b/arch/ppc/amiga/ints.c new file mode 100644 index 00000000000..5d318e498f0 --- /dev/null +++ b/arch/ppc/amiga/ints.c @@ -0,0 +1,160 @@ +/* + * arch/ppc/amiga/ints.c + * + * Linux/m68k general interrupt handling code from arch/m68k/kernel/ints.c + * Needed to drive the m68k emulating IRQ hardware on the PowerUp boards. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* table for system interrupt handlers */ +static irq_handler_t irq_list[SYS_IRQS]; + +static const char *default_names[SYS_IRQS] = { + "spurious int", "int1 handler", "int2 handler", "int3 handler", + "int4 handler", "int5 handler", "int6 handler", "int7 handler" +}; + +/* The number of spurious interrupts */ +volatile unsigned int num_spurious; + +#define NUM_IRQ_NODES 100 +static irq_node_t nodes[NUM_IRQ_NODES]; + + +/* + * void init_IRQ(void) + * + * Parameters: None + * + * Returns: Nothing + * + * This function should be called during kernel startup to initialize + * the IRQ handling routines. + */ + +__init +void m68k_init_IRQ(void) +{ + int i; + + for (i = 0; i < SYS_IRQS; i++) { + if (mach_default_handler) + irq_list[i].handler = (*mach_default_handler)[i]; + irq_list[i].flags = 0; + irq_list[i].dev_id = NULL; + irq_list[i].devname = default_names[i]; + } + + for (i = 0; i < NUM_IRQ_NODES; i++) + nodes[i].handler = NULL; + + mach_init_IRQ (); +} + +irq_node_t *new_irq_node(void) +{ + irq_node_t *node; + short i; + + for (node = nodes, i = NUM_IRQ_NODES-1; i >= 0; node++, i--) + if (!node->handler) + return node; + + printk ("new_irq_node: out of nodes\n"); + return NULL; +} + +int sys_request_irq(unsigned int irq, + void (*handler)(int, void *, struct pt_regs *), + unsigned long flags, const char *devname, void *dev_id) +{ + if (irq < IRQ1 || irq > IRQ7) { + printk("%s: Incorrect IRQ %d from %s\n", + __FUNCTION__, irq, devname); + return -ENXIO; + } + +#if 0 + if (!(irq_list[irq].flags & IRQ_FLG_STD)) { + if (irq_list[irq].flags & IRQ_FLG_LOCK) { + printk("%s: IRQ %d from %s is not replaceable\n", + __FUNCTION__, irq, irq_list[irq].devname); + return -EBUSY; + } + if (!(flags & IRQ_FLG_REPLACE)) { + printk("%s: %s can't replace IRQ %d from %s\n", + __FUNCTION__, devname, irq, irq_list[irq].devname); + return -EBUSY; + } + } +#endif + + irq_list[irq].handler = handler; + irq_list[irq].flags = flags; + irq_list[irq].dev_id = dev_id; + irq_list[irq].devname = devname; + return 0; +} + +void sys_free_irq(unsigned int irq, void *dev_id) +{ + if (irq < IRQ1 || irq > IRQ7) { + printk("%s: Incorrect IRQ %d\n", __FUNCTION__, irq); + return; + } + + if (irq_list[irq].dev_id != dev_id) + printk("%s: Removing probably wrong IRQ %d from %s\n", + __FUNCTION__, irq, irq_list[irq].devname); + + irq_list[irq].handler = (*mach_default_handler)[irq]; + irq_list[irq].flags = 0; + irq_list[irq].dev_id = NULL; + irq_list[irq].devname = default_names[irq]; +} + +asmlinkage void process_int(unsigned long vec, struct pt_regs *fp) +{ + if (vec >= VEC_INT1 && vec <= VEC_INT7 && !MACH_IS_BVME6000) { + vec -= VEC_SPUR; + kstat_cpu(0).irqs[vec]++; + irq_list[vec].handler(vec, irq_list[vec].dev_id, fp); + } else { + if (mach_process_int) + mach_process_int(vec, fp); + else + panic("Can't process interrupt vector %ld\n", vec); + return; + } +} + +int m68k_get_irq_list(struct seq_file *p, void *v) +{ + int i; + + /* autovector interrupts */ + if (mach_default_handler) { + for (i = 0; i < SYS_IRQS; i++) { + seq_printf(p, "auto %2d: %10u ", i, + i ? kstat_cpu(0).irqs[i] : num_spurious); + seq_puts(p, " "); + seq_printf(p, "%s\n", irq_list[i].devname); + } + } + + mach_get_irq_list(p, v); + return 0; +} diff --git a/arch/ppc/amiga/pcmcia.c b/arch/ppc/amiga/pcmcia.c new file mode 100644 index 00000000000..5d29dc65093 --- /dev/null +++ b/arch/ppc/amiga/pcmcia.c @@ -0,0 +1 @@ +#include "../../m68k/amiga/pcmcia.c" diff --git a/arch/ppc/amiga/time.c b/arch/ppc/amiga/time.c new file mode 100644 index 00000000000..0073527a703 --- /dev/null +++ b/arch/ppc/amiga/time.c @@ -0,0 +1,58 @@ +#include /* CONFIG_HEARTBEAT */ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +unsigned long m68k_get_rtc_time(void) +{ + unsigned int year, mon, day, hour, min, sec; + + extern void arch_gettod(int *year, int *mon, int *day, int *hour, + int *min, int *sec); + + arch_gettod (&year, &mon, &day, &hour, &min, &sec); + + if ((year += 1900) < 1970) + year += 100; + + return mktime(year, mon, day, hour, min, sec); +} + +int m68k_set_rtc_time(unsigned long nowtime) +{ + if (mach_set_clock_mmss) + return mach_set_clock_mmss (nowtime); + return -1; +} + +void apus_heartbeat (void) +{ +#ifdef CONFIG_HEARTBEAT + static unsigned cnt = 0, period = 0, dist = 0; + + if (cnt == 0 || cnt == dist) + mach_heartbeat( 1 ); + else if (cnt == 7 || cnt == dist+7) + mach_heartbeat( 0 ); + + if (++cnt > period) { + cnt = 0; + /* The hyperbolic function below modifies the heartbeat period + * length in dependency of the current (5min) load. It goes + * through the points f(0)=126, f(1)=86, f(5)=51, + * f(inf)->30. */ + period = ((672< + * + * 2002 (c) MontaVista Software, Inc. This file is licensed under the terms + * of the GNU General Public License version 2. This program is licensed + * "as is" without any warranty of any kind, whether express or implied. + */ + +#include +#include +#include + +#include "nonstdio.h" + +static struct bi_record * birec = NULL; + +static struct bi_record * +__bootinfo_build(struct bi_record *rec, unsigned long tag, unsigned long size, + void *data) +{ + /* set the tag */ + rec->tag = tag; + + /* if the caller has any data, copy it */ + if (size) + memcpy(rec->data, (char *)data, size); + + /* set the record size */ + rec->size = sizeof(struct bi_record) + size; + + /* advance to the next available space */ + rec = (struct bi_record *)((unsigned long)rec + rec->size); + + return rec; +} + +void +bootinfo_init(struct bi_record *rec) +{ + + /* save start of birec area */ + birec = rec; + + /* create an empty list */ + rec = __bootinfo_build(rec, BI_FIRST, 0, NULL); + (void) __bootinfo_build(rec, BI_LAST, 0, NULL); + +} + +void +bootinfo_append(unsigned long tag, unsigned long size, void * data) +{ + + struct bi_record *rec = birec; + + /* paranoia */ + if ((rec == NULL) || (rec->tag != BI_FIRST)) + return; + + /* find the last entry in the list */ + while (rec->tag != BI_LAST) + rec = (struct bi_record *)((ulong)rec + rec->size); + + /* overlay BI_LAST record with new one and tag on a new BI_LAST */ + rec = __bootinfo_build(rec, tag, size, data); + (void) __bootinfo_build(rec, BI_LAST, 0, NULL); +} diff --git a/arch/ppc/boot/common/crt0.S b/arch/ppc/boot/common/crt0.S new file mode 100644 index 00000000000..4d31b824bbd --- /dev/null +++ b/arch/ppc/boot/common/crt0.S @@ -0,0 +1,81 @@ +/* Copyright (c) 1997 Paul Mackerras + * Initial Power Macintosh COFF version. + * Copyright (c) 1999 Grant Erickson + * Modifications for IBM PowerPC 400-class processor evaluation + * boards. + * + * Module name: crt0.S + * + * Description: + * Boot loader execution entry point. Clears out .bss section as per + * ANSI C requirements. Invalidates and flushes the caches over the + * range covered by the boot loader's .text section. Sets up a stack + * below the .text section entry point. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include + + .text + + .globl _start +_start: +#ifdef XCOFF + .long __start,0,0 + + .globl __start +__start: +#endif + + ## Flush and invalidate the caches for the range in memory covering + ## the .text section of the boot loader + + lis r9,_start@h # r9 = &_start + lis r8,_etext@ha # + addi r8,r8,_etext@l # r8 = &_etext +3: dcbf r0,r9 # Flush the data cache + icbi r0,r9 # Invalidate the instruction cache + addi r9,r9,0x10 # Increment by one cache line + cmplw cr0,r9,r8 # Are we at the end yet? + blt 3b # No, keep flushing and invalidating + sync # sync ; isync after flushing the icache + isync + + ## Clear out the BSS as per ANSI C requirements + + lis r7,_end@ha + addi r7,r7,_end@l # r7 = &_end + lis r8,__bss_start@ha # + addi r8,r8,__bss_start@l # r8 = &_bss_start + + ## Determine how large an area, in number of words, to clear + + subf r7,r8,r7 # r7 = &_end - &_bss_start + 1 + addi r7,r7,3 # r7 += 3 + srwi. r7,r7,2 # r7 = size in words. + beq 2f # If the size is zero, do not bother + addi r8,r8,-4 # r8 -= 4 + mtctr r7 # SPRN_CTR = number of words to clear + li r0,0 # r0 = 0 +1: stwu r0,4(r8) # Clear out a word + bdnz 1b # If we are not done yet, keep clearing +2: + +#ifdef CONFIG_40x + ## Set up the stack + + lis r9,_start@h # r9 = &_start (text section entry) + ori r9,r9,_start@l + subi r1,r9,64 # Start the stack 64 bytes below _start + clrrwi r1,r1,4 # Make sure it is aligned on 16 bytes. + li r0,0 + stwu r0,-16(r1) + mtlr r9 +#endif + + b start # All done, start the real work. diff --git a/arch/ppc/boot/common/misc-common.c b/arch/ppc/boot/common/misc-common.c new file mode 100644 index 00000000000..e79e6b3f276 --- /dev/null +++ b/arch/ppc/boot/common/misc-common.c @@ -0,0 +1,553 @@ +/* + * arch/ppc/boot/common/misc-common.c + * + * Misc. bootloader code (almost) all platforms can use + * + * Author: Johnnie Peters + * Editor: Tom Rini + * + * Derived from arch/ppc/boot/prep/misc.c + * + * 2000-2001 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include /* for va_ bits */ +#include +#include +#include +#include "nonstdio.h" + +/* If we're on a PReP, assume we have a keyboard controller + * Also note, if we're not PReP, we assume you are a serial + * console - Tom */ +#if defined(CONFIG_PPC_PREP) && defined(CONFIG_VGA_CONSOLE) +extern void cursor(int x, int y); +extern void scroll(void); +extern char *vidmem; +extern int lines, cols; +extern int orig_x, orig_y; +extern int keyb_present; +extern int CRT_tstc(void); +extern int CRT_getc(void); +#else +int cursor(int x, int y) {return 0;} +void scroll(void) {} +char vidmem[1]; +#define lines 0 +#define cols 0 +int orig_x = 0; +int orig_y = 0; +#define keyb_present 0 +int CRT_tstc(void) {return 0;} +int CRT_getc(void) {return 0;} +#endif + +extern char *avail_ram; +extern char *end_avail; +extern char _end[]; + +void puts(const char *); +void putc(const char c); +void puthex(unsigned long val); +void gunzip(void *, int, unsigned char *, int *); +static int _cvt(unsigned long val, char *buf, long radix, char *digits); + +void _vprintk(void(*putc)(const char), const char *fmt0, va_list ap); +unsigned char *ISA_io = NULL; + +#if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \ + || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \ + || defined(CONFIG_SERIAL_MPSC_CONSOLE) +extern unsigned long com_port; + +extern int serial_tstc(unsigned long com_port); +extern unsigned char serial_getc(unsigned long com_port); +extern void serial_putc(unsigned long com_port, unsigned char c); +#endif + +void pause(void) +{ + puts("pause\n"); +} + +void exit(void) +{ + puts("exit\n"); + while(1); +} + +int tstc(void) +{ +#if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \ + || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \ + || defined(CONFIG_SERIAL_MPSC_CONSOLE) + if(keyb_present) + return (CRT_tstc() || serial_tstc(com_port)); + else + return (serial_tstc(com_port)); +#else + return CRT_tstc(); +#endif +} + +int getc(void) +{ + while (1) { +#if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \ + || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \ + || defined(CONFIG_SERIAL_MPSC_CONSOLE) + if (serial_tstc(com_port)) + return (serial_getc(com_port)); +#endif /* serial console */ + if (keyb_present) + if(CRT_tstc()) + return (CRT_getc()); + } +} + +void +putc(const char c) +{ + int x,y; + +#if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \ + || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \ + || defined(CONFIG_SERIAL_MPSC_CONSOLE) + serial_putc(com_port, c); + if ( c == '\n' ) + serial_putc(com_port, '\r'); +#endif /* serial console */ + + x = orig_x; + y = orig_y; + + if ( c == '\n' ) { + x = 0; + if ( ++y >= lines ) { + scroll(); + y--; + } + } else if (c == '\r') { + x = 0; + } else if (c == '\b') { + if (x > 0) { + x--; + } + } else { + vidmem [ ( x + cols * y ) * 2 ] = c; + if ( ++x >= cols ) { + x = 0; + if ( ++y >= lines ) { + scroll(); + y--; + } + } + } + + cursor(x, y); + + orig_x = x; + orig_y = y; +} + +void puts(const char *s) +{ + int x,y; + char c; + + x = orig_x; + y = orig_y; + + while ( ( c = *s++ ) != '\0' ) { +#if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) \ + || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \ + || defined(CONFIG_SERIAL_MPSC_CONSOLE) + serial_putc(com_port, c); + if ( c == '\n' ) serial_putc(com_port, '\r'); +#endif /* serial console */ + + if ( c == '\n' ) { + x = 0; + if ( ++y >= lines ) { + scroll(); + y--; + } + } else if (c == '\b') { + if (x > 0) { + x--; + } + } else { + vidmem [ ( x + cols * y ) * 2 ] = c; + if ( ++x >= cols ) { + x = 0; + if ( ++y >= lines ) { + scroll(); + y--; + } + } + } + } + + cursor(x, y); + + orig_x = x; + orig_y = y; +} + +void error(char *x) +{ + puts("\n\n"); + puts(x); + puts("\n\n -- System halted"); + + while(1); /* Halt */ +} + +static void *zalloc(unsigned size) +{ + void *p = avail_ram; + + size = (size + 7) & -8; + avail_ram += size; + if (avail_ram > end_avail) { + puts("oops... out of memory\n"); + pause(); + } + return p; +} + +#define HEAD_CRC 2 +#define EXTRA_FIELD 4 +#define ORIG_NAME 8 +#define COMMENT 0x10 +#define RESERVED 0xe0 + +void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp) +{ + z_stream s; + int r, i, flags; + + /* skip header */ + i = 10; + flags = src[3]; + if (src[2] != Z_DEFLATED || (flags & RESERVED) != 0) { + puts("bad gzipped data\n"); + exit(); + } + if ((flags & EXTRA_FIELD) != 0) + i = 12 + src[10] + (src[11] << 8); + if ((flags & ORIG_NAME) != 0) + while (src[i++] != 0) + ; + if ((flags & COMMENT) != 0) + while (src[i++] != 0) + ; + if ((flags & HEAD_CRC) != 0) + i += 2; + if (i >= *lenp) { + puts("gunzip: ran out of data in header\n"); + exit(); + } + + /* Initialize ourself. */ + s.workspace = zalloc(zlib_inflate_workspacesize()); + r = zlib_inflateInit2(&s, -MAX_WBITS); + if (r != Z_OK) { + puts("zlib_inflateInit2 returned "); puthex(r); puts("\n"); + exit(); + } + s.next_in = src + i; + s.avail_in = *lenp - i; + s.next_out = dst; + s.avail_out = dstlen; + r = zlib_inflate(&s, Z_FINISH); + if (r != Z_OK && r != Z_STREAM_END) { + puts("inflate returned "); puthex(r); puts("\n"); + exit(); + } + *lenp = s.next_out - (unsigned char *) dst; + zlib_inflateEnd(&s); +} + +void +puthex(unsigned long val) +{ + + unsigned char buf[10]; + int i; + for (i = 7; i >= 0; i--) + { + buf[i] = "0123456789ABCDEF"[val & 0x0F]; + val >>= 4; + } + buf[8] = '\0'; + puts(buf); +} + +#define FALSE 0 +#define TRUE 1 + +void +_printk(char const *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + _vprintk(putc, fmt, ap); + va_end(ap); + return; +} + +#define is_digit(c) ((c >= '0') && (c <= '9')) + +void +_vprintk(void(*putc)(const char), const char *fmt0, va_list ap) +{ + char c, sign, *cp = 0; + int left_prec, right_prec, zero_fill, length = 0, pad, pad_on_right; + char buf[32]; + long val; + while ((c = *fmt0++)) + { + if (c == '%') + { + c = *fmt0++; + left_prec = right_prec = pad_on_right = 0; + if (c == '-') + { + c = *fmt0++; + pad_on_right++; + } + if (c == '0') + { + zero_fill = TRUE; + c = *fmt0++; + } else + { + zero_fill = FALSE; + } + while (is_digit(c)) + { + left_prec = (left_prec * 10) + (c - '0'); + c = *fmt0++; + } + if (c == '.') + { + c = *fmt0++; + zero_fill++; + while (is_digit(c)) + { + right_prec = (right_prec * 10) + (c - '0'); + c = *fmt0++; + } + } else + { + right_prec = left_prec; + } + sign = '\0'; + switch (c) + { + case 'd': + case 'x': + case 'X': + val = va_arg(ap, long); + switch (c) + { + case 'd': + if (val < 0) + { + sign = '-'; + val = -val; + } + length = _cvt(val, buf, 10, "0123456789"); + break; + case 'x': + length = _cvt(val, buf, 16, "0123456789abcdef"); + break; + case 'X': + length = _cvt(val, buf, 16, "0123456789ABCDEF"); + break; + } + cp = buf; + break; + case 's': + cp = va_arg(ap, char *); + length = strlen(cp); + break; + case 'c': + c = va_arg(ap, long /*char*/); + (*putc)(c); + continue; + default: + (*putc)('?'); + } + pad = left_prec - length; + if (sign != '\0') + { + pad--; + } + if (zero_fill) + { + c = '0'; + if (sign != '\0') + { + (*putc)(sign); + sign = '\0'; + } + } else + { + c = ' '; + } + if (!pad_on_right) + { + while (pad-- > 0) + { + (*putc)(c); + } + } + if (sign != '\0') + { + (*putc)(sign); + } + while (length-- > 0) + { + (*putc)(c = *cp++); + if (c == '\n') + { + (*putc)('\r'); + } + } + if (pad_on_right) + { + while (pad-- > 0) + { + (*putc)(c); + } + } + } else + { + (*putc)(c); + if (c == '\n') + { + (*putc)('\r'); + } + } + } +} + +int +_cvt(unsigned long val, char *buf, long radix, char *digits) +{ + char temp[80]; + char *cp = temp; + int length = 0; + if (val == 0) + { /* Special case */ + *cp++ = '0'; + } else + while (val) + { + *cp++ = digits[val % radix]; + val /= radix; + } + while (cp != temp) + { + *buf++ = *--cp; + length++; + } + *buf = '\0'; + return (length); +} + +void +_dump_buf_with_offset(unsigned char *p, int s, unsigned char *base) +{ + int i, c; + if ((unsigned int)s > (unsigned int)p) + { + s = (unsigned int)s - (unsigned int)p; + } + while (s > 0) + { + if (base) + { + _printk("%06X: ", (int)p - (int)base); + } else + { + _printk("%06X: ", p); + } + for (i = 0; i < 16; i++) + { + if (i < s) + { + _printk("%02X", p[i] & 0xFF); + } else + { + _printk(" "); + } + if ((i % 2) == 1) _printk(" "); + if ((i % 8) == 7) _printk(" "); + } + _printk(" |"); + for (i = 0; i < 16; i++) + { + if (i < s) + { + c = p[i] & 0xFF; + if ((c < 0x20) || (c >= 0x7F)) c = '.'; + } else + { + c = ' '; + } + _printk("%c", c); + } + _printk("|\n"); + s -= 16; + p += 16; + } +} + +void +_dump_buf(unsigned char *p, int s) +{ + _printk("\n"); + _dump_buf_with_offset(p, s, 0); +} + +/* Very simple inb/outb routines. We declare ISA_io to be 0 above, and + * then modify it on platforms which need to. We do it like this + * because on some platforms we give inb/outb an exact location, and + * on others it's an offset from a given location. -- Tom + */ + +void ISA_init(unsigned long base) +{ + ISA_io = (unsigned char *)base; +} + +void +outb(int port, unsigned char val) +{ + /* Ensure I/O operations complete */ + __asm__ volatile("eieio"); + ISA_io[port] = val; +} + +unsigned char +inb(int port) +{ + /* Ensure I/O operations complete */ + __asm__ volatile("eieio"); + return (ISA_io[port]); +} + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ diff --git a/arch/ppc/boot/common/ns16550.c b/arch/ppc/boot/common/ns16550.c new file mode 100644 index 00000000000..9017c547a6f --- /dev/null +++ b/arch/ppc/boot/common/ns16550.c @@ -0,0 +1,99 @@ +/* + * COM1 NS16550 support + */ + +#include +#include +#include +#include +#include + +#include "nonstdio.h" +#include "serial.h" + +#define SERIAL_BAUD 9600 + +extern unsigned long ISA_io; + +static struct serial_state rs_table[RS_TABLE_SIZE] = { + SERIAL_PORT_DFNS /* Defined in */ +}; + +static int shift; + +unsigned long serial_init(int chan, void *ignored) +{ + unsigned long com_port; + unsigned char lcr, dlm; + + /* We need to find out which type io we're expecting. If it's + * 'SERIAL_IO_PORT', we get an offset from the isa_io_base. + * If it's 'SERIAL_IO_MEM', we can the exact location. -- Tom */ + switch (rs_table[chan].io_type) { + case SERIAL_IO_PORT: + com_port = rs_table[chan].port; + break; + case SERIAL_IO_MEM: + com_port = (unsigned long)rs_table[chan].iomem_base; + break; + default: + /* We can't deal with it. */ + return -1; + } + + /* How far apart the registers are. */ + shift = rs_table[chan].iomem_reg_shift; + + /* save the LCR */ + lcr = inb(com_port + (UART_LCR << shift)); + /* Access baud rate */ + outb(com_port + (UART_LCR << shift), 0x80); + dlm = inb(com_port + (UART_DLM << shift)); + /* + * Test if serial port is unconfigured. + * We assume that no-one uses less than 110 baud or + * less than 7 bits per character these days. + * -- paulus. + */ + + if ((dlm <= 4) && (lcr & 2)) + /* port is configured, put the old LCR back */ + outb(com_port + (UART_LCR << shift), lcr); + else { + /* Input clock. */ + outb(com_port + (UART_DLL << shift), + (BASE_BAUD / SERIAL_BAUD) & 0xFF); + outb(com_port + (UART_DLM << shift), + (BASE_BAUD / SERIAL_BAUD) >> 8); + /* 8 data, 1 stop, no parity */ + outb(com_port + (UART_LCR << shift), 0x03); + /* RTS/DTR */ + outb(com_port + (UART_MCR << shift), 0x03); + } + /* Clear & enable FIFOs */ + outb(com_port + (UART_FCR << shift), 0x07); + + return (com_port); +} + +void +serial_putc(unsigned long com_port, unsigned char c) +{ + while ((inb(com_port + (UART_LSR << shift)) & UART_LSR_THRE) == 0) + ; + outb(com_port, c); +} + +unsigned char +serial_getc(unsigned long com_port) +{ + while ((inb(com_port + (UART_LSR << shift)) & UART_LSR_DR) == 0) + ; + return inb(com_port); +} + +int +serial_tstc(unsigned long com_port) +{ + return ((inb(com_port + (UART_LSR << shift)) & UART_LSR_DR) != 0); +} diff --git a/arch/ppc/boot/common/serial_stub.c b/arch/ppc/boot/common/serial_stub.c new file mode 100644 index 00000000000..03dfaa01fa6 --- /dev/null +++ b/arch/ppc/boot/common/serial_stub.c @@ -0,0 +1,23 @@ +/* + * arch/ppc/boot/common/serial_stub.c + * + * This is a few stub routines to make the boot code cleaner looking when + * there is no serial port support doesn't need to be closed, for example. + * + * Author: Tom Rini + * + * 2003 (c) MontaVista, Software, Inc. This file is licensed under the terms + * of the GNU General Public License version 2. This program is licensed "as + * is" without any warranty of any kind, whether express or implied. + */ + +unsigned long __attribute__ ((weak)) +serial_init(int chan, void *ignored) +{ + return 0; +} + +void __attribute__ ((weak)) +serial_close(unsigned long com_port) +{ +} diff --git a/arch/ppc/boot/common/string.S b/arch/ppc/boot/common/string.S new file mode 100644 index 00000000000..8016e43c477 --- /dev/null +++ b/arch/ppc/boot/common/string.S @@ -0,0 +1,150 @@ +/* + * String handling functions for PowerPC. + * + * Copyright (C) 1996 Paul Mackerras. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#define r0 0 +#define r3 3 +#define r4 4 +#define r5 5 +#define r6 6 +#define r7 7 +#define r8 8 + + .globl strlen +strlen: + addi r4,r3,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + bne 1b + subf r3,r3,r4 + blr + + .globl memset +memset: + rlwimi r4,r4,8,16,23 + rlwimi r4,r4,16,0,15 + addi r6,r3,-4 + cmplwi 0,r5,4 + blt 7f + stwu r4,4(r6) + beqlr + andi. r0,r6,3 + add r5,r0,r5 + subf r6,r0,r6 + rlwinm r0,r5,32-2,2,31 + mtctr r0 + bdz 6f +1: stwu r4,4(r6) + bdnz 1b +6: andi. r5,r5,3 +7: cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r6,r6,3 +8: stbu r4,1(r6) + bdnz 8b + blr + + .globl memmove +memmove: + cmplw 0,r3,r4 + bgt backwards_memcpy + /* fall through */ + + .globl memcpy +memcpy: + rlwinm. r7,r5,32-3,3,31 /* r0 = r5 >> 3 */ + addi r6,r3,-4 + addi r4,r4,-4 + beq 2f /* if less than 8 bytes to do */ + andi. r0,r6,3 /* get dest word aligned */ + mtctr r7 + bne 5f +1: lwz r7,4(r4) + lwzu r8,8(r4) + stw r7,4(r6) + stwu r8,8(r6) + bdnz 1b + andi. r5,r5,7 +2: cmplwi 0,r5,4 + blt 3f + lwzu r0,4(r4) + addi r5,r5,-4 + stwu r0,4(r6) +3: cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r4,r4,3 + addi r6,r6,3 +4: lbzu r0,1(r4) + stbu r0,1(r6) + bdnz 4b + blr +5: subfic r0,r0,4 + mtctr r0 +6: lbz r7,4(r4) + addi r4,r4,1 + stb r7,4(r6) + addi r6,r6,1 + bdnz 6b + subf r5,r0,r5 + rlwinm. r7,r5,32-3,3,31 + beq 2b + mtctr r7 + b 1b + + .globl backwards_memcpy +backwards_memcpy: + rlwinm. r7,r5,32-3,3,31 /* r0 = r5 >> 3 */ + add r6,r3,r5 + add r4,r4,r5 + beq 2f + andi. r0,r6,3 + mtctr r7 + bne 5f +1: lwz r7,-4(r4) + lwzu r8,-8(r4) + stw r7,-4(r6) + stwu r8,-8(r6) + bdnz 1b + andi. r5,r5,7 +2: cmplwi 0,r5,4 + blt 3f + lwzu r0,-4(r4) + subi r5,r5,4 + stwu r0,-4(r6) +3: cmpwi 0,r5,0 + beqlr + mtctr r5 +4: lbzu r0,-1(r4) + stbu r0,-1(r6) + bdnz 4b + blr +5: mtctr r0 +6: lbzu r7,-1(r4) + stbu r7,-1(r6) + bdnz 6b + subf r5,r0,r5 + rlwinm. r7,r5,32-3,3,31 + beq 2b + mtctr r7 + b 1b + + .globl memcmp +memcmp: + cmpwi 0,r5,0 + blelr + mtctr r5 + addi r6,r3,-1 + addi r4,r4,-1 +1: lbzu r3,1(r6) + lbzu r0,1(r4) + subf. r3,r0,r3 + bdnzt 2,1b + blr diff --git a/arch/ppc/boot/common/util.S b/arch/ppc/boot/common/util.S new file mode 100644 index 00000000000..47e641455bc --- /dev/null +++ b/arch/ppc/boot/common/util.S @@ -0,0 +1,293 @@ +/* + * arch/ppc/boot/common/util.S + * + * Useful bootup functions, which are more easily done in asm than C. + * + * NOTE: Be very very careful about the registers you use here. + * We don't follow any ABI calling convention among the + * assembler functions that call each other, especially early + * in the initialization. Please preserve at least r3 and r4 + * for these early functions, as they often contain information + * passed from boot roms into the C decompress function. + * + * Author: Tom Rini + * trini@mvista.com + * Derived from arch/ppc/boot/prep/head.S (Cort Dougan, many others). + * + * 2001-2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include + + + .text + +#ifdef CONFIG_6xx + .globl disable_6xx_mmu +disable_6xx_mmu: + /* Establish default MSR value, exception prefix 0xFFF. + * If necessary, this function must fix up the LR if we + * return to a different address space once the MMU is + * disabled. + */ + li r8,MSR_IP|MSR_FP + mtmsr r8 + isync + + /* Test for a 601 */ + mfpvr r10 + srwi r10,r10,16 + cmpwi 0,r10,1 /* 601 ? */ + beq .clearbats_601 + + /* Clear BATs */ + li r8,0 + mtspr SPRN_DBAT0U,r8 + mtspr SPRN_DBAT0L,r8 + mtspr SPRN_DBAT1U,r8 + mtspr SPRN_DBAT1L,r8 + mtspr SPRN_DBAT2U,r8 + mtspr SPRN_DBAT2L,r8 + mtspr SPRN_DBAT3U,r8 + mtspr SPRN_DBAT3L,r8 +.clearbats_601: + mtspr SPRN_IBAT0U,r8 + mtspr SPRN_IBAT0L,r8 + mtspr SPRN_IBAT1U,r8 + mtspr SPRN_IBAT1L,r8 + mtspr SPRN_IBAT2U,r8 + mtspr SPRN_IBAT2L,r8 + mtspr SPRN_IBAT3U,r8 + mtspr SPRN_IBAT3L,r8 + isync + sync + sync + + /* Set segment registers */ + li r8,16 /* load up segment register values */ + mtctr r8 /* for context 0 */ + lis r8,0x2000 /* Ku = 1, VSID = 0 */ + li r10,0 +3: mtsrin r8,r10 + addi r8,r8,0x111 /* increment VSID */ + addis r10,r10,0x1000 /* address of next segment */ + bdnz 3b + blr + + .globl disable_6xx_l1cache +disable_6xx_l1cache: + /* Enable, invalidate and then disable the L1 icache/dcache. */ + li r8,0 + ori r8,r8,(HID0_ICE|HID0_DCE|HID0_ICFI|HID0_DCI) + mfspr r11,SPRN_HID0 + or r11,r11,r8 + andc r10,r11,r8 + isync + mtspr SPRN_HID0,r8 + sync + isync + mtspr SPRN_HID0,r10 + sync + isync + blr +#endif + + .globl _setup_L2CR +_setup_L2CR: +/* + * We should be skipping this section on CPUs where this results in an + * illegal instruction. If not, please send trini@kernel.crashing.org + * the PVR of your CPU. + */ + /* Invalidate/disable L2 cache */ + sync + isync + mfspr r8,SPRN_L2CR + rlwinm r8,r8,0,1,31 + oris r8,r8,L2CR_L2I@h + sync + isync + mtspr SPRN_L2CR,r8 + sync + isync + + /* Wait for the invalidation to complete */ + mfspr r8,SPRN_PVR + srwi r8,r8,16 + cmplwi cr0,r8,0x8000 /* 7450 */ + cmplwi cr1,r8,0x8001 /* 7455 */ + cmplwi cr2,r8,0x8002 /* 7457 */ + cror 4*cr0+eq,4*cr0+eq,4*cr1+eq /* Now test if any are true. */ + cror 4*cr0+eq,4*cr0+eq,4*cr2+eq + bne 2f + +1: mfspr r8,SPRN_L2CR /* On 745x, poll L2I bit (bit 10) */ + rlwinm. r9,r8,0,10,10 + bne 1b + b 3f + +2: mfspr r8,SPRN_L2CR /* On 75x & 74[01]0, poll L2IP bit (bit 31) */ + rlwinm. r9,r8,0,31,31 + bne 2b + +3: rlwinm r8,r8,0,11,9 /* Turn off L2I bit */ + sync + isync + mtspr SPRN_L2CR,r8 + sync + isync + blr + + .globl _setup_L3CR +_setup_L3CR: + /* Invalidate/disable L3 cache */ + sync + isync + mfspr r8,SPRN_L3CR + rlwinm r8,r8,0,1,31 + ori r8,r8,L3CR_L3I@l + sync + isync + mtspr SPRN_L3CR,r8 + sync + isync + + /* Wait for the invalidation to complete */ +1: mfspr r8,SPRN_L3CR + rlwinm. r9,r8,0,21,21 + bne 1b + + rlwinm r8,r8,0,22,20 /* Turn off L3I bit */ + sync + isync + mtspr SPRN_L3CR,r8 + sync + isync + blr + + +/* udelay (on non-601 processors) needs to know the period of the + * timebase in nanoseconds. This used to be hardcoded to be 60ns + * (period of 66MHz/4). Now a variable is used that is initialized to + * 60 for backward compatibility, but it can be overridden as necessary + * with code something like this: + * extern unsigned long timebase_period_ns; + * timebase_period_ns = 1000000000 / bd->bi_tbfreq; + */ + .data + .globl timebase_period_ns +timebase_period_ns: + .long 60 + + .text +/* + * Delay for a number of microseconds + */ + .globl udelay +udelay: + mfspr r4,SPRN_PVR + srwi r4,r4,16 + cmpwi 0,r4,1 /* 601 ? */ + bne .udelay_not_601 +00: li r0,86 /* Instructions / microsecond? */ + mtctr r0 +10: addi r0,r0,0 /* NOP */ + bdnz 10b + subic. r3,r3,1 + bne 00b + blr + +.udelay_not_601: + mulli r4,r3,1000 /* nanoseconds */ + /* Change r4 to be the number of ticks using: + * (nanoseconds + (timebase_period_ns - 1 )) / timebase_period_ns + * timebase_period_ns defaults to 60 (16.6MHz) */ + lis r5,timebase_period_ns@ha + lwz r5,timebase_period_ns@l(r5) + add r4,r4,r5 + addi r4,r4,-1 + divw r4,r4,r5 /* BUS ticks */ +1: mftbu r5 + mftb r6 + mftbu r7 + cmpw 0,r5,r7 + bne 1b /* Get [synced] base time */ + addc r9,r6,r4 /* Compute end time */ + addze r8,r5 +2: mftbu r5 + cmpw 0,r5,r8 + blt 2b + bgt 3f + mftb r6 + cmpw 0,r6,r9 + blt 2b +3: blr + + .section ".relocate_code","xa" +/* + * Flush and enable instruction cache + * First, flush the data cache in case it was enabled and may be + * holding instructions for copy back. + */ +_GLOBAL(flush_instruction_cache) + mflr r6 + bl flush_data_cache + +#ifdef CONFIG_8xx + lis r3, IDC_INVALL@h + mtspr SPRN_IC_CST, r3 + lis r3, IDC_ENABLE@h + mtspr SPRN_IC_CST, r3 + lis r3, IDC_DISABLE@h + mtspr SPRN_DC_CST, r3 +#elif CONFIG_4xx + lis r3,start@h # r9 = &_start + lis r4,_etext@ha + addi r4,r4,_etext@l # r8 = &_etext +1: dcbf r0,r3 # Flush the data cache + icbi r0,r3 # Invalidate the instruction cache + addi r3,r3,0x10 # Increment by one cache line + cmplwi cr0,r3,r4 # Are we at the end yet? + blt 1b # No, keep flushing and invalidating +#else + /* Enable, invalidate and then disable the L1 icache/dcache. */ + li r3,0 + ori r3,r3,(HID0_ICE|HID0_DCE|HID0_ICFI|HID0_DCI) + mfspr r4,SPRN_HID0 + or r5,r4,r3 + isync + mtspr SPRN_HID0,r5 + sync + isync + ori r5,r4,HID0_ICE /* Enable cache */ + mtspr SPRN_HID0,r5 + sync + isync +#endif + mtlr r6 + blr + +#define NUM_CACHE_LINES 128*8 +#define cache_flush_buffer 0x1000 + +/* + * Flush data cache + * Do this by just reading lots of stuff into the cache. + */ +_GLOBAL(flush_data_cache) + lis r3,cache_flush_buffer@h + ori r3,r3,cache_flush_buffer@l + li r4,NUM_CACHE_LINES + mtctr r4 +00: lwz r4,0(r3) + addi r3,r3,L1_CACHE_BYTES /* Next line, please */ + bdnz 00b +10: blr + + .previous + diff --git a/arch/ppc/boot/images/Makefile b/arch/ppc/boot/images/Makefile new file mode 100644 index 00000000000..774de8e2387 --- /dev/null +++ b/arch/ppc/boot/images/Makefile @@ -0,0 +1,27 @@ +# +# This dir holds all of the images for PPC machines. +# Tom Rini January 2001 + +MKIMAGE := $(srctree)/scripts/mkuboot.sh + +extra-y := vmlinux.bin vmlinux.gz + +OBJCOPYFLAGS_vmlinux.bin := -O binary +$(obj)/vmlinux.bin: vmlinux FORCE + $(call if_changed,objcopy) + +$(obj)/vmlinux.gz: $(obj)/vmlinux.bin FORCE + $(call if_changed,gzip) + +quiet_cmd_uimage = UIMAGE $@ + cmd_uimage = $(CONFIG_SHELL) $(MKIMAGE) -A ppc -O linux -T kernel \ + -C gzip -a 00000000 -e 00000000 -n 'Linux-$(KERNELRELEASE)' \ + -d $< $@ + +targets += uImage +$(obj)/uImage: $(obj)/vmlinux.gz + $(call if_changed,uimage) + @echo ' Image $@ is ready' + +# Files generated that shall be removed upon make clean +clean-files := sImage vmapus vmlinux* miboot* zImage* uImage diff --git a/arch/ppc/boot/include/cpc700.h b/arch/ppc/boot/include/cpc700.h new file mode 100644 index 00000000000..28cfcde4490 --- /dev/null +++ b/arch/ppc/boot/include/cpc700.h @@ -0,0 +1,26 @@ + +#ifndef __PPC_BOOT_CPC700_H +#define __PPC_BOOT_CPC700_H + +#define CPC700_MEM_CFGADDR 0xff500008 +#define CPC700_MEM_CFGDATA 0xff50000c + +#define CPC700_MB0SA 0x38 +#define CPC700_MB0EA 0x58 +#define CPC700_MB1SA 0x3c +#define CPC700_MB1EA 0x5c +#define CPC700_MB2SA 0x40 +#define CPC700_MB2EA 0x60 +#define CPC700_MB3SA 0x44 +#define CPC700_MB3EA 0x64 +#define CPC700_MB4SA 0x48 +#define CPC700_MB4EA 0x68 + +static inline long +cpc700_read_memreg(int reg) +{ + out_be32((volatile unsigned int *) CPC700_MEM_CFGADDR, reg); + return in_be32((volatile unsigned int *) CPC700_MEM_CFGDATA); +} + +#endif diff --git a/arch/ppc/boot/include/iso_font.h b/arch/ppc/boot/include/iso_font.h new file mode 100644 index 00000000000..bff050e002b --- /dev/null +++ b/arch/ppc/boot/include/iso_font.h @@ -0,0 +1,257 @@ +static const unsigned char font[] = { +/* 0x00 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0x01 */ 0x00,0x00,0x7E,0x81,0xA5,0x81,0x81,0xBD,0x99,0x81,0x81,0x7E,0x00,0x00,0x00,0x00, +/* 0x02 */ 0x00,0x00,0x7E,0xFF,0xDB,0xFF,0xFF,0xC3,0xC3,0xE7,0xFF,0x7E,0x00,0x00,0x00,0x00, +/* 0x03 */ 0x00,0x00,0x00,0x00,0x6C,0xFE,0xFE,0xFE,0xFE,0x7C,0x38,0x10,0x00,0x00,0x00,0x00, +/* 0x04 */ 0x00,0x00,0x00,0x00,0x10,0x38,0x7C,0xFE,0x7C,0x38,0x10,0x00,0x00,0x00,0x00,0x00, +/* 0x05 */ 0x00,0x00,0x00,0x18,0x3C,0x3C,0xE7,0xE7,0xE7,0x18,0x18,0x3C,0x00,0x00,0x00,0x00, +/* 0x06 */ 0x00,0x00,0x00,0x18,0x3C,0x7E,0xFF,0xFF,0x7E,0x18,0x18,0x3C,0x00,0x00,0x00,0x00, +/* 0x07 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x3C,0x3C,0x18,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0x08 */ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE7,0xC3,0xC3,0xE7,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +/* 0x09 */ 0x00,0x00,0x00,0x00,0x00,0x3C,0x66,0x42,0x42,0x66,0x3C,0x00,0x00,0x00,0x00,0x00, +/* 0x0A */ 0xFF,0xFF,0xFF,0xFF,0xFF,0xC3,0x99,0xBD,0xBD,0x99,0xC3,0xFF,0xFF,0xFF,0xFF,0xFF, +/* 0x0B */ 0x00,0x00,0x3E,0x0E,0x1A,0x32,0x78,0xCC,0xCC,0xCC,0xCC,0x78,0x00,0x00,0x00,0x00, +/* 0x0C */ 0x00,0x00,0x3C,0x66,0x66,0x66,0x66,0x3C,0x18,0x7E,0x18,0x18,0x00,0x00,0x00,0x00, +/* 0x0D */ 0x00,0x00,0x30,0x38,0x3C,0x36,0x33,0x30,0x30,0x70,0xF0,0xE0,0x00,0x00,0x00,0x00, +/* 0x0E */ 0x00,0x00,0x7F,0x63,0x7F,0x63,0x63,0x63,0x63,0x67,0xE7,0xE6,0xC0,0x00,0x00,0x00, +/* 0x0F */ 0x00,0x00,0x00,0x18,0x18,0xDB,0x3C,0xE7,0x3C,0xDB,0x18,0x18,0x00,0x00,0x00,0x00, +/* 0x10 */ 0x00,0x80,0xC0,0xE0,0xF0,0xF8,0xFE,0xF8,0xF0,0xE0,0xC0,0x80,0x00,0x00,0x00,0x00, +/* 0x11 */ 0x00,0x02,0x06,0x0E,0x1E,0x3E,0xFE,0x3E,0x1E,0x0E,0x06,0x02,0x00,0x00,0x00,0x00, +/* 0x12 */ 0x00,0x00,0x18,0x3C,0x7E,0x18,0x18,0x18,0x7E,0x3C,0x18,0x00,0x00,0x00,0x00,0x00, +/* 0x13 */ 0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x66,0x66,0x00,0x00,0x00,0x00, +/* 0x14 */ 0x00,0x00,0x7F,0xDB,0xDB,0xDB,0x7B,0x1B,0x1B,0x1B,0x1B,0x1B,0x00,0x00,0x00,0x00, +/* 0x15 */ 0x00,0x7C,0xC6,0x60,0x38,0x6C,0xC6,0xC6,0x6C,0x38,0x0C,0xC6,0x7C,0x00,0x00,0x00, +/* 0x16 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0xFE,0xFE,0xFE,0x00,0x00,0x00,0x00, +/* 0x17 */ 0x00,0x00,0x18,0x3C,0x7E,0x18,0x18,0x18,0x7E,0x3C,0x18,0x7E,0x00,0x00,0x00,0x00, +/* 0x18 */ 0x00,0x00,0x18,0x3C,0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00, +/* 0x19 */ 0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x3C,0x18,0x00,0x00,0x00,0x00, +/* 0x1A */ 0x00,0x00,0x00,0x00,0x00,0x18,0x0C,0xFE,0x0C,0x18,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0x1B */ 0x00,0x00,0x00,0x00,0x00,0x30,0x60,0xFE,0x60,0x30,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0x1C */ 0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0xC0,0xC0,0xFE,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0x1D */ 0x00,0x00,0x00,0x00,0x00,0x28,0x6C,0xFE,0x6C,0x28,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0x1E */ 0x00,0x00,0x00,0x00,0x10,0x38,0x38,0x7C,0x7C,0xFE,0xFE,0x00,0x00,0x00,0x00,0x00, +/* 0x1F */ 0x00,0x00,0x00,0x00,0xFE,0xFE,0x7C,0x7C,0x38,0x38,0x10,0x00,0x00,0x00,0x00,0x00, +/* 0x20 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0x21 */ 0x00,0x00,0x18,0x3C,0x3C,0x3C,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00, +/* 0x22 */ 0x00,0x66,0x66,0x66,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0x23 */ 0x00,0x00,0x00,0x6C,0x6C,0xFE,0x6C,0x6C,0x6C,0xFE,0x6C,0x6C,0x00,0x00,0x00,0x00, +/* 0x24 */ 0x18,0x18,0x7C,0xC6,0xC2,0xC0,0x7C,0x06,0x06,0x86,0xC6,0x7C,0x18,0x18,0x00,0x00, +/* 0x25 */ 0x00,0x00,0x00,0x00,0xC2,0xC6,0x0C,0x18,0x30,0x60,0xC6,0x86,0x00,0x00,0x00,0x00, +/* 0x26 */ 0x00,0x00,0x38,0x6C,0x6C,0x38,0x76,0xDC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00, +/* 0x27 */ 0x00,0x30,0x30,0x30,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0x28 */ 0x00,0x00,0x0C,0x18,0x30,0x30,0x30,0x30,0x30,0x30,0x18,0x0C,0x00,0x00,0x00,0x00, +/* 0x29 */ 0x00,0x00,0x30,0x18,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x18,0x30,0x00,0x00,0x00,0x00, +/* 0x2A */ 0x00,0x00,0x00,0x00,0x00,0x66,0x3C,0xFF,0x3C,0x66,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0x2B */ 0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0x2C */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x30,0x00,0x00,0x00, +/* 0x2D */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0x2E */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00, +/* 0x2F */ 0x00,0x00,0x00,0x00,0x02,0x06,0x0C,0x18,0x30,0x60,0xC0,0x80,0x00,0x00,0x00,0x00, +/* 0x30 */ 0x00,0x00,0x38,0x6C,0xC6,0xC6,0xD6,0xD6,0xC6,0xC6,0x6C,0x38,0x00,0x00,0x00,0x00, +/* 0x31 */ 0x00,0x00,0x18,0x38,0x78,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x00,0x00,0x00,0x00, +/* 0x32 */ 0x00,0x00,0x7C,0xC6,0x06,0x0C,0x18,0x30,0x60,0xC0,0xC6,0xFE,0x00,0x00,0x00,0x00, +/* 0x33 */ 0x00,0x00,0x7C,0xC6,0x06,0x06,0x3C,0x06,0x06,0x06,0xC6,0x7C,0x00,0x00,0x00,0x00, +/* 0x34 */ 0x00,0x00,0x0C,0x1C,0x3C,0x6C,0xCC,0xFE,0x0C,0x0C,0x0C,0x1E,0x00,0x00,0x00,0x00, +/* 0x35 */ 0x00,0x00,0xFE,0xC0,0xC0,0xC0,0xFC,0x06,0x06,0x06,0xC6,0x7C,0x00,0x00,0x00,0x00, +/* 0x36 */ 0x00,0x00,0x38,0x60,0xC0,0xC0,0xFC,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00, +/* 0x37 */ 0x00,0x00,0xFE,0xC6,0x06,0x06,0x0C,0x18,0x30,0x30,0x30,0x30,0x00,0x00,0x00,0x00, +/* 0x38 */ 0x00,0x00,0x7C,0xC6,0xC6,0xC6,0x7C,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00, +/* 0x39 */ 0x00,0x00,0x7C,0xC6,0xC6,0xC6,0x7E,0x06,0x06,0x06,0x0C,0x78,0x00,0x00,0x00,0x00, +/* 0x3A */ 0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00, +/* 0x3B */ 0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x30,0x00,0x00,0x00,0x00, +/* 0x3C */ 0x00,0x00,0x00,0x06,0x0C,0x18,0x30,0x60,0x30,0x18,0x0C,0x06,0x00,0x00,0x00,0x00, +/* 0x3D */ 0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0x3E */ 0x00,0x00,0x00,0x60,0x30,0x18,0x0C,0x06,0x0C,0x18,0x30,0x60,0x00,0x00,0x00,0x00, +/* 0x3F */ 0x00,0x00,0x7C,0xC6,0xC6,0x0C,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00, +/* 0x40 */ 0x00,0x00,0x00,0x7C,0xC6,0xC6,0xDE,0xDE,0xDE,0xDC,0xC0,0x7C,0x00,0x00,0x00,0x00, +/* 0x41 */ 0x00,0x00,0x10,0x38,0x6C,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00, +/* 0x42 */ 0x00,0x00,0xFC,0x66,0x66,0x66,0x7C,0x66,0x66,0x66,0x66,0xFC,0x00,0x00,0x00,0x00, +/* 0x43 */ 0x00,0x00,0x3C,0x66,0xC2,0xC0,0xC0,0xC0,0xC0,0xC2,0x66,0x3C,0x00,0x00,0x00,0x00, +/* 0x44 */ 0x00,0x00,0xF8,0x6C,0x66,0x66,0x66,0x66,0x66,0x66,0x6C,0xF8,0x00,0x00,0x00,0x00, +/* 0x45 */ 0x00,0x00,0xFE,0x66,0x62,0x68,0x78,0x68,0x60,0x62,0x66,0xFE,0x00,0x00,0x00,0x00, +/* 0x46 */ 0x00,0x00,0xFE,0x66,0x62,0x68,0x78,0x68,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00, +/* 0x47 */ 0x00,0x00,0x3C,0x66,0xC2,0xC0,0xC0,0xDE,0xC6,0xC6,0x66,0x3A,0x00,0x00,0x00,0x00, +/* 0x48 */ 0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00, +/* 0x49 */ 0x00,0x00,0x3C,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00, +/* 0x4A */ 0x00,0x00,0x1E,0x0C,0x0C,0x0C,0x0C,0x0C,0xCC,0xCC,0xCC,0x78,0x00,0x00,0x00,0x00, +/* 0x4B */ 0x00,0x00,0xE6,0x66,0x66,0x6C,0x78,0x78,0x6C,0x66,0x66,0xE6,0x00,0x00,0x00,0x00, +/* 0x4C */ 0x00,0x00,0xF0,0x60,0x60,0x60,0x60,0x60,0x60,0x62,0x66,0xFE,0x00,0x00,0x00,0x00, +/* 0x4D */ 0x00,0x00,0xC6,0xEE,0xFE,0xFE,0xD6,0xC6,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00, +/* 0x4E */ 0x00,0x00,0xC6,0xE6,0xF6,0xFE,0xDE,0xCE,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00, +/* 0x4F */ 0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00, +/* 0x50 */ 0x00,0x00,0xFC,0x66,0x66,0x66,0x7C,0x60,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00, +/* 0x51 */ 0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xD6,0xDE,0x7C,0x0C,0x0E,0x00,0x00, +/* 0x52 */ 0x00,0x00,0xFC,0x66,0x66,0x66,0x7C,0x6C,0x66,0x66,0x66,0xE6,0x00,0x00,0x00,0x00, +/* 0x53 */ 0x00,0x00,0x7C,0xC6,0xC6,0x60,0x38,0x0C,0x06,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00, +/* 0x54 */ 0x00,0x00,0x7E,0x7E,0x5A,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00, +/* 0x55 */ 0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00, +/* 0x56 */ 0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x6C,0x38,0x10,0x00,0x00,0x00,0x00, +/* 0x57 */ 0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xD6,0xD6,0xD6,0xFE,0xEE,0x6C,0x00,0x00,0x00,0x00, +/* 0x58 */ 0x00,0x00,0xC6,0xC6,0x6C,0x7C,0x38,0x38,0x7C,0x6C,0xC6,0xC6,0x00,0x00,0x00,0x00, +/* 0x59 */ 0x00,0x00,0x66,0x66,0x66,0x66,0x3C,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00, +/* 0x5A */ 0x00,0x00,0xFE,0xC6,0x86,0x0C,0x18,0x30,0x60,0xC2,0xC6,0xFE,0x00,0x00,0x00,0x00, +/* 0x5B */ 0x00,0x00,0x3C,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x3C,0x00,0x00,0x00,0x00, +/* 0x5C */ 0x00,0x00,0x00,0x80,0xC0,0xE0,0x70,0x38,0x1C,0x0E,0x06,0x02,0x00,0x00,0x00,0x00, +/* 0x5D */ 0x00,0x00,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00,0x00,0x00,0x00, +/* 0x5E */ 0x10,0x38,0x6C,0xC6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0x5F */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00, +/* 0x60 */ 0x30,0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0x61 */ 0x00,0x00,0x00,0x00,0x00,0x78,0x0C,0x7C,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00, +/* 0x62 */ 0x00,0x00,0xE0,0x60,0x60,0x78,0x6C,0x66,0x66,0x66,0x66,0x7C,0x00,0x00,0x00,0x00, +/* 0x63 */ 0x00,0x00,0x00,0x00,0x00,0x7C,0xC6,0xC0,0xC0,0xC0,0xC6,0x7C,0x00,0x00,0x00,0x00, +/* 0x64 */ 0x00,0x00,0x1C,0x0C,0x0C,0x3C,0x6C,0xCC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00, +/* 0x65 */ 0x00,0x00,0x00,0x00,0x00,0x7C,0xC6,0xFE,0xC0,0xC0,0xC6,0x7C,0x00,0x00,0x00,0x00, +/* 0x66 */ 0x00,0x00,0x38,0x6C,0x64,0x60,0xF0,0x60,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00, +/* 0x67 */ 0x00,0x00,0x00,0x00,0x00,0x3E,0x66,0x66,0x66,0x66,0x66,0x3E,0x06,0x66,0x3C,0x00, +/* 0x68 */ 0x00,0x00,0xE0,0x60,0x60,0x6C,0x76,0x66,0x66,0x66,0x66,0xE6,0x00,0x00,0x00,0x00, +/* 0x69 */ 0x00,0x00,0x18,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00, +/* 0x6A */ 0x00,0x00,0x06,0x06,0x00,0x0E,0x06,0x06,0x06,0x06,0x06,0x06,0x66,0x66,0x3C,0x00, +/* 0x6B */ 0x00,0x00,0xE0,0x60,0x60,0x66,0x6C,0x78,0x78,0x6C,0x66,0xE6,0x00,0x00,0x00,0x00, +/* 0x6C */ 0x00,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00, +/* 0x6D */ 0x00,0x00,0x00,0x00,0x00,0x6C,0xFE,0xD6,0xD6,0xD6,0xC6,0xC6,0x00,0x00,0x00,0x00, +/* 0x6E */ 0x00,0x00,0x00,0x00,0x00,0xDC,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x00,0x00,0x00, +/* 0x6F */ 0x00,0x00,0x00,0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00, +/* 0x70 */ 0x00,0x00,0x00,0x00,0x00,0xFC,0x66,0x66,0x66,0x66,0x66,0x7C,0x60,0x60,0xF0,0x00, +/* 0x71 */ 0x00,0x00,0x00,0x00,0x00,0x7E,0xCC,0xCC,0xCC,0xCC,0xCC,0x7C,0x0C,0x0C,0x1E,0x00, +/* 0x72 */ 0x00,0x00,0x00,0x00,0x00,0xDC,0x76,0x66,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00, +/* 0x73 */ 0x00,0x00,0x00,0x00,0x00,0x7C,0xC6,0x60,0x38,0x0C,0xC6,0x7C,0x00,0x00,0x00,0x00, +/* 0x74 */ 0x00,0x00,0x10,0x30,0x30,0xFC,0x30,0x30,0x30,0x30,0x36,0x1C,0x00,0x00,0x00,0x00, +/* 0x75 */ 0x00,0x00,0x00,0x00,0x00,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00, +/* 0x76 */ 0x00,0x00,0x00,0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x3C,0x18,0x00,0x00,0x00,0x00, +/* 0x77 */ 0x00,0x00,0x00,0x00,0x00,0xC6,0xC6,0xD6,0xD6,0xD6,0xFE,0x6C,0x00,0x00,0x00,0x00, +/* 0x78 */ 0x00,0x00,0x00,0x00,0x00,0xC6,0x6C,0x38,0x38,0x38,0x6C,0xC6,0x00,0x00,0x00,0x00, +/* 0x79 */ 0x00,0x00,0x00,0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7E,0x06,0x0C,0xF8,0x00, +/* 0x7A */ 0x00,0x00,0x00,0x00,0x00,0xFE,0xCC,0x18,0x30,0x60,0xC6,0xFE,0x00,0x00,0x00,0x00, +/* 0x7B */ 0x00,0x00,0x0E,0x18,0x18,0x18,0x70,0x18,0x18,0x18,0x18,0x0E,0x00,0x00,0x00,0x00, +/* 0x7C */ 0x00,0x00,0x18,0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00, +/* 0x7D */ 0x00,0x00,0x70,0x18,0x18,0x18,0x0E,0x18,0x18,0x18,0x18,0x70,0x00,0x00,0x00,0x00, +/* 0x7E */ 0x00,0x00,0x76,0xDC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0x7F */ 0x00,0x00,0x00,0x00,0x10,0x38,0x6C,0xC6,0xC6,0xC6,0xFE,0x00,0x00,0x00,0x00,0x00, +/* 0x80 */ 0x00,0x00,0x3C,0x66,0xC2,0xC0,0xC0,0xC0,0xC2,0x66,0x3C,0x0C,0x06,0x7C,0x00,0x00, +/* 0x81 */ 0x00,0x00,0xCC,0x00,0x00,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00, +/* 0x82 */ 0x00,0x0C,0x18,0x30,0x00,0x7C,0xC6,0xC6,0xFE,0xC0,0xC6,0x7C,0x00,0x00,0x00,0x00, +/* 0x83 */ 0x00,0x10,0x38,0x6C,0x00,0x78,0x0C,0x7C,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00, +/* 0x84 */ 0x00,0x00,0xCC,0x00,0x00,0x78,0x0C,0x7C,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00, +/* 0x85 */ 0x00,0x60,0x30,0x18,0x00,0x78,0x0C,0x7C,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00, +/* 0x86 */ 0x00,0x38,0x6C,0x38,0x00,0x78,0x0C,0x7C,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00, +/* 0x87 */ 0x00,0x00,0x00,0x00,0x3C,0x66,0x60,0x60,0x66,0x3C,0x0C,0x06,0x3C,0x00,0x00,0x00, +/* 0x88 */ 0x00,0x10,0x38,0x6C,0x00,0x7C,0xC6,0xC6,0xFE,0xC0,0xC6,0x7C,0x00,0x00,0x00,0x00, +/* 0x89 */ 0x00,0x00,0xC6,0x00,0x00,0x7C,0xC6,0xC6,0xFE,0xC0,0xC6,0x7C,0x00,0x00,0x00,0x00, +/* 0x8A */ 0x00,0x60,0x30,0x18,0x00,0x7C,0xC6,0xC6,0xFE,0xC0,0xC6,0x7C,0x00,0x00,0x00,0x00, +/* 0x8B */ 0x00,0x00,0x66,0x00,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00, +/* 0x8C */ 0x00,0x18,0x3C,0x66,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00, +/* 0x8D */ 0x00,0x60,0x30,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00, +/* 0x8E */ 0x00,0xC6,0x00,0x10,0x38,0x6C,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00, +/* 0x8F */ 0x38,0x6C,0x38,0x00,0x38,0x6C,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00, +/* 0x90 */ 0x18,0x30,0x60,0x00,0xFE,0x66,0x60,0x7C,0x60,0x60,0x66,0xFE,0x00,0x00,0x00,0x00, +/* 0x91 */ 0x00,0x00,0x00,0x00,0x00,0xCC,0x76,0x36,0x7E,0xD8,0xD8,0x6E,0x00,0x00,0x00,0x00, +/* 0x92 */ 0x00,0x00,0x3E,0x6C,0xCC,0xCC,0xFE,0xCC,0xCC,0xCC,0xCC,0xCE,0x00,0x00,0x00,0x00, +/* 0x93 */ 0x00,0x10,0x38,0x6C,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00, +/* 0x94 */ 0x00,0x00,0xC6,0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00, +/* 0x95 */ 0x00,0x60,0x30,0x18,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00, +/* 0x96 */ 0x00,0x30,0x78,0xCC,0x00,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00, +/* 0x97 */ 0x00,0x60,0x30,0x18,0x00,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00, +/* 0x98 */ 0x00,0x00,0xC6,0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7E,0x06,0x0C,0x78,0x00, +/* 0x99 */ 0x00,0xC6,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00, +/* 0x9A */ 0x00,0xC6,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00, +/* 0x9B */ 0x00,0x18,0x18,0x3C,0x66,0x60,0x60,0x60,0x66,0x3C,0x18,0x18,0x00,0x00,0x00,0x00, +/* 0x9C */ 0x00,0x38,0x6C,0x64,0x60,0xF8,0x60,0x60,0x60,0x60,0xE6,0xFC,0x00,0x00,0x00,0x00, +/* 0x9D */ 0x00,0x00,0x66,0x66,0x3C,0x18,0x7E,0x18,0x7E,0x18,0x18,0x18,0x00,0x00,0x00,0x00, +/* 0x9E */ 0x00,0xF8,0xCC,0xCC,0xF8,0xC4,0xCC,0xDE,0xCC,0xCC,0xCC,0xC6,0x00,0x00,0x00,0x00, +/* 0x9F */ 0x00,0x0E,0x1B,0x18,0x18,0x18,0x7E,0x18,0x18,0x18,0x18,0x18,0xD8,0x70,0x00,0x00, +/* 0xA0 */ 0x00,0x18,0x30,0x60,0x00,0x78,0x0C,0x7C,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00, +/* 0xA1 */ 0x00,0x0C,0x18,0x30,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00, +/* 0xA2 */ 0x00,0x18,0x30,0x60,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00, +/* 0xA3 */ 0x00,0x18,0x30,0x60,0x00,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00, +/* 0xA4 */ 0x00,0x00,0x76,0xDC,0x00,0xDC,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x00,0x00,0x00, +/* 0xA5 */ 0x76,0xDC,0x00,0xC6,0xE6,0xF6,0xFE,0xDE,0xCE,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00, +/* 0xA6 */ 0x00,0x3C,0x6C,0x6C,0x3E,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0xA7 */ 0x00,0x38,0x6C,0x6C,0x38,0x00,0x7C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0xA8 */ 0x00,0x00,0x30,0x30,0x00,0x30,0x30,0x60,0xC0,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00, +/* 0xA9 */ 0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0xC0,0xC0,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00, +/* 0xAA */ 0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x06,0x06,0x06,0x06,0x00,0x00,0x00,0x00,0x00, +/* 0xAB */ 0x00,0xC0,0xC0,0xC2,0xC6,0xCC,0x18,0x30,0x60,0xDC,0x86,0x0C,0x18,0x3E,0x00,0x00, +/* 0xAC */ 0x00,0xC0,0xC0,0xC2,0xC6,0xCC,0x18,0x30,0x66,0xCE,0x9E,0x3E,0x06,0x06,0x00,0x00, +/* 0xAD */ 0x00,0x00,0x18,0x18,0x00,0x18,0x18,0x18,0x3C,0x3C,0x3C,0x18,0x00,0x00,0x00,0x00, +/* 0xAE */ 0x00,0x00,0x00,0x00,0x00,0x36,0x6C,0xD8,0x6C,0x36,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0xAF */ 0x00,0x00,0x00,0x00,0x00,0xD8,0x6C,0x36,0x6C,0xD8,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0xB0 */ 0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44, +/* 0xB1 */ 0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA, +/* 0xB2 */ 0xDD,0x77,0xDD,0x77,0xDD,0x77,0xDD,0x77,0xDD,0x77,0xDD,0x77,0xDD,0x77,0xDD,0x77, +/* 0xB3 */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* 0xB4 */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xF8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* 0xB5 */ 0x18,0x18,0x18,0x18,0x18,0xF8,0x18,0xF8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* 0xB6 */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xF6,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* 0xB7 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* 0xB8 */ 0x00,0x00,0x00,0x00,0x00,0xF8,0x18,0xF8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* 0xB9 */ 0x36,0x36,0x36,0x36,0x36,0xF6,0x06,0xF6,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* 0xBA */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* 0xBB */ 0x00,0x00,0x00,0x00,0x00,0xFE,0x06,0xF6,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* 0xBC */ 0x36,0x36,0x36,0x36,0x36,0xF6,0x06,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0xBD */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0xBE */ 0x18,0x18,0x18,0x18,0x18,0xF8,0x18,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0xBF */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* 0xC0 */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0xC1 */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0xC2 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* 0xC3 */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1F,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* 0xC4 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0xC5 */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xFF,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* 0xC6 */ 0x18,0x18,0x18,0x18,0x18,0x1F,0x18,0x1F,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* 0xC7 */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x37,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* 0xC8 */ 0x36,0x36,0x36,0x36,0x36,0x37,0x30,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0xC9 */ 0x00,0x00,0x00,0x00,0x00,0x3F,0x30,0x37,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* 0xCA */ 0x36,0x36,0x36,0x36,0x36,0xF7,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0xCB */ 0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0xF7,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* 0xCC */ 0x36,0x36,0x36,0x36,0x36,0x37,0x30,0x37,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* 0xCD */ 0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0xCE */ 0x36,0x36,0x36,0x36,0x36,0xF7,0x00,0xF7,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* 0xCF */ 0x18,0x18,0x18,0x18,0x18,0xFF,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0xD0 */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0xD1 */ 0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0xFF,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* 0xD2 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* 0xD3 */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0xD4 */ 0x18,0x18,0x18,0x18,0x18,0x1F,0x18,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0xD5 */ 0x00,0x00,0x00,0x00,0x00,0x1F,0x18,0x1F,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* 0xD6 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* 0xD7 */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xFF,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* 0xD8 */ 0x18,0x18,0x18,0x18,0x18,0xFF,0x18,0xFF,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* 0xD9 */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0xDA */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* 0xDB */ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +/* 0xDC */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +/* 0xDD */ 0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0, +/* 0xDE */ 0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, +/* 0xDF */ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0xE0 */ 0x00,0x00,0x00,0x00,0x00,0x76,0xDC,0xD8,0xD8,0xD8,0xDC,0x76,0x00,0x00,0x00,0x00, +/* 0xE1 */ 0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xFC,0xC6,0xC6,0xC6,0xC6,0xDC,0xC0,0xC0,0x00,0x00, +/* 0xE2 */ 0x00,0x00,0xFE,0xC6,0xC6,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0x00,0x00,0x00,0x00, +/* 0xE3 */ 0x00,0x00,0x00,0x00,0x00,0xFE,0x6C,0x6C,0x6C,0x6C,0x6C,0x6C,0x00,0x00,0x00,0x00, +/* 0xE4 */ 0x00,0x00,0xFE,0xC6,0x60,0x30,0x18,0x18,0x30,0x60,0xC6,0xFE,0x00,0x00,0x00,0x00, +/* 0xE5 */ 0x00,0x00,0x00,0x00,0x00,0x7E,0xD8,0xD8,0xD8,0xD8,0xD8,0x70,0x00,0x00,0x00,0x00, +/* 0xE6 */ 0x00,0x00,0x00,0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x7C,0x60,0xC0,0x00,0x00,0x00, +/* 0xE7 */ 0x00,0x00,0x00,0x00,0x00,0x76,0xDC,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00, +/* 0xE8 */ 0x00,0x00,0x7E,0x18,0x3C,0x66,0x66,0x66,0x66,0x3C,0x18,0x7E,0x00,0x00,0x00,0x00, +/* 0xE9 */ 0x00,0x00,0x38,0x6C,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0x6C,0x38,0x00,0x00,0x00,0x00, +/* 0xEA */ 0x00,0x00,0x38,0x6C,0xC6,0xC6,0xC6,0x6C,0x6C,0x6C,0x6C,0xEE,0x00,0x00,0x00,0x00, +/* 0xEB */ 0x00,0x00,0x1E,0x30,0x18,0x0C,0x3E,0x66,0x66,0x66,0x66,0x3C,0x00,0x00,0x00,0x00, +/* 0xEC */ 0x00,0x00,0x00,0x00,0x00,0x7E,0xDB,0xDB,0xDB,0x7E,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0xED */ 0x00,0x00,0x00,0x03,0x06,0x7E,0xDB,0xDB,0xF3,0x7E,0x60,0xC0,0x00,0x00,0x00,0x00, +/* 0xEE */ 0x00,0x00,0x1C,0x30,0x60,0x60,0x7C,0x60,0x60,0x60,0x30,0x1C,0x00,0x00,0x00,0x00, +/* 0xEF */ 0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00, +/* 0xF0 */ 0x00,0x00,0x00,0x00,0xFE,0x00,0x00,0xFE,0x00,0x00,0xFE,0x00,0x00,0x00,0x00,0x00, +/* 0xF1 */ 0x00,0x00,0x00,0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00,0xFF,0x00,0x00,0x00,0x00, +/* 0xF2 */ 0x00,0x00,0x00,0x30,0x18,0x0C,0x06,0x0C,0x18,0x30,0x00,0x7E,0x00,0x00,0x00,0x00, +/* 0xF3 */ 0x00,0x00,0x00,0x0C,0x18,0x30,0x60,0x30,0x18,0x0C,0x00,0x7E,0x00,0x00,0x00,0x00, +/* 0xF4 */ 0x00,0x0E,0x1B,0x1B,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* 0xF5 */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xD8,0xD8,0xD8,0x70,0x00,0x00,0x00,0x00, +/* 0xF6 */ 0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x7E,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00, +/* 0xF7 */ 0x00,0x00,0x00,0x00,0x00,0x76,0xDC,0x00,0x76,0xDC,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0xF8 */ 0x00,0x38,0x6C,0x6C,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0xF9 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0xFA */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0xFB */ 0x00,0x0F,0x0C,0x0C,0x0C,0x0C,0x0C,0xEC,0x6C,0x6C,0x3C,0x1C,0x00,0x00,0x00,0x00, +/* 0xFC */ 0x00,0xD8,0x6C,0x6C,0x6C,0x6C,0x6C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0xFD */ 0x00,0x70,0xD8,0x30,0x60,0xC8,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 0xFE */ 0x00,0x00,0x00,0x00,0x7C,0x7C,0x7C,0x7C,0x7C,0x7C,0x7C,0x00,0x00,0x00,0x00,0x00, +}; diff --git a/arch/ppc/boot/include/mpc10x.h b/arch/ppc/boot/include/mpc10x.h new file mode 100644 index 00000000000..6cd40ecabc7 --- /dev/null +++ b/arch/ppc/boot/include/mpc10x.h @@ -0,0 +1,65 @@ +/* + * arch/ppc/boot/include/mpc10.h + * + * Common defines for the Motorola SPS MPC106/8240/107 Host bridge/Mem + * ctrl/EPIC/etc. + * + * Author: Tom Rini + * + * This is a heavily stripped down version of: + * include/asm-ppc/mpc10x.h + * + * Author: Mark A. Greer + * mgreer@mvista.com + * + * 2001-2002 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#ifndef __BOOT_MPC10X_H__ +#define __BOOT_MPC10X_H__ + +/* + * The values here don't completely map everything but should work in most + * cases. + * + * MAP A (PReP Map) + * Processor: 0x80000000 - 0x807fffff -> PCI I/O: 0x00000000 - 0x007fffff + * Processor: 0xc0000000 - 0xdfffffff -> PCI MEM: 0x00000000 - 0x1fffffff + * PCI MEM: 0x80000000 -> Processor System Memory: 0x00000000 + * EUMB mapped to: ioremap_base - 0x00100000 (ioremap_base - 1 MB) + * + * MAP B (CHRP Map) + * Processor: 0xfe000000 - 0xfebfffff -> PCI I/O: 0x00000000 - 0x00bfffff + * Processor: 0x80000000 - 0xbfffffff -> PCI MEM: 0x80000000 - 0xbfffffff + * PCI MEM: 0x00000000 -> Processor System Memory: 0x00000000 + * EUMB mapped to: ioremap_base - 0x00100000 (ioremap_base - 1 MB) + */ + +/* Define the type of map to use */ +#define MPC10X_MEM_MAP_A 1 +#define MPC10X_MEM_MAP_B 2 + +/* Map A (PReP Map) Defines */ +#define MPC10X_MAPA_CNFG_ADDR 0x80000cf8 +#define MPC10X_MAPA_CNFG_DATA 0x80000cfc + +/* Map B (CHRP Map) Defines */ +#define MPC10X_MAPB_CNFG_ADDR 0xfec00000 +#define MPC10X_MAPB_CNFG_DATA 0xfee00000 + +/* Define offsets for the memory controller registers in the config space */ +#define MPC10X_MCTLR_MEM_START_1 0x80 /* Banks 0-3 */ +#define MPC10X_MCTLR_MEM_START_2 0x84 /* Banks 4-7 */ +#define MPC10X_MCTLR_EXT_MEM_START_1 0x88 /* Banks 0-3 */ +#define MPC10X_MCTLR_EXT_MEM_START_2 0x8c /* Banks 4-7 */ + +#define MPC10X_MCTLR_MEM_END_1 0x90 /* Banks 0-3 */ +#define MPC10X_MCTLR_MEM_END_2 0x94 /* Banks 4-7 */ +#define MPC10X_MCTLR_EXT_MEM_END_1 0x98 /* Banks 0-3 */ +#define MPC10X_MCTLR_EXT_MEM_END_2 0x9c /* Banks 4-7 */ + +#define MPC10X_MCTLR_MEM_BANK_ENABLES 0xa0 + +#endif /* __BOOT_MPC10X_H__ */ diff --git a/arch/ppc/boot/include/mpsc_defs.h b/arch/ppc/boot/include/mpsc_defs.h new file mode 100644 index 00000000000..2ce7bbba727 --- /dev/null +++ b/arch/ppc/boot/include/mpsc_defs.h @@ -0,0 +1,146 @@ +/* + * drivers/serial/mpsc/mpsc_defs.h + * + * Register definitions for the Marvell Multi-Protocol Serial Controller (MPSC), + * Serial DMA Controller (SDMA), and Baud Rate Generator (BRG). + * + * Author: Mark A. Greer + * + * 2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#ifndef _PPC_BOOT_MPSC_DEFS_H__ +#define _PPC_BOOT_MPSC_DEFS_H__ + +#define MPSC_NUM_CTLRS 2 + +/* + ***************************************************************************** + * + * Multi-Protocol Serial Controller Interface Registers + * + ***************************************************************************** + */ + +/* Main Configuratino Register Offsets */ +#define MPSC_MMCRL 0x0000 +#define MPSC_MMCRH 0x0004 +#define MPSC_MPCR 0x0008 +#define MPSC_CHR_1 0x000c +#define MPSC_CHR_2 0x0010 +#define MPSC_CHR_3 0x0014 +#define MPSC_CHR_4 0x0018 +#define MPSC_CHR_5 0x001c +#define MPSC_CHR_6 0x0020 +#define MPSC_CHR_7 0x0024 +#define MPSC_CHR_8 0x0028 +#define MPSC_CHR_9 0x002c +#define MPSC_CHR_10 0x0030 +#define MPSC_CHR_11 0x0034 + +#define MPSC_MPCR_CL_5 0 +#define MPSC_MPCR_CL_6 1 +#define MPSC_MPCR_CL_7 2 +#define MPSC_MPCR_CL_8 3 +#define MPSC_MPCR_SBL_1 0 +#define MPSC_MPCR_SBL_2 3 + +#define MPSC_CHR_2_TEV (1<<1) +#define MPSC_CHR_2_TA (1<<7) +#define MPSC_CHR_2_TTCS (1<<9) +#define MPSC_CHR_2_REV (1<<17) +#define MPSC_CHR_2_RA (1<<23) +#define MPSC_CHR_2_CRD (1<<25) +#define MPSC_CHR_2_EH (1<<31) +#define MPSC_CHR_2_PAR_ODD 0 +#define MPSC_CHR_2_PAR_SPACE 1 +#define MPSC_CHR_2_PAR_EVEN 2 +#define MPSC_CHR_2_PAR_MARK 3 + +/* MPSC Signal Routing */ +#define MPSC_MRR 0x0000 +#define MPSC_RCRR 0x0004 +#define MPSC_TCRR 0x0008 + +/* + ***************************************************************************** + * + * Serial DMA Controller Interface Registers + * + ***************************************************************************** + */ + +#define SDMA_SDC 0x0000 +#define SDMA_SDCM 0x0008 +#define SDMA_RX_DESC 0x0800 +#define SDMA_RX_BUF_PTR 0x0808 +#define SDMA_SCRDP 0x0810 +#define SDMA_TX_DESC 0x0c00 +#define SDMA_SCTDP 0x0c10 +#define SDMA_SFTDP 0x0c14 + +#define SDMA_DESC_CMDSTAT_PE (1<<0) +#define SDMA_DESC_CMDSTAT_CDL (1<<1) +#define SDMA_DESC_CMDSTAT_FR (1<<3) +#define SDMA_DESC_CMDSTAT_OR (1<<6) +#define SDMA_DESC_CMDSTAT_BR (1<<9) +#define SDMA_DESC_CMDSTAT_MI (1<<10) +#define SDMA_DESC_CMDSTAT_A (1<<11) +#define SDMA_DESC_CMDSTAT_AM (1<<12) +#define SDMA_DESC_CMDSTAT_CT (1<<13) +#define SDMA_DESC_CMDSTAT_C (1<<14) +#define SDMA_DESC_CMDSTAT_ES (1<<15) +#define SDMA_DESC_CMDSTAT_L (1<<16) +#define SDMA_DESC_CMDSTAT_F (1<<17) +#define SDMA_DESC_CMDSTAT_P (1<<18) +#define SDMA_DESC_CMDSTAT_EI (1<<23) +#define SDMA_DESC_CMDSTAT_O (1<<31) + +#define SDMA_DESC_DFLT (SDMA_DESC_CMDSTAT_O | \ + SDMA_DESC_CMDSTAT_EI) + +#define SDMA_SDC_RFT (1<<0) +#define SDMA_SDC_SFM (1<<1) +#define SDMA_SDC_BLMR (1<<6) +#define SDMA_SDC_BLMT (1<<7) +#define SDMA_SDC_POVR (1<<8) +#define SDMA_SDC_RIFB (1<<9) + +#define SDMA_SDCM_ERD (1<<7) +#define SDMA_SDCM_AR (1<<15) +#define SDMA_SDCM_STD (1<<16) +#define SDMA_SDCM_TXD (1<<23) +#define SDMA_SDCM_AT (1<<31) + +#define SDMA_0_CAUSE_RXBUF (1<<0) +#define SDMA_0_CAUSE_RXERR (1<<1) +#define SDMA_0_CAUSE_TXBUF (1<<2) +#define SDMA_0_CAUSE_TXEND (1<<3) +#define SDMA_1_CAUSE_RXBUF (1<<8) +#define SDMA_1_CAUSE_RXERR (1<<9) +#define SDMA_1_CAUSE_TXBUF (1<<10) +#define SDMA_1_CAUSE_TXEND (1<<11) + +#define SDMA_CAUSE_RX_MASK (SDMA_0_CAUSE_RXBUF | SDMA_0_CAUSE_RXERR | \ + SDMA_1_CAUSE_RXBUF | SDMA_1_CAUSE_RXERR) +#define SDMA_CAUSE_TX_MASK (SDMA_0_CAUSE_TXBUF | SDMA_0_CAUSE_TXEND | \ + SDMA_1_CAUSE_TXBUF | SDMA_1_CAUSE_TXEND) + +/* SDMA Interrupt registers */ +#define SDMA_INTR_CAUSE 0x0000 +#define SDMA_INTR_MASK 0x0080 + +/* + ***************************************************************************** + * + * Baud Rate Generator Interface Registers + * + ***************************************************************************** + */ + +#define BRG_BCR 0x0000 +#define BRG_BTR 0x0004 + +#endif /*_PPC_BOOT_MPSC_DEFS_H__ */ diff --git a/arch/ppc/boot/include/nonstdio.h b/arch/ppc/boot/include/nonstdio.h new file mode 100644 index 00000000000..f2b5526faef --- /dev/null +++ b/arch/ppc/boot/include/nonstdio.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * This is sort of a catchall for I/O related functions. Stuff that + * wouldn't be in 'stdio.h' normally is here, and it's 'nonstdio.h' + * for a reason. -- Tom + */ +typedef int FILE; +extern FILE *stdin, *stdout; +#define NULL ((void *)0) +#define EOF (-1) +#define fopen(n, m) NULL +#define fflush(f) 0 +#define fclose(f) 0 +#define perror(s) printf("%s: no files!\n", (s)) + +extern int getc(void); +extern int printf(const char *format, ...); +extern int sprintf(char *str, const char *format, ...); +extern int tstc(void); +extern void exit(void); +extern void outb(int port, unsigned char val); +extern void putc(const char c); +extern void puthex(unsigned long val); +extern void puts(const char *); +extern void udelay(long delay); +extern unsigned char inb(int port); +extern void board_isa_init(void); +extern void ISA_init(unsigned long base); diff --git a/arch/ppc/boot/include/of1275.h b/arch/ppc/boot/include/of1275.h new file mode 100644 index 00000000000..69173df76db --- /dev/null +++ b/arch/ppc/boot/include/of1275.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * Copyright (C) Leigh Brown 2002. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +typedef void *prom_handle; +typedef void *ihandle; +typedef void *phandle; +typedef int (*prom_entry)(void *); + +#define OF_INVALID_HANDLE ((prom_handle)-1UL) + +extern prom_entry of_prom_entry; + +/* function declarations */ + +void * claim(unsigned int virt, unsigned int size, unsigned int align); +int map(unsigned int phys, unsigned int virt, unsigned int size); +void enter(void); +void exit(void); +phandle finddevice(const char *name); +int getprop(phandle node, const char *name, void *buf, int buflen); +void ofinit(prom_entry entry); +int ofstdio(ihandle *stdin, ihandle *stdout, ihandle *stderr); +int read(ihandle instance, void *buf, int buflen); +void release(void *virt, unsigned int size); +int write(ihandle instance, void *buf, int buflen); + +/* inlines */ + +extern inline void pause(void) +{ + enter(); +} diff --git a/arch/ppc/boot/include/rs6000.h b/arch/ppc/boot/include/rs6000.h new file mode 100644 index 00000000000..433f45084e4 --- /dev/null +++ b/arch/ppc/boot/include/rs6000.h @@ -0,0 +1,243 @@ +/* IBM RS/6000 "XCOFF" file definitions for BFD. + Copyright (C) 1990, 1991 Free Software Foundation, Inc. + FIXME: Can someone provide a transliteration of this name into ASCII? + Using the following chars caused a compiler warning on HIUX (so I replaced + them with octal escapes), and isn't useful without an understanding of what + character set it is. + Written by Mimi Ph\373\364ng-Th\345o V\365 of IBM + and John Gilmore of Cygnus Support. */ + +/********************** FILE HEADER **********************/ + +struct external_filehdr { + char f_magic[2]; /* magic number */ + char f_nscns[2]; /* number of sections */ + char f_timdat[4]; /* time & date stamp */ + char f_symptr[4]; /* file pointer to symtab */ + char f_nsyms[4]; /* number of symtab entries */ + char f_opthdr[2]; /* sizeof(optional hdr) */ + char f_flags[2]; /* flags */ +}; + + /* IBM RS/6000 */ +#define U802WRMAGIC 0730 /* writeable text segments **chh** */ +#define U802ROMAGIC 0735 /* readonly sharable text segments */ +#define U802TOCMAGIC 0737 /* readonly text segments and TOC */ + +#define BADMAG(x) \ + ((x).f_magic != U802ROMAGIC && (x).f_magic != U802WRMAGIC && \ + (x).f_magic != U802TOCMAGIC) + +#define FILHDR struct external_filehdr +#define FILHSZ 20 + + +/********************** AOUT "OPTIONAL HEADER" **********************/ + + +typedef struct +{ + unsigned char magic[2]; /* type of file */ + unsigned char vstamp[2]; /* version stamp */ + unsigned char tsize[4]; /* text size in bytes, padded to FW bdry */ + unsigned char dsize[4]; /* initialized data " " */ + unsigned char bsize[4]; /* uninitialized data " " */ + unsigned char entry[4]; /* entry pt. */ + unsigned char text_start[4]; /* base of text used for this file */ + unsigned char data_start[4]; /* base of data used for this file */ + unsigned char o_toc[4]; /* address of TOC */ + unsigned char o_snentry[2]; /* section number of entry point */ + unsigned char o_sntext[2]; /* section number of .text section */ + unsigned char o_sndata[2]; /* section number of .data section */ + unsigned char o_sntoc[2]; /* section number of TOC */ + unsigned char o_snloader[2]; /* section number of .loader section */ + unsigned char o_snbss[2]; /* section number of .bss section */ + unsigned char o_algntext[2]; /* .text alignment */ + unsigned char o_algndata[2]; /* .data alignment */ + unsigned char o_modtype[2]; /* module type (??) */ + unsigned char o_cputype[2]; /* cpu type */ + unsigned char o_maxstack[4]; /* max stack size (??) */ + unsigned char o_maxdata[4]; /* max data size (??) */ + unsigned char o_resv2[12]; /* reserved */ +} +AOUTHDR; + +#define AOUTSZ 72 +#define SMALL_AOUTSZ (28) +#define AOUTHDRSZ 72 + +#define RS6K_AOUTHDR_OMAGIC 0x0107 /* old: text & data writeable */ +#define RS6K_AOUTHDR_NMAGIC 0x0108 /* new: text r/o, data r/w */ +#define RS6K_AOUTHDR_ZMAGIC 0x010B /* paged: text r/o, both page-aligned */ + + +/********************** SECTION HEADER **********************/ + + +struct external_scnhdr { + char s_name[8]; /* section name */ + char s_paddr[4]; /* physical address, aliased s_nlib */ + char s_vaddr[4]; /* virtual address */ + char s_size[4]; /* section size */ + char s_scnptr[4]; /* file ptr to raw data for section */ + char s_relptr[4]; /* file ptr to relocation */ + char s_lnnoptr[4]; /* file ptr to line numbers */ + char s_nreloc[2]; /* number of relocation entries */ + char s_nlnno[2]; /* number of line number entries*/ + char s_flags[4]; /* flags */ +}; + +/* + * names of "special" sections + */ +#define _TEXT ".text" +#define _DATA ".data" +#define _BSS ".bss" +#define _PAD ".pad" +#define _LOADER ".loader" + +#define SCNHDR struct external_scnhdr +#define SCNHSZ 40 + +/* XCOFF uses a special .loader section with type STYP_LOADER. */ +#define STYP_LOADER 0x1000 + +/* XCOFF uses a special .debug section with type STYP_DEBUG. */ +#define STYP_DEBUG 0x2000 + +/* XCOFF handles line number or relocation overflow by creating + another section header with STYP_OVRFLO set. */ +#define STYP_OVRFLO 0x8000 + +/********************** LINE NUMBERS **********************/ + +/* 1 line number entry for every "breakpointable" source line in a section. + * Line numbers are grouped on a per function basis; first entry in a function + * grouping will have l_lnno = 0 and in place of physical address will be the + * symbol table index of the function name. + */ +struct external_lineno { + union { + char l_symndx[4]; /* function name symbol index, iff l_lnno == 0*/ + char l_paddr[4]; /* (physical) address of line number */ + } l_addr; + char l_lnno[2]; /* line number */ +}; + + +#define LINENO struct external_lineno +#define LINESZ 6 + + +/********************** SYMBOLS **********************/ + +#define E_SYMNMLEN 8 /* # characters in a symbol name */ +#define E_FILNMLEN 14 /* # characters in a file name */ +#define E_DIMNUM 4 /* # array dimensions in auxiliary entry */ + +struct external_syment +{ + union { + char e_name[E_SYMNMLEN]; + struct { + char e_zeroes[4]; + char e_offset[4]; + } e; + } e; + char e_value[4]; + char e_scnum[2]; + char e_type[2]; + char e_sclass[1]; + char e_numaux[1]; +}; + + + +#define N_BTMASK (017) +#define N_TMASK (060) +#define N_BTSHFT (4) +#define N_TSHIFT (2) + + +union external_auxent { + struct { + char x_tagndx[4]; /* str, un, or enum tag indx */ + union { + struct { + char x_lnno[2]; /* declaration line number */ + char x_size[2]; /* str/union/array size */ + } x_lnsz; + char x_fsize[4]; /* size of function */ + } x_misc; + union { + struct { /* if ISFCN, tag, or .bb */ + char x_lnnoptr[4]; /* ptr to fcn line # */ + char x_endndx[4]; /* entry ndx past block end */ + } x_fcn; + struct { /* if ISARY, up to 4 dimen. */ + char x_dimen[E_DIMNUM][2]; + } x_ary; + } x_fcnary; + char x_tvndx[2]; /* tv index */ + } x_sym; + + union { + char x_fname[E_FILNMLEN]; + struct { + char x_zeroes[4]; + char x_offset[4]; + } x_n; + } x_file; + + struct { + char x_scnlen[4]; /* section length */ + char x_nreloc[2]; /* # relocation entries */ + char x_nlinno[2]; /* # line numbers */ + } x_scn; + + struct { + char x_tvfill[4]; /* tv fill value */ + char x_tvlen[2]; /* length of .tv */ + char x_tvran[2][2]; /* tv range */ + } x_tv; /* info about .tv section (in auxent of symbol .tv)) */ + + struct { + unsigned char x_scnlen[4]; + unsigned char x_parmhash[4]; + unsigned char x_snhash[2]; + unsigned char x_smtyp[1]; + unsigned char x_smclas[1]; + unsigned char x_stab[4]; + unsigned char x_snstab[2]; + } x_csect; + +}; + +#define SYMENT struct external_syment +#define SYMESZ 18 +#define AUXENT union external_auxent +#define AUXESZ 18 +#define DBXMASK 0x80 /* for dbx storage mask */ +#define SYMNAME_IN_DEBUG(symptr) ((symptr)->n_sclass & DBXMASK) + + + +/********************** RELOCATION DIRECTIVES **********************/ + + +struct external_reloc { + char r_vaddr[4]; + char r_symndx[4]; + char r_size[1]; + char r_type[1]; +}; + + +#define RELOC struct external_reloc +#define RELSZ 10 + +#define DEFAULT_DATA_SECTION_ALIGNMENT 4 +#define DEFAULT_BSS_SECTION_ALIGNMENT 4 +#define DEFAULT_TEXT_SECTION_ALIGNMENT 4 +/* For new sections we havn't heard of before */ +#define DEFAULT_SECTION_ALIGNMENT 4 diff --git a/arch/ppc/boot/include/serial.h b/arch/ppc/boot/include/serial.h new file mode 100644 index 00000000000..d710eabb425 --- /dev/null +++ b/arch/ppc/boot/include/serial.h @@ -0,0 +1,46 @@ +/* + * A really private header file for the (dumb) serial driver in arch/ppc/boot + * + * Shamelessly taken from include/linux/serialP.h: + * + * Copyright (C) 1997 by Theodore Ts'o. + * + * Redistribution of this file is permitted under the terms of the GNU + * Public License (GPL) + */ + +#ifndef _PPC_BOOT_SERIALP_H +#define _PPC_BOOT_SERIALP_H + +/* + * This is our internal structure for each serial port's state. + * + * Many fields are paralleled by the structure used by the serial_struct + * structure. + * + * Given that this is how SERIAL_PORT_DFNS are done, and that we need + * to use a few of their fields, we need to have our own copy of it. + */ +struct serial_state { + int magic; + int baud_base; + unsigned long port; + int irq; + int flags; + int hub6; + int type; + int line; + int revision; /* Chip revision (950) */ + int xmit_fifo_size; + int custom_divisor; + int count; + u8 *iomem_base; + u16 iomem_reg_shift; + unsigned short close_delay; + unsigned short closing_wait; /* time to wait before closing */ + unsigned long icount; + int io_type; + void *info; + void *dev; +}; +#endif /* _PPC_BOOT_SERIAL_H */ diff --git a/arch/ppc/boot/ld.script b/arch/ppc/boot/ld.script new file mode 100644 index 00000000000..6ee602d8b6a --- /dev/null +++ b/arch/ppc/boot/ld.script @@ -0,0 +1,88 @@ +OUTPUT_ARCH(powerpc) +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = + SIZEOF_HEADERS; + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .rel.text : { *(.rel.text) } + .rela.text : { *(.rela.text) } + .rel.data : { *(.rel.data) } + .rela.data : { *(.rela.data) } + .rel.rodata : { *(.rel.rodata) } + .rela.rodata : { *(.rela.rodata) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + .plt : { *(.plt) } + .text : + { + *(.text) + *(.fixup) + __relocate_start = .; + *(.relocate_code) + __relocate_end = .; + } + _etext = .; + PROVIDE (etext = .); + + /* Read-write section, merged into data segment: */ + . = ALIGN(4096); + .data : + { + *(.data) + *(.data1) + *(.data.boot) + *(.sdata) + *(.sdata2) + *(.got.plt) *(.got) + *(.dynamic) + *(.rodata) + *(.rodata.*) + *(.rodata1) + *(.got1) + __image_begin = .; + *(.image) + __image_end = .; + . = ALIGN(4096); + __ramdisk_begin = .; + *(.ramdisk) + __ramdisk_end = .; + . = ALIGN(4096); + __sysmap_begin = .; + *(.sysmap) + __sysmap_end = .; + CONSTRUCTORS + } + _edata = .; + PROVIDE (edata = .); + + . = ALIGN(4096); + __bss_start = .; + .bss : + { + *(.sbss) *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + } + _end = . ; + PROVIDE (end = .); + + /DISCARD/ : { + *(__ksymtab) + *(__ksymtab_strings) + *(__bug_table) + *(__kcrctab) + } + +} diff --git a/arch/ppc/boot/lib/Makefile b/arch/ppc/boot/lib/Makefile new file mode 100644 index 00000000000..d4077e69086 --- /dev/null +++ b/arch/ppc/boot/lib/Makefile @@ -0,0 +1,23 @@ +# +# Makefile for some libs needed by zImage. +# + +CFLAGS_kbd.o := -Idrivers/char +CFLAGS_vreset.o := -I$(srctree)/arch/ppc/boot/include + +zlib := infblock.c infcodes.c inffast.c inflate.c inftrees.c infutil.c + +lib-y += $(zlib:.c=.o) div64.o +lib-$(CONFIG_VGA_CONSOLE) += vreset.o kbd.o + + +# zlib files needs header from their original place +EXTRA_CFLAGS += -Ilib/zlib_inflate + +quiet_cmd_copy_zlib = COPY $@ + cmd_copy_zlib = cat $< > $@ + +$(addprefix $(obj)/,$(zlib)): $(obj)/%: $(srctree)/lib/zlib_inflate/% + $(call cmd,copy_zlib) + +clean-files := $(zlib) diff --git a/arch/ppc/boot/lib/div64.S b/arch/ppc/boot/lib/div64.S new file mode 100644 index 00000000000..3527569e992 --- /dev/null +++ b/arch/ppc/boot/lib/div64.S @@ -0,0 +1,58 @@ +/* + * Divide a 64-bit unsigned number by a 32-bit unsigned number. + * This routine assumes that the top 32 bits of the dividend are + * non-zero to start with. + * On entry, r3 points to the dividend, which get overwritten with + * the 64-bit quotient, and r4 contains the divisor. + * On exit, r3 contains the remainder. + * + * Copyright (C) 2002 Paul Mackerras, IBM Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include + +_GLOBAL(__div64_32) + lwz r5,0(r3) # get the dividend into r5/r6 + lwz r6,4(r3) + cmplw r5,r4 + li r7,0 + li r8,0 + blt 1f + divwu r7,r5,r4 # if dividend.hi >= divisor, + mullw r0,r7,r4 # quotient.hi = dividend.hi / divisor + subf. r5,r0,r5 # dividend.hi %= divisor + beq 3f +1: mr r11,r5 # here dividend.hi != 0 + andis. r0,r5,0xc000 + bne 2f + cntlzw r0,r5 # we are shifting the dividend right + li r10,-1 # to make it < 2^32, and shifting + srw r10,r10,r0 # the divisor right the same amount, + add r9,r4,r10 # rounding up (so the estimate cannot + andc r11,r6,r10 # ever be too large, only too small) + andc r9,r9,r10 + or r11,r5,r11 + rotlw r9,r9,r0 + rotlw r11,r11,r0 + divwu r11,r11,r9 # then we divide the shifted quantities +2: mullw r10,r11,r4 # to get an estimate of the quotient, + mulhwu r9,r11,r4 # multiply the estimate by the divisor, + subfc r6,r10,r6 # take the product from the divisor, + add r8,r8,r11 # and add the estimate to the accumulated + subfe. r5,r9,r5 # quotient + bne 1b +3: cmplw r6,r4 + blt 4f + divwu r0,r6,r4 # perform the remaining 32-bit division + mullw r10,r0,r4 # and get the remainder + add r8,r8,r0 + subf r6,r10,r6 +4: stw r7,0(r3) # return the quotient in *r3 + stw r8,4(r3) + mr r3,r6 # return the remainder in r3 + blr diff --git a/arch/ppc/boot/lib/kbd.c b/arch/ppc/boot/lib/kbd.c new file mode 100644 index 00000000000..3931727434d --- /dev/null +++ b/arch/ppc/boot/lib/kbd.c @@ -0,0 +1,248 @@ +#include + +#include "defkeymap.c" /* yeah I know it's bad -- Cort */ + + +unsigned char shfts, ctls, alts, caps; + +#define KBDATAP 0x60 /* kbd data port */ +#define KBSTATUSPORT 0x61 /* kbd status */ +#define KBSTATP 0x64 /* kbd status port */ +#define KBINRDY 0x01 +#define KBOUTRDY 0x02 + +extern unsigned char inb(int port); +extern void outb(int port, char val); +extern void puts(const char *); +extern void puthex(unsigned long val); +extern void udelay(long x); + +static int kbd(int noblock) +{ + unsigned char dt, brk, val; + unsigned code; +loop: + if (noblock) { + if ((inb(KBSTATP) & KBINRDY) == 0) + return (-1); + } else while((inb(KBSTATP) & KBINRDY) == 0) ; + + dt = inb(KBDATAP); + + brk = dt & 0x80; /* brk == 1 on key release */ + dt = dt & 0x7f; /* keycode */ + + if (shfts) + code = shift_map[dt]; + else if (ctls) + code = ctrl_map[dt]; + else + code = plain_map[dt]; + + val = KVAL(code); + switch (KTYP(code) & 0x0f) { + case KT_LATIN: + if (brk) + break; + if (alts) + val |= 0x80; + if (val == 0x7f) /* map delete to backspace */ + val = '\b'; + return val; + + case KT_LETTER: + if (brk) + break; + if (caps) + val -= 'a'-'A'; + return val; + + case KT_SPEC: + if (brk) + break; + if (val == KVAL(K_CAPS)) + caps = !caps; + else if (val == KVAL(K_ENTER)) { +enter: /* Wait for key up */ + while (1) { + while((inb(KBSTATP) & KBINRDY) == 0) ; + dt = inb(KBDATAP); + if (dt & 0x80) /* key up */ break; + } + return 10; + } + break; + + case KT_PAD: + if (brk) + break; + if (val < 10) + return val; + if (val == KVAL(K_PENTER)) + goto enter; + break; + + case KT_SHIFT: + switch (val) { + case KG_SHIFT: + case KG_SHIFTL: + case KG_SHIFTR: + shfts = brk ? 0 : 1; + break; + case KG_ALT: + case KG_ALTGR: + alts = brk ? 0 : 1; + break; + case KG_CTRL: + case KG_CTRLL: + case KG_CTRLR: + ctls = brk ? 0 : 1; + break; + } + break; + + case KT_LOCK: + switch (val) { + case KG_SHIFT: + case KG_SHIFTL: + case KG_SHIFTR: + if (brk) + shfts = !shfts; + break; + case KG_ALT: + case KG_ALTGR: + if (brk) + alts = !alts; + break; + case KG_CTRL: + case KG_CTRLL: + case KG_CTRLR: + if (brk) + ctls = !ctls; + break; + } + break; + } + if (brk) return (-1); /* Ignore initial 'key up' codes */ + goto loop; +} + +static int __kbdreset(void) +{ + unsigned char c; + int i, t; + + /* flush input queue */ + t = 2000; + while ((inb(KBSTATP) & KBINRDY)) + { + (void)inb(KBDATAP); + if (--t == 0) + return 1; + } + /* Send self-test */ + t = 20000; + while (inb(KBSTATP) & KBOUTRDY) + if (--t == 0) + return 2; + outb(KBSTATP,0xAA); + t = 200000; + while ((inb(KBSTATP) & KBINRDY) == 0) /* wait input ready */ + if (--t == 0) + return 3; + if ((c = inb(KBDATAP)) != 0x55) + { + puts("Keyboard self test failed - result:"); + puthex(c); + puts("\n"); + } + /* Enable interrupts and keyboard controller */ + t = 20000; + while (inb(KBSTATP) & KBOUTRDY) + if (--t == 0) return 4; + outb(KBSTATP,0x60); + t = 20000; + while (inb(KBSTATP) & KBOUTRDY) + if (--t == 0) return 5; + outb(KBDATAP,0x45); + for (i = 0; i < 10000; i++) udelay(1); + + t = 20000; + while (inb(KBSTATP) & KBOUTRDY) + if (--t == 0) return 6; + outb(KBSTATP,0x20); + t = 200000; + while ((inb(KBSTATP) & KBINRDY) == 0) /* wait input ready */ + if (--t == 0) return 7; + if (! (inb(KBDATAP) & 0x40)) { + /* + * Quote from PS/2 System Reference Manual: + * + * "Address hex 0060 and address hex 0064 should be + * written only when the input-buffer-full bit and + * output-buffer-full bit in the Controller Status + * register are set 0." (KBINRDY and KBOUTRDY) + */ + t = 200000; + while (inb(KBSTATP) & (KBINRDY | KBOUTRDY)) + if (--t == 0) return 8; + outb(KBDATAP,0xF0); + t = 200000; + while (inb(KBSTATP) & (KBINRDY | KBOUTRDY)) + if (--t == 0) return 9; + outb(KBDATAP,0x01); + } + t = 20000; + while (inb(KBSTATP) & KBOUTRDY) + if (--t == 0) return 10; + outb(KBSTATP,0xAE); + return 0; +} + +static void kbdreset(void) +{ + int ret = __kbdreset(); + + if (ret) { + puts("__kbdreset failed: "); + puthex(ret); + puts("\n"); + } +} + +/* We have to actually read the keyboard when CRT_tstc is called, + * since the pending data might be a key release code, and therefore + * not valid data. In this case, kbd() will return -1, even though there's + * data to be read. Of course, we might actually read a valid key press, + * in which case it gets queued into key_pending for use by CRT_getc. + */ + +static int kbd_reset = 0; + +static int key_pending = -1; + +int CRT_getc(void) +{ + int c; + if (!kbd_reset) {kbdreset(); kbd_reset++; } + + if (key_pending != -1) { + c = key_pending; + key_pending = -1; + return c; + } else { + while ((c = kbd(0)) == 0) ; + return c; + } +} + +int CRT_tstc(void) +{ + if (!kbd_reset) {kbdreset(); kbd_reset++; } + + while (key_pending == -1 && ((inb(KBSTATP) & KBINRDY) != 0)) { + key_pending = kbd(1); + } + + return (key_pending != -1); +} diff --git a/arch/ppc/boot/lib/vreset.c b/arch/ppc/boot/lib/vreset.c new file mode 100644 index 00000000000..463ba001fb9 --- /dev/null +++ b/arch/ppc/boot/lib/vreset.c @@ -0,0 +1,805 @@ +/* + * vreset.c + * + * Initialize the VGA control registers to 80x25 text mode. + * + * Adapted from a program by: + * Steve Sellgren + * San Francisco Indigo Company + * sfindigo!sellgren@uunet.uu.net + * + * Original concept by: + * Gary Thomas + * Adapted for Moto boxes by: + * Pat Kane & Mark Scott, 1996 + * Adapted for IBM portables by: + * Takeshi Ishimoto + * Multi-console support: + * Terje Malmedal + */ + +#include "iso_font.h" +#include "nonstdio.h" + +extern char *vidmem; +extern int lines, cols; +struct VaRegs; + +/* + * VGA Register + */ +struct VgaRegs +{ + unsigned short io_port; + unsigned char io_index; + unsigned char io_value; +}; + +void unlockVideo(int slot); +void setTextRegs(struct VgaRegs *svp); +void setTextCLUT(int shift); +void clearVideoMemory(void); +void loadFont(unsigned char *ISA_mem); + +static void mdelay(int ms) +{ + for (; ms > 0; --ms) + udelay(1000); +} + +/* + * Default console text mode registers used to reset + * graphics adapter. + */ +#define NREGS 54 +#define ENDMK 0xFFFF /* End marker */ + +#define S3Vendor 0x5333 +#define CirrusVendor 0x1013 +#define DiamondVendor 0x100E +#define MatroxVendor 0x102B +#define ParadiseVendor 0x101C + +struct VgaRegs GenVgaTextRegs[NREGS+1] = { + /* port index value */ + /* SR Regs */ + { 0x3c4, 0x1, 0x0 }, + { 0x3c4, 0x2, 0x3 }, + { 0x3c4, 0x3, 0x0 }, + { 0x3c4, 0x4, 0x2 }, + /* CR Regs */ + { 0x3d4, 0x0, 0x5f }, + { 0x3d4, 0x1, 0x4f }, + { 0x3d4, 0x2, 0x50 }, + { 0x3d4, 0x3, 0x82 }, + { 0x3d4, 0x4, 0x55 }, + { 0x3d4, 0x5, 0x81 }, + { 0x3d4, 0x6, 0xbf }, + { 0x3d4, 0x7, 0x1f }, + { 0x3d4, 0x8, 0x00 }, + { 0x3d4, 0x9, 0x4f }, + { 0x3d4, 0xa, 0x0d }, + { 0x3d4, 0xb, 0x0e }, + { 0x3d4, 0xc, 0x00 }, + { 0x3d4, 0xd, 0x00 }, + { 0x3d4, 0xe, 0x00 }, + { 0x3d4, 0xf, 0x00 }, + { 0x3d4, 0x10, 0x9c }, + { 0x3d4, 0x11, 0x8e }, + { 0x3d4, 0x12, 0x8f }, + { 0x3d4, 0x13, 0x28 }, + { 0x3d4, 0x14, 0x1f }, + { 0x3d4, 0x15, 0x96 }, + { 0x3d4, 0x16, 0xb9 }, + { 0x3d4, 0x17, 0xa3 }, + /* GR Regs */ + { 0x3ce, 0x0, 0x0 }, + { 0x3ce, 0x1, 0x0 }, + { 0x3ce, 0x2, 0x0 }, + { 0x3ce, 0x3, 0x0 }, + { 0x3ce, 0x4, 0x0 }, + { 0x3ce, 0x5, 0x10 }, + { 0x3ce, 0x6, 0xe }, + { 0x3ce, 0x7, 0x0 }, + { 0x3ce, 0x8, 0xff }, + { ENDMK } +}; + +struct RGBColors +{ + unsigned char r, g, b; +}; + +/* + * Default console text mode color table. + * These values were obtained by booting Linux with + * text mode firmware & then dumping the registers. + */ +struct RGBColors TextCLUT[256] = +{ + /* red green blue */ + { 0x0, 0x0, 0x0 }, + { 0x0, 0x0, 0x2a }, + { 0x0, 0x2a, 0x0 }, + { 0x0, 0x2a, 0x2a }, + { 0x2a, 0x0, 0x0 }, + { 0x2a, 0x0, 0x2a }, + { 0x2a, 0x2a, 0x0 }, + { 0x2a, 0x2a, 0x2a }, + { 0x0, 0x0, 0x15 }, + { 0x0, 0x0, 0x3f }, + { 0x0, 0x2a, 0x15 }, + { 0x0, 0x2a, 0x3f }, + { 0x2a, 0x0, 0x15 }, + { 0x2a, 0x0, 0x3f }, + { 0x2a, 0x2a, 0x15 }, + { 0x2a, 0x2a, 0x3f }, + { 0x0, 0x15, 0x0 }, + { 0x0, 0x15, 0x2a }, + { 0x0, 0x3f, 0x0 }, + { 0x0, 0x3f, 0x2a }, + { 0x2a, 0x15, 0x0 }, + { 0x2a, 0x15, 0x2a }, + { 0x2a, 0x3f, 0x0 }, + { 0x2a, 0x3f, 0x2a }, + { 0x0, 0x15, 0x15 }, + { 0x0, 0x15, 0x3f }, + { 0x0, 0x3f, 0x15 }, + { 0x0, 0x3f, 0x3f }, + { 0x2a, 0x15, 0x15 }, + { 0x2a, 0x15, 0x3f }, + { 0x2a, 0x3f, 0x15 }, + { 0x2a, 0x3f, 0x3f }, + { 0x15, 0x0, 0x0 }, + { 0x15, 0x0, 0x2a }, + { 0x15, 0x2a, 0x0 }, + { 0x15, 0x2a, 0x2a }, + { 0x3f, 0x0, 0x0 }, + { 0x3f, 0x0, 0x2a }, + { 0x3f, 0x2a, 0x0 }, + { 0x3f, 0x2a, 0x2a }, + { 0x15, 0x0, 0x15 }, + { 0x15, 0x0, 0x3f }, + { 0x15, 0x2a, 0x15 }, + { 0x15, 0x2a, 0x3f }, + { 0x3f, 0x0, 0x15 }, + { 0x3f, 0x0, 0x3f }, + { 0x3f, 0x2a, 0x15 }, + { 0x3f, 0x2a, 0x3f }, + { 0x15, 0x15, 0x0 }, + { 0x15, 0x15, 0x2a }, + { 0x15, 0x3f, 0x0 }, + { 0x15, 0x3f, 0x2a }, + { 0x3f, 0x15, 0x0 }, + { 0x3f, 0x15, 0x2a }, + { 0x3f, 0x3f, 0x0 }, + { 0x3f, 0x3f, 0x2a }, + { 0x15, 0x15, 0x15 }, + { 0x15, 0x15, 0x3f }, + { 0x15, 0x3f, 0x15 }, + { 0x15, 0x3f, 0x3f }, + { 0x3f, 0x15, 0x15 }, + { 0x3f, 0x15, 0x3f }, + { 0x3f, 0x3f, 0x15 }, + { 0x3f, 0x3f, 0x3f }, + { 0x39, 0xc, 0x5 }, + { 0x15, 0x2c, 0xf }, + { 0x26, 0x10, 0x3d }, + { 0x29, 0x29, 0x38 }, + { 0x4, 0x1a, 0xe }, + { 0x2, 0x1e, 0x3a }, + { 0x3c, 0x25, 0x33 }, + { 0x3c, 0xc, 0x2c }, + { 0x3f, 0x3, 0x2b }, + { 0x1c, 0x9, 0x13 }, + { 0x25, 0x2a, 0x35 }, + { 0x1e, 0xa, 0x38 }, + { 0x24, 0x8, 0x3 }, + { 0x3, 0xe, 0x36 }, + { 0xc, 0x6, 0x2a }, + { 0x26, 0x3, 0x32 }, + { 0x5, 0x2f, 0x33 }, + { 0x3c, 0x35, 0x2f }, + { 0x2d, 0x26, 0x3e }, + { 0xd, 0xa, 0x10 }, + { 0x25, 0x3c, 0x11 }, + { 0xd, 0x4, 0x2e }, + { 0x5, 0x19, 0x3e }, + { 0xc, 0x13, 0x34 }, + { 0x2b, 0x6, 0x24 }, + { 0x4, 0x3, 0xd }, + { 0x2f, 0x3c, 0xc }, + { 0x2a, 0x37, 0x1f }, + { 0xf, 0x12, 0x38 }, + { 0x38, 0xe, 0x2a }, + { 0x12, 0x2f, 0x19 }, + { 0x29, 0x2e, 0x31 }, + { 0x25, 0x13, 0x3e }, + { 0x33, 0x3e, 0x33 }, + { 0x1d, 0x2c, 0x25 }, + { 0x15, 0x15, 0x5 }, + { 0x32, 0x25, 0x39 }, + { 0x1a, 0x7, 0x1f }, + { 0x13, 0xe, 0x1d }, + { 0x36, 0x17, 0x34 }, + { 0xf, 0x15, 0x23 }, + { 0x2, 0x35, 0xd }, + { 0x15, 0x3f, 0xc }, + { 0x14, 0x2f, 0xf }, + { 0x19, 0x21, 0x3e }, + { 0x27, 0x11, 0x2f }, + { 0x38, 0x3f, 0x3c }, + { 0x36, 0x2d, 0x15 }, + { 0x16, 0x17, 0x2 }, + { 0x1, 0xa, 0x3d }, + { 0x1b, 0x11, 0x3f }, + { 0x21, 0x3c, 0xd }, + { 0x1a, 0x39, 0x3d }, + { 0x8, 0xe, 0xe }, + { 0x22, 0x21, 0x23 }, + { 0x1e, 0x30, 0x5 }, + { 0x1f, 0x22, 0x3d }, + { 0x1e, 0x2f, 0xa }, + { 0x0, 0x1c, 0xe }, + { 0x0, 0x1c, 0x15 }, + { 0x0, 0x1c, 0x1c }, + { 0x0, 0x15, 0x1c }, + { 0x0, 0xe, 0x1c }, + { 0x0, 0x7, 0x1c }, + { 0xe, 0xe, 0x1c }, + { 0x11, 0xe, 0x1c }, + { 0x15, 0xe, 0x1c }, + { 0x18, 0xe, 0x1c }, + { 0x1c, 0xe, 0x1c }, + { 0x1c, 0xe, 0x18 }, + { 0x1c, 0xe, 0x15 }, + { 0x1c, 0xe, 0x11 }, + { 0x1c, 0xe, 0xe }, + { 0x1c, 0x11, 0xe }, + { 0x1c, 0x15, 0xe }, + { 0x1c, 0x18, 0xe }, + { 0x1c, 0x1c, 0xe }, + { 0x18, 0x1c, 0xe }, + { 0x15, 0x1c, 0xe }, + { 0x11, 0x1c, 0xe }, + { 0xe, 0x1c, 0xe }, + { 0xe, 0x1c, 0x11 }, + { 0xe, 0x1c, 0x15 }, + { 0xe, 0x1c, 0x18 }, + { 0xe, 0x1c, 0x1c }, + { 0xe, 0x18, 0x1c }, + { 0xe, 0x15, 0x1c }, + { 0xe, 0x11, 0x1c }, + { 0x14, 0x14, 0x1c }, + { 0x16, 0x14, 0x1c }, + { 0x18, 0x14, 0x1c }, + { 0x1a, 0x14, 0x1c }, + { 0x1c, 0x14, 0x1c }, + { 0x1c, 0x14, 0x1a }, + { 0x1c, 0x14, 0x18 }, + { 0x1c, 0x14, 0x16 }, + { 0x1c, 0x14, 0x14 }, + { 0x1c, 0x16, 0x14 }, + { 0x1c, 0x18, 0x14 }, + { 0x1c, 0x1a, 0x14 }, + { 0x1c, 0x1c, 0x14 }, + { 0x1a, 0x1c, 0x14 }, + { 0x18, 0x1c, 0x14 }, + { 0x16, 0x1c, 0x14 }, + { 0x14, 0x1c, 0x14 }, + { 0x14, 0x1c, 0x16 }, + { 0x14, 0x1c, 0x18 }, + { 0x14, 0x1c, 0x1a }, + { 0x14, 0x1c, 0x1c }, + { 0x14, 0x1a, 0x1c }, + { 0x14, 0x18, 0x1c }, + { 0x14, 0x16, 0x1c }, + { 0x0, 0x0, 0x10 }, + { 0x4, 0x0, 0x10 }, + { 0x8, 0x0, 0x10 }, + { 0xc, 0x0, 0x10 }, + { 0x10, 0x0, 0x10 }, + { 0x10, 0x0, 0xc }, + { 0x10, 0x0, 0x8 }, + { 0x10, 0x0, 0x4 }, + { 0x10, 0x0, 0x0 }, + { 0x10, 0x4, 0x0 }, + { 0x10, 0x8, 0x0 }, + { 0x10, 0xc, 0x0 }, + { 0x10, 0x10, 0x0 }, + { 0xc, 0x10, 0x0 }, + { 0x8, 0x10, 0x0 }, + { 0x4, 0x10, 0x0 }, + { 0x0, 0x10, 0x0 }, + { 0x0, 0x10, 0x4 }, + { 0x0, 0x10, 0x8 }, + { 0x0, 0x10, 0xc }, + { 0x0, 0x10, 0x10 }, + { 0x0, 0xc, 0x10 }, + { 0x0, 0x8, 0x10 }, + { 0x0, 0x4, 0x10 }, + { 0x8, 0x8, 0x10 }, + { 0xa, 0x8, 0x10 }, + { 0xc, 0x8, 0x10 }, + { 0xe, 0x8, 0x10 }, + { 0x10, 0x8, 0x10 }, + { 0x10, 0x8, 0xe }, + { 0x10, 0x8, 0xc }, + { 0x10, 0x8, 0xa }, + { 0x10, 0x8, 0x8 }, + { 0x10, 0xa, 0x8 }, + { 0x10, 0xc, 0x8 }, + { 0x10, 0xe, 0x8 }, + { 0x10, 0x10, 0x8 }, + { 0xe, 0x10, 0x8 }, + { 0xc, 0x10, 0x8 }, + { 0xa, 0x10, 0x8 }, + { 0x8, 0x10, 0x8 }, + { 0x8, 0x10, 0xa }, + { 0x8, 0x10, 0xc }, + { 0x8, 0x10, 0xe }, + { 0x8, 0x10, 0x10 }, + { 0x8, 0xe, 0x10 }, + { 0x8, 0xc, 0x10 }, + { 0x8, 0xa, 0x10 }, + { 0xb, 0xb, 0x10 }, + { 0xc, 0xb, 0x10 }, + { 0xd, 0xb, 0x10 }, + { 0xf, 0xb, 0x10 }, + { 0x10, 0xb, 0x10 }, + { 0x10, 0xb, 0xf }, + { 0x10, 0xb, 0xd }, + { 0x10, 0xb, 0xc }, + { 0x10, 0xb, 0xb }, + { 0x10, 0xc, 0xb }, + { 0x10, 0xd, 0xb }, + { 0x10, 0xf, 0xb }, + { 0x10, 0x10, 0xb }, + { 0xf, 0x10, 0xb }, + { 0xd, 0x10, 0xb }, + { 0xc, 0x10, 0xb }, + { 0xb, 0x10, 0xb }, + { 0xb, 0x10, 0xc }, + { 0xb, 0x10, 0xd }, + { 0xb, 0x10, 0xf }, + { 0xb, 0x10, 0x10 }, + { 0xb, 0xf, 0x10 }, + { 0xb, 0xd, 0x10 }, + { 0xb, 0xc, 0x10 }, + { 0x0, 0x0, 0x0 }, + { 0x0, 0x0, 0x0 }, + { 0x0, 0x0, 0x0 }, + { 0x0, 0x0, 0x0 }, + { 0x0, 0x0, 0x0 }, + { 0x0, 0x0, 0x0 }, + { 0x0, 0x0, 0x0 } +}; + +unsigned char AC[21] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, + 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + 0x0C, 0x00, 0x0F, 0x08, 0x00}; + +static int scanPCI(int start_slt); +static int PCIVendor(int); +#ifdef DEBUG +static void printslots(void); +#endif +extern void puthex(unsigned long); +extern void puts(const char *); +static void unlockS3(void); + +static inline void +outw(int port, unsigned short val) +{ + outb(port, val >> 8); + outb(port+1, val); +} + +int +vga_init(unsigned char *ISA_mem) +{ + int slot; + struct VgaRegs *VgaTextRegs; + + /* See if VGA already in TEXT mode - exit if so! */ + outb(0x3CE, 0x06); + if ((inb(0x3CF) & 0x01) == 0){ + puts("VGA already in text mode\n"); + return 0; + } + + /* If no VGA responding in text mode, then we have some work to do... + */ + slot = -1; + while((slot = scanPCI(slot)) > -1) { /* find video card in use */ + unlockVideo(slot); /* enable I/O to card */ + VgaTextRegs = GenVgaTextRegs; + + switch (PCIVendor(slot)) { + default: + break; + case(S3Vendor): + unlockS3(); + break; + + case(CirrusVendor): + outw(0x3C4, 0x0612); /* unlock ext regs */ + outw(0x3C4, 0x0700); /* reset ext sequence mode */ + break; + + case(ParadiseVendor): /* IBM Portable 850 */ + outw(0x3ce, 0x0f05); /* unlock pardise registers */ + outw(0x3c4, 0x0648); + outw(0x3d4, 0x2985); + outw(0x3d4, 0x34a6); + outb(0x3ce, 0x0b); /* disable linear addressing */ + outb(0x3cf, inb(0x3cf) & ~0x30); + outw(0x3c4, 0x1400); + outb(0x3ce, 0x0e); /* disable 256 color mode */ + outb(0x3cf, inb(0x3cf) & ~0x01); + outb(0xd00, 0xff); /* enable auto-centering */ + if (!(inb(0xd01) & 0x03)) { + outb(0x3d4, 0x33); + outb(0x3d5, inb(0x3d5) & ~0x90); + outb(0x3d4, 0x32); + outb(0x3d5, inb(0x3d5) | 0x04); + outw(0x3d4, 0x0250); + outw(0x3d4, 0x07ba); + outw(0x3d4, 0x0900); + outw(0x3d4, 0x15e7); + outw(0x3d4, 0x2a95); + } + outw(0x3d4, 0x34a0); + break; + + #if 0 /* Untested - probably doesn't work */ + case(MatroxVendor): + case(DiamondVendor): + puts("VGA Chip Vendor ID: "); + puthex(PCIVendor(slot)); + puts("\n"); + mdelay(1000); + #endif + }; + + outw(0x3C4, 0x0120); /* disable video */ + setTextRegs(VgaTextRegs); /* initial register setup */ + setTextCLUT(0); /* load color lookup table */ + loadFont(ISA_mem); /* load font */ + setTextRegs(VgaTextRegs); /* reload registers */ + outw(0x3C4, 0x0100); /* re-enable video */ + clearVideoMemory(); + + if (PCIVendor(slot) == S3Vendor) { + outb(0x3c2, 0x63); /* MISC */ + } /* endif */ + + #ifdef DEBUG + printslots(); + mdelay(5000); + #endif + + mdelay(1000); /* give time for the video monitor to come up */ + } + return (1); /* 'CRT' I/O supported */ +} + +/* + * Write to VGA Attribute registers. + */ +void +writeAttr(unsigned char index, unsigned char data, unsigned char videoOn) +{ + unsigned char v; + v = inb(0x3da); /* reset attr. address toggle */ + if (videoOn) + outb(0x3c0, (index & 0x1F) | 0x20); + else + outb(0x3c0, (index & 0x1F)); + outb(0x3c0, data); +} + +void +setTextRegs(struct VgaRegs *svp) +{ + int i; + + /* + * saved settings + */ + while( svp->io_port != ENDMK ) { + outb(svp->io_port, svp->io_index); + outb(svp->io_port+1, svp->io_value); + svp++; + } + + outb(0x3c2, 0x67); /* MISC */ + outb(0x3c6, 0xff); /* MASK */ + + for ( i = 0; i < 0x10; i++) + writeAttr(i, AC[i], 0); /* pallete */ + writeAttr(0x10, 0x0c, 0); /* text mode */ + writeAttr(0x11, 0x00, 0); /* overscan color (border) */ + writeAttr(0x12, 0x0f, 0); /* plane enable */ + writeAttr(0x13, 0x08, 0); /* pixel panning */ + writeAttr(0x14, 0x00, 1); /* color select; video on */ +} + +void +setTextCLUT(int shift) +{ + int i; + + outb(0x3C6, 0xFF); + i = inb(0x3C7); + outb(0x3C8, 0); + i = inb(0x3C7); + + for ( i = 0; i < 256; i++) { + outb(0x3C9, TextCLUT[i].r << shift); + outb(0x3C9, TextCLUT[i].g << shift); + outb(0x3C9, TextCLUT[i].b << shift); + } +} + +void +loadFont(unsigned char *ISA_mem) +{ + int i, j; + unsigned char *font_page = (unsigned char *) &ISA_mem[0xA0000]; + + outb(0x3C2, 0x67); + /* + * Load font + */ + i = inb(0x3DA); /* Reset Attr toggle */ + + outb(0x3C0,0x30); + outb(0x3C0, 0x01); /* graphics mode */ + + outw(0x3C4, 0x0001); /* reset sequencer */ + outw(0x3C4, 0x0204); /* write to plane 2 */ + outw(0x3C4, 0x0406); /* enable plane graphics */ + outw(0x3C4, 0x0003); /* reset sequencer */ + outw(0x3CE, 0x0402); /* read plane 2 */ + outw(0x3CE, 0x0500); /* write mode 0, read mode 0 */ + outw(0x3CE, 0x0605); /* set graphics mode */ + + for (i = 0; i < sizeof(font); i += 16) { + for (j = 0; j < 16; j++) { + __asm__ volatile("eieio"); + font_page[(2*i)+j] = font[i+j]; + } + } +} + +static void +unlockS3(void) +{ + int s3_device_id; + outw(0x3d4, 0x3848); + outw(0x3d4, 0x39a5); + outb(0x3d4, 0x2d); + s3_device_id = inb(0x3d5) << 8; + outb(0x3d4, 0x2e); + s3_device_id |= inb(0x3d5); + + if (s3_device_id != 0x8812) { + /* From the S3 manual */ + outb(0x46E8, 0x10); /* Put into setup mode */ + outb(0x3C3, 0x10); + outb(0x102, 0x01); /* Enable registers */ + outb(0x46E8, 0x08); /* Enable video */ + outb(0x3C3, 0x08); + outb(0x4AE8, 0x00); + +#if 0 + outb(0x42E8, 0x80); /* Reset graphics engine? */ +#endif + + outb(0x3D4, 0x38); /* Unlock all registers */ + outb(0x3D5, 0x48); + outb(0x3D4, 0x39); + outb(0x3D5, 0xA5); + outb(0x3D4, 0x40); + outb(0x3D5, inb(0x3D5)|0x01); + outb(0x3D4, 0x33); + outb(0x3D5, inb(0x3D5)&~0x52); + outb(0x3D4, 0x35); + outb(0x3D5, inb(0x3D5)&~0x30); + outb(0x3D4, 0x3A); + outb(0x3D5, 0x00); + outb(0x3D4, 0x53); + outb(0x3D5, 0x00); + outb(0x3D4, 0x31); + outb(0x3D5, inb(0x3D5)&~0x4B); + outb(0x3D4, 0x58); + + outb(0x3D5, 0); + + outb(0x3D4, 0x54); + outb(0x3D5, 0x38); + outb(0x3D4, 0x60); + outb(0x3D5, 0x07); + outb(0x3D4, 0x61); + outb(0x3D5, 0x80); + outb(0x3D4, 0x62); + outb(0x3D5, 0xA1); + outb(0x3D4, 0x69); /* High order bits for cursor address */ + outb(0x3D5, 0); + + outb(0x3D4, 0x32); + outb(0x3D5, inb(0x3D5)&~0x10); + } else { + outw(0x3c4, 0x0806); /* IBM Portable 860 */ + outw(0x3c4, 0x1041); + outw(0x3c4, 0x1128); + outw(0x3d4, 0x4000); + outw(0x3d4, 0x3100); + outw(0x3d4, 0x3a05); + outw(0x3d4, 0x6688); + outw(0x3d4, 0x5800); /* disable linear addressing */ + outw(0x3d4, 0x4500); /* disable H/W cursor */ + outw(0x3c4, 0x5410); /* enable auto-centering */ + outw(0x3c4, 0x561f); + outw(0x3c4, 0x1b80); /* lock DCLK selection */ + outw(0x3d4, 0x3900); /* lock S3 registers */ + outw(0x3d4, 0x3800); + } /* endif */ +} + +/* + * cursor() sets an offset (0-1999) into the 80x25 text area. + */ +void +cursor(int x, int y) +{ + int pos = (y*cols)+x; + outb(0x3D4, 14); + outb(0x3D5, pos >> 8); + outb(0x3D4, 15); + outb(0x3D5, pos); +} + +void +clearVideoMemory(void) +{ + int i, j; + for (i = 0; i < lines; i++) { + for (j = 0; j < cols; j++) { + vidmem[((i*cols)+j)*2] = 0x20; /* fill with space character */ + vidmem[((i*cols)+j)*2+1] = 0x07; /* set bg & fg attributes */ + } + } +} + +/* ============ */ + + +#define NSLOTS 8 +#define NPCIREGS 5 + + +/* + should use devfunc number/indirect method to be totally safe on + all machines, this works for now on 3 slot Moto boxes +*/ + +struct PCI_ConfigInfo { + unsigned long * config_addr; + unsigned long regs[NPCIREGS]; +} PCI_slots [NSLOTS] = { + + { (unsigned long *)0x80808000, {0xDEADBEEF,} }, /* onboard */ + { (unsigned long *)0x80800800, {0xDEADBEEF,} }, /* onboard */ + { (unsigned long *)0x80801000, {0xDEADBEEF,} }, /* onboard */ + { (unsigned long *)0x80802000, {0xDEADBEEF,} }, /* onboard */ + { (unsigned long *)0x80804000, {0xDEADBEEF,} }, /* onboard */ + { (unsigned long *)0x80810000, {0xDEADBEEF,} }, /* slot A/1 */ + { (unsigned long *)0x80820000, {0xDEADBEEF,} }, /* slot B/2 */ + { (unsigned long *)0x80840000, {0xDEADBEEF,} } /* slot C/3 */ +}; + + + +/* + * The following code modifies the PCI Command register + * to enable memory and I/O accesses. + */ +void +unlockVideo(int slot) +{ + volatile unsigned char * ppci; + + ppci = (unsigned char * )PCI_slots[slot].config_addr; + ppci[4] = 0x0003; /* enable memory and I/O accesses */ + ppci[0x10] = 0x00000; /* turn off memory mapping */ + ppci[0x11] = 0x00000; /* mem_base = 0 */ + ppci[0x12] = 0x00000; + ppci[0x13] = 0x00000; + __asm__ volatile("eieio"); + + outb(0x3d4, 0x11); + outb(0x3d5, 0x0e); /* unlock CR0-CR7 */ +} + +long +SwapBytes(long lv) /* turn little endian into big indian long */ +{ + long t; + t = (lv&0x000000FF) << 24; + t |= (lv&0x0000FF00) << 8; + t |= (lv&0x00FF0000) >> 8; + t |= (lv&0xFF000000) >> 24; + return(t); +} + + +#define DEVID 0 +#define CMD 1 +#define CLASS 2 +#define MEMBASE 4 + +int +scanPCI(int start_slt) +{ + int slt, r; + struct PCI_ConfigInfo *pslot; + int theSlot = -1; + int highVgaSlot = 0; + + for ( slt = start_slt + 1; slt < NSLOTS; slt++) { + pslot = &PCI_slots[slt]; + for ( r = 0; r < NPCIREGS; r++) { + pslot->regs[r] = SwapBytes ( pslot->config_addr[r] ); + } + /* card in slot ? */ + if ( pslot->regs[DEVID] != 0xFFFFFFFF ) { + /* VGA ? */ + if ( ((pslot->regs[CLASS] & 0xFFFFFF00) == 0x03000000) || + ((pslot->regs[CLASS] & 0xFFFFFF00) == 0x00010000)) { + highVgaSlot = slt; + /* did firmware enable it ? */ + if ( (pslot->regs[CMD] & 0x03) ) { + theSlot = slt; + break; + } + } + } + } + + return ( theSlot ); +} + +/* return Vendor ID of card in the slot */ +static +int PCIVendor(int slotnum) { + struct PCI_ConfigInfo *pslot; + + pslot = &PCI_slots[slotnum]; + +return (pslot->regs[DEVID] & 0xFFFF); +} + +#ifdef DEBUG +static +void printslots(void) +{ + int i; +#if 0 + struct PCI_ConfigInfo *pslot; +#endif + for(i=0; i < NSLOTS; i++) { +#if 0 + pslot = &PCI_slots[i]; + printf("Slot: %d, Addr: %x, Vendor: %08x, Class: %08x\n", + i, pslot->config_addr, pslot->regs[0], pslot->regs[2]); +#else + puts("PCI Slot number: "); puthex(i); + puts(" Vendor ID: "); + puthex(PCIVendor(i)); puts("\n"); +#endif + } +} +#endif /* DEBUG */ diff --git a/arch/ppc/boot/of1275/Makefile b/arch/ppc/boot/of1275/Makefile new file mode 100644 index 00000000000..02e6f235d7c --- /dev/null +++ b/arch/ppc/boot/of1275/Makefile @@ -0,0 +1,6 @@ +# +# Makefile of1275 stuff +# + +lib-y := claim.o enter.o exit.o finddevice.o getprop.o ofinit.o \ + ofstdio.o read.o release.o write.o map.o diff --git a/arch/ppc/boot/of1275/claim.c b/arch/ppc/boot/of1275/claim.c new file mode 100644 index 00000000000..e060292ae2a --- /dev/null +++ b/arch/ppc/boot/of1275/claim.c @@ -0,0 +1,34 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * Copyright (C) Leigh Brown 2002. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include "of1275.h" + +void * +claim(unsigned int virt, unsigned int size, unsigned int align) +{ + struct prom_args { + char *service; + int nargs; + int nret; + unsigned int virt; + unsigned int size; + unsigned int align; + void *ret; + } args; + + args.service = "claim"; + args.nargs = 3; + args.nret = 1; + args.virt = virt; + args.size = size; + args.align = align; + (*of_prom_entry)(&args); + return args.ret; +} diff --git a/arch/ppc/boot/of1275/enter.c b/arch/ppc/boot/of1275/enter.c new file mode 100644 index 00000000000..abe87a8fe2d --- /dev/null +++ b/arch/ppc/boot/of1275/enter.c @@ -0,0 +1,22 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * Copyright (C) Leigh Brown 2002. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include "of1275.h" + +void +enter(void) +{ + struct prom_args { + char *service; + } args; + + args.service = "enter"; + (*of_prom_entry)(&args); +} diff --git a/arch/ppc/boot/of1275/exit.c b/arch/ppc/boot/of1275/exit.c new file mode 100644 index 00000000000..b9f89b6a8b4 --- /dev/null +++ b/arch/ppc/boot/of1275/exit.c @@ -0,0 +1,24 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * Copyright (C) Leigh Brown 2002. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include "of1275.h" + +void +exit(void) +{ + struct prom_args { + char *service; + } args; + + for (;;) { + args.service = "exit"; + (*of_prom_entry)(&args); + } +} diff --git a/arch/ppc/boot/of1275/finddevice.c b/arch/ppc/boot/of1275/finddevice.c new file mode 100644 index 00000000000..2c0f7cbb793 --- /dev/null +++ b/arch/ppc/boot/of1275/finddevice.c @@ -0,0 +1,31 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * Copyright (C) Leigh Brown 2002. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include "of1275.h" + +phandle +finddevice(const char *name) +{ + struct prom_args { + char *service; + int nargs; + int nret; + const char *devspec; + phandle device; + } args; + + args.service = "finddevice"; + args.nargs = 1; + args.nret = 1; + args.devspec = name; + args.device = OF_INVALID_HANDLE; + (*of_prom_entry)(&args); + return args.device; +} diff --git a/arch/ppc/boot/of1275/getprop.c b/arch/ppc/boot/of1275/getprop.c new file mode 100644 index 00000000000..0cf75f035e4 --- /dev/null +++ b/arch/ppc/boot/of1275/getprop.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * Copyright (C) Leigh Brown 2002. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include "of1275.h" + +int +getprop(phandle node, const char *name, void *buf, int buflen) +{ + struct prom_args { + char *service; + int nargs; + int nret; + phandle node; + const char *name; + void *buf; + int buflen; + int size; + } args; + + args.service = "getprop"; + args.nargs = 4; + args.nret = 1; + args.node = node; + args.name = name; + args.buf = buf; + args.buflen = buflen; + args.size = -1; + (*of_prom_entry)(&args); + return args.size; +} diff --git a/arch/ppc/boot/of1275/map.c b/arch/ppc/boot/of1275/map.c new file mode 100644 index 00000000000..443256c6f6d --- /dev/null +++ b/arch/ppc/boot/of1275/map.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * Copyright (C) Leigh Brown 2002. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include "of1275.h" +#include "nonstdio.h" + +extern ihandle of_prom_mmu; + +int +map(unsigned int phys, unsigned int virt, unsigned int size) +{ + struct prom_args { + char *service; + int nargs; + int nret; + char *method; + ihandle mmu_ihandle; + int misc; + unsigned int size; + unsigned int virt; + unsigned int phys; + int ret0; + } args; + + if (of_prom_mmu == 0) { + printf("map() called, no MMU found\n"); + return -1; + } + args.service = "call-method"; + args.nargs = 6; + args.nret = 1; + args.method = "map"; + args.mmu_ihandle = of_prom_mmu; + args.misc = 0; + args.phys = phys; + args.virt = virt; + args.size = size; + (*of_prom_entry)(&args); + + return (int)args.ret0; +} diff --git a/arch/ppc/boot/of1275/ofinit.c b/arch/ppc/boot/of1275/ofinit.c new file mode 100644 index 00000000000..0ee8af7639e --- /dev/null +++ b/arch/ppc/boot/of1275/ofinit.c @@ -0,0 +1,27 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * Copyright (C) Leigh Brown 2002. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include "of1275.h" + +prom_entry of_prom_entry; +ihandle of_prom_mmu; + +void +ofinit(prom_entry prom_ptr) +{ + phandle chosen; + + of_prom_entry = prom_ptr; + + if ((chosen = finddevice("/chosen")) == OF_INVALID_HANDLE) + return; + if (getprop(chosen, "mmu", &of_prom_mmu, sizeof(ihandle)) != 4) + return; +} diff --git a/arch/ppc/boot/of1275/ofstdio.c b/arch/ppc/boot/of1275/ofstdio.c new file mode 100644 index 00000000000..10abbe32b31 --- /dev/null +++ b/arch/ppc/boot/of1275/ofstdio.c @@ -0,0 +1,32 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * Copyright (C) Leigh Brown 2002. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include "of1275.h" + +int +ofstdio(ihandle *stdin, ihandle *stdout, ihandle *stderr) +{ + ihandle in, out; + phandle chosen; + + if ((chosen = finddevice("/chosen")) == OF_INVALID_HANDLE) + goto err; + if (getprop(chosen, "stdout", &out, sizeof(out)) != 4) + goto err; + if (getprop(chosen, "stdin", &in, sizeof(in)) != 4) + goto err; + + *stdin = in; + *stdout = out; + *stderr = out; + return 0; +err: + return -1; +} diff --git a/arch/ppc/boot/of1275/read.c b/arch/ppc/boot/of1275/read.c new file mode 100644 index 00000000000..122813649fc --- /dev/null +++ b/arch/ppc/boot/of1275/read.c @@ -0,0 +1,35 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * Copyright (C) Leigh Brown 2002. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include "of1275.h" + +int +read(ihandle instance, void *buf, int buflen) +{ + struct prom_args { + char *service; + int nargs; + int nret; + ihandle instance; + void *buf; + int buflen; + int actual; + } args; + + args.service = "read"; + args.nargs = 3; + args.nret = 1; + args.instance = instance; + args.buf = buf; + args.buflen = buflen; + args.actual = -1; + (*of_prom_entry)(&args); + return args.actual; +} diff --git a/arch/ppc/boot/of1275/release.c b/arch/ppc/boot/of1275/release.c new file mode 100644 index 00000000000..28032d37145 --- /dev/null +++ b/arch/ppc/boot/of1275/release.c @@ -0,0 +1,30 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * Copyright (C) Leigh Brown 2002. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include "of1275.h" + +void +release(void *virt, unsigned int size) +{ + struct prom_args { + char *service; + int nargs; + int nret; + void *virt; + unsigned int size; + } args; + + args.service = "release"; + args.nargs = 2; + args.nret = 0; + args.virt = virt; + args.size = size; + (*of_prom_entry)(&args); +} diff --git a/arch/ppc/boot/of1275/write.c b/arch/ppc/boot/of1275/write.c new file mode 100644 index 00000000000..7361b9b2fca --- /dev/null +++ b/arch/ppc/boot/of1275/write.c @@ -0,0 +1,35 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * Copyright (C) Leigh Brown 2002. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include "of1275.h" + +int +write(ihandle instance, void *buf, int buflen) +{ + struct prom_args { + char *service; + int nargs; + int nret; + ihandle instance; + void *buf; + int buflen; + int actual; + } args; + + args.service = "write"; + args.nargs = 3; + args.nret = 1; + args.instance = instance; + args.buf = buf; + args.buflen = buflen; + args.actual = -1; + (*of_prom_entry)(&args); + return args.actual; +} diff --git a/arch/ppc/boot/openfirmware/Makefile b/arch/ppc/boot/openfirmware/Makefile new file mode 100644 index 00000000000..4eacbd8c772 --- /dev/null +++ b/arch/ppc/boot/openfirmware/Makefile @@ -0,0 +1,188 @@ +# Makefile for making bootable images on various OpenFirmware machines. +# +# Paul Mackerras January 1997 +# XCOFF bootable images for PowerMacs +# Geert Uytterhoeven September 1997 +# ELF bootable iamges for CHRP machines. +# Tom Rini January 2001 +# Cleaned up, moved into arch/ppc/boot/pmac +# Tom Rini July/August 2002 +# Merged 'chrp' and 'pmac' into 'openfirmware', and cleaned up the +# rules. + +zImage.initrd znetboot.initrd: del-ramdisk-sec := -R .ramdisk +zImage.initrd znetboot.initrd: initrd := .initrd + + +boot := arch/ppc/boot +common := $(boot)/common +utils := $(boot)/utils +bootlib := $(boot)/lib +of1275 := $(boot)/of1275 +images := $(boot)/images + +OBJCOPY_ARGS := -O aixcoff-rs6000 -R .stab -R .stabstr -R .comment +COFF_LD_ARGS := -T $(srctree)/$(boot)/ld.script -e _start -Ttext 0x00500000 \ + -Bstatic +CHRP_LD_ARGS := -T $(srctree)/$(boot)/ld.script -e _start -Ttext 0x00800000 +NEWWORLD_LD_ARGS:= -T $(srctree)/$(boot)/ld.script -e _start -Ttext 0x01000000 + +COMMONOBJS := start.o misc.o common.o +COFFOBJS := coffcrt0.o $(COMMONOBJS) coffmain.o +CHRPOBJS := crt0.o $(COMMONOBJS) chrpmain.o +NEWWORLDOBJS := crt0.o $(COMMONOBJS) newworldmain.o + +targets := $(COFFOBJS) $(CHRPOBJS) $(NEWWORLDOBJS) dummy.o +COFFOBJS := $(addprefix $(obj)/, $(COFFOBJS)) +CHRPOBJS := $(addprefix $(obj)/, $(CHRPOBJS)) +NEWWORLDOBJS := $(addprefix $(obj)/, $(NEWWORLDOBJS)) + +LIBS := lib/lib.a $(bootlib)/lib.a $(of1275)/lib.a $(common)/lib.a + +HACKCOFF := $(utils)/hack-coff + +ifdef CONFIG_SMP +END := .smp +endif +ifdef CONFIG_PPC64BRIDGE +END += .64 +endif + + +$(images)/ramdisk.image.gz: + @echo ' MISSING $@' + @echo ' RAM disk image must be provided separately' + @/bin/false + +objcpxmon-$(CONFIG_XMON) := --add-section=.sysmap=System.map \ + --set-section-flags=.sysmap=contents,alloc,load,readonly,data +quiet_cmd_genimage = GEN $@ + cmd_genimage = $(OBJCOPY) -R .comment \ + --add-section=.image=$(images)/vmlinux.gz \ + --set-section-flags=.image=contents,alloc,load,readonly,data \ + $(objcpxmon-y) $< $@ + +targets += image.o +$(obj)/image.o: $(obj)/dummy.o $(images)/vmlinux.gz FORCE + $(call if_changed,genimage) + +# Place the ramdisk in the initrd image. +quiet_cmd_genimage-initrd = GEN $@ + cmd_genimage-initrd = $(OBJCOPY) $< $@ \ + --add-section=.ramdisk=$(images)/ramdisk.image.gz \ + --set-section-flags=.ramdisk=contents,alloc,load,readonly,data +targets += image.initrd.o +$(obj)/image.initrd.o: $(obj)/image.o $(images)/ramdisk.image.gz FORCE + $(call if_changed,genimage-initrd) + +# Create the note section for New-World PowerMacs. +quiet_cmd_mknote = MKNOTE $@ + cmd_mknote = $(utils)/mknote > $@ +targets += note +$(obj)/note: $(utils)/mknote FORCE + $(call if_changed,mknote) + + +$(obj)/coffcrt0.o: EXTRA_AFLAGS := -traditional -DXCOFF +$(obj)/crt0.o: EXTRA_AFLAGS := -traditional +targets += coffcrt0.o crt0.o +$(obj)/coffcrt0.o $(obj)/crt0.o: $(common)/crt0.S FORCE + $(call if_changed_dep,as_o_S) + +quiet_cmd_gencoffb = COFF $@ + cmd_gencoffb = $(LD) -o $@ $(COFF_LD_ARGS) $(COFFOBJS) $< $(LIBS) && \ + $(OBJCOPY) $@ $@ -R .comment $(del-ramdisk-sec) +targets += coffboot +$(obj)/coffboot: $(obj)/image.o $(COFFOBJS) $(LIBS) $(srctree)/$(boot)/ld.script FORCE + $(call if_changed,gencoffb) +targets += coffboot.initrd +$(obj)/coffboot.initrd: $(obj)/image.initrd.o $(COFFOBJS) $(LIBS) \ + $(srctree)/$(boot)/ld.script FORCE + $(call if_changed,gencoffb) + + +quiet_cmd_gen-coff = COFF $@ + cmd_gen-coff = $(OBJCOPY) $(OBJCOPY_ARGS) $< $@ && \ + $(HACKCOFF) $@ && \ + ln -sf $(notdir $@) $(images)/zImage$(initrd).pmac + +$(images)/vmlinux.coff: $(obj)/coffboot + $(call cmd,gen-coff) + +$(images)/vmlinux.initrd.coff: $(obj)/coffboot.initrd + $(call cmd,gen-coff) + +quiet_cmd_gen-elf-pmac = ELF $@ + cmd_gen-elf-pmac = $(LD) $(NEWWORLD_LD_ARGS) -o $@ \ + $(NEWWORLDOBJS) $(LIBS) $< && \ + $(OBJCOPY) $@ $@ --add-section=.note=$(obj)/note \ + -R .comment $(del-ramdisk-sec) + +$(images)/vmlinux.elf-pmac: $(obj)/image.o $(NEWWORLDOBJS) $(LIBS) \ + $(obj)/note $(srctree)/$(boot)/ld.script + $(call cmd,gen-elf-pmac) +$(images)/vmlinux.initrd.elf-pmac: $(obj)/image.initrd.o $(NEWWORLDOBJS) \ + $(LIBS) $(obj)/note \ + $(srctree)/$(boot)/ld.script + $(call cmd,gen-elf-pmac) + +quiet_cmd_gen-chrp = CHRP $@ + cmd_gen-chrp = $(LD) $(CHRP_LD_ARGS) -o $@ $(CHRPOBJS) $< $(LIBS) && \ + $(OBJCOPY) $@ $@ -R .comment $(del-ramdisk-sec) + +$(images)/zImage.chrp: $(obj)/image.o $(CHRPOBJS) $(LIBS) \ + $(srctree)/$(boot)/ld.script + $(call cmd,gen-chrp) +$(images)/zImage.initrd.chrp: $(obj)/image.initrd.o $(CHRPOBJS) $(LIBS) \ + $(srctree)/$(boot)/ld.script + $(call cmd,gen-chrp) + +quiet_cmd_addnote = ADDNOTE $@ + cmd_addnote = cat $< > $@ && $(utils)/addnote $@ +$(images)/zImage.chrp-rs6k $(images)/zImage.initrd.chrp-rs6k: \ + %-rs6k: % + $(call cmd,addnote) + +quiet_cmd_gen-miboot = GEN $@ + cmd_gen-miboot = $(OBJCOPY) $(OBJCOPY_ARGS) \ + --add-section=$1=$(word 2, $^) $< $@ +$(images)/miboot.image: $(obj)/dummy.o $(images)/vmlinux.gz + $(call cmd,gen-miboot,image) + +$(images)/miboot.initrd.image: $(images)/miboot.image $(images)/ramdisk.image.gz + $(call cmd,gen-miboot,initrd) + +# The targets used on the make command-line + +.PHONY: zImage zImage.initrd +zImage: $(images)/vmlinux.coff \ + $(images)/vmlinux.elf-pmac \ + $(images)/zImage.chrp \ + $(images)/zImage.chrp-rs6k \ + $(images)/miboot.image + @echo ' kernel: $@ is ready ($<)' +zImage.initrd: $(images)/vmlinux.initrd.coff \ + $(images)/vmlinux.initrd.elf-pmac \ + $(images)/zImage.initrd.chrp \ + $(images)/zImage.initrd.chrp-rs6k \ + $(images)/miboot.initrd.image + @echo ' kernel: $@ is ready ($<)' + +TFTPIMAGE := /tftpboot/zImage + +.PHONY: znetboot znetboot.initrd +znetboot: $(images)/vmlinux.coff \ + $(images)/vmlinux.elf-pmac \ + $(images)/zImage.chrp + cp $(images)/vmlinux.coff $(TFTPIMAGE).pmac$(END) + cp $(images)/vmlinux.elf-pmac $(TFTPIMAGE).pmac$(END).elf + cp $(images)/zImage.chrp $(TFTPIMAGE).chrp$(END) + @echo ' kernel: $@ is ready ($<)' +znetboot.initrd:$(images)/vmlinux.initrd.coff \ + $(images)/vmlinux.initrd.elf-pmac \ + $(images)/zImage.initrd.chrp + cp $(images)/vmlinux.initrd.coff $(TFTPIMAGE).pmac$(END) + cp $(images)/vmlinux.initrd.elf-pmac $(TFTPIMAGE).pmac$(END).elf + cp $(images)/zImage.initrd.chrp $(TFTPIMAGE).chrp$(END) + @echo ' kernel: $@ is ready ($<)' + diff --git a/arch/ppc/boot/openfirmware/chrpmain.c b/arch/ppc/boot/openfirmware/chrpmain.c new file mode 100644 index 00000000000..6fb4f738728 --- /dev/null +++ b/arch/ppc/boot/openfirmware/chrpmain.c @@ -0,0 +1,101 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include "nonstdio.h" +#include "of1275.h" +#include +#include + +/* Passed from the linker */ +extern char __image_begin, __image_end; +extern char __ramdisk_begin, __ramdisk_end; +extern char _start, _end; + +extern unsigned int heap_max; +extern void flush_cache(void *, unsigned long); +extern void gunzip(void *, int, unsigned char *, int *); +extern void make_bi_recs(unsigned long addr, char *name, unsigned int mach, + unsigned int progend); + +char *avail_ram; +char *begin_avail, *end_avail; +char *avail_high; + +#define RAM_START 0x00000000 +#define RAM_END (64<<20) + +#define BOOT_START ((unsigned long)_start) +#define BOOT_END ((unsigned long)(_end + 0xFFF) & ~0xFFF) + +#define RAM_FREE ((unsigned long)(_end+0x1000)&~0xFFF) +#define PROG_START 0x00010000 +#define PROG_SIZE 0x007f0000 /* 8MB */ + +#define SCRATCH_SIZE (128 << 10) + +static char scratch[SCRATCH_SIZE]; /* 1MB of scratch space for gunzip */ + +typedef void (*kernel_start_t)(int, int, void *, unsigned int, unsigned int); + +void +boot(int a1, int a2, void *prom) +{ + unsigned sa, len; + void *dst; + unsigned char *im; + unsigned int initrd_size, initrd_start; + + printf("chrpboot starting: loaded at 0x%p\n\r", &_start); + + initrd_size = &__ramdisk_end - &__ramdisk_begin; + if (initrd_size) { + initrd_start = (RAM_END - initrd_size) & ~0xFFF; + a1 = initrd_start; + a2 = initrd_size; + claim(initrd_start, RAM_END - initrd_start, 0); + printf("initial ramdisk moving 0x%x <- 0x%p (%x bytes)\n\r", + initrd_start, &__ramdisk_begin, initrd_size); + memcpy((char *)initrd_start, &__ramdisk_begin, initrd_size); + } else { + initrd_start = 0; + initrd_size = 0; + a2 = 0xdeadbeef; + } + + im = &__image_begin; + len = &__image_end - &__image_begin; + /* claim 4MB starting at PROG_START */ + claim(PROG_START, PROG_SIZE - PROG_START, 0); + dst = (void *) PROG_START; + if (im[0] == 0x1f && im[1] == 0x8b) { + avail_ram = scratch; + begin_avail = avail_high = avail_ram; + end_avail = scratch + sizeof(scratch); + printf("gunzipping (0x%p <- 0x%p:0x%p)...", dst, im, im+len); + gunzip(dst, 0x400000, im, &len); + printf("done %u bytes\n\r", len); + printf("%u bytes of heap consumed, max in use %u\n\r", + avail_high - begin_avail, heap_max); + } else { + memmove(dst, im, len); + } + + flush_cache(dst, len); + make_bi_recs(((unsigned long) dst + len), "chrpboot", _MACH_chrp, + (PROG_START + PROG_SIZE)); + + sa = PROG_START; + printf("start address = 0x%x\n\r", sa); + + (*(kernel_start_t)sa)(a1, a2, prom, initrd_start, initrd_size); + + printf("returned?\n\r"); + + pause(); +} diff --git a/arch/ppc/boot/openfirmware/coffmain.c b/arch/ppc/boot/openfirmware/coffmain.c new file mode 100644 index 00000000000..04ba9d57e11 --- /dev/null +++ b/arch/ppc/boot/openfirmware/coffmain.c @@ -0,0 +1,101 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include + +#include "nonstdio.h" +#include "of1275.h" + +/* Passed from the linker */ +extern char __image_begin, __image_end; +extern char __ramdisk_begin[], __ramdisk_end; +extern char _start, _end; + +extern char image_data[], initrd_data[]; +extern int initrd_len, image_len; +extern unsigned int heap_max; +extern void flush_cache(void *start, unsigned int len); +extern void gunzip(void *, int, unsigned char *, int *); +extern void make_bi_recs(unsigned long addr, char *name, unsigned int mach, + unsigned int progend); +extern void setup_bats(unsigned long start); + +char *avail_ram; +char *begin_avail, *end_avail; +char *avail_high; + +#define SCRATCH_SIZE (128 << 10) + +static char heap[SCRATCH_SIZE]; + +static unsigned long ram_start = 0; +static unsigned long ram_end = 0x1000000; + +static unsigned long prog_start = 0x900000; +static unsigned long prog_size = 0x700000; + +typedef void (*kernel_start_t)(int, int, void *); + +void boot(int a1, int a2, void *prom) +{ + unsigned sa, len; + void *dst; + unsigned char *im; + unsigned initrd_start, initrd_size; + + printf("coffboot starting: loaded at 0x%p\n", &_start); + setup_bats(ram_start); + + initrd_size = (char *)(&__ramdisk_end) - (char *)(&__ramdisk_begin); + if (initrd_size) { + initrd_start = (ram_end - initrd_size) & ~0xFFF; + a1 = initrd_start; + a2 = initrd_size; + claim(initrd_start, ram_end - initrd_start, 0); + printf("initial ramdisk moving 0x%x <- 0x%p (%x bytes)\n\r", + initrd_start, (char *)(&__ramdisk_begin), initrd_size); + memcpy((char *)initrd_start, (char *)(&__ramdisk_begin), initrd_size); + prog_size = initrd_start - prog_start; + } else + a2 = 0xdeadbeef; + + im = (char *)(&__image_begin); + len = (char *)(&__image_end) - (char *)(&__image_begin); + /* claim 4MB starting at PROG_START */ + claim(prog_start, prog_size, 0); + map(prog_start, prog_start, prog_size); + dst = (void *) prog_start; + if (im[0] == 0x1f && im[1] == 0x8b) { + /* set up scratch space */ + begin_avail = avail_high = avail_ram = heap; + end_avail = heap + sizeof(heap); + printf("heap at 0x%p\n", avail_ram); + printf("gunzipping (0x%p <- 0x%p:0x%p)...", dst, im, im+len); + gunzip(dst, prog_size, im, &len); + printf("done %u bytes\n", len); + printf("%u bytes of heap consumed, max in use %u\n", + avail_high - begin_avail, heap_max); + } else { + memmove(dst, im, len); + } + + flush_cache(dst, len); + make_bi_recs(((unsigned long) dst + len), "coffboot", _MACH_Pmac, + (prog_start + prog_size)); + + sa = (unsigned long)prog_start; + printf("start address = 0x%x\n", sa); + + (*(kernel_start_t)sa)(a1, a2, prom); + + printf("returned?\n"); + + pause(); +} diff --git a/arch/ppc/boot/openfirmware/common.c b/arch/ppc/boot/openfirmware/common.c new file mode 100644 index 00000000000..9e6952781f1 --- /dev/null +++ b/arch/ppc/boot/openfirmware/common.c @@ -0,0 +1,162 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include "nonstdio.h" +#include "of1275.h" +#include +#include +#include +#include + +/* Information from the linker */ +extern char __sysmap_begin, __sysmap_end; + +extern int strcmp(const char *s1, const char *s2); +extern char *avail_ram, *avail_high; +extern char *end_avail; + +unsigned int heap_use, heap_max; + +struct memchunk { + unsigned int size; + struct memchunk *next; +}; + +static struct memchunk *freechunks; + +static void *zalloc(unsigned size) +{ + void *p; + struct memchunk **mpp, *mp; + + size = (size + 7) & -8; + heap_use += size; + if (heap_use > heap_max) + heap_max = heap_use; + for (mpp = &freechunks; (mp = *mpp) != 0; mpp = &mp->next) { + if (mp->size == size) { + *mpp = mp->next; + return mp; + } + } + p = avail_ram; + avail_ram += size; + if (avail_ram > avail_high) + avail_high = avail_ram; + if (avail_ram > end_avail) { + printf("oops... out of memory\n\r"); + pause(); + } + return p; +} + +#define HEAD_CRC 2 +#define EXTRA_FIELD 4 +#define ORIG_NAME 8 +#define COMMENT 0x10 +#define RESERVED 0xe0 + +void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp) +{ + z_stream s; + int r, i, flags; + + /* skip header */ + i = 10; + flags = src[3]; + if (src[2] != Z_DEFLATED || (flags & RESERVED) != 0) { + printf("bad gzipped data\n\r"); + exit(); + } + if ((flags & EXTRA_FIELD) != 0) + i = 12 + src[10] + (src[11] << 8); + if ((flags & ORIG_NAME) != 0) + while (src[i++] != 0) + ; + if ((flags & COMMENT) != 0) + while (src[i++] != 0) + ; + if ((flags & HEAD_CRC) != 0) + i += 2; + if (i >= *lenp) { + printf("gunzip: ran out of data in header\n\r"); + exit(); + } + + /* Initialize ourself. */ + s.workspace = zalloc(zlib_inflate_workspacesize()); + r = zlib_inflateInit2(&s, -MAX_WBITS); + if (r != Z_OK) { + printf("zlib_inflateInit2 returned %d\n\r", r); + exit(); + } + s.next_in = src + i; + s.avail_in = *lenp - i; + s.next_out = dst; + s.avail_out = dstlen; + r = zlib_inflate(&s, Z_FINISH); + if (r != Z_OK && r != Z_STREAM_END) { + printf("inflate returned %d msg: %s\n\r", r, s.msg); + exit(); + } + *lenp = s.next_out - (unsigned char *) dst; + zlib_inflateEnd(&s); +} + +/* Make a bi_rec in OF. We need to be passed a name for BI_BOOTLOADER_ID, + * a machine type for BI_MACHTYPE, and the location where the end of the + * bootloader is (PROG_START + PROG_SIZE) + */ +void make_bi_recs(unsigned long addr, char *name, unsigned int mach, + unsigned long progend) +{ + unsigned long sysmap_size; + struct bi_record *rec; + + /* Figure out the size of a possible System.map we're going to + * pass along. + * */ + sysmap_size = (unsigned long)(&__sysmap_end) - + (unsigned long)(&__sysmap_begin); + + /* leave a 1MB gap then align to the next 1MB boundary */ + addr = _ALIGN(addr+ (1<<20) - 1, (1<<20)); + /* oldworld machine seem very unhappy about this. -- Tom */ + if (addr >= progend) + claim(addr, 0x1000, 0); + + rec = (struct bi_record *)addr; + rec->tag = BI_FIRST; + rec->size = sizeof(struct bi_record); + rec = (struct bi_record *)((unsigned long)rec + rec->size); + + rec->tag = BI_BOOTLOADER_ID; + sprintf( (char *)rec->data, name); + rec->size = sizeof(struct bi_record) + strlen(name) + 1; + rec = (struct bi_record *)((unsigned long)rec + rec->size); + + rec->tag = BI_MACHTYPE; + rec->data[0] = mach; + rec->data[1] = 1; + rec->size = sizeof(struct bi_record) + 2 * sizeof(unsigned long); + rec = (struct bi_record *)((unsigned long)rec + rec->size); + + if (sysmap_size) { + rec->tag = BI_SYSMAP; + rec->data[0] = (unsigned long)(&__sysmap_begin); + rec->data[1] = sysmap_size; + rec->size = sizeof(struct bi_record) + 2 * + sizeof(unsigned long); + rec = (struct bi_record *)((unsigned long)rec + rec->size); + } + + rec->tag = BI_LAST; + rec->size = sizeof(struct bi_record); + rec = (struct bi_record *)((unsigned long)rec + rec->size); +} diff --git a/arch/ppc/boot/openfirmware/dummy.c b/arch/ppc/boot/openfirmware/dummy.c new file mode 100644 index 00000000000..31dbf45bf99 --- /dev/null +++ b/arch/ppc/boot/openfirmware/dummy.c @@ -0,0 +1,4 @@ +int main(void) +{ + return 0; +} diff --git a/arch/ppc/boot/openfirmware/misc.S b/arch/ppc/boot/openfirmware/misc.S new file mode 100644 index 00000000000..ab9e897cadd --- /dev/null +++ b/arch/ppc/boot/openfirmware/misc.S @@ -0,0 +1,67 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + .text + +/* + * Use the BAT2 & 3 registers to map the 1st 16MB of RAM to + * the address given as the 1st argument. + */ + .globl setup_bats +setup_bats: + mfpvr 5 + rlwinm 5,5,16,16,31 /* r3 = 1 for 601, 4 for 604 */ + cmpwi 0,5,1 + li 0,0 + bne 4f + mtibatl 3,0 /* invalidate BAT first */ + ori 3,3,4 /* set up BAT registers for 601 */ + li 4,0x7f + mtibatu 2,3 + mtibatl 2,4 + oris 3,3,0x80 + oris 4,4,0x80 + mtibatu 3,3 + mtibatl 3,4 + b 5f +4: mtdbatu 3,0 /* invalidate BATs first */ + mtibatu 3,0 + ori 3,3,0xff /* set up BAT registers for 604 */ + li 4,2 + mtdbatl 2,4 + mtdbatu 2,3 + mtibatl 2,4 + mtibatu 2,3 + oris 3,3,0x80 + oris 4,4,0x80 + mtdbatl 3,4 + mtdbatu 3,3 + mtibatl 3,4 + mtibatu 3,3 +5: sync + isync + blr + +/* + * Flush the dcache and invalidate the icache for a range of addresses. + * + * flush_cache(addr, len) + */ + .global flush_cache +flush_cache: + addi 4,4,0x1f /* len = (len + 0x1f) / 0x20 */ + rlwinm. 4,4,27,5,31 + mtctr 4 + beqlr +1: dcbf 0,3 + icbi 0,3 + addi 3,3,0x20 + bdnz 1b + sync + isync + blr diff --git a/arch/ppc/boot/openfirmware/newworldmain.c b/arch/ppc/boot/openfirmware/newworldmain.c new file mode 100644 index 00000000000..fa8a8f9313f --- /dev/null +++ b/arch/ppc/boot/openfirmware/newworldmain.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include "nonstdio.h" +#include "of1275.h" +#include +#include + +/* Passed from the linker */ +extern char __image_begin, __image_end; +extern char __ramdisk_begin[], __ramdisk_end; +extern char _start, _end; + +extern unsigned int heap_max; +extern void flush_cache(void *start, unsigned int len); +extern void gunzip(void *, int, unsigned char *, int *); +extern void make_bi_recs(unsigned long addr, char *name, unsigned int mach, + unsigned int progend); + +char *avail_ram; +char *begin_avail, *end_avail; +char *avail_high; + + +#define RAM_END (16 << 20) + +#define PROG_START 0x00010000 +#define PROG_SIZE 0x007f0000 + +#define SCRATCH_SIZE (128 << 10) + +typedef void (*kernel_start_t)(int, int, void *); + +void boot(int a1, int a2, void *prom) +{ + unsigned sa, len; + void *dst; + unsigned char *im; + unsigned initrd_start, initrd_size; + + printf("chrpboot starting: loaded at 0x%p\n", &_start); + + initrd_size = (char *)(&__ramdisk_end) - (char *)(&__ramdisk_begin); + if (initrd_size) { + initrd_start = (RAM_END - initrd_size) & ~0xFFF; + a1 = initrd_start; + a2 = initrd_size; + claim(initrd_start, RAM_END - initrd_start, 0); + printf("initial ramdisk moving 0x%x <- 0x%p (%x bytes)\n\r", + initrd_start, (char *)(&__ramdisk_begin), initrd_size); + memcpy((char *)initrd_start, (char *)(&__ramdisk_begin), initrd_size); + } else + a2 = 0xdeadbeef; + + im = (char *)(&__image_begin); + len = (char *)(&__image_end) - (char *)(&__image_begin); + /* claim 3MB starting at PROG_START */ + claim(PROG_START, PROG_SIZE, 0); + dst = (void *) PROG_START; + if (im[0] == 0x1f && im[1] == 0x8b) { + /* claim some memory for scratch space */ + avail_ram = (char *) claim(0, SCRATCH_SIZE, 0x10); + begin_avail = avail_high = avail_ram; + end_avail = avail_ram + SCRATCH_SIZE; + printf("heap at 0x%p\n", avail_ram); + printf("gunzipping (0x%p <- 0x%p:0x%p)...", dst, im, im+len); + gunzip(dst, PROG_SIZE, im, &len); + printf("done %u bytes\n", len); + printf("%u bytes of heap consumed, max in use %u\n", + avail_high - begin_avail, heap_max); + release(begin_avail, SCRATCH_SIZE); + } else { + memmove(dst, im, len); + } + + flush_cache(dst, len); + make_bi_recs(((unsigned long) dst + len), "chrpboot", _MACH_Pmac, + (PROG_START + PROG_SIZE)); + + sa = (unsigned long)PROG_START; + printf("start address = 0x%x\n", sa); + + (*(kernel_start_t)sa)(a1, a2, prom); + + printf("returned?\n"); + + pause(); +} diff --git a/arch/ppc/boot/openfirmware/start.c b/arch/ppc/boot/openfirmware/start.c new file mode 100644 index 00000000000..1617a26956b --- /dev/null +++ b/arch/ppc/boot/openfirmware/start.c @@ -0,0 +1,172 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include "of1275.h" + +extern int strlen(const char *s); +extern void boot(int a1, int a2, void *prom); + +phandle stdin; +phandle stdout; +phandle stderr; + +void printk(char *fmt, ...); + +void +start(int a1, int a2, void *promptr) +{ + ofinit(promptr); + if (ofstdio(&stdin, &stdout, &stderr)) + exit(); + + boot(a1, a2, promptr); + for (;;) + exit(); +} + +int writestring(void *f, char *ptr, int nb) +{ + int w = 0, i; + char *ret = "\r"; + + for (i = 0; i < nb; ++i) { + if (ptr[i] == '\n') { + if (i > w) { + write(f, ptr + w, i - w); + w = i; + } + write(f, ret, 1); + } + } + if (w < nb) + write(f, ptr + w, nb - w); + return nb; +} + +int +putc(int c, void *f) +{ + char ch = c; + + return writestring(f, &ch, 1) == 1? c: -1; +} + +int +putchar(int c) +{ + return putc(c, stdout); +} + +int +fputs(char *str, void *f) +{ + int n = strlen(str); + + return writestring(f, str, n) == n? 0: -1; +} + +int +readchar(void) +{ + char ch; + + for (;;) { + switch (read(stdin, &ch, 1)) { + case 1: + return ch; + case -1: + printk("read(stdin) returned -1\n"); + return -1; + } + } +} + +static char line[256]; +static char *lineptr; +static int lineleft; + +int +getchar(void) +{ + int c; + + if (lineleft == 0) { + lineptr = line; + for (;;) { + c = readchar(); + if (c == -1 || c == 4) + break; + if (c == '\r' || c == '\n') { + *lineptr++ = '\n'; + putchar('\n'); + break; + } + switch (c) { + case 0177: + case '\b': + if (lineptr > line) { + putchar('\b'); + putchar(' '); + putchar('\b'); + --lineptr; + } + break; + case 'U' & 0x1F: + while (lineptr > line) { + putchar('\b'); + putchar(' '); + putchar('\b'); + --lineptr; + } + break; + default: + if (lineptr >= &line[sizeof(line) - 1]) + putchar('\a'); + else { + putchar(c); + *lineptr++ = c; + } + } + } + lineleft = lineptr - line; + lineptr = line; + } + if (lineleft == 0) + return -1; + --lineleft; + return *lineptr++; +} + +extern int vsprintf(char *buf, const char *fmt, va_list args); +static char sprint_buf[1024]; + +void +printk(char *fmt, ...) +{ + va_list args; + int n; + + va_start(args, fmt); + n = vsprintf(sprint_buf, fmt, args); + va_end(args); + writestring(stdout, sprint_buf, n); +} + +int +printf(char *fmt, ...) +{ + va_list args; + int n; + + va_start(args, fmt); + n = vsprintf(sprint_buf, fmt, args); + va_end(args); + writestring(stdout, sprint_buf, n); + return n; +} diff --git a/arch/ppc/boot/simple/Makefile b/arch/ppc/boot/simple/Makefile new file mode 100644 index 00000000000..d8d801fcee1 --- /dev/null +++ b/arch/ppc/boot/simple/Makefile @@ -0,0 +1,252 @@ +# This is far from simple, but I couldn't think of a good name. This is +# for making the 'zImage' or 'zImage.initrd' on a number of targets. +# +# Author: Tom Rini +# +# Notes: +# (1) For machines that do not want to use the ELF image directly (including +# stripping just the ELF header off), they must set the variables +# zimage-$(CONFIG_MACHINE) and zimagerd-$(CONFIG_MACHINE) to the target +# that produces the desired image and they must set end-$(CONFIG_MACHINE) +# to what will be suffixed to the image filename. +# (2) Regardless of (1), to have the resulting image be something other +# than 'zImage.elf', set end-$(CONFIG_MACHINE) to be the suffix used for +# the zImage, znetboot, and znetbootrd targets. +# (3) For machine targets which use the mktree program, you can optionally +# set entrypoint-$(CONFIG_MACHINE) to the location which the image should be +# loaded at. The optimal setting for entrypoint-$(CONFIG_MACHINE) is the link +# address. +# (4) It is advisable to pass in the memory size using BI_MEMSIZE and +# get_mem_size(), which is memory controller dependent. Add in the correct +# XXX_memory.o file for this to work, as well as editing the +# misc-$(CONFIG_MACHINE) variable. + +boot := arch/ppc/boot +common := $(boot)/common +utils := $(boot)/utils +bootlib := $(boot)/lib +images := $(boot)/images +of1275 := $(boot)/of1275 +tftpboot := /tftpboot + +# Normally, we use the 'misc.c' file for decompress_kernel and +# whatnot. Sometimes we need to override this however. +misc-y := misc.o + +# Normally, we have our images end in .elf, but something we want to +# change this. +end-y := elf + +# Additionally, we normally don't need to mess with the L2 / L3 caches +# if present on 'classic' PPC. +cacheflag-y := -DCLEAR_CACHES="" +# This file will flush / disable the L2, and L3 if present. +clear_L2_L3 := $(srctree)/$(boot)/simple/clear.S + +# +# See arch/ppc/kconfig and arch/ppc/platforms/Kconfig +# for definition of what platform each config option refer to. +#---------------------------------------------------------------------------- + zimage-$(CONFIG_CPCI690) := zImage-STRIPELF +zimageinitrd-$(CONFIG_CPCI690) := zImage.initrd-STRIPELF + extra.o-$(CONFIG_CPCI690) := misc-cpci690.o + end-$(CONFIG_CPCI690) := cpci690 + cacheflag-$(CONFIG_CPCI690) := -include $(clear_L2_L3) + + zimage-$(CONFIG_IBM_OPENBIOS) := zImage-TREE +zimageinitrd-$(CONFIG_IBM_OPENBIOS) := zImage.initrd-TREE + end-$(CONFIG_IBM_OPENBIOS) := treeboot + misc-$(CONFIG_IBM_OPENBIOS) := misc-embedded.o + + end-$(CONFIG_EMBEDDEDBOOT) := embedded + misc-$(CONFIG_EMBEDDEDBOOT) := misc-embedded.o + + zimage-$(CONFIG_EBONY) := zImage-TREE +zimageinitrd-$(CONFIG_EBONY) := zImage.initrd-TREE + end-$(CONFIG_EBONY) := ebony + entrypoint-$(CONFIG_EBONY) := 0x01000000 + extra.o-$(CONFIG_EBONY) := openbios.o + + zimage-$(CONFIG_LUAN) := zImage-TREE +zimageinitrd-$(CONFIG_LUAN) := zImage.initrd-TREE + end-$(CONFIG_LUAN) := luan + entrypoint-$(CONFIG_LUAN) := 0x01000000 + extra.o-$(CONFIG_LUAN) := pibs.o + + zimage-$(CONFIG_OCOTEA) := zImage-TREE +zimageinitrd-$(CONFIG_OCOTEA) := zImage.initrd-TREE + end-$(CONFIG_OCOTEA) := ocotea + entrypoint-$(CONFIG_OCOTEA) := 0x01000000 + extra.o-$(CONFIG_OCOTEA) := pibs.o + + extra.o-$(CONFIG_EV64260) := misc-ev64260.o + end-$(CONFIG_EV64260) := ev64260 + cacheflag-$(CONFIG_EV64260) := -include $(clear_L2_L3) + + extra.o-$(CONFIG_CHESTNUT) := misc-chestnut.o + end-$(CONFIG_CHESTNUT) := chestnut + + zimage-$(CONFIG_GEMINI) := zImage-STRIPELF +zimageinitrd-$(CONFIG_GEMINI) := zImage.initrd-STRIPELF + end-$(CONFIG_GEMINI) := gemini + + extra.o-$(CONFIG_K2) := prepmap.o + end-$(CONFIG_K2) := k2 + cacheflag-$(CONFIG_K2) := -include $(clear_L2_L3) + + extra.o-$(CONFIG_KATANA) := misc-katana.o + end-$(CONFIG_KATANA) := katana + cacheflag-$(CONFIG_KATANA) := -include $(clear_L2_L3) + + extra.o-$(CONFIG_RADSTONE_PPC7D) := misc-radstone_ppc7d.o + end-$(CONFIG_RADSTONE_PPC7D) := radstone_ppc7d + cacheflag-$(CONFIG_RADSTONE_PPC7D) := -include $(clear_L2_L3) + +# kconfig 'feature', only one of these will ever be 'y' at a time. +# The rest will be unset. +motorola := $(CONFIG_MCPN765)$(CONFIG_MVME5100)$(CONFIG_PRPMC750) \ +$(CONFIG_PRPMC800)$(CONFIG_LOPEC)$(CONFIG_PPLUS) +motorola := $(strip $(motorola)) +pcore := $(CONFIG_PCORE)$(CONFIG_POWERPMC250) + + zimage-$(motorola) := zImage-PPLUS +zimageinitrd-$(motorola) := zImage.initrd-PPLUS + end-$(motorola) := pplus + +# Overrides previous assingment + extra.o-$(CONFIG_PPLUS) := prepmap.o + extra.o-$(CONFIG_LOPEC) := mpc10x_memory.o + + zimage-$(pcore) := zImage-STRIPELF +zimageinitrd-$(pcore) := zImage.initrd-STRIPELF + extra.o-$(pcore) := chrpmap.o + end-$(pcore) := pcore + cacheflag-$(pcore) := -include $(clear_L2_L3) + + zimage-$(CONFIG_PPC_PREP) := zImage-PPLUS +zimageinitrd-$(CONFIG_PPC_PREP) := zImage.initrd-PPLUS + extra.o-$(CONFIG_PPC_PREP) := prepmap.o + misc-$(CONFIG_PPC_PREP) += misc-prep.o mpc10x_memory.o + end-$(CONFIG_PPC_PREP) := prep + + end-$(CONFIG_SANDPOINT) := sandpoint + cacheflag-$(CONFIG_SANDPOINT) := -include $(clear_L2_L3) + + zimage-$(CONFIG_SPRUCE) := zImage-TREE +zimageinitrd-$(CONFIG_SPRUCE) := zImage.initrd-TREE + end-$(CONFIG_SPRUCE) := spruce + entrypoint-$(CONFIG_SPRUCE) := 0x00800000 + misc-$(CONFIG_SPRUCE) += misc-spruce.o + + zimage-$(CONFIG_LITE5200) := zImage-STRIPELF +zimageinitrd-$(CONFIG_LITE5200) := zImage.initrd-STRIPELF + end-$(CONFIG_LITE5200) := lite5200 + cacheflag-$(CONFIG_LITE5200) := -include $(clear_L2_L3) + + +# SMP images should have a '.smp' suffix. + end-$(CONFIG_SMP) := $(end-y).smp + +# This is a treeboot that needs init functions until the +# boot rom is sorted out (i.e. this is short lived) +extra-aflags-$(CONFIG_REDWOOD_4) := -Wa,-m405 +extra.o-$(CONFIG_REDWOOD_4) := rw4/rw4_init.o rw4/rw4_init_brd.o +EXTRA_AFLAGS := $(extra-aflags-y) +# head.o needs to get the cacheflags defined. +AFLAGS_head.o += $(cacheflag-y) + +# Linker args. This specifies where the image will be run at. +LD_ARGS := -T $(srctree)/$(boot)/ld.script \ + -Ttext $(CONFIG_BOOT_LOAD) -Bstatic +OBJCOPY_ARGS := -O elf32-powerpc + +# head.o and relocate.o must be at the start. +boot-y := head.o relocate.o $(extra.o-y) $(misc-y) +boot-$(CONFIG_40x) += embed_config.o +boot-$(CONFIG_8xx) += embed_config.o +boot-$(CONFIG_8260) += embed_config.o +boot-$(CONFIG_BSEIP) += iic.o +boot-$(CONFIG_MBX) += iic.o pci.o qspan_pci.o +boot-$(CONFIG_MV64X60) += misc-mv64x60.o +boot-$(CONFIG_RPXCLASSIC) += iic.o pci.o qspan_pci.o +boot-$(CONFIG_RPXLITE) += iic.o +# Different boards need different serial implementations. +ifeq ($(CONFIG_SERIAL_CPM_CONSOLE),y) +boot-$(CONFIG_8xx) += m8xx_tty.o +boot-$(CONFIG_8260) += m8260_tty.o +endif +boot-$(CONFIG_SERIAL_MPC52xx_CONSOLE) += mpc52xx_tty.o +boot-$(CONFIG_SERIAL_MPSC_CONSOLE) += mv64x60_tty.o + +LIBS := $(common)/lib.a $(bootlib)/lib.a +ifeq ($(CONFIG_PPC_PREP),y) +LIBS += $(of1275)/lib.a +endif + +OBJS := $(addprefix $(obj)/,$(boot-y)) + +# Tools +MKBUGBOOT := $(utils)/mkbugboot +MKPREP := $(utils)/mkprep +MKTREE := $(utils)/mktree + +targets := dummy.o + +$(obj)/zvmlinux: $(OBJS) $(LIBS) $(srctree)/$(boot)/ld.script \ + $(images)/vmlinux.gz $(obj)/dummy.o + $(OBJCOPY) $(OBJCOPY_ARGS) \ + --add-section=.image=$(images)/vmlinux.gz \ + --set-section-flags=.image=contents,alloc,load,readonly,data \ + $(obj)/dummy.o $(obj)/image.o + $(LD) $(LD_ARGS) -o $@ $(OBJS) $(obj)/image.o $(LIBS) + $(OBJCOPY) $(OBJCOPY_ARGS) $@ $@ -R .comment -R .stab \ + -R .stabstr -R .ramdisk -R .sysmap + +$(obj)/zvmlinux.initrd: $(OBJS) $(LIBS) $(srctree)/$(boot)/ld.script \ + $(images)/vmlinux.gz $(obj)/dummy.o + $(OBJCOPY) $(OBJCOPY_ARGS) \ + --add-section=.ramdisk=$(images)/ramdisk.image.gz \ + --set-section-flags=.ramdisk=contents,alloc,load,readonly,data \ + --add-section=.image=$(images)/vmlinux.gz \ + --set-section-flags=.image=contents,alloc,load,readonly,data \ + $(obj)/dummy.o $(obj)/image.o + $(LD) $(LD_ARGS) -o $@ $(OBJS) $(obj)/image.o $(LIBS) + $(OBJCOPY) $(OBJCOPY_ARGS) $@ $@ -R .comment -R .stab \ + -R .stabstr -R .sysmap + +# Sort-of dummy rules, that let us format the image we want. +zImage: $(images)/$(zimage-y) $(obj)/zvmlinux + cp -f $(obj)/zvmlinux $(images)/zImage.elf + rm -f $(obj)/zvmlinux + +zImage.initrd: $(images)/$(zimageinitrd-y) $(obj)/zvmlinux.initrd + cp -f $(obj)/zvmlinux.initrd $(images)/zImage.initrd.elf + rm -f $(obj)/zvmlinux.initrd + +znetboot: zImage + cp $(images)/zImage.$(end-y) $(tftpboot)/zImage.$(end-y) + +znetboot.initrd: zImage.initrd + cp $(images)/zImage.initrd.$(end-y) $(tftpboot)/zImage.initrd.$(end-y) + +$(images)/zImage-STRIPELF: $(obj)/zvmlinux + dd if=$(obj)/zvmlinux of=$(images)/zImage.$(end-y) skip=64 bs=1k + +$(images)/zImage.initrd-STRIPELF: $(obj)/zvmlinux.initrd + dd if=$(obj)/zvmlinux.initrd of=$(images)/zImage.initrd.$(end-y) \ + skip=64 bs=1k + +$(images)/zImage-TREE: $(obj)/zvmlinux $(MKTREE) + $(MKTREE) $(obj)/zvmlinux $(images)/zImage.$(end-y) $(ENTRYPOINT) + +$(images)/zImage.initrd-TREE: $(obj)/zvmlinux.initrd $(MKTREE) + $(MKTREE) $(obj)/zvmlinux.initrd $(images)/zImage.initrd.$(end-y) \ + $(ENTRYPOINT) + +$(images)/zImage-PPLUS: $(obj)/zvmlinux $(MKPREP) $(MKBUGBOOT) + $(MKPREP) -pbp $(obj)/zvmlinux $(images)/zImage.$(end-y) + $(MKBUGBOOT) $(obj)/zvmlinux $(images)/zImage.bugboot + +$(images)/zImage.initrd-PPLUS: $(obj)/zvmlinux.initrd $(MKPREP) $(MKBUGBOOT) + $(MKPREP) -pbp $(obj)/zvmlinux.initrd $(images)/zImage.initrd.$(end-y) + $(MKBUGBOOT) $(obj)/zvmlinux.initrd $(images)/zImage.initrd.bugboot diff --git a/arch/ppc/boot/simple/chrpmap.c b/arch/ppc/boot/simple/chrpmap.c new file mode 100644 index 00000000000..14d9e05d98b --- /dev/null +++ b/arch/ppc/boot/simple/chrpmap.c @@ -0,0 +1,12 @@ +/* + * 2004 (C) IBM. This file is licensed under the terms of the GNU General + * Public License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include + +void board_isa_init(void) +{ + ISA_init(0xFE000000); +} diff --git a/arch/ppc/boot/simple/clear.S b/arch/ppc/boot/simple/clear.S new file mode 100644 index 00000000000..95c5647a0f5 --- /dev/null +++ b/arch/ppc/boot/simple/clear.S @@ -0,0 +1,19 @@ +/* + * Code to call _setup_L2CR to flus, invalidate and disable the L2, + * and if present, do the same to the L3. + */ + +#define CLEAR_CACHES \ + bl _setup_L2CR; \ + \ + /* If 745x, turn off L3CR as well */ \ + mfspr r8,SPRN_PVR; \ + srwi r8,r8,16; \ + \ + cmpli cr0,r8,0x8000; /* 7450 */ \ + cmpli cr1,r8,0x8001; /* 7455 */ \ + cmpli cr2,r8,0x8002; /* 7457 */ \ + /* Now test if any are true. */ \ + cror 4*cr0+eq,4*cr0+eq,4*cr1+eq; \ + cror 4*cr0+eq,4*cr0+eq,4*cr2+eq; \ + beql _setup_L3CR diff --git a/arch/ppc/boot/simple/cpc700_memory.c b/arch/ppc/boot/simple/cpc700_memory.c new file mode 100644 index 00000000000..8c75cf6c238 --- /dev/null +++ b/arch/ppc/boot/simple/cpc700_memory.c @@ -0,0 +1,36 @@ +/* + * arch/ppc/boot/common/cpc700_memory.c + * + * Find memory based upon settings in the CPC700 bridge + * + * Author: Dan Cox + * + * 2001-2002 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include "cpc700.h" + +unsigned long +cpc700_get_mem_size(void) +{ + int i; + unsigned long len, amt; + + /* Start at MB1EA, since MB0EA will most likely be the ending address + for ROM space. */ + for(len = 0, i = CPC700_MB1EA; i <= CPC700_MB4EA; i+=4) { + amt = cpc700_read_memreg(i); + if (amt == 0) + break; + len = amt; + } + + return len; +} + + diff --git a/arch/ppc/boot/simple/dummy.c b/arch/ppc/boot/simple/dummy.c new file mode 100644 index 00000000000..31dbf45bf99 --- /dev/null +++ b/arch/ppc/boot/simple/dummy.c @@ -0,0 +1,4 @@ +int main(void) +{ + return 0; +} diff --git a/arch/ppc/boot/simple/embed_config.c b/arch/ppc/boot/simple/embed_config.c new file mode 100644 index 00000000000..c342b47e763 --- /dev/null +++ b/arch/ppc/boot/simple/embed_config.c @@ -0,0 +1,981 @@ +/* Board specific functions for those embedded 8xx boards that do + * not have boot monitor support for board information. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#ifdef CONFIG_8xx +#include +#endif +#ifdef CONFIG_8260 +#include +#include +#endif +#ifdef CONFIG_40x +#include +#endif +extern unsigned long timebase_period_ns; + +/* For those boards that don't provide one. +*/ +#if !defined(CONFIG_MBX) +static bd_t bdinfo; +#endif + +/* IIC functions. + * These are just the basic master read/write operations so we can + * examine serial EEPROM. + */ +extern void iic_read(uint devaddr, u_char *buf, uint offset, uint count); + +/* Supply a default Ethernet address for those eval boards that don't + * ship with one. This is an address from the MBX board I have, so + * it is unlikely you will find it on your network. + */ +static ushort def_enet_addr[] = { 0x0800, 0x3e26, 0x1559 }; + +#if defined(CONFIG_MBX) + +/* The MBX hands us a pretty much ready to go board descriptor. This + * is where the idea started in the first place. + */ +void +embed_config(bd_t **bdp) +{ + u_char *mp; + u_char eebuf[128]; + int i = 8; + bd_t *bd; + + bd = *bdp; + + /* Read the first 128 bytes of the EEPROM. There is more, + * but this is all we need. + */ + iic_read(0xa4, eebuf, 0, 128); + + /* All we are looking for is the Ethernet MAC address. The + * first 8 bytes are 'MOTOROLA', so check for part of that. + * Next, the VPD describes a MAC 'packet' as being of type 08 + * and size 06. So we look for that and the MAC must follow. + * If there are more than one, we still only care about the first. + * If it's there, assume we have a valid MAC address. If not, + * grab our default one. + */ + if ((*(uint *)eebuf) == 0x4d4f544f) { + while (i < 127 && !(eebuf[i] == 0x08 && eebuf[i + 1] == 0x06)) + i += eebuf[i + 1] + 2; /* skip this packet */ + + if (i == 127) /* Couldn't find. */ + mp = (u_char *)def_enet_addr; + else + mp = &eebuf[i + 2]; + } + else + mp = (u_char *)def_enet_addr; + + for (i=0; i<6; i++) + bd->bi_enetaddr[i] = *mp++; + + /* The boot rom passes these to us in MHz. Linux now expects + * them to be in Hz. + */ + bd->bi_intfreq *= 1000000; + bd->bi_busfreq *= 1000000; + + /* Stuff a baud rate here as well. + */ + bd->bi_baudrate = 9600; +} +#endif /* CONFIG_MBX */ + +#if defined(CONFIG_RPXLITE) || defined(CONFIG_RPXCLASSIC) || \ + defined(CONFIG_RPX8260) || defined(CONFIG_EP405) +/* Helper functions for Embedded Planet boards. +*/ +/* Because I didn't find anything that would do this....... +*/ +u_char +aschex_to_byte(u_char *cp) +{ + u_char byte, c; + + c = *cp++; + + if ((c >= 'A') && (c <= 'F')) { + c -= 'A'; + c += 10; + } else if ((c >= 'a') && (c <= 'f')) { + c -= 'a'; + c += 10; + } else + c -= '0'; + + byte = c * 16; + + c = *cp; + + if ((c >= 'A') && (c <= 'F')) { + c -= 'A'; + c += 10; + } else if ((c >= 'a') && (c <= 'f')) { + c -= 'a'; + c += 10; + } else + c -= '0'; + + byte += c; + + return(byte); +} + +static void +rpx_eth(bd_t *bd, u_char *cp) +{ + int i; + + for (i=0; i<6; i++) { + bd->bi_enetaddr[i] = aschex_to_byte(cp); + cp += 2; + } +} + +#ifdef CONFIG_RPX8260 +static uint +rpx_baseten(u_char *cp) +{ + uint retval; + + retval = 0; + + while (*cp != '\n') { + retval *= 10; + retval += (*cp) - '0'; + cp++; + } + return(retval); +} +#endif + +#if defined(CONFIG_RPXLITE) || defined(CONFIG_RPXCLASSIC) +static void +rpx_brate(bd_t *bd, u_char *cp) +{ + uint rate; + + rate = 0; + + while (*cp != '\n') { + rate *= 10; + rate += (*cp) - '0'; + cp++; + } + + bd->bi_baudrate = rate * 100; +} + +static void +rpx_cpuspeed(bd_t *bd, u_char *cp) +{ + uint num, den; + + num = den = 0; + + while (*cp != '\n') { + num *= 10; + num += (*cp) - '0'; + cp++; + if (*cp == '/') { + cp++; + den = (*cp) - '0'; + break; + } + } + + /* I don't know why the RPX just can't state the actual + * CPU speed..... + */ + if (den) { + num /= den; + num *= den; + } + bd->bi_intfreq = bd->bi_busfreq = num * 1000000; + + /* The 8xx can only run a maximum 50 MHz bus speed (until + * Motorola changes this :-). Greater than 50 MHz parts + * run internal/2 for bus speed. + */ + if (num > 50) + bd->bi_busfreq /= 2; +} +#endif + +#if defined(CONFIG_RPXLITE) || defined(CONFIG_RPXCLASSIC) || defined(CONFIG_EP405) +static void +rpx_memsize(bd_t *bd, u_char *cp) +{ + uint size; + + size = 0; + + while (*cp != '\n') { + size *= 10; + size += (*cp) - '0'; + cp++; + } + + bd->bi_memsize = size * 1024 * 1024; +} +#endif /* LITE || CLASSIC || EP405 */ +#if defined(CONFIG_EP405) +static void +rpx_nvramsize(bd_t *bd, u_char *cp) +{ + uint size; + + size = 0; + + while (*cp != '\n') { + size *= 10; + size += (*cp) - '0'; + cp++; + } + + bd->bi_nvramsize = size * 1024; +} +#endif /* CONFIG_EP405 */ + +#endif /* Embedded Planet boards */ + +#if defined(CONFIG_RPXLITE) || defined(CONFIG_RPXCLASSIC) + +/* Read the EEPROM on the RPX-Lite board. +*/ +void +embed_config(bd_t **bdp) +{ + u_char eebuf[256], *cp; + bd_t *bd; + + /* Read the first 256 bytes of the EEPROM. I think this + * is really all there is, and I hope if it gets bigger the + * info we want is still up front. + */ + bd = &bdinfo; + *bdp = bd; + +#if 1 + iic_read(0xa8, eebuf, 0, 128); + iic_read(0xa8, &eebuf[128], 128, 128); + + /* We look for two things, the Ethernet address and the + * serial baud rate. The records are separated by + * newlines. + */ + cp = eebuf; + for (;;) { + if (*cp == 'E') { + cp++; + if (*cp == 'A') { + cp += 2; + rpx_eth(bd, cp); + } + } + if (*cp == 'S') { + cp++; + if (*cp == 'B') { + cp += 2; + rpx_brate(bd, cp); + } + } + if (*cp == 'D') { + cp++; + if (*cp == '1') { + cp += 2; + rpx_memsize(bd, cp); + } + } + if (*cp == 'H') { + cp++; + if (*cp == 'Z') { + cp += 2; + rpx_cpuspeed(bd, cp); + } + } + + /* Scan to the end of the record. + */ + while ((*cp != '\n') && (*cp != 0xff)) + cp++; + + /* If the next character is a 0 or ff, we are done. + */ + cp++; + if ((*cp == 0) || (*cp == 0xff)) + break; + } + bd->bi_memstart = 0; +#else + /* For boards without initialized EEPROM. + */ + bd->bi_memstart = 0; + bd->bi_memsize = (8 * 1024 * 1024); + bd->bi_intfreq = 48000000; + bd->bi_busfreq = 48000000; + bd->bi_baudrate = 9600; +#endif +} +#endif /* RPXLITE || RPXCLASSIC */ + +#ifdef CONFIG_BSEIP +/* Build a board information structure for the BSE ip-Engine. + * There is more to come since we will add some environment + * variables and a function to read them. + */ +void +embed_config(bd_t **bdp) +{ + u_char *cp; + int i; + bd_t *bd; + + bd = &bdinfo; + *bdp = bd; + + /* Baud rate and processor speed will eventually come + * from the environment variables. + */ + bd->bi_baudrate = 9600; + + /* Get the Ethernet station address from the Flash ROM. + */ + cp = (u_char *)0xfe003ffa; + for (i=0; i<6; i++) { + bd->bi_enetaddr[i] = *cp++; + } + + /* The rest of this should come from the environment as well. + */ + bd->bi_memstart = 0; + bd->bi_memsize = (16 * 1024 * 1024); + bd->bi_intfreq = 48000000; + bd->bi_busfreq = 48000000; +} +#endif /* BSEIP */ + +#ifdef CONFIG_FADS +/* Build a board information structure for the FADS. + */ +void +embed_config(bd_t **bdp) +{ + u_char *cp; + int i; + bd_t *bd; + + bd = &bdinfo; + *bdp = bd; + + /* Just fill in some known values. + */ + bd->bi_baudrate = 9600; + + /* Use default enet. + */ + cp = (u_char *)def_enet_addr; + for (i=0; i<6; i++) { + bd->bi_enetaddr[i] = *cp++; + } + + bd->bi_memstart = 0; + bd->bi_memsize = (8 * 1024 * 1024); + bd->bi_intfreq = 40000000; + bd->bi_busfreq = 40000000; +} +#endif /* FADS */ + +#ifdef CONFIG_8260 +/* Compute 8260 clock values if the rom doesn't provide them. + */ +static unsigned char bus2core_8260[] = { +/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ + 3, 2, 2, 2, 4, 4, 5, 9, 6, 11, 8, 10, 3, 12, 7, 2, + 6, 5, 13, 2, 14, 4, 15, 2, 3, 11, 8, 10, 16, 12, 7, 2, +}; + +static void +clk_8260(bd_t *bd) +{ + uint scmr, vco_out, clkin; + uint plldf, pllmf, corecnf; + volatile cpm2_map_t *ip; + + ip = (cpm2_map_t *)CPM_MAP_ADDR; + scmr = ip->im_clkrst.car_scmr; + + /* The clkin is always bus frequency. + */ + clkin = bd->bi_busfreq; + + /* Collect the bits from the scmr. + */ + plldf = (scmr >> 12) & 1; + pllmf = scmr & 0xfff; + corecnf = (scmr >> 24) &0x1f; + + /* This is arithmetic from the 8260 manual. + */ + vco_out = clkin / (plldf + 1); + vco_out *= 2 * (pllmf + 1); + bd->bi_vco = vco_out; /* Save for later */ + + bd->bi_cpmfreq = vco_out / 2; /* CPM Freq, in MHz */ + bd->bi_intfreq = bd->bi_busfreq * bus2core_8260[corecnf] / 2; + + /* Set Baud rate divisor. The power up default is divide by 16, + * but we set it again here in case it was changed. + */ + ip->im_clkrst.car_sccr = 1; /* DIV 16 BRG */ + bd->bi_brgfreq = vco_out / 16; +} + +static unsigned char bus2core_8280[] = { +/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ + 3, 2, 2, 2, 4, 4, 5, 9, 6, 11, 8, 10, 3, 12, 7, 2, + 6, 5, 13, 2, 14, 2, 15, 2, 3, 2, 2, 2, 16, 2, 2, 2, +}; + +static void +clk_8280(bd_t *bd) +{ + uint scmr, main_clk, clkin; + uint pllmf, corecnf; + volatile cpm2_map_t *ip; + + ip = (cpm2_map_t *)CPM_MAP_ADDR; + scmr = ip->im_clkrst.car_scmr; + + /* The clkin is always bus frequency. + */ + clkin = bd->bi_busfreq; + + /* Collect the bits from the scmr. + */ + pllmf = scmr & 0xf; + corecnf = (scmr >> 24) & 0x1f; + + /* This is arithmetic from the 8280 manual. + */ + main_clk = clkin * (pllmf + 1); + + bd->bi_cpmfreq = main_clk / 2; /* CPM Freq, in MHz */ + bd->bi_intfreq = bd->bi_busfreq * bus2core_8280[corecnf] / 2; + + /* Set Baud rate divisor. The power up default is divide by 16, + * but we set it again here in case it was changed. + */ + ip->im_clkrst.car_sccr = (ip->im_clkrst.car_sccr & 0x3) | 0x1; + bd->bi_brgfreq = main_clk / 16; +} +#endif + +#ifdef CONFIG_SBC82xx +void +embed_config(bd_t **bdp) +{ + u_char *cp; + int i; + bd_t *bd; + unsigned long pvr; + + bd = *bdp; + + bd = &bdinfo; + *bdp = bd; + bd->bi_baudrate = 9600; + bd->bi_memsize = 256 * 1024 * 1024; /* just a guess */ + + cp = (void*)SBC82xx_MACADDR_NVRAM_SCC1; + memcpy(bd->bi_enetaddr, cp, 6); + + /* can busfreq be calculated? */ + pvr = mfspr(SPRN_PVR); + if ((pvr & 0xffff0000) == 0x80820000) { + bd->bi_busfreq = 100000000; + clk_8280(bd); + } else { + bd->bi_busfreq = 66000000; + clk_8260(bd); + } + +} +#endif /* SBC82xx */ + +#if defined(CONFIG_EST8260) || defined(CONFIG_TQM8260) +void +embed_config(bd_t **bdp) +{ + u_char *cp; + int i; + bd_t *bd; + + bd = *bdp; +#if 0 + /* This is actually provided by my boot rom. I have it + * here for those people that may load the kernel with + * a JTAG/COP tool and not the rom monitor. + */ + bd->bi_baudrate = 115200; + bd->bi_intfreq = 200000000; + bd->bi_busfreq = 66666666; + bd->bi_cpmfreq = 66666666; + bd->bi_brgfreq = 33333333; + bd->bi_memsize = 16 * 1024 * 1024; +#else + /* The boot rom passes these to us in MHz. Linux now expects + * them to be in Hz. + */ + bd->bi_intfreq *= 1000000; + bd->bi_busfreq *= 1000000; + bd->bi_cpmfreq *= 1000000; + bd->bi_brgfreq *= 1000000; +#endif + + cp = (u_char *)def_enet_addr; + for (i=0; i<6; i++) { + bd->bi_enetaddr[i] = *cp++; + } +} +#endif /* EST8260 */ + +#ifdef CONFIG_SBS8260 +void +embed_config(bd_t **bdp) +{ + u_char *cp; + int i; + bd_t *bd; + + /* This should provided by the boot rom. + */ + bd = &bdinfo; + *bdp = bd; + bd->bi_baudrate = 9600; + bd->bi_memsize = 64 * 1024 * 1024; + + /* Set all of the clocks. We have to know the speed of the + * external clock. The development board had 66 MHz. + */ + bd->bi_busfreq = 66666666; + clk_8260(bd); + + /* I don't know how to compute this yet. + */ + bd->bi_intfreq = 133000000; + + + cp = (u_char *)def_enet_addr; + for (i=0; i<6; i++) { + bd->bi_enetaddr[i] = *cp++; + } +} +#endif /* SBS8260 */ + +#ifdef CONFIG_RPX8260 +void +embed_config(bd_t **bdp) +{ + u_char *cp, *keyvals; + int i; + bd_t *bd; + + keyvals = (u_char *)*bdp; + + bd = &bdinfo; + *bdp = bd; + + /* This is almost identical to the RPX-Lite/Classic functions + * on the 8xx boards. It would be nice to have a key lookup + * function in a string, but the format of all of the fields + * is slightly different. + */ + cp = keyvals; + for (;;) { + if (*cp == 'E') { + cp++; + if (*cp == 'A') { + cp += 2; + rpx_eth(bd, cp); + } + } + if (*cp == 'S') { + cp++; + if (*cp == 'B') { + cp += 2; + bd->bi_baudrate = rpx_baseten(cp); + } + } + if (*cp == 'D') { + cp++; + if (*cp == '1') { + cp += 2; + bd->bi_memsize = rpx_baseten(cp) * 1024 * 1024; + } + } + if (*cp == 'X') { + cp++; + if (*cp == 'T') { + cp += 2; + bd->bi_busfreq = rpx_baseten(cp); + } + } + if (*cp == 'N') { + cp++; + if (*cp == 'V') { + cp += 2; + bd->bi_nvsize = rpx_baseten(cp) * 1024 * 1024; + } + } + + /* Scan to the end of the record. + */ + while ((*cp != '\n') && (*cp != 0xff)) + cp++; + + /* If the next character is a 0 or ff, we are done. + */ + cp++; + if ((*cp == 0) || (*cp == 0xff)) + break; + } + bd->bi_memstart = 0; + + /* The memory size includes both the 60x and local bus DRAM. + * I don't want to use the local bus DRAM for real memory, + * so subtract it out. It would be nice if they were separate + * keys. + */ + bd->bi_memsize -= 32 * 1024 * 1024; + + /* Set all of the clocks. We have to know the speed of the + * external clock. + */ + clk_8260(bd); + + /* I don't know how to compute this yet. + */ + bd->bi_intfreq = 200000000; +} +#endif /* RPX6 for testing */ + +#ifdef CONFIG_ADS8260 +void +embed_config(bd_t **bdp) +{ + u_char *cp; + int i; + bd_t *bd; + + /* This should provided by the boot rom. + */ + bd = &bdinfo; + *bdp = bd; + bd->bi_baudrate = 9600; + bd->bi_memsize = 16 * 1024 * 1024; + + /* Set all of the clocks. We have to know the speed of the + * external clock. The development board had 66 MHz. + */ + bd->bi_busfreq = 66666666; + clk_8260(bd); + + /* I don't know how to compute this yet. + */ + bd->bi_intfreq = 200000000; + + + cp = (u_char *)def_enet_addr; + for (i=0; i<6; i++) { + bd->bi_enetaddr[i] = *cp++; + } +} +#endif /* ADS8260 */ + +#ifdef CONFIG_WILLOW +void +embed_config(bd_t **bdp) +{ + u_char *cp; + int i; + bd_t *bd; + + /* Willow has Open Firmware....I should learn how to get this + * information from it. + */ + bd = &bdinfo; + *bdp = bd; + bd->bi_baudrate = 9600; + bd->bi_memsize = 32 * 1024 * 1024; + + /* Set all of the clocks. We have to know the speed of the + * external clock. The development board had 66 MHz. + */ + bd->bi_busfreq = 66666666; + clk_8260(bd); + + /* I don't know how to compute this yet. + */ + bd->bi_intfreq = 200000000; + + + cp = (u_char *)def_enet_addr; + for (i=0; i<6; i++) { + bd->bi_enetaddr[i] = *cp++; + } +} +#endif /* WILLOW */ + +#ifdef CONFIG_XILINX_ML300 +void +embed_config(bd_t ** bdp) +{ + static const unsigned long line_size = 32; + static const unsigned long congruence_classes = 256; + unsigned long addr; + unsigned long dccr; + bd_t *bd; + + /* + * Invalidate the data cache if the data cache is turned off. + * - The 405 core does not invalidate the data cache on power-up + * or reset but does turn off the data cache. We cannot assume + * that the cache contents are valid. + * - If the data cache is turned on this must have been done by + * a bootloader and we assume that the cache contents are + * valid. + */ + __asm__("mfdccr %0": "=r" (dccr)); + if (dccr == 0) { + for (addr = 0; + addr < (congruence_classes * line_size); + addr += line_size) { + __asm__("dccci 0,%0": :"b"(addr)); + } + } + + bd = &bdinfo; + *bdp = bd; + bd->bi_memsize = XPAR_DDR_0_SIZE; + bd->bi_intfreq = XPAR_CORE_CLOCK_FREQ_HZ; + bd->bi_busfreq = XPAR_PLB_CLOCK_FREQ_HZ; + bd->bi_pci_busfreq = XPAR_PCI_0_CLOCK_FREQ_HZ; + timebase_period_ns = 1000000000 / bd->bi_tbfreq; + /* see bi_tbfreq definition in arch/ppc/platforms/4xx/xilinx_ml300.h */ +} +#endif /* CONFIG_XILINX_ML300 */ + +#ifdef CONFIG_IBM_OPENBIOS +/* This could possibly work for all treeboot roms. +*/ +#if defined(CONFIG_ASH) || defined(CONFIG_BEECH) || defined(CONFIG_BUBINGA) +#define BOARD_INFO_VECTOR 0xFFF80B50 /* openbios 1.19 moved this vector down - armin */ +#else +#define BOARD_INFO_VECTOR 0xFFFE0B50 +#endif + +#ifdef CONFIG_BEECH +static void +get_board_info(bd_t **bdp) +{ + typedef void (*PFV)(bd_t *bd); + ((PFV)(*(unsigned long *)BOARD_INFO_VECTOR))(*bdp); + return; +} + +void +embed_config(bd_t **bdp) +{ + *bdp = &bdinfo; + get_board_info(bdp); +} +#else /* !CONFIG_BEECH */ +void +embed_config(bd_t **bdp) +{ + u_char *cp; + int i; + bd_t *bd, *treeboot_bd; + bd_t *(*get_board_info)(void) = + (bd_t *(*)(void))(*(unsigned long *)BOARD_INFO_VECTOR); +#if !defined(CONFIG_STB03xxx) + + /* shut down the Ethernet controller that the boot rom + * sometimes leaves running. + */ + mtdcr(DCRN_MALCR(DCRN_MAL_BASE), MALCR_MMSR); /* 1st reset MAL */ + while (mfdcr(DCRN_MALCR(DCRN_MAL_BASE)) & MALCR_MMSR) {}; /* wait for the reset */ + out_be32((volatile u32*)EMAC0_BASE,0x20000000); /* then reset EMAC */ +#endif + + bd = &bdinfo; + *bdp = bd; + if ((treeboot_bd = get_board_info()) != NULL) { + memcpy(bd, treeboot_bd, sizeof(bd_t)); + } + else { + /* Hmmm...better try to stuff some defaults. + */ + bd->bi_memsize = 16 * 1024 * 1024; + cp = (u_char *)def_enet_addr; + for (i=0; i<6; i++) { + /* I should probably put different ones here, + * hopefully only one is used. + */ + bd->BD_EMAC_ADDR(0,i) = *cp; + +#ifdef CONFIG_PCI + bd->bi_pci_enetaddr[i] = *cp++; +#endif + } + bd->bi_tbfreq = 200 * 1000 * 1000; + bd->bi_intfreq = 200000000; + bd->bi_busfreq = 100000000; +#ifdef CONFIG_PCI + bd->bi_pci_busfreq = 66666666; +#endif + } + /* Yeah, this look weird, but on Redwood 4 they are + * different object in the structure. Sincr Redwwood 5 + * and Redwood 6 use OpenBIOS, it requires a special value. + */ +#if defined(CONFIG_REDWOOD_5) || defined (CONFIG_REDWOOD_6) + bd->bi_tbfreq = 27 * 1000 * 1000; +#endif + timebase_period_ns = 1000000000 / bd->bi_tbfreq; +} +#endif /* CONFIG_BEECH */ +#endif /* CONFIG_IBM_OPENBIOS */ + +#ifdef CONFIG_EP405 +#include + +void +embed_config(bd_t **bdp) +{ + u32 chcr0; + u_char *cp; + bd_t *bd; + + /* Different versions of the PlanetCore firmware vary in how + they set up the serial port - in particular whether they + use the internal or external serial clock for UART0. Make + sure the UART is in a known state. */ + /* FIXME: We should use the board's 11.0592MHz external serial + clock - it will be more accurate for serial rates. For + now, however the baud rates in ep405.h are for the internal + clock. */ + chcr0 = mfdcr(DCRN_CHCR0); + if ( (chcr0 & 0x1fff) != 0x103e ) { + mtdcr(DCRN_CHCR0, (chcr0 & 0xffffe000) | 0x103e); + /* The following tricks serial_init() into resetting the baud rate */ + writeb(0, UART0_IO_BASE + UART_LCR); + } + + /* We haven't seen actual problems with the EP405 leaving the + * EMAC running (as we have on Walnut). But the registers + * suggest it may not be left completely quiescent. Reset it + * just to be sure. */ + mtdcr(DCRN_MALCR(DCRN_MAL_BASE), MALCR_MMSR); /* 1st reset MAL */ + while (mfdcr(DCRN_MALCR(DCRN_MAL_BASE)) & MALCR_MMSR) {}; /* wait for the reset */ + out_be32((unsigned *)EMAC0_BASE,0x20000000); /* then reset EMAC */ + + bd = &bdinfo; + *bdp = bd; +#if 1 + cp = (u_char *)0xF0000EE0; + for (;;) { + if (*cp == 'E') { + cp++; + if (*cp == 'A') { + cp += 2; + rpx_eth(bd, cp); + } + } + + if (*cp == 'D') { + cp++; + if (*cp == '1') { + cp += 2; + rpx_memsize(bd, cp); + } + } + + if (*cp == 'N') { + cp++; + if (*cp == 'V') { + cp += 2; + rpx_nvramsize(bd, cp); + } + } + while ((*cp != '\n') && (*cp != 0xff)) + cp++; + + cp++; + if ((*cp == 0) || (*cp == 0xff)) + break; + } + bd->bi_intfreq = 200000000; + bd->bi_busfreq = 100000000; + bd->bi_pci_busfreq= 33000000 ; +#else + + bd->bi_memsize = 64000000; + bd->bi_intfreq = 200000000; + bd->bi_busfreq = 100000000; + bd->bi_pci_busfreq= 33000000 ; +#endif +} +#endif + +#ifdef CONFIG_RAINIER +/* Rainier uses vxworks bootrom */ +void +embed_config(bd_t **bdp) +{ + u_char *cp; + int i; + bd_t *bd; + + bd = &bdinfo; + *bdp = bd; + + for(i=0;i<8192;i+=32) { + __asm__("dccci 0,%0" :: "r" (i)); + } + __asm__("iccci 0,0"); + __asm__("sync;isync"); + + /* init ram for parity */ + memset(0, 0,0x400000); /* Lo memory */ + + + bd->bi_memsize = (32 * 1024 * 1024) ; + bd->bi_intfreq = 133000000; //the internal clock is 133 MHz + bd->bi_busfreq = 100000000; + bd->bi_pci_busfreq= 33000000; + + cp = (u_char *)def_enet_addr; + for (i=0; i<6; i++) { + bd->bi_enetaddr[i] = *cp++; + } + +} +#endif + diff --git a/arch/ppc/boot/simple/head.S b/arch/ppc/boot/simple/head.S new file mode 100644 index 00000000000..524053202bb --- /dev/null +++ b/arch/ppc/boot/simple/head.S @@ -0,0 +1,142 @@ +/* + * arch/ppc/boot/simple/head.S + * + * Initial board bringup code for many different boards. + * + * Author: Tom Rini + * trini@mvista.com + * Derived from arch/ppc/boot/prep/head.S (Cort Dougan, many others). + * + * 2001-2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include + + .text + +/* + * Begin at some arbitrary location in RAM or Flash + * Initialize core registers + * Configure memory controller (Not executing from RAM) + * Move the boot code to the link address (8M) + * Setup C stack + * Initialize UART + * Decompress the kernel to 0x0 + * Jump to the kernel entry + * + */ + + .globl start +start: + bl start_ +#ifdef CONFIG_IBM_OPENBIOS + /* The IBM "Tree" bootrom knows that the address of the bootrom + * read only structure is 4 bytes after _start. + */ + .long 0x62726f6d # structure ID - "brom" + .long 0x5f726f00 # - "_ro\0" + .long 1 # structure version + .long bootrom_cmdline # address of *bootrom_cmdline +#endif + +start_: +#ifdef CONFIG_FORCE + /* We have some really bad firmware. We must disable the L1 + * icache/dcache now or the board won't boot. + */ + li r4,0x0000 + isync + mtspr SPRN_HID0,r4 + sync + isync +#endif + +#if defined(CONFIG_MBX) || defined(CONFIG_RPX8260) || defined(CONFIG_PPC_PREP) + mr r29,r3 /* On the MBX860, r3 is the board info pointer. + * On the RPXSUPER, r3 points to the NVRAM + * configuration keys. + * On PReP, r3 is the pointer to the residual data. + */ +#endif + + mflr r3 /* Save our actual starting address. */ + + /* The following functions we call must not modify r3 or r4..... + */ +#ifdef CONFIG_6xx + /* On PReP we must look at the OpenFirmware pointer and sanity + * test it. On other platforms, we disable the MMU right now + * and other bits. + */ +#ifdef CONFIG_PPC_PREP +/* + * Save the OF pointer to r25, but only if the entry point is in a sane + * location; if not we store 0. If there is no entry point, or it is + * invalid, we establish the default MSR value immediately. Otherwise, + * we defer doing that, to allow OF functions to be called, until we + * begin uncompressing the kernel. + */ + lis r8,0x0fff /* r8 = 0x0fffffff */ + ori r8,r8,0xffff + + subc r8,r8,r5 /* r8 = (r5 <= r8) ? ~0 : 0 */ + subfe r8,r8,r8 + nand r8,r8,r8 + + and. r5,r5,r8 /* r5 will be cleared if (r5 > r8) */ + bne+ haveOF + + li r8,MSR_IP|MSR_FP /* Not OF: set MSR immediately */ + mtmsr r8 + isync +haveOF: + mr r25,r5 +#else + bl disable_6xx_mmu +#endif + bl disable_6xx_l1cache + + CLEAR_CACHES +#endif + +#ifdef CONFIG_8xx + mfmsr r8 /* Turn off interrupts */ + li r9,0 + ori r9,r9,MSR_EE + andc r8,r8,r9 + mtmsr r8 + + /* We do this because some boot roms don't initialize the + * processor correctly. Don't do this if you want to debug + * using a BDM device. + */ + li r4,0 /* Zero DER to prevent FRZ */ + mtspr SPRN_DER,r4 +#endif + +#ifdef CONFIG_REDWOOD_4 + /* All of this Redwood 4 stuff will soon disappear when the + * boot rom is straightened out. + */ + mr r29, r3 /* Easier than changing the other code */ + bl HdwInit + mr r3, r29 +#endif + +#if defined(CONFIG_MBX) || defined(CONFIG_RPX8260) || defined(CONFIG_PPC_PREP) + mr r4,r29 /* put the board info pointer where the relocate + * routine will find it + */ +#endif + + /* Get the load address. + */ + subi r3, r3, 4 /* Get the actual IP, not NIP */ + b relocate + diff --git a/arch/ppc/boot/simple/iic.c b/arch/ppc/boot/simple/iic.c new file mode 100644 index 00000000000..e4efd838bfa --- /dev/null +++ b/arch/ppc/boot/simple/iic.c @@ -0,0 +1,214 @@ +/* Minimal support functions to read configuration from IIC EEPROMS + * on MPC8xx boards. Originally written for RPGC RPX-Lite. + * Dan Malek (dmalek@jlc.net). + */ +#include +#include +#include +#include + + +/* IIC functions. + * These are just the basic master read/write operations so we can + * examine serial EEPROM. + */ +void iic_read(uint devaddr, u_char *buf, uint offset, uint count); + +static int iic_init_done; + +static void +iic_init(void) +{ + volatile iic_t *iip; + volatile i2c8xx_t *i2c; + volatile cpm8xx_t *cp; + volatile immap_t *immap; + uint dpaddr; + + immap = (immap_t *)IMAP_ADDR; + cp = (cpm8xx_t *)&(immap->im_cpm); + + /* Reset the CPM. This is necessary on the 860 processors + * that may have started the SCC1 ethernet without relocating + * the IIC. + * This also stops the Ethernet in case we were loaded by a + * BOOTP rom monitor. + */ + cp->cp_cpcr = (CPM_CR_RST | CPM_CR_FLG); + + /* Wait for it. + */ + while (cp->cp_cpcr & (CPM_CR_RST | CPM_CR_FLG)); + + /* Remove any microcode patches. We will install our own + * later. + */ + cp->cp_cpmcr1 = 0; + cp->cp_cpmcr2 = 0; + cp->cp_cpmcr3 = 0; + cp->cp_cpmcr4 = 0; + cp->cp_rccr = 0; + + iip = (iic_t *)&cp->cp_dparam[PROFF_IIC]; + i2c = (i2c8xx_t *)&(immap->im_i2c); + + /* Initialize Port B IIC pins. + */ + cp->cp_pbpar |= 0x00000030; + cp->cp_pbdir |= 0x00000030; + cp->cp_pbodr |= 0x00000030; + + /* Initialize the parameter ram. + */ + + /* Allocate space for a two transmit and one receive buffer + * descriptor in the DP ram. + * For now, this address seems OK, but it may have to + * change with newer versions of the firmware. + */ + dpaddr = 0x0840; + + /* Set up the IIC parameters in the parameter ram. + */ + iip->iic_tbase = dpaddr; + iip->iic_rbase = dpaddr + (2 * sizeof(cbd_t)); + + iip->iic_tfcr = SMC_EB; + iip->iic_rfcr = SMC_EB; + + /* This should really be done by the reader/writer. + */ + iip->iic_mrblr = 128; + + /* Initialize Tx/Rx parameters. + */ + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_INIT_TRX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + + /* Select an arbitrary address. Just make sure it is unique. + */ + i2c->i2c_i2add = 0x34; + + /* Make clock run maximum slow. + */ + i2c->i2c_i2brg = 7; + + /* Disable interrupts. + */ + i2c->i2c_i2cmr = 0; + i2c->i2c_i2cer = 0xff; + + /* Enable SDMA. + */ + immap->im_siu_conf.sc_sdcr = 1; + + iic_init_done = 1; +} + +/* Read from IIC. + * Caller provides device address, memory buffer, and byte count. + */ +static u_char iitemp[32]; + +void +iic_read(uint devaddr, u_char *buf, uint offset, uint count) +{ + volatile iic_t *iip; + volatile i2c8xx_t *i2c; + volatile cbd_t *tbdf, *rbdf; + volatile cpm8xx_t *cp; + volatile immap_t *immap; + u_char *tb; + uint temp; + + /* If the interface has not been initialized, do that now. + */ + if (!iic_init_done) + iic_init(); + + immap = (immap_t *)IMAP_ADDR; + cp = (cpm8xx_t *)&(immap->im_cpm); + + iip = (iic_t *)&cp->cp_dparam[PROFF_IIC]; + i2c = (i2c8xx_t *)&(immap->im_i2c); + + tbdf = (cbd_t *)&cp->cp_dpmem[iip->iic_tbase]; + rbdf = (cbd_t *)&cp->cp_dpmem[iip->iic_rbase]; + + /* Send a "dummy write" operation. This is a write request with + * only the offset sent, followed by another start condition. + * This will ensure we start reading from the first location + * of the EEPROM. + */ + tb = iitemp; + tb = (u_char *)(((uint)tb + 15) & ~15); + tbdf->cbd_bufaddr = (int)tb; + *tb = devaddr & 0xfe; /* Device address */ + *(tb+1) = offset; /* Offset */ + tbdf->cbd_datlen = 2; /* Length */ + tbdf->cbd_sc = + BD_SC_READY | BD_SC_LAST | BD_SC_WRAP | BD_IIC_START; + + i2c->i2c_i2mod = 1; /* Enable */ + i2c->i2c_i2cer = 0xff; + i2c->i2c_i2com = 0x81; /* Start master */ + + /* Wait for IIC transfer. + */ +#if 0 + while ((i2c->i2c_i2cer & 3) == 0); + + if (tbdf->cbd_sc & BD_SC_READY) + printf("IIC ra complete but tbuf ready\n"); +#else + temp = 10000000; + while ((tbdf->cbd_sc & BD_SC_READY) && (temp != 0)) + temp--; +#if 0 + /* We can't do this...there is no serial port yet! + */ + if (temp == 0) { + printf("Timeout reading EEPROM\n"); + return; + } +#endif +#endif + + /* Chip errata, clear enable. + */ + i2c->i2c_i2mod = 0; + + /* To read, we need an empty buffer of the proper length. + * All that is used is the first byte for address, the remainder + * is just used for timing (and doesn't really have to exist). + */ + tbdf->cbd_bufaddr = (int)tb; + *tb = devaddr | 1; /* Device address */ + rbdf->cbd_bufaddr = (uint)buf; /* Desination buffer */ + tbdf->cbd_datlen = rbdf->cbd_datlen = count + 1; /* Length */ + tbdf->cbd_sc = BD_SC_READY | BD_SC_LAST | BD_SC_WRAP | BD_IIC_START; + rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP; + + /* Chip bug, set enable here. + */ + i2c->i2c_i2mod = 1; /* Enable */ + i2c->i2c_i2cer = 0xff; + i2c->i2c_i2com = 0x81; /* Start master */ + + /* Wait for IIC transfer. + */ +#if 0 + while ((i2c->i2c_i2cer & 1) == 0); + + if (rbdf->cbd_sc & BD_SC_EMPTY) + printf("IIC read complete but rbuf empty\n"); +#else + temp = 10000000; + while ((tbdf->cbd_sc & BD_SC_READY) && (temp != 0)) + temp--; +#endif + + /* Chip errata, clear enable. + */ + i2c->i2c_i2mod = 0; +} diff --git a/arch/ppc/boot/simple/m8260_tty.c b/arch/ppc/boot/simple/m8260_tty.c new file mode 100644 index 00000000000..d770947e9b8 --- /dev/null +++ b/arch/ppc/boot/simple/m8260_tty.c @@ -0,0 +1,325 @@ +/* Minimal serial functions needed to send messages out the serial + * port on SMC1. + */ +#include +#include +#include +#include + +uint no_print; +extern char *params[]; +extern int nparams; +static u_char cons_hold[128], *sgptr; +static int cons_hold_cnt; + +/* If defined, enables serial console. The value (1 through 4) + * should designate which SCC is used, but this isn't complete. Only + * SCC1 is known to work at this time. + * We're only linked if SERIAL_CPM_CONSOLE=y, so we only need to test + * SERIAL_CPM_SCC1. + */ +#ifdef CONFIG_SERIAL_CPM_SCC1 +#define SCC_CONSOLE 1 +#endif + +unsigned long +serial_init(int ignored, bd_t *bd) +{ +#ifdef SCC_CONSOLE + volatile scc_t *sccp; + volatile scc_uart_t *sup; +#else + volatile smc_t *sp; + volatile smc_uart_t *up; +#endif + volatile cbd_t *tbdf, *rbdf; + volatile cpm2_map_t *ip; + volatile iop_cpm2_t *io; + volatile cpm_cpm2_t *cp; + uint dpaddr, memaddr; + + ip = (cpm2_map_t *)CPM_MAP_ADDR; + cp = &ip->im_cpm; + io = &ip->im_ioport; + + /* Perform a reset. + */ + cp->cp_cpcr = (CPM_CR_RST | CPM_CR_FLG); + + /* Wait for it. + */ + while (cp->cp_cpcr & CPM_CR_FLG); + +#ifdef CONFIG_ADS8260 + /* Enable the RS-232 transceivers. + */ + *(volatile uint *)(BCSR_ADDR + 4) &= + ~(BCSR1_RS232_EN1 | BCSR1_RS232_EN2); +#endif + +#ifdef SCC_CONSOLE + sccp = (scc_t *)&(ip->im_scc[SCC_CONSOLE-1]); + sup = (scc_uart_t *)&ip->im_dprambase[PROFF_SCC1 + ((SCC_CONSOLE-1) << 8)]; + sccp->scc_sccm &= ~(UART_SCCM_TX | UART_SCCM_RX); + sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); + + /* Use Port D for SCC1 instead of other functions. + */ + io->iop_ppard |= 0x00000003; + io->iop_psord &= ~0x00000001; /* Rx */ + io->iop_psord |= 0x00000002; /* Tx */ + io->iop_pdird &= ~0x00000001; /* Rx */ + io->iop_pdird |= 0x00000002; /* Tx */ + +#else + sp = (smc_t*)&(ip->im_smc[0]); + *(ushort *)(&ip->im_dprambase[PROFF_SMC1_BASE]) = PROFF_SMC1; + up = (smc_uart_t *)&ip->im_dprambase[PROFF_SMC1]; + + /* Disable transmitter/receiver. + */ + sp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); + + /* Use Port D for SMC1 instead of other functions. + */ + io->iop_ppard |= 0x00c00000; + io->iop_pdird |= 0x00400000; + io->iop_pdird &= ~0x00800000; + io->iop_psord &= ~0x00c00000; +#endif + + /* Allocate space for two buffer descriptors in the DP ram. + * For now, this address seems OK, but it may have to + * change with newer versions of the firmware. + */ + dpaddr = 0x0800; + + /* Grab a few bytes from the top of memory. + */ + memaddr = (bd->bi_memsize - 256) & ~15; + + /* Set the physical address of the host memory buffers in + * the buffer descriptors. + */ + rbdf = (cbd_t *)&ip->im_dprambase[dpaddr]; + rbdf->cbd_bufaddr = memaddr; + rbdf->cbd_sc = 0; + tbdf = rbdf + 1; + tbdf->cbd_bufaddr = memaddr+128; + tbdf->cbd_sc = 0; + + /* Set up the uart parameters in the parameter ram. + */ +#ifdef SCC_CONSOLE + sup->scc_genscc.scc_rbase = dpaddr; + sup->scc_genscc.scc_tbase = dpaddr + sizeof(cbd_t); + + /* Set up the uart parameters in the + * parameter ram. + */ + sup->scc_genscc.scc_rfcr = CPMFCR_GBL | CPMFCR_EB; + sup->scc_genscc.scc_tfcr = CPMFCR_GBL | CPMFCR_EB; + + sup->scc_genscc.scc_mrblr = 128; + sup->scc_maxidl = 8; + sup->scc_brkcr = 1; + sup->scc_parec = 0; + sup->scc_frmec = 0; + sup->scc_nosec = 0; + sup->scc_brkec = 0; + sup->scc_uaddr1 = 0; + sup->scc_uaddr2 = 0; + sup->scc_toseq = 0; + sup->scc_char1 = 0x8000; + sup->scc_char2 = 0x8000; + sup->scc_char3 = 0x8000; + sup->scc_char4 = 0x8000; + sup->scc_char5 = 0x8000; + sup->scc_char6 = 0x8000; + sup->scc_char7 = 0x8000; + sup->scc_char8 = 0x8000; + sup->scc_rccm = 0xc0ff; + + /* Send the CPM an initialize command. + */ + cp->cp_cpcr = mk_cr_cmd(CPM_CR_SCC1_PAGE, CPM_CR_SCC1_SBLOCK, 0, + CPM_CR_INIT_TRX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + + /* Set UART mode, 8 bit, no parity, one stop. + * Enable receive and transmit. + */ + sccp->scc_gsmrh = 0; + sccp->scc_gsmrl = + (SCC_GSMRL_MODE_UART | SCC_GSMRL_TDCR_16 | SCC_GSMRL_RDCR_16); + + /* Disable all interrupts and clear all pending + * events. + */ + sccp->scc_sccm = 0; + sccp->scc_scce = 0xffff; + sccp->scc_dsr = 0x7e7e; + sccp->scc_psmr = 0x3000; + + /* Wire BRG1 to SCC1. The console driver will take care of + * others. + */ + ip->im_cpmux.cmx_scr = 0; +#else + up->smc_rbase = dpaddr; + up->smc_tbase = dpaddr+sizeof(cbd_t); + up->smc_rfcr = CPMFCR_EB; + up->smc_tfcr = CPMFCR_EB; + up->smc_brklen = 0; + up->smc_brkec = 0; + up->smc_brkcr = 0; + up->smc_mrblr = 128; + up->smc_maxidl = 8; + + /* Set UART mode, 8 bit, no parity, one stop. + * Enable receive and transmit. + */ + sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART; + + /* Mask all interrupts and remove anything pending. + */ + sp->smc_smcm = 0; + sp->smc_smce = 0xff; + + /* Set up the baud rate generator. + */ + ip->im_cpmux.cmx_smr = 0; +#endif + + /* The baud rate divisor needs to be coordinated with clk_8260(). + */ + ip->im_brgc1 = + (((bd->bi_brgfreq/16) / bd->bi_baudrate) << 1) | + CPM_BRG_EN; + + /* Make the first buffer the only buffer. + */ + tbdf->cbd_sc |= BD_SC_WRAP; + rbdf->cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP; + + /* Initialize Tx/Rx parameters. + */ +#ifdef SCC_CONSOLE + sccp->scc_gsmrl |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT); +#else + cp->cp_cpcr = mk_cr_cmd(CPM_CR_SMC1_PAGE, CPM_CR_SMC1_SBLOCK, 0, CPM_CR_INIT_TRX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + + /* Enable transmitter/receiver. + */ + sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN; +#endif + + /* This is ignored. + */ + return 0; +} + +int +serial_readbuf(u_char *cbuf) +{ + volatile cbd_t *rbdf; + volatile char *buf; +#ifdef SCC_CONSOLE + volatile scc_uart_t *sup; +#else + volatile smc_uart_t *up; +#endif + volatile cpm2_map_t *ip; + int i, nc; + + ip = (cpm2_map_t *)CPM_MAP_ADDR; + +#ifdef SCC_CONSOLE + sup = (scc_uart_t *)&ip->im_dprambase[PROFF_SCC1 + ((SCC_CONSOLE-1) << 8)]; + rbdf = (cbd_t *)&ip->im_dprambase[sup->scc_genscc.scc_rbase]; +#else + up = (smc_uart_t *)&(ip->im_dprambase[PROFF_SMC1]); + rbdf = (cbd_t *)&ip->im_dprambase[up->smc_rbase]; +#endif + + /* Wait for character to show up. + */ + buf = (char *)rbdf->cbd_bufaddr; + while (rbdf->cbd_sc & BD_SC_EMPTY); + nc = rbdf->cbd_datlen; + for (i=0; icbd_sc |= BD_SC_EMPTY; + + return(nc); +} + +void +serial_putc(void *ignored, const char c) +{ + volatile cbd_t *tbdf; + volatile char *buf; +#ifdef SCC_CONSOLE + volatile scc_uart_t *sup; +#else + volatile smc_uart_t *up; +#endif + volatile cpm2_map_t *ip; + + ip = (cpm2_map_t *)CPM_MAP_ADDR; +#ifdef SCC_CONSOLE + sup = (scc_uart_t *)&ip->im_dprambase[PROFF_SCC1 + ((SCC_CONSOLE-1) << 8)]; + tbdf = (cbd_t *)&ip->im_dprambase[sup->scc_genscc.scc_tbase]; +#else + up = (smc_uart_t *)&(ip->im_dprambase[PROFF_SMC1]); + tbdf = (cbd_t *)&ip->im_dprambase[up->smc_tbase]; +#endif + + /* Wait for last character to go. + */ + buf = (char *)tbdf->cbd_bufaddr; + while (tbdf->cbd_sc & BD_SC_READY); + + *buf = c; + tbdf->cbd_datlen = 1; + tbdf->cbd_sc |= BD_SC_READY; +} + +char +serial_getc(void *ignored) +{ + char c; + + if (cons_hold_cnt <= 0) { + cons_hold_cnt = serial_readbuf(cons_hold); + sgptr = cons_hold; + } + c = *sgptr++; + cons_hold_cnt--; + + return(c); +} + +int +serial_tstc(void *ignored) +{ + volatile cbd_t *rbdf; +#ifdef SCC_CONSOLE + volatile scc_uart_t *sup; +#else + volatile smc_uart_t *up; +#endif + volatile cpm2_map_t *ip; + + ip = (cpm2_map_t *)CPM_MAP_ADDR; +#ifdef SCC_CONSOLE + sup = (scc_uart_t *)&ip->im_dprambase[PROFF_SCC1 + ((SCC_CONSOLE-1) << 8)]; + rbdf = (cbd_t *)&ip->im_dprambase[sup->scc_genscc.scc_rbase]; +#else + up = (smc_uart_t *)&(ip->im_dprambase[PROFF_SMC1]); + rbdf = (cbd_t *)&ip->im_dprambase[up->smc_rbase]; +#endif + + return(!(rbdf->cbd_sc & BD_SC_EMPTY)); +} diff --git a/arch/ppc/boot/simple/m8xx_tty.c b/arch/ppc/boot/simple/m8xx_tty.c new file mode 100644 index 00000000000..1d2778e248c --- /dev/null +++ b/arch/ppc/boot/simple/m8xx_tty.c @@ -0,0 +1,290 @@ +/* Minimal serial functions needed to send messages out the serial + * port on the MBX console. + * + * The MBX uxes SMC1 for the serial port. We reset the port and use + * only the first BD that EPPC-Bug set up as a character FIFO. + * + * Later versions (at least 1.4, maybe earlier) of the MBX EPPC-Bug + * use COM1 instead of SMC1 as the console port. This kinda sucks + * for the rest of the kernel, so here we force the use of SMC1 again. + */ +#include +#include +#include +#include +#include + +#ifdef CONFIG_MBX +#define MBX_CSR1 ((volatile u_char *)0xfa100000) +#define CSR1_COMEN (u_char)0x02 +#endif + +#ifdef TQM_SMC2_CONSOLE +#define PROFF_CONS PROFF_SMC2 +#define CPM_CR_CH_CONS CPM_CR_CH_SMC2 +#define SMC_INDEX 1 +static volatile iop8xx_t *iopp = (iop8xx_t *)&(((immap_t *)IMAP_ADDR)->im_ioport); +#else +#define PROFF_CONS PROFF_SMC1 +#define CPM_CR_CH_CONS CPM_CR_CH_SMC1 +#define SMC_INDEX 0 +#endif + +static cpm8xx_t *cpmp = (cpm8xx_t *)&(((immap_t *)IMAP_ADDR)->im_cpm); + +unsigned long +serial_init(int ignored, bd_t *bd) +{ + volatile smc_t *sp; + volatile smc_uart_t *up; + volatile cbd_t *tbdf, *rbdf; + volatile cpm8xx_t *cp; + uint dpaddr, memaddr; +#ifndef CONFIG_MBX + uint ui; +#endif + + cp = cpmp; + sp = (smc_t*)&(cp->cp_smc[SMC_INDEX]); + up = (smc_uart_t *)&cp->cp_dparam[PROFF_CONS]; + + /* Disable transmitter/receiver. + */ + sp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); + +#ifdef CONFIG_FADS + /* Enable SMC1/2 transceivers. + */ + *((volatile uint *)BCSR1) &= ~(BCSR1_RS232EN_1|BCSR1_RS232EN_2); +#endif + +#ifndef CONFIG_MBX + { + /* Initialize SMCx and use it for the console port. + */ + + /* Enable SDMA. + */ + ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sdcr = 1; + +#ifdef TQM_SMC2_CONSOLE + /* Use Port A for SMC2 instead of other functions. + */ + iopp->iop_papar |= 0x00c0; + iopp->iop_padir &= ~0x00c0; + iopp->iop_paodr &= ~0x00c0; +#else + /* Use Port B for SMCs instead of other functions. + */ + cp->cp_pbpar |= 0x00000cc0; + cp->cp_pbdir &= ~0x00000cc0; + cp->cp_pbodr &= ~0x00000cc0; +#endif + + /* Allocate space for two buffer descriptors in the DP ram. + * For now, this address seems OK, but it may have to + * change with newer versions of the firmware. + */ + dpaddr = 0x0800; + + /* Grab a few bytes from the top of memory for SMC FIFOs. + */ + memaddr = (bd->bi_memsize - 32) & ~15; + + /* Set the physical address of the host memory buffers in + * the buffer descriptors. + */ + rbdf = (cbd_t *)&cp->cp_dpmem[dpaddr]; + rbdf->cbd_bufaddr = memaddr; + rbdf->cbd_sc = 0; + tbdf = rbdf + 1; + tbdf->cbd_bufaddr = memaddr+4; + tbdf->cbd_sc = 0; + + /* Set up the uart parameters in the parameter ram. + */ + up->smc_rbase = dpaddr; + up->smc_tbase = dpaddr+sizeof(cbd_t); + up->smc_rfcr = SMC_EB; + up->smc_tfcr = SMC_EB; + + /* Set UART mode, 8 bit, no parity, one stop. + * Enable receive and transmit. + */ + sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART; + + /* Mask all interrupts and remove anything pending. + */ + sp->smc_smcm = 0; + sp->smc_smce = 0xff; + + /* Set up the baud rate generator. + * See 8xx_io/commproc.c for details. + * This wires BRG1 to SMC1 and BRG2 to SMC2; + */ + cp->cp_simode = 0x10000000; + ui = bd->bi_intfreq / 16 / bd->bi_baudrate; +#ifdef TQM_SMC2_CONSOLE + cp->cp_brgc2 = +#else + cp->cp_brgc1 = +#endif + ((ui - 1) < 4096) + ? (((ui - 1) << 1) | CPM_BRG_EN) + : ((((ui / 16) - 1) << 1) | CPM_BRG_EN | CPM_BRG_DIV16); + +#else /* CONFIG_MBX */ + if (*MBX_CSR1 & CSR1_COMEN) { + /* COM1 is enabled. Initialize SMC1 and use it for + * the console port. + */ + + /* Enable SDMA. + */ + ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sdcr = 1; + + /* Use Port B for SMCs instead of other functions. + */ + cp->cp_pbpar |= 0x00000cc0; + cp->cp_pbdir &= ~0x00000cc0; + cp->cp_pbodr &= ~0x00000cc0; + + /* Allocate space for two buffer descriptors in the DP ram. + * For now, this address seems OK, but it may have to + * change with newer versions of the firmware. + */ + dpaddr = 0x0800; + + /* Grab a few bytes from the top of memory. EPPC-Bug isn't + * running any more, so we can do this. + */ + memaddr = (bd->bi_memsize - 32) & ~15; + + /* Set the physical address of the host memory buffers in + * the buffer descriptors. + */ + rbdf = (cbd_t *)&cp->cp_dpmem[dpaddr]; + rbdf->cbd_bufaddr = memaddr; + rbdf->cbd_sc = 0; + tbdf = rbdf + 1; + tbdf->cbd_bufaddr = memaddr+4; + tbdf->cbd_sc = 0; + + /* Set up the uart parameters in the parameter ram. + */ + up->smc_rbase = dpaddr; + up->smc_tbase = dpaddr+sizeof(cbd_t); + up->smc_rfcr = SMC_EB; + up->smc_tfcr = SMC_EB; + + /* Set UART mode, 8 bit, no parity, one stop. + * Enable receive and transmit. + */ + sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART; + + /* Mask all interrupts and remove anything pending. + */ + sp->smc_smcm = 0; + sp->smc_smce = 0xff; + + /* Set up the baud rate generator. + * See 8xx_io/commproc.c for details. + */ + cp->cp_simode = 0x10000000; + cp->cp_brgc1 = + (((bd->bi_intfreq/16) / 9600) << 1) | CPM_BRG_EN; + + /* Enable SMC1 for console output. + */ + *MBX_CSR1 &= ~CSR1_COMEN; + } + else { +#endif /* ndef CONFIG_MBX */ + /* SMCx is used as console port. + */ + tbdf = (cbd_t *)&cp->cp_dpmem[up->smc_tbase]; + rbdf = (cbd_t *)&cp->cp_dpmem[up->smc_rbase]; + + /* Issue a stop transmit, and wait for it. + */ + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_CONS, + CPM_CR_STOP_TX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + } + + /* Make the first buffer the only buffer. + */ + tbdf->cbd_sc |= BD_SC_WRAP; + rbdf->cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP; + + /* Single character receive. + */ + up->smc_mrblr = 1; + up->smc_maxidl = 0; + + /* Initialize Tx/Rx parameters. + */ + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_CONS, CPM_CR_INIT_TRX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG); + + /* Enable transmitter/receiver. + */ + sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN; + + /* This is ignored. + */ + return 0; +} + +void +serial_putc(void *ignored, const char c) +{ + volatile cbd_t *tbdf; + volatile char *buf; + volatile smc_uart_t *up; + + up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_CONS]; + tbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_tbase]; + + /* Wait for last character to go. + */ + buf = (char *)tbdf->cbd_bufaddr; + while (tbdf->cbd_sc & BD_SC_READY); + + *buf = c; + tbdf->cbd_datlen = 1; + tbdf->cbd_sc |= BD_SC_READY; +} + +char +serial_getc(void *ignored) +{ + volatile cbd_t *rbdf; + volatile char *buf; + volatile smc_uart_t *up; + char c; + + up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_CONS]; + rbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase]; + + /* Wait for character to show up. + */ + buf = (char *)rbdf->cbd_bufaddr; + while (rbdf->cbd_sc & BD_SC_EMPTY); + c = *buf; + rbdf->cbd_sc |= BD_SC_EMPTY; + + return(c); +} + +int +serial_tstc(void *ignored) +{ + volatile cbd_t *rbdf; + volatile smc_uart_t *up; + + up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_CONS]; + rbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase]; + + return(!(rbdf->cbd_sc & BD_SC_EMPTY)); +} diff --git a/arch/ppc/boot/simple/misc-chestnut.c b/arch/ppc/boot/simple/misc-chestnut.c new file mode 100644 index 00000000000..0dce7f3557e --- /dev/null +++ b/arch/ppc/boot/simple/misc-chestnut.c @@ -0,0 +1,35 @@ +/* + * arch/ppc/boot/simple/misc-chestnut.c + * + * Setup for the IBM Chestnut (ibm-750fxgx_eval) + * + * Author: Mark A. Greer + * + * 2005 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include + +/* Not in the kernel so won't include kernel.h to get its 'max' definition */ +#define max(a,b) (((a) > (b)) ? (a) : (b)) + +void +mv64x60_board_init(void __iomem *old_base, void __iomem *new_base) +{ +#ifdef CONFIG_SERIAL_8250_CONSOLE + /* + * Change device bus 2 window so that bootoader can do I/O thru + * 8250/16550 UART that's mapped in that window. + */ + out_le32(new_base + MV64x60_CPU2DEV_2_BASE, CHESTNUT_UART_BASE >> 16); + out_le32(new_base + MV64x60_CPU2DEV_2_SIZE, CHESTNUT_UART_SIZE >> 16); + __asm__ __volatile__("sync"); +#endif +} diff --git a/arch/ppc/boot/simple/misc-cpci690.c b/arch/ppc/boot/simple/misc-cpci690.c new file mode 100644 index 00000000000..ef08e86c9b2 --- /dev/null +++ b/arch/ppc/boot/simple/misc-cpci690.c @@ -0,0 +1,27 @@ +/* + * arch/ppc/boot/simple/misc-cpci690.c + * + * Add birec data for Force CPCI690 board. + * + * Author: Mark A. Greer + * + * 2003 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include + +extern u32 mv64x60_console_baud; +extern u32 mv64x60_mpsc_clk_src; +extern u32 mv64x60_mpsc_clk_freq; + +void +mv64x60_board_init(void __iomem *old_base, void __iomem *new_base) +{ + mv64x60_console_baud = CPCI690_MPSC_BAUD; + mv64x60_mpsc_clk_src = CPCI690_MPSC_CLK_SRC; + mv64x60_mpsc_clk_freq = CPCI690_BUS_FREQ; +} diff --git a/arch/ppc/boot/simple/misc-embedded.c b/arch/ppc/boot/simple/misc-embedded.c new file mode 100644 index 00000000000..3865f3f8dcd --- /dev/null +++ b/arch/ppc/boot/simple/misc-embedded.c @@ -0,0 +1,275 @@ +/* + * Originally adapted by Gary Thomas. Much additional work by + * Cort Dougan . On top of that still more work by + * Dan Malek . + * + * Currently maintained by: Tom Rini + */ + +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_4xx) +#include +#elif defined(CONFIG_8xx) +#include +#elif defined(CONFIG_8260) +#include +#endif + +#include "nonstdio.h" + +/* The linker tells us where the image is. */ +extern char __image_begin, __image_end; +extern char __ramdisk_begin, __ramdisk_end; +extern char _end[]; + +/* Because of the limited amount of memory on embedded, it presents + * loading problems. The biggest is that we load this boot program + * into a relatively low memory address, and the Linux kernel Bss often + * extends into this space when it get loaded. When the kernel starts + * and zeros the BSS space, it also writes over the information we + * save here and pass to the kernel (usually board info). + * On these boards, we grab some known memory holes to hold this information. + */ +char cmd_buf[256]; +char *cmd_line = cmd_buf; +char *avail_ram; +char *end_avail; +char *zimage_start; + +/* This is for 4xx treeboot. It provides a place for the bootrom + * give us a pointer to a rom environment command line. + */ +char *bootrom_cmdline = ""; + +/* This is the default cmdline that will be given to the user at boot time.. + * If none was specified at compile time, we'll give it one that should work. + * -- Tom */ +#ifdef CONFIG_CMDLINE_BOOL +char compiled_string[] = CONFIG_CMDLINE; +#endif +char ramroot_string[] = "root=/dev/ram"; +char netroot_string[] = "root=/dev/nfs rw ip=on"; + +/* Serial port to use. */ +unsigned long com_port; + +/* We need to make sure that this is before the images to ensure + * that it's in a mapped location. - Tom */ +bd_t hold_resid_buf __attribute__ ((__section__ (".data.boot"))); +bd_t *hold_residual = &hold_resid_buf; + +extern unsigned long serial_init(int chan, bd_t *bp); +extern void serial_close(unsigned long com_port); +extern unsigned long start; +extern void flush_instruction_cache(void); +extern void gunzip(void *, int, unsigned char *, int *); +extern void embed_config(bd_t **bp); + +/* Weak function for boards which don't need to build the + * board info struct because they are using PPCBoot/U-Boot. + */ +void __attribute__ ((weak)) +embed_config(bd_t **bdp) +{ +} + +unsigned long +load_kernel(unsigned long load_addr, int num_words, unsigned long cksum, bd_t *bp) +{ + char *cp, ch; + int timer = 0, zimage_size; + unsigned long initrd_size; + + /* First, capture the embedded board information. Then + * initialize the serial console port. + */ + embed_config(&bp); +#if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) + com_port = serial_init(0, bp); +#endif + + /* Grab some space for the command line and board info. Since + * we no longer use the ELF header, but it was loaded, grab + * that space. + */ +#ifdef CONFIG_MBX + /* Because of the way the MBX loads the ELF image, we can't + * tell where we started. We read a magic variable from the NVRAM + * that gives us the intermediate buffer load address. + */ + load_addr = *(uint *)0xfa000020; + load_addr += 0x10000; /* Skip ELF header */ +#endif + /* copy board data */ + if (bp) + memcpy(hold_residual,bp,sizeof(bd_t)); + + /* Set end of memory available to us. It is always the highest + * memory address provided by the board information. + */ + end_avail = (char *)(bp->bi_memsize); + + puts("\nloaded at: "); puthex(load_addr); + puts(" "); puthex((unsigned long)(load_addr + (4*num_words))); puts("\n"); + if ( (unsigned long)load_addr != (unsigned long)&start ) { + puts("relocated to: "); puthex((unsigned long)&start); + puts(" "); + puthex((unsigned long)((unsigned long)&start + (4*num_words))); + puts("\n"); + } + + if ( bp ) { + puts("board data at: "); puthex((unsigned long)bp); + puts(" "); + puthex((unsigned long)((unsigned long)bp + sizeof(bd_t))); + puts("\nrelocated to: "); + puthex((unsigned long)hold_residual); + puts(" "); + puthex((unsigned long)((unsigned long)hold_residual + sizeof(bd_t))); + puts("\n"); + } + + /* + * We link ourself to an arbitrary low address. When we run, we + * relocate outself to that address. __image_being points to + * the part of the image where the zImage is. -- Tom + */ + zimage_start = (char *)(unsigned long)(&__image_begin); + zimage_size = (unsigned long)(&__image_end) - + (unsigned long)(&__image_begin); + + initrd_size = (unsigned long)(&__ramdisk_end) - + (unsigned long)(&__ramdisk_begin); + + /* + * The zImage and initrd will be between start and _end, so they've + * already been moved once. We're good to go now. -- Tom + */ + puts("zimage at: "); puthex((unsigned long)zimage_start); + puts(" "); puthex((unsigned long)(zimage_size+zimage_start)); + puts("\n"); + + if ( initrd_size ) { + puts("initrd at: "); + puthex((unsigned long)(&__ramdisk_begin)); + puts(" "); puthex((unsigned long)(&__ramdisk_end));puts("\n"); + } + + /* + * setup avail_ram - this is the first part of ram usable + * by the uncompress code. Anything after this program in RAM + * is now fair game. -- Tom + */ + avail_ram = (char *)PAGE_ALIGN((unsigned long)_end); + + puts("avail ram: "); puthex((unsigned long)avail_ram); puts(" "); + puthex((unsigned long)end_avail); puts("\n"); + puts("\nLinux/PPC load: "); + cp = cmd_line; + /* This is where we try and pick the right command line for booting. + * If we were given one at compile time, use it. It Is Right. + * If we weren't, see if we have a ramdisk. If so, thats root. + * When in doubt, give them the netroot (root=/dev/nfs rw) -- Tom + */ +#ifdef CONFIG_CMDLINE_BOOL + memcpy (cmd_line, compiled_string, sizeof(compiled_string)); +#else + if ( initrd_size ) + memcpy (cmd_line, ramroot_string, sizeof(ramroot_string)); + else + memcpy (cmd_line, netroot_string, sizeof(netroot_string)); +#endif + while ( *cp ) + putc(*cp++); + while (timer++ < 5*1000) { + if (tstc()) { + while ((ch = getc()) != '\n' && ch != '\r') { + if (ch == '\b' || ch == '\177') { + if (cp != cmd_line) { + cp--; + puts("\b \b"); + } + } else if (ch == '\030' /* ^x */ + || ch == '\025') { /* ^u */ + while (cp != cmd_line) { + cp--; + puts("\b \b"); + } + } else { + *cp++ = ch; + putc(ch); + } + } + break; /* Exit 'timer' loop */ + } + udelay(1000); /* 1 msec */ + } + *cp = 0; + puts("\nUncompressing Linux..."); + + gunzip(0, 0x400000, zimage_start, &zimage_size); + flush_instruction_cache(); + puts("done.\n"); + { + struct bi_record *rec; + unsigned long initrd_loc = 0; + unsigned long rec_loc = _ALIGN((unsigned long)(zimage_size) + + (1 << 20) - 1, (1 << 20)); + rec = (struct bi_record *)rec_loc; + + /* We need to make sure that the initrd and bi_recs do not + * overlap. */ + if ( initrd_size ) { + initrd_loc = (unsigned long)(&__ramdisk_begin); + /* If the bi_recs are in the middle of the current + * initrd, move the initrd to the next MB + * boundary. */ + if ((rec_loc > initrd_loc) && + ((initrd_loc + initrd_size) + > rec_loc)) { + initrd_loc = _ALIGN((unsigned long)(zimage_size) + + (2 << 20) - 1, (2 << 20)); + memmove((void *)initrd_loc, &__ramdisk_begin, + initrd_size); + puts("initrd moved: "); puthex(initrd_loc); + puts(" "); puthex(initrd_loc + initrd_size); + puts("\n"); + } + } + + rec->tag = BI_FIRST; + rec->size = sizeof(struct bi_record); + rec = (struct bi_record *)((unsigned long)rec + rec->size); + + rec->tag = BI_CMD_LINE; + memcpy( (char *)rec->data, cmd_line, strlen(cmd_line)+1); + rec->size = sizeof(struct bi_record) + strlen(cmd_line) + 1; + rec = (struct bi_record *)((unsigned long)rec + rec->size); + + if ( initrd_size ) { + rec->tag = BI_INITRD; + rec->data[0] = initrd_loc; + rec->data[1] = initrd_size; + rec->size = sizeof(struct bi_record) + 2 * + sizeof(unsigned long); + rec = (struct bi_record *)((unsigned long)rec + + rec->size); + } + + rec->tag = BI_LAST; + rec->size = sizeof(struct bi_record); + rec = (struct bi_record *)((unsigned long)rec + rec->size); + } + puts("Now booting the kernel\n"); +#if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) + serial_close(com_port); +#endif + + return (unsigned long)hold_residual; +} diff --git a/arch/ppc/boot/simple/misc-ev64260.c b/arch/ppc/boot/simple/misc-ev64260.c new file mode 100644 index 00000000000..52ece6937a7 --- /dev/null +++ b/arch/ppc/boot/simple/misc-ev64260.c @@ -0,0 +1,57 @@ +/* + * arch/ppc/boot/simple/misc-ev64260.c + * + * Host bridge init code for the Marvell/Galileo EV-64260-BP evaluation board + * with a GT64260 onboard. + * + * Author: Mark A. Greer + * + * 2001 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_SERIAL_MPSC_CONSOLE +extern u32 mv64x60_console_baud; +extern u32 mv64x60_mpsc_clk_src; +extern u32 mv64x60_mpsc_clk_freq; +#endif + +void +mv64x60_board_init(void __iomem *old_base, void __iomem *new_base) +{ + u32 p, v; + + /* DINK doesn't enable 745x timebase, so enable here (Adrian Cox) */ + p = mfspr(SPRN_PVR); + p >>= 16; + + /* Reasonable SWAG at a 745x PVR value */ + if (((p & 0xfff0) == 0x8000) && (p != 0x800c)) { + v = mfspr(SPRN_HID0); + v |= HID0_TBEN; + mtspr(SPRN_HID0, v); + } + +#ifdef CONFIG_SERIAL_8250_CONSOLE + /* + * Change device bus 2 window so that bootoader can do I/O thru + * 8250/16550 UART that's mapped in that window. + */ + out_le32(new_base + MV64x60_CPU2DEV_2_BASE, EV64260_UART_BASE >> 20); + out_le32(new_base + MV64x60_CPU2DEV_2_SIZE, EV64260_UART_END >> 20); + __asm__ __volatile__("sync"); +#elif defined(CONFIG_SERIAL_MPSC_CONSOLE) + mv64x60_console_baud = EV64260_DEFAULT_BAUD; + mv64x60_mpsc_clk_src = EV64260_MPSC_CLK_SRC; + mv64x60_mpsc_clk_freq = EV64260_MPSC_CLK_FREQ; +#endif +} diff --git a/arch/ppc/boot/simple/misc-katana.c b/arch/ppc/boot/simple/misc-katana.c new file mode 100644 index 00000000000..b6e1bb83315 --- /dev/null +++ b/arch/ppc/boot/simple/misc-katana.c @@ -0,0 +1,37 @@ +/* + * arch/ppc/boot/simple/misc-katana.c + * + * Set up MPSC values to bootwrapper can prompt user. + * + * Author: Mark A. Greer + * + * 2004 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include + +extern u32 mv64x60_console_baud; +extern u32 mv64x60_mpsc_clk_src; +extern u32 mv64x60_mpsc_clk_freq; + +/* Not in the kernel so won't include kernel.h to get its 'min' definition */ +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +void +mv64x60_board_init(void __iomem *old_base, void __iomem *new_base) +{ + mv64x60_console_baud = KATANA_DEFAULT_BAUD; + mv64x60_mpsc_clk_src = KATANA_MPSC_CLK_SRC; + mv64x60_mpsc_clk_freq = + min(katana_bus_freq((void __iomem *)KATANA_CPLD_BASE), + MV64x60_TCLK_FREQ_MAX); +} diff --git a/arch/ppc/boot/simple/misc-mv64x60.c b/arch/ppc/boot/simple/misc-mv64x60.c new file mode 100644 index 00000000000..7e88fc6d207 --- /dev/null +++ b/arch/ppc/boot/simple/misc-mv64x60.c @@ -0,0 +1,61 @@ +/* + * arch/ppc/boot/simple/misc-mv64x60.c + * + * Relocate bridge's register base and call board specific routine. + * + * Author: Mark A. Greer + * + * 2005 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include + +extern struct bi_record *decompress_kernel(unsigned long load_addr, + int num_words, unsigned long cksum); + +void +mv64x60_move_base(void __iomem *old_base, void __iomem *new_base) +{ + u32 bits, mask, b; + + if (old_base != new_base) { +#ifdef CONFIG_GT64260 + bits = 12; + mask = 0x07000000; +#else /* Must be mv64[34]60 */ + bits = 16; + mask = 0x03000000; +#endif + b = in_le32(old_base + MV64x60_INTERNAL_SPACE_DECODE); + b &= mask; + b |= ((u32)new_base >> (32 - bits)); + out_le32(old_base + MV64x60_INTERNAL_SPACE_DECODE, b); + + __asm__ __volatile__("sync"); + + /* Wait for change to happen (in accordance with the manual) */ + while (in_le32(new_base + MV64x60_INTERNAL_SPACE_DECODE) != b); + } +} + +void __attribute__ ((weak)) +mv64x60_board_init(void __iomem *old_base, void __iomem *new_base) +{ +} + +void * +load_kernel(unsigned long load_addr, int num_words, unsigned long cksum, + void *ign1, void *ign2) +{ + mv64x60_move_base((void __iomem *)CONFIG_MV64X60_BASE, + (void __iomem *)CONFIG_MV64X60_NEW_BASE); + mv64x60_board_init((void __iomem *)CONFIG_MV64X60_BASE, + (void __iomem *)CONFIG_MV64X60_NEW_BASE); + return decompress_kernel(load_addr, num_words, cksum); +} diff --git a/arch/ppc/boot/simple/misc-prep.c b/arch/ppc/boot/simple/misc-prep.c new file mode 100644 index 00000000000..75380ac4166 --- /dev/null +++ b/arch/ppc/boot/simple/misc-prep.c @@ -0,0 +1,212 @@ +/* + * arch/ppc/boot/simple/misc-prep.c + * + * Maintainer: Tom Rini + * + * In the past: Gary Thomas, Cort Dougan + */ + +#include +#include +#include +#include +#include +#include +#include "mpc10x.h" +#include "of1275.h" +#include "nonstdio.h" + +extern int keyb_present; /* keyboard controller is present by default */ +RESIDUAL hold_resid_buf; +RESIDUAL *hold_residual = &hold_resid_buf; +static void *OFW_interface; /* Pointer to OF, if available. */ + +#ifdef CONFIG_VGA_CONSOLE +char *vidmem = (char *)0xC00B8000; +int lines = 25, cols = 80; +int orig_x, orig_y = 24; +#endif /* CONFIG_VGA_CONSOLE */ + +extern int CRT_tstc(void); +extern int vga_init(unsigned char *ISA_mem); +extern void gunzip(void *, int, unsigned char *, int *); +extern unsigned long serial_init(int chan, void *ignored); +extern void serial_fixups(void); +extern struct bi_record *decompress_kernel(unsigned long load_addr, + int num_words, unsigned long cksum); +extern void disable_6xx_mmu(void); +extern unsigned long mpc10x_get_mem_size(void); + +static void +writel(unsigned int val, unsigned int address) +{ + /* Ensure I/O operations complete */ + __asm__ volatile("eieio"); + *(unsigned int *)address = cpu_to_le32(val); +} + +#define PCI_CFG_ADDR(dev,off) ((0x80<<24) | (dev<<8) | (off&0xfc)) +#define PCI_CFG_DATA(off) (MPC10X_MAPA_CNFG_DATA+(off&3)) + +static void +pci_read_config_32(unsigned char devfn, + unsigned char offset, + unsigned int *val) +{ + /* Ensure I/O operations complete */ + __asm__ volatile("eieio"); + *(unsigned int *)PCI_CFG_ADDR(devfn,offset) = + cpu_to_le32(MPC10X_MAPA_CNFG_ADDR); + /* Ensure I/O operations complete */ + __asm__ volatile("eieio"); + *val = le32_to_cpu(*(unsigned int *)PCI_CFG_DATA(offset)); + return; +} + +#ifdef CONFIG_VGA_CONSOLE +void +scroll(void) +{ + int i; + + memcpy ( vidmem, vidmem + cols * 2, ( lines - 1 ) * cols * 2 ); + for ( i = ( lines - 1 ) * cols * 2; i < lines * cols * 2; i += 2 ) + vidmem[i] = ' '; +} +#endif /* CONFIG_VGA_CONSOLE */ + +unsigned long +load_kernel(unsigned long load_addr, int num_words, unsigned long cksum, + RESIDUAL *residual, void *OFW) +{ + int start_multi = 0; + unsigned int pci_viddid, pci_did, tulip_pci_base, tulip_base; + + /* If we have Open Firmware, initialise it immediately */ + if (OFW) { + OFW_interface = OFW; + ofinit(OFW_interface); + } + + board_isa_init(); +#if defined(CONFIG_VGA_CONSOLE) + vga_init((unsigned char *)0xC0000000); +#endif /* CONFIG_VGA_CONSOLE */ + + if (residual) { + /* Is this Motorola PPCBug? */ + if ((1 & residual->VitalProductData.FirmwareSupports) && + (1 == residual->VitalProductData.FirmwareSupplier)) { + unsigned char base_mod; + unsigned char board_type = inb(0x801) & 0xF0; + + /* + * Reset the onboard 21x4x Ethernet + * Motorola Ethernet is at IDSEL 14 (devfn 0x70) + */ + pci_read_config_32(0x70, 0x00, &pci_viddid); + pci_did = (pci_viddid & 0xffff0000) >> 16; + /* Be sure we've really found a 21x4x chip */ + if (((pci_viddid & 0xffff) == PCI_VENDOR_ID_DEC) && + ((pci_did == PCI_DEVICE_ID_DEC_TULIP_FAST) || + (pci_did == PCI_DEVICE_ID_DEC_TULIP) || + (pci_did == PCI_DEVICE_ID_DEC_TULIP_PLUS) || + (pci_did == PCI_DEVICE_ID_DEC_21142))) { + pci_read_config_32(0x70, + 0x10, + &tulip_pci_base); + /* Get the physical base address */ + tulip_base = + (tulip_pci_base & ~0x03UL) + 0x80000000; + /* Strobe the 21x4x reset bit in CSR0 */ + writel(0x1, tulip_base); + } + + /* If this is genesis 2 board then check for no + * keyboard controller and more than one processor. + */ + if (board_type == 0xe0) { + base_mod = inb(0x803); + /* if a MVME2300/2400 or a Sitka then no keyboard */ + if((base_mod == 0xFA) || (base_mod == 0xF9) || + (base_mod == 0xE1)) { + keyb_present = 0; /* no keyboard */ + } + } + /* If this is a multiprocessor system then + * park the other processor so that the + * kernel knows where to find them. + */ + if (residual->MaxNumCpus > 1) + start_multi = 1; + } + memcpy(hold_residual,residual,sizeof(RESIDUAL)); + } + + /* Call decompress_kernel */ + decompress_kernel(load_addr, num_words, cksum); + + if (start_multi) { + residual->VitalProductData.SmpIar = (unsigned long)0xc0; + residual->Cpus[1].CpuState = CPU_GOOD; + hold_residual->VitalProductData.Reserved5 = 0xdeadbeef; + } + + /* Now go and clear out the BATs and ensure that our MSR is + * correct .*/ + disable_6xx_mmu(); + + /* Make r3 be a pointer to the residual data. */ + return (unsigned long)hold_residual; +} + +unsigned long +get_mem_size(void) +{ + unsigned int pci_viddid, pci_did; + + /* First, figure out what kind of host bridge we are on. If it's + * an MPC10x, we can ask it directly how much memory it has. + * Otherwise, see if the residual data has anything. This isn't + * the best way, but it can be the only way. If there's nothing, + * assume 32MB. -- Tom. + */ + /* See what our host bridge is. */ + pci_read_config_32(0x00, 0x00, &pci_viddid); + pci_did = (pci_viddid & 0xffff0000) >> 16; + /* See if we are on an MPC10x. */ + if (((pci_viddid & 0xffff) == PCI_VENDOR_ID_MOTOROLA) + && ((pci_did == PCI_DEVICE_ID_MOTOROLA_MPC105) + || (pci_did == PCI_DEVICE_ID_MOTOROLA_MPC106) + || (pci_did == PCI_DEVICE_ID_MOTOROLA_MPC107))) + return mpc10x_get_mem_size(); + /* If it's not, see if we have anything in the residual data. */ + else if (hold_residual && hold_residual->TotalMemory) + return hold_residual->TotalMemory; + else if (OFW_interface) { + /* + * This is a 'best guess' check. We want to make sure + * we don't try this on a PReP box without OF + * -- Cort + */ + while (OFW_interface) + { + phandle dev_handle; + int mem_info[2]; + + /* get handle to memory description */ + if (!(dev_handle = finddevice("/memory@0"))) + break; + + /* get the info */ + if (getprop(dev_handle, "reg", mem_info, + sizeof(mem_info)) != 8) + break; + + return mem_info[1]; + } + } + + /* Fall back to hard-coding 32MB. */ + return 32*1024*1024; +} diff --git a/arch/ppc/boot/simple/misc-radstone_ppc7d.c b/arch/ppc/boot/simple/misc-radstone_ppc7d.c new file mode 100644 index 00000000000..569e0d4feea --- /dev/null +++ b/arch/ppc/boot/simple/misc-radstone_ppc7d.c @@ -0,0 +1,26 @@ +/* + * arch/ppc/boot/simple/misc-radstone_ppc7d.c + * + * Misc data for Radstone PPC7D board. + * + * Author: James Chapman + */ + +#include +#include + +#if defined(CONFIG_SERIAL_MPSC_CONSOLE) +extern u32 mv64x60_console_baud; +extern u32 mv64x60_mpsc_clk_src; +extern u32 mv64x60_mpsc_clk_freq; +#endif + +void +mv64x60_board_init(void __iomem *old_base, void __iomem *new_base) +{ +#if defined(CONFIG_SERIAL_MPSC_CONSOLE) + mv64x60_console_baud = PPC7D_DEFAULT_BAUD; + mv64x60_mpsc_clk_src = PPC7D_MPSC_CLK_SRC; + mv64x60_mpsc_clk_freq = PPC7D_MPSC_CLK_FREQ; +#endif +} diff --git a/arch/ppc/boot/simple/misc-spruce.c b/arch/ppc/boot/simple/misc-spruce.c new file mode 100644 index 00000000000..d012c39278f --- /dev/null +++ b/arch/ppc/boot/simple/misc-spruce.c @@ -0,0 +1,274 @@ +/* + * arch/ppc/boot/spruce/misc.c + * + * Misc. bootloader code for IBM Spruce reference platform + * + * Authors: Johnnie Peters + * Matt Porter + * + * Derived from arch/ppc/boot/prep/misc.c + * + * 2000-2001 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include + +#include + +extern unsigned long decompress_kernel(unsigned long load_addr, int num_words, + unsigned long cksum); + +/* Define some important locations of the Spruce. */ +#define SPRUCE_PCI_CONFIG_ADDR 0xfec00000 +#define SPRUCE_PCI_CONFIG_DATA 0xfec00004 + +/* PCI configuration space access routines. */ +unsigned int *pci_config_address = (unsigned int *)SPRUCE_PCI_CONFIG_ADDR; +unsigned char *pci_config_data = (unsigned char *)SPRUCE_PCI_CONFIG_DATA; + +void cpc700_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char *val) +{ + out_le32(pci_config_address, + (((bus & 0xff)<<16) | (dev_fn<<8) | (offset&0xfc) | 0x80000000)); + + *val= (in_le32((unsigned *)pci_config_data) >> (8 * (offset & 3))) & 0xff; +} + +void cpc700_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val) +{ + out_le32(pci_config_address, + (((bus & 0xff)<<16) | (dev_fn<<8) | (offset&0xfc) | 0x80000000)); + + out_8(pci_config_data + (offset&3), val); +} + +void cpc700_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val) +{ + out_le32(pci_config_address, + (((bus & 0xff)<<16) | (dev_fn<<8) | (offset&0xfc) | 0x80000000)); + + *val= in_le16((unsigned short *)(pci_config_data + (offset&3))); +} + +void cpc700_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val) +{ + out_le32(pci_config_address, + (((bus & 0xff)<<16) | (dev_fn<<8) | (offset&0xfc) | 0x80000000)); + + out_le16((unsigned short *)(pci_config_data + (offset&3)), val); +} + +void cpc700_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val) +{ + out_le32(pci_config_address, + (((bus & 0xff)<<16) | (dev_fn<<8) | (offset&0xfc) | 0x80000000)); + + *val= in_le32((unsigned *)pci_config_data); +} + +void cpc700_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val) +{ + out_le32(pci_config_address, + (((bus & 0xff)<<16) | (dev_fn<<8) | (offset&0xfc) | 0x80000000)); + + out_le32((unsigned *)pci_config_data, val); +} + +#define PCNET32_WIO_RDP 0x10 +#define PCNET32_WIO_RAP 0x12 +#define PCNET32_WIO_RESET 0x14 + +#define PCNET32_DWIO_RDP 0x10 +#define PCNET32_DWIO_RAP 0x14 +#define PCNET32_DWIO_RESET 0x18 + +/* Processor interface config register access */ +#define PIFCFGADDR 0xff500000 +#define PIFCFGDATA 0xff500004 + +#define PLBMIFOPT 0x18 /* PLB Master Interface Options */ + +#define MEM_MBEN 0x24 +#define MEM_TYPE 0x28 +#define MEM_B1SA 0x3c +#define MEM_B1EA 0x5c +#define MEM_B2SA 0x40 +#define MEM_B2EA 0x60 + +unsigned long +get_mem_size(void) +{ + int loop; + unsigned long mem_size = 0; + unsigned long mem_mben; + unsigned long mem_type; + unsigned long mem_start; + unsigned long mem_end; + volatile int *mem_addr = (int *)0xff500008; + volatile int *mem_data = (int *)0xff50000c; + + /* Get the size of memory from the memory controller. */ + *mem_addr = MEM_MBEN; + asm("sync"); + mem_mben = *mem_data; + asm("sync"); + for(loop = 0; loop < 1000; loop++); + + *mem_addr = MEM_TYPE; + asm("sync"); + mem_type = *mem_data; + asm("sync"); + for(loop = 0; loop < 1000; loop++); + + *mem_addr = MEM_TYPE; + /* Confirm bank 1 has DRAM memory */ + if ((mem_mben & 0x40000000) && + ((mem_type & 0x30000000) == 0x10000000)) { + *mem_addr = MEM_B1SA; + asm("sync"); + mem_start = *mem_data; + asm("sync"); + for(loop = 0; loop < 1000; loop++); + + *mem_addr = MEM_B1EA; + asm("sync"); + mem_end = *mem_data; + asm("sync"); + for(loop = 0; loop < 1000; loop++); + + mem_size = mem_end - mem_start + 0x100000; + } + + /* Confirm bank 2 has DRAM memory */ + if ((mem_mben & 0x20000000) && + ((mem_type & 0xc000000) == 0x4000000)) { + *mem_addr = MEM_B2SA; + asm("sync"); + mem_start = *mem_data; + asm("sync"); + for(loop = 0; loop < 1000; loop++); + + *mem_addr = MEM_B2EA; + asm("sync"); + mem_end = *mem_data; + asm("sync"); + for(loop = 0; loop < 1000; loop++); + + mem_size += mem_end - mem_start + 0x100000; + } + return mem_size; +} + +unsigned long +load_kernel(unsigned long load_addr, int num_words, unsigned long cksum, + void *ign1, void *ign2) +{ + int csr0; + int csr_id; + int pci_devfn; + int found_multi = 0; + unsigned short vendor; + unsigned short device; + unsigned short command; + unsigned char header_type; + unsigned int bar0; + volatile int *pif_addr = (int *)0xff500000; + volatile int *pif_data = (int *)0xff500004; + + /* + * Gah, these firmware guys need to learn that hardware + * byte swapping is evil! Disable all hardware byte + * swapping so it doesn't hurt anyone. + */ + *pif_addr = PLBMIFOPT; + asm("sync"); + *pif_data = 0x00000000; + asm("sync"); + + /* Search out and turn off the PcNet ethernet boot device. */ + for (pci_devfn = 1; pci_devfn < 0xff; pci_devfn++) { + if (PCI_FUNC(pci_devfn) && !found_multi) + continue; + + cpc700_pcibios_read_config_byte(0, pci_devfn, + PCI_HEADER_TYPE, &header_type); + + if (!PCI_FUNC(pci_devfn)) + found_multi = header_type & 0x80; + + cpc700_pcibios_read_config_word(0, pci_devfn, PCI_VENDOR_ID, + &vendor); + + if (vendor != 0xffff) { + cpc700_pcibios_read_config_word(0, pci_devfn, + PCI_DEVICE_ID, &device); + + /* If this PCI device is the Lance PCNet board then turn it off */ + if ((vendor == PCI_VENDOR_ID_AMD) && + (device == PCI_DEVICE_ID_AMD_LANCE)) { + + /* Turn on I/O Space on the board. */ + cpc700_pcibios_read_config_word(0, pci_devfn, + PCI_COMMAND, &command); + command |= 0x1; + cpc700_pcibios_write_config_word(0, pci_devfn, + PCI_COMMAND, command); + + /* Get the I/O space address */ + cpc700_pcibios_read_config_dword(0, pci_devfn, + PCI_BASE_ADDRESS_0, &bar0); + bar0 &= 0xfffffffe; + + /* Reset the PCNet Board */ + inl (bar0+PCNET32_DWIO_RESET); + inw (bar0+PCNET32_WIO_RESET); + + /* First do a work oriented read of csr0. If the value is + * 4 then this is the correct mode to access the board. + * If not try a double word ortiented read. + */ + outw(0, bar0 + PCNET32_WIO_RAP); + csr0 = inw(bar0 + PCNET32_WIO_RDP); + + if (csr0 == 4) { + /* Check the Chip id register */ + outw(88, bar0 + PCNET32_WIO_RAP); + csr_id = inw(bar0 + PCNET32_WIO_RDP); + + if (csr_id) { + /* This is the valid mode - set the stop bit */ + outw(0, bar0 + PCNET32_WIO_RAP); + outw(csr0, bar0 + PCNET32_WIO_RDP); + } + } else { + outl(0, bar0 + PCNET32_DWIO_RAP); + csr0 = inl(bar0 + PCNET32_DWIO_RDP); + if (csr0 == 4) { + /* Check the Chip id register */ + outl(88, bar0 + PCNET32_WIO_RAP); + csr_id = inl(bar0 + PCNET32_WIO_RDP); + + if (csr_id) { + /* This is the valid mode - set the stop bit*/ + outl(0, bar0 + PCNET32_WIO_RAP); + outl(csr0, bar0 + PCNET32_WIO_RDP); + } + } + } + } + } + } + + return decompress_kernel(load_addr, num_words, cksum); +} diff --git a/arch/ppc/boot/simple/misc.c b/arch/ppc/boot/simple/misc.c new file mode 100644 index 00000000000..ab0f9902cb6 --- /dev/null +++ b/arch/ppc/boot/simple/misc.c @@ -0,0 +1,284 @@ +/* + * arch/ppc/simple/misc.c + * + * Misc. bootloader code for many machines. This assumes you have are using + * a 6xx/7xx/74xx CPU in your machine. This assumes the chunk of memory + * below 8MB is free. Finally, it assumes you have a NS16550-style uart for + * your serial console. If a machine meets these requirements, it can quite + * likely use this code during boot. + * + * Author: Matt Porter + * Derived from arch/ppc/boot/prep/misc.c + * + * 2001 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include + +#include +#include +#include +#ifdef CONFIG_44x +#include +#endif +#include + +#include "nonstdio.h" + +/* Default cmdline */ +#ifdef CONFIG_CMDLINE +#define CMDLINE CONFIG_CMDLINE +#else +#define CMDLINE "" +#endif + +/* Keyboard (and VGA console)? */ +#ifdef CONFIG_VGA_CONSOLE +#define HAS_KEYB 1 +#else +#define HAS_KEYB 0 +#endif + +/* Will / Can the user give input? + * Val Henson has requested that Gemini doesn't wait for the + * user to edit the cmdline or not. + */ +#if (defined(CONFIG_SERIAL_8250_CONSOLE) \ + || defined(CONFIG_VGA_CONSOLE) \ + || defined(CONFIG_SERIAL_MPC52xx_CONSOLE) \ + || defined(CONFIG_SERIAL_MPSC_CONSOLE)) \ + && !defined(CONFIG_GEMINI) +#define INTERACTIVE_CONSOLE 1 +#endif + +char *avail_ram; +char *end_avail; +char *zimage_start; +char cmd_preset[] = CMDLINE; +char cmd_buf[256]; +char *cmd_line = cmd_buf; +int keyb_present = HAS_KEYB; +int zimage_size; + +unsigned long com_port; +unsigned long initrd_size = 0; + +/* The linker tells us various locations in the image */ +extern char __image_begin, __image_end; +extern char __ramdisk_begin, __ramdisk_end; +extern char _end[]; +/* Original location */ +extern unsigned long start; + +extern int CRT_tstc(void); +extern unsigned long serial_init(int chan, void *ignored); +extern void serial_close(unsigned long com_port); +extern void gunzip(void *, int, unsigned char *, int *); +extern void serial_fixups(void); + +/* Allow get_mem_size to be hooked into. This is the default. */ +unsigned long __attribute__ ((weak)) +get_mem_size(void) +{ + return 0; +} + +struct bi_record * +decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum) +{ +#ifdef INTERACTIVE_CONSOLE + int timer = 0; + char ch; +#endif + char *cp; + struct bi_record *rec; + unsigned long initrd_loc = 0, TotalMemory = 0; + +#if defined(CONFIG_SERIAL_8250_CONSOLE) || defined(CONFIG_SERIAL_MPSC_CONSOLE) + com_port = serial_init(0, NULL); +#endif + +#if defined(CONFIG_44x) && defined(PPC44x_EMAC0_MR0) + /* Reset MAL */ + mtdcr(DCRN_MALCR(DCRN_MAL_BASE), MALCR_MMSR); + /* Wait for reset */ + while (mfdcr(DCRN_MALCR(DCRN_MAL_BASE)) & MALCR_MMSR) {}; + /* Reset EMAC */ + *(volatile unsigned long *)PPC44x_EMAC0_MR0 = 0x20000000; + __asm__ __volatile__("eieio"); +#endif + + /* + * Call get_mem_size(), which is memory controller dependent, + * and we must have the correct file linked in here. + */ + TotalMemory = get_mem_size(); + + /* assume the chunk below 8M is free */ + end_avail = (char *)0x00800000; + + /* + * Reveal where we were loaded at and where we + * were relocated to. + */ + puts("loaded at: "); puthex(load_addr); + puts(" "); puthex((unsigned long)(load_addr + (4*num_words))); + puts("\n"); + if ( (unsigned long)load_addr != (unsigned long)&start ) + { + puts("relocated to: "); puthex((unsigned long)&start); + puts(" "); + puthex((unsigned long)((unsigned long)&start + (4*num_words))); + puts("\n"); + } + + /* + * We link ourself to 0x00800000. When we run, we relocate + * ourselves there. So we just need __image_begin for the + * start. -- Tom + */ + zimage_start = (char *)(unsigned long)(&__image_begin); + zimage_size = (unsigned long)(&__image_end) - + (unsigned long)(&__image_begin); + + initrd_size = (unsigned long)(&__ramdisk_end) - + (unsigned long)(&__ramdisk_begin); + + /* + * The zImage and initrd will be between start and _end, so they've + * already been moved once. We're good to go now. -- Tom + */ + avail_ram = (char *)PAGE_ALIGN((unsigned long)_end); + puts("zimage at: "); puthex((unsigned long)zimage_start); + puts(" "); puthex((unsigned long)(zimage_size+zimage_start)); + puts("\n"); + + if ( initrd_size ) { + puts("initrd at: "); + puthex((unsigned long)(&__ramdisk_begin)); + puts(" "); puthex((unsigned long)(&__ramdisk_end));puts("\n"); + } + + avail_ram = (char *)0x00400000; + end_avail = (char *)0x00800000; + puts("avail ram: "); puthex((unsigned long)avail_ram); puts(" "); + puthex((unsigned long)end_avail); puts("\n"); + + if (keyb_present) + CRT_tstc(); /* Forces keyboard to be initialized */ +#ifdef CONFIG_GEMINI + /* + * If cmd_line is empty and cmd_preset is not, copy cmd_preset + * to cmd_line. This way we can override cmd_preset with the + * command line from Smon. + */ + + if ( (cmd_line[0] == '\0') && (cmd_preset[0] != '\0')) + memcpy (cmd_line, cmd_preset, sizeof(cmd_preset)); +#endif + + /* Display standard Linux/PPC boot prompt for kernel args */ + puts("\nLinux/PPC load: "); + cp = cmd_line; + memcpy (cmd_line, cmd_preset, sizeof(cmd_preset)); + while ( *cp ) putc(*cp++); + +#ifdef INTERACTIVE_CONSOLE + /* + * If they have a console, allow them to edit the command line. + * Otherwise, don't bother wasting the five seconds. + */ + while (timer++ < 5*1000) { + if (tstc()) { + while ((ch = getc()) != '\n' && ch != '\r') { + /* Test for backspace/delete */ + if (ch == '\b' || ch == '\177') { + if (cp != cmd_line) { + cp--; + puts("\b \b"); + } + /* Test for ^x/^u (and wipe the line) */ + } else if (ch == '\030' || ch == '\025') { + while (cp != cmd_line) { + cp--; + puts("\b \b"); + } + } else { + *cp++ = ch; + putc(ch); + } + } + break; /* Exit 'timer' loop */ + } + udelay(1000); /* 1 msec */ + } + *cp = 0; +#endif + puts("\n"); + + puts("Uncompressing Linux..."); + gunzip(0x0, 0x400000, zimage_start, &zimage_size); + puts("done.\n"); + + /* get the bi_rec address */ + rec = bootinfo_addr(zimage_size); + + /* We need to make sure that the initrd and bi_recs do not + * overlap. */ + if ( initrd_size ) { + unsigned long rec_loc = (unsigned long) rec; + initrd_loc = (unsigned long)(&__ramdisk_begin); + /* If the bi_recs are in the middle of the current + * initrd, move the initrd to the next MB + * boundary. */ + if ((rec_loc > initrd_loc) && + ((initrd_loc + initrd_size) > rec_loc)) { + initrd_loc = _ALIGN((unsigned long)(zimage_size) + + (2 << 20) - 1, (2 << 20)); + memmove((void *)initrd_loc, &__ramdisk_begin, + initrd_size); + puts("initrd moved: "); puthex(initrd_loc); + puts(" "); puthex(initrd_loc + initrd_size); + puts("\n"); + } + } + + bootinfo_init(rec); + if ( TotalMemory ) + bootinfo_append(BI_MEMSIZE, sizeof(int), (void*)&TotalMemory); + + bootinfo_append(BI_CMD_LINE, strlen(cmd_line)+1, (void*)cmd_line); + + /* add a bi_rec for the initrd if it exists */ + if (initrd_size) { + unsigned long initrd[2]; + + initrd[0] = initrd_loc; + initrd[1] = initrd_size; + + bootinfo_append(BI_INITRD, sizeof(initrd), &initrd); + } + puts("Now booting the kernel\n"); + serial_close(com_port); + + return rec; +} + +void __attribute__ ((weak)) +board_isa_init(void) +{ +} + +/* Allow decompress_kernel to be hooked into. This is the default. */ +void * __attribute__ ((weak)) +load_kernel(unsigned long load_addr, int num_words, unsigned long cksum, + void *ign1, void *ign2) +{ + board_isa_init(); + return decompress_kernel(load_addr, num_words, cksum); +} diff --git a/arch/ppc/boot/simple/mpc10x_memory.c b/arch/ppc/boot/simple/mpc10x_memory.c new file mode 100644 index 00000000000..977daedc14c --- /dev/null +++ b/arch/ppc/boot/simple/mpc10x_memory.c @@ -0,0 +1,111 @@ +/* + * arch/ppc/boot/common/mpc10x_common.c + * + * A routine to find out how much memory the machine has. + * + * Based on: + * arch/ppc/kernel/mpc10x_common.c + * + * Author: Mark A. Greer + * mgreer@mvista.com + * + * 2001-2002 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include "mpc10x.h" + +/* + * *** WARNING - A BAT MUST be set to access the PCI config addr/data regs *** + */ + +/* + * PCI config space macros, similar to indirect_xxx and early_xxx macros. + * We assume bus 0. + */ +#define MPC10X_CFG_read(val, addr, type, op) *val = op((type)(addr)) +#define MPC10X_CFG_write(val, addr, type, op) op((type *)(addr), (val)) + +#define MPC10X_PCI_OP(rw, size, type, op, mask) \ +static void \ +mpc10x_##rw##_config_##size(unsigned int *cfg_addr, \ + unsigned int *cfg_data, int devfn, int offset, \ + type val) \ +{ \ + out_be32(cfg_addr, \ + ((offset & 0xfc) << 24) | (devfn << 16) \ + | (0 << 8) | 0x80); \ + MPC10X_CFG_##rw(val, cfg_data + (offset & mask), type, op); \ + return; \ +} + +MPC10X_PCI_OP(read, byte, u8 *, in_8, 3) +MPC10X_PCI_OP(read, dword, u32 *, in_le32, 0) + +/* + * Read the memory controller registers to determine the amount of memory in + * the system. This assumes that the firmware has correctly set up the memory + * controller registers. On CONFIG_PPC_PREP, we know we are being called + * under a PReP memory map. On all other machines, we assume we are under + * a CHRP memory map. Further, on CONFIG_PPC_MULTIPLATFORM we must rename + * this function. + */ +#ifdef CONFIG_PPC_MULTIPLATFORM +#define get_mem_size mpc10x_get_mem_size +#endif +unsigned long +get_mem_size(void) +{ + unsigned int *config_addr, *config_data, val; + unsigned long start, end, total, offset; + int i; + unsigned char bank_enables; + +#ifdef CONFIG_PPC_PREP + config_addr = (unsigned int *)MPC10X_MAPA_CNFG_ADDR; + config_data = (unsigned int *)MPC10X_MAPA_CNFG_DATA; +#else + config_addr = (unsigned int *)MPC10X_MAPB_CNFG_ADDR; + config_data = (unsigned int *)MPC10X_MAPB_CNFG_DATA; +#endif + + mpc10x_read_config_byte(config_addr, config_data, PCI_DEVFN(0,0), + MPC10X_MCTLR_MEM_BANK_ENABLES, &bank_enables); + + total = 0; + + for (i = 0; i < 8; i++) { + if (bank_enables & (1 << i)) { + offset = MPC10X_MCTLR_MEM_START_1 + ((i > 3) ? 4 : 0); + mpc10x_read_config_dword(config_addr, config_data, + PCI_DEVFN(0,0), offset, &val); + start = (val >> ((i & 3) << 3)) & 0xff; + + offset = MPC10X_MCTLR_EXT_MEM_START_1 + ((i>3) ? 4 : 0); + mpc10x_read_config_dword(config_addr, config_data, + PCI_DEVFN(0,0), offset, &val); + val = (val >> ((i & 3) << 3)) & 0x03; + start = (val << 28) | (start << 20); + + offset = MPC10X_MCTLR_MEM_END_1 + ((i > 3) ? 4 : 0); + mpc10x_read_config_dword(config_addr, config_data, + PCI_DEVFN(0,0), offset, &val); + end = (val >> ((i & 3) << 3)) & 0xff; + + offset = MPC10X_MCTLR_EXT_MEM_END_1 + ((i > 3) ? 4 : 0); + mpc10x_read_config_dword(config_addr, config_data, + PCI_DEVFN(0,0), offset, &val); + val = (val >> ((i & 3) << 3)) & 0x03; + end = (val << 28) | (end << 20) | 0xfffff; + + total += (end - start + 1); + } + } + + return total; +} diff --git a/arch/ppc/boot/simple/mpc52xx_tty.c b/arch/ppc/boot/simple/mpc52xx_tty.c new file mode 100644 index 00000000000..3acc6b7c072 --- /dev/null +++ b/arch/ppc/boot/simple/mpc52xx_tty.c @@ -0,0 +1,140 @@ +/* + * arch/ppc/boot/simple/mpc52xx_tty.c + * + * Minimal serial functions needed to send messages out a MPC52xx + * Programmable Serial Controller (PSC). + * + * Author: Dale Farnsworth + * + * 2003-2004 (c) MontaVista, Software, Inc. This file is licensed under the + * terms of the GNU General Public License version 2. This program is licensed + * "as is" without any warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +#ifdef MPC52xx_PF_CONSOLE_PORT +#define MPC52xx_CONSOLE MPC52xx_PSCx_OFFSET(MPC52xx_PF_CONSOLE_PORT) +#define MPC52xx_PSC_CONFIG_SHIFT ((MPC52xx_PF_CONSOLE_PORT-1)<<2) +#else +#error "MPC52xx_PF_CONSOLE_PORT not defined" +#endif + +static struct mpc52xx_psc __iomem *psc = + (struct mpc52xx_psc __iomem *) MPC52xx_PA(MPC52xx_CONSOLE); + +/* The decrementer counts at the system bus clock frequency + * divided by four. The most accurate time base is connected to the + * rtc. We read the decrementer change during one rtc tick + * and multiply by 4 to get the system bus clock frequency. Since a + * rtc tick is one seconds, and that's pretty long, we change the rtc + * dividers temporarly to set them 64x faster ;) + */ +static int +mpc52xx_ipbfreq(void) +{ + struct mpc52xx_rtc __iomem *rtc = + (struct mpc52xx_rtc __iomem *) MPC52xx_PA(MPC52xx_RTC_OFFSET); + struct mpc52xx_cdm __iomem *cdm = + (struct mpc52xx_cdm __iomem *) MPC52xx_PA(MPC52xx_CDM_OFFSET); + int current_time, previous_time; + int tbl_start, tbl_end; + int xlbfreq, ipbfreq; + + out_be32(&rtc->dividers, 0x8f1f0000); /* Set RTC 64x faster */ + previous_time = in_be32(&rtc->time); + while ((current_time = in_be32(&rtc->time)) == previous_time) ; + tbl_start = get_tbl(); + previous_time = current_time; + while ((current_time = in_be32(&rtc->time)) == previous_time) ; + tbl_end = get_tbl(); + out_be32(&rtc->dividers, 0xffff0000); /* Restore RTC */ + + xlbfreq = (tbl_end - tbl_start) << 8; + ipbfreq = (in_8(&cdm->ipb_clk_sel) & 1) ? xlbfreq / 2 : xlbfreq; + + return ipbfreq; +} + +unsigned long +serial_init(int ignored, void *ignored2) +{ + struct mpc52xx_gpio __iomem *gpio = + (struct mpc52xx_gpio __iomem *) MPC52xx_PA(MPC52xx_GPIO_OFFSET); + int divisor; + int mode1; + int mode2; + u32 val32; + + static int been_here = 0; + + if (been_here) + return 0; + + been_here = 1; + + val32 = in_be32(&gpio->port_config); + val32 &= ~(0x7 << MPC52xx_PSC_CONFIG_SHIFT); + val32 |= MPC52xx_GPIO_PSC_CONFIG_UART_WITHOUT_CD + << MPC52xx_PSC_CONFIG_SHIFT; + out_be32(&gpio->port_config, val32); + + out_8(&psc->command, MPC52xx_PSC_RST_TX + | MPC52xx_PSC_RX_DISABLE | MPC52xx_PSC_TX_ENABLE); + out_8(&psc->command, MPC52xx_PSC_RST_RX); + + out_be32(&psc->sicr, 0x0); + out_be16(&psc->mpc52xx_psc_clock_select, 0xdd00); + out_be16(&psc->tfalarm, 0xf8); + + out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1 + | MPC52xx_PSC_RX_ENABLE + | MPC52xx_PSC_TX_ENABLE); + + divisor = ((mpc52xx_ipbfreq() + / (CONFIG_SERIAL_MPC52xx_CONSOLE_BAUD * 16)) + 1) >> 1; + + mode1 = MPC52xx_PSC_MODE_8_BITS | MPC52xx_PSC_MODE_PARNONE + | MPC52xx_PSC_MODE_ERR; + mode2 = MPC52xx_PSC_MODE_ONE_STOP; + + out_8(&psc->ctur, divisor>>8); + out_8(&psc->ctlr, divisor); + out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1); + out_8(&psc->mode, mode1); + out_8(&psc->mode, mode2); + + return 0; /* ignored */ +} + +void +serial_putc(void *ignored, const char c) +{ + serial_init(0, NULL); + + while (!(in_be16(&psc->mpc52xx_psc_status) & MPC52xx_PSC_SR_TXEMP)) ; + out_8(&psc->mpc52xx_psc_buffer_8, c); + while (!(in_be16(&psc->mpc52xx_psc_status) & MPC52xx_PSC_SR_TXEMP)) ; +} + +char +serial_getc(void *ignored) +{ + while (!(in_be16(&psc->mpc52xx_psc_status) & MPC52xx_PSC_SR_RXRDY)) ; + + return in_8(&psc->mpc52xx_psc_buffer_8); +} + +int +serial_tstc(void *ignored) +{ + return (in_be16(&psc->mpc52xx_psc_status) & MPC52xx_PSC_SR_RXRDY) != 0; +} diff --git a/arch/ppc/boot/simple/mv64x60_tty.c b/arch/ppc/boot/simple/mv64x60_tty.c new file mode 100644 index 00000000000..5b45eb46b66 --- /dev/null +++ b/arch/ppc/boot/simple/mv64x60_tty.c @@ -0,0 +1,360 @@ +/* + * arch/ppc/boot/simple/mv64x60_tty.c + * + * Bootloader version of the embedded MPSC/UART driver for the Marvell 64x60. + * Note: Due to a GT64260A erratum, DMA will be used for UART input (via SDMA). + * + * Author: Mark A. Greer + * + * 2001 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +/* This code assumes that the data cache has been disabled (L1, L2, L3). */ + +#include +#include +#include +#include +#include +#include +#include + +u32 mv64x60_console_baud = 9600; +u32 mv64x60_mpsc_clk_src = 8; /* TCLK */ +u32 mv64x60_mpsc_clk_freq = 100000000; + +extern void udelay(long); +static void stop_dma(int chan); + +static void __iomem *mv64x60_base = (void __iomem *)CONFIG_MV64X60_NEW_BASE; + +struct sdma_regs { + u32 sdc; + u32 sdcm; + u32 rx_desc; + u32 rx_buf_ptr; + u32 scrdp; + u32 tx_desc; + u32 sctdp; + u32 sftdp; +}; + +static struct sdma_regs sdma_regs[2]; + +#define SDMA_REGS_INIT(s, reg_base) { \ + (s)->sdc = (reg_base) + SDMA_SDC; \ + (s)->sdcm = (reg_base) + SDMA_SDCM; \ + (s)->rx_desc = (reg_base) + SDMA_RX_DESC; \ + (s)->rx_buf_ptr = (reg_base) + SDMA_RX_BUF_PTR; \ + (s)->scrdp = (reg_base) + SDMA_SCRDP; \ + (s)->tx_desc = (reg_base) + SDMA_TX_DESC; \ + (s)->sctdp = (reg_base) + SDMA_SCTDP; \ + (s)->sftdp = (reg_base) + SDMA_SFTDP; \ +} + +static u32 mpsc_base[2] = { MV64x60_MPSC_0_OFFSET, MV64x60_MPSC_1_OFFSET }; + +struct mv64x60_rx_desc { + u16 bufsize; + u16 bytecnt; + u32 cmd_stat; + u32 next_desc_ptr; + u32 buffer; +}; + +struct mv64x60_tx_desc { + u16 bytecnt; + u16 shadow; + u32 cmd_stat; + u32 next_desc_ptr; + u32 buffer; +}; + +#define MAX_RESET_WAIT 10000 +#define MAX_TX_WAIT 10000 + +#define RX_NUM_DESC 2 +#define TX_NUM_DESC 2 + +#define RX_BUF_SIZE 32 +#define TX_BUF_SIZE 32 + +static struct mv64x60_rx_desc rd[2][RX_NUM_DESC] __attribute__ ((aligned(32))); +static struct mv64x60_tx_desc td[2][TX_NUM_DESC] __attribute__ ((aligned(32))); + +static char rx_buf[2][RX_NUM_DESC * RX_BUF_SIZE] __attribute__ ((aligned(32))); +static char tx_buf[2][TX_NUM_DESC * TX_BUF_SIZE] __attribute__ ((aligned(32))); + +static int cur_rd[2] = { 0, 0 }; +static int cur_td[2] = { 0, 0 }; + +static char chan_initialized[2] = { 0, 0 }; + + +#define RX_INIT_RDP(rdp) { \ + (rdp)->bufsize = 2; \ + (rdp)->bytecnt = 0; \ + (rdp)->cmd_stat = SDMA_DESC_CMDSTAT_L | SDMA_DESC_CMDSTAT_F | \ + SDMA_DESC_CMDSTAT_O; \ +} + +#ifdef CONFIG_MV64360 +static u32 cpu2mem_tab[MV64x60_CPU2MEM_WINDOWS][2] = { + { MV64x60_CPU2MEM_0_BASE, MV64x60_CPU2MEM_0_SIZE }, + { MV64x60_CPU2MEM_1_BASE, MV64x60_CPU2MEM_1_SIZE }, + { MV64x60_CPU2MEM_2_BASE, MV64x60_CPU2MEM_2_SIZE }, + { MV64x60_CPU2MEM_3_BASE, MV64x60_CPU2MEM_3_SIZE } +}; + +static u32 com2mem_tab[MV64x60_CPU2MEM_WINDOWS][2] = { + { MV64360_MPSC2MEM_0_BASE, MV64360_MPSC2MEM_0_SIZE }, + { MV64360_MPSC2MEM_1_BASE, MV64360_MPSC2MEM_1_SIZE }, + { MV64360_MPSC2MEM_2_BASE, MV64360_MPSC2MEM_2_SIZE }, + { MV64360_MPSC2MEM_3_BASE, MV64360_MPSC2MEM_3_SIZE } +}; + +static u32 dram_selects[MV64x60_CPU2MEM_WINDOWS] = { 0xe, 0xd, 0xb, 0x7 }; +#endif + +unsigned long +serial_init(int chan, void *ignored) +{ + u32 mpsc_routing_base, sdma_base, brg_bcr, cdv; + int i; + + chan = (chan == 1); /* default to chan 0 if anything but 1 */ + + if (chan_initialized[chan]) + return chan; + + chan_initialized[chan] = 1; + + if (chan == 0) { + sdma_base = MV64x60_SDMA_0_OFFSET; + brg_bcr = MV64x60_BRG_0_OFFSET + BRG_BCR; + SDMA_REGS_INIT(&sdma_regs[0], MV64x60_SDMA_0_OFFSET); + } else { + sdma_base = MV64x60_SDMA_1_OFFSET; + brg_bcr = MV64x60_BRG_1_OFFSET + BRG_BCR; + SDMA_REGS_INIT(&sdma_regs[0], MV64x60_SDMA_1_OFFSET); + } + + mpsc_routing_base = MV64x60_MPSC_ROUTING_OFFSET; + + stop_dma(chan); + + /* Set up ring buffers */ + for (i=0; i= TX_NUM_DESC) + cur_td[com_port] = 0; + + *(unchar *)(tdp->buffer ^ 7) = c; + tdp->bytecnt = 1; + tdp->shadow = 1; + tdp->cmd_stat = SDMA_DESC_CMDSTAT_L | SDMA_DESC_CMDSTAT_F | + SDMA_DESC_CMDSTAT_O; + + out_le32(mv64x60_base + sdma_regs[com_port].sctdp, (int)tdp); + out_le32(mv64x60_base + sdma_regs[com_port].sftdp, (int)tdp); + out_le32(mv64x60_base + sdma_regs[com_port].sdcm, + in_le32(mv64x60_base + sdma_regs[com_port].sdcm) | + SDMA_SDCM_TXD); +} + +unsigned char +serial_getc(unsigned long com_port) +{ + struct mv64x60_rx_desc *rdp; + unchar c = '\0'; + + rdp = &rd[com_port][cur_rd[com_port]]; + + if ((rdp->cmd_stat & (SDMA_DESC_CMDSTAT_O|SDMA_DESC_CMDSTAT_ES)) == 0) { + c = *(unchar *)(rdp->buffer ^ 7); + RX_INIT_RDP(rdp); + if (++cur_rd[com_port] >= RX_NUM_DESC) + cur_rd[com_port] = 0; + } + + return c; +} + +int +serial_tstc(unsigned long com_port) +{ + struct mv64x60_rx_desc *rdp; + int loop_count = 0; + int rc = 0; + + rdp = &rd[com_port][cur_rd[com_port]]; + + /* Go thru rcv desc's until empty looking for one with data (no error)*/ + while (((rdp->cmd_stat & SDMA_DESC_CMDSTAT_O) == 0) && + (loop_count++ < RX_NUM_DESC)) { + + /* If there was an error, reinit the desc & continue */ + if ((rdp->cmd_stat & SDMA_DESC_CMDSTAT_ES) != 0) { + RX_INIT_RDP(rdp); + if (++cur_rd[com_port] >= RX_NUM_DESC) + cur_rd[com_port] = 0; + rdp = (struct mv64x60_rx_desc *)rdp->next_desc_ptr; + } else { + rc = 1; + break; + } + } + + return rc; +} + +void +serial_close(unsigned long com_port) +{ + stop_dma(com_port); +} diff --git a/arch/ppc/boot/simple/openbios.c b/arch/ppc/boot/simple/openbios.c new file mode 100644 index 00000000000..c732b6d70cf --- /dev/null +++ b/arch/ppc/boot/simple/openbios.c @@ -0,0 +1,37 @@ +/* + * arch/ppc/boot/simple/openbios.c + * + * 2005 (c) SYSGO AG - g.jaeger@sysgo.com + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without + * any warranty of any kind, whether express or implied. + * + * Derived from arch/ppc/boot/simple/pibs.c (from MontaVista) + */ + +#include +#include +#include +#include +#include + +extern unsigned long decompress_kernel(unsigned long load_addr, int num_words, + unsigned long cksum); + +/* We need to make sure that this is before the images to ensure + * that it's in a mapped location. */ +bd_t hold_resid_buf __attribute__ ((__section__ (".data.boot"))); +bd_t *hold_residual = &hold_resid_buf; + +void * +load_kernel(unsigned long load_addr, int num_words, unsigned long cksum, + void *ign1, void *ign2) +{ + decompress_kernel(load_addr, num_words, cksum); + + /* simply copy the MAC addresses */ + memcpy(hold_residual->bi_enetaddr, (char *)EBONY_OPENBIOS_MAC_BASE, 6); + memcpy(hold_residual->bi_enet1addr, (char *)(EBONY_OPENBIOS_MAC_BASE+EBONY_OPENBIOS_MAC_OFFSET), 6); + + return (void *)hold_residual; +} diff --git a/arch/ppc/boot/simple/pci.c b/arch/ppc/boot/simple/pci.c new file mode 100644 index 00000000000..b0f673c8b7d --- /dev/null +++ b/arch/ppc/boot/simple/pci.c @@ -0,0 +1,274 @@ +/* Stand alone funtions for QSpan Tundra support. + */ +#include +#include +#include + +extern void puthex(unsigned long val); +extern void puts(const char *); + +/* To map PCI devices, you first write 0xffffffff into the device + * base address registers. When the register is read back, the + * number of most significant '1' bits describes the amount of address + * space needed for mapping. If the most significant bit is not set, + * either the device does not use that address register, or it has + * a fixed address that we can't change. After the address is assigned, + * the command register has to be written to enable the card. + */ +typedef struct { + u_char pci_bus; + u_char pci_devfn; + ushort pci_command; + uint pci_addrs[6]; +} pci_map_t; + +/* We should probably dynamically allocate these structures. +*/ +#define MAX_PCI_DEVS 32 +int pci_dev_cnt; +pci_map_t pci_map[MAX_PCI_DEVS]; + +void pci_conf_write(int bus, int device, int func, int reg, uint writeval); +void pci_conf_read(int bus, int device, int func, int reg, void *readval); +void probe_addresses(int bus, int devfn); +void map_pci_addrs(void); + +extern int +qs_pci_read_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char *val); +extern int +qs_pci_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val); +extern int +qs_pci_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val); +extern int +qs_pci_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val); +extern int +qs_pci_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val); +extern int +qs_pci_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val); + + +/* This is a really stripped version of PCI bus scan. All we are + * looking for are devices that exist. + */ +void +pci_scanner(int addr_probe) +{ + unsigned int devfn, l, class, bus_number; + unsigned char hdr_type, is_multi; + + is_multi = 0; + bus_number = 0; + for (devfn = 0; devfn < 0xff; ++devfn) { + /* The device numbers are comprised of upper 5 bits of + * device number and lower 3 bits of multi-function number. + */ + if ((devfn & 7) && !is_multi) { + /* Don't scan multifunction addresses if this is + * not a multifunction device. + */ + continue; + } + + /* Read the header to determine card type. + */ + qs_pci_read_config_byte(bus_number, devfn, PCI_HEADER_TYPE, + &hdr_type); + + /* If this is a base device number, check the header to + * determine if it is mulifunction. + */ + if ((devfn & 7) == 0) + is_multi = hdr_type & 0x80; + + /* Check to see if the board is really in the slot. + */ + qs_pci_read_config_dword(bus_number, devfn, PCI_VENDOR_ID, &l); + /* some broken boards return 0 if a slot is empty: */ + if (l == 0xffffffff || l == 0x00000000 || l == 0x0000ffff || + l == 0xffff0000) { + /* Nothing there. + */ + is_multi = 0; + continue; + } + + /* If we are not performing an address probe, + * just simply print out some information. + */ + if (!addr_probe) { + qs_pci_read_config_dword(bus_number, devfn, + PCI_CLASS_REVISION, &class); + + class >>= 8; /* upper 3 bytes */ + +#if 0 + printf("Found (%3d:%d): vendor 0x%04x, device 0x%04x, class 0x%06x\n", + (devfn >> 3), (devfn & 7), + (l & 0xffff), (l >> 16) & 0xffff, class); +#else + puts("Found ("); puthex(devfn >> 3); + puts(":"); puthex(devfn & 7); + puts("): vendor "); puthex(l & 0xffff); + puts(", device "); puthex((l >> 16) & 0xffff); + puts(", class "); puthex(class); puts("\n"); +#endif + } + else { + /* If this is a "normal" device, build address list. + */ + if ((hdr_type & 0x7f) == PCI_HEADER_TYPE_NORMAL) + probe_addresses(bus_number, devfn); + } + } + + /* Now map the boards. + */ + if (addr_probe) + map_pci_addrs(); +} + +/* Probe addresses for the specified device. This is a destructive + * operation because it writes the registers. + */ +void +probe_addresses(bus, devfn) +{ + int i; + uint pciaddr; + ushort pcicmd; + pci_map_t *pm; + + if (pci_dev_cnt >= MAX_PCI_DEVS) { + puts("Too many PCI devices\n"); + return; + } + + pm = &pci_map[pci_dev_cnt++]; + + pm->pci_bus = bus; + pm->pci_devfn = devfn; + + for (i=0; i<6; i++) { + qs_pci_write_config_dword(bus, devfn, PCI_BASE_ADDRESS_0 + (i * 4), -1); + qs_pci_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_0 + (i * 4), + &pciaddr); + pm->pci_addrs[i] = pciaddr; + qs_pci_read_config_word(bus, devfn, PCI_COMMAND, &pcicmd); + pm->pci_command = pcicmd; + } +} + +/* Map the cards into the PCI space. The PCI has separate memory + * and I/O spaces. In addition, some memory devices require mapping + * below 1M. The least significant 4 bits of the address register + * provide information. If this is an I/O device, only the LS bit + * is used to indicate that, so I/O devices can be mapped to a two byte + * boundard. Memory addresses can be mapped to a 32 byte boundary. + * The QSpan implementations usually have a 1Gbyte space for each + * memory and I/O spaces. + * + * This isn't a terribly fancy algorithm. I just map the spaces from + * the top starting with the largest address space. When finished, + * the registers are written and the card enabled. + * + * While the Tundra can map a large address space on most boards, we + * need to be careful because it may overlap other devices (like IMMR). + */ +#define MEMORY_SPACE_SIZE 0x20000000 +#define IO_SPACE_SIZE 0x20000000 + +void +map_pci_addrs() +{ + uint pci_mem_top, pci_mem_low; + uint pci_io_top; + uint addr_mask, reg_addr, space; + int i, j; + pci_map_t *pm; + + pci_mem_top = MEMORY_SPACE_SIZE; + pci_io_top = IO_SPACE_SIZE; + pci_mem_low = (1 * 1024 * 1024); /* Below one meg addresses */ + + /* We can't map anything more than the maximum space, but test + * for it anyway to catch devices out of range. + */ + addr_mask = 0x80000000; + + do { + space = (~addr_mask) + 1; /* Size of the space */ + for (i=0; ipci_addrs[j]; + if ((reg_addr & 0x80000000) == 0) + continue; + if (reg_addr & PCI_BASE_ADDRESS_SPACE_IO) { + if ((reg_addr & PCI_BASE_ADDRESS_IO_MASK) != addr_mask) + continue; + if (pci_io_top < space) { + puts("Out of PCI I/O space\n"); + } + else { + pci_io_top -= space; + pm->pci_addrs[j] = pci_io_top; + pm->pci_command |= PCI_COMMAND_IO; + } + } + else { + if ((reg_addr & PCI_BASE_ADDRESS_MEM_MASK) != addr_mask) + continue; + + /* Memory space. Test if below 1M. + */ + if (reg_addr & PCI_BASE_ADDRESS_MEM_TYPE_1M) { + if (pci_mem_low < space) { + puts("Out of PCI 1M space\n"); + } + else { + pci_mem_low -= space; + pm->pci_addrs[j] = pci_mem_low; + } + } + else { + if (pci_mem_top < space) { + puts("Out of PCI Mem space\n"); + } + else { + pci_mem_top -= space; + pm->pci_addrs[j] = pci_mem_top; + } + } + pm->pci_command |= PCI_COMMAND_MEMORY; + } + } + } + addr_mask >>= 1; + addr_mask |= 0x80000000; + } while (addr_mask != 0xfffffffe); + + /* Now, run the list one more time and map everything. + */ + for (i=0; ipci_bus, pm->pci_devfn, + PCI_BASE_ADDRESS_0 + (j * 4), pm->pci_addrs[j]); + } + + /* Enable memory or address mapping. + */ + qs_pci_write_config_word(pm->pci_bus, pm->pci_devfn, PCI_COMMAND, + pm->pci_command); + } +} + diff --git a/arch/ppc/boot/simple/pibs.c b/arch/ppc/boot/simple/pibs.c new file mode 100644 index 00000000000..1348740e503 --- /dev/null +++ b/arch/ppc/boot/simple/pibs.c @@ -0,0 +1,103 @@ +/* + * 2004-2005 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include + +extern unsigned long decompress_kernel(unsigned long load_addr, int num_words, + unsigned long cksum); + +/* We need to make sure that this is before the images to ensure + * that it's in a mapped location. - Tom */ +bd_t hold_resid_buf __attribute__ ((__section__ (".data.boot"))); +bd_t *hold_residual = &hold_resid_buf; + +/* String functions lifted from lib/vsprintf.c and lib/ctype.c */ +unsigned char _ctype[] = { +_C,_C,_C,_C,_C,_C,_C,_C, /* 0-7 */ +_C,_C|_S,_C|_S,_C|_S,_C|_S,_C|_S,_C,_C, /* 8-15 */ +_C,_C,_C,_C,_C,_C,_C,_C, /* 16-23 */ +_C,_C,_C,_C,_C,_C,_C,_C, /* 24-31 */ +_S|_SP,_P,_P,_P,_P,_P,_P,_P, /* 32-39 */ +_P,_P,_P,_P,_P,_P,_P,_P, /* 40-47 */ +_D,_D,_D,_D,_D,_D,_D,_D, /* 48-55 */ +_D,_D,_P,_P,_P,_P,_P,_P, /* 56-63 */ +_P,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U, /* 64-71 */ +_U,_U,_U,_U,_U,_U,_U,_U, /* 72-79 */ +_U,_U,_U,_U,_U,_U,_U,_U, /* 80-87 */ +_U,_U,_U,_P,_P,_P,_P,_P, /* 88-95 */ +_P,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L, /* 96-103 */ +_L,_L,_L,_L,_L,_L,_L,_L, /* 104-111 */ +_L,_L,_L,_L,_L,_L,_L,_L, /* 112-119 */ +_L,_L,_L,_P,_P,_P,_P,_C, /* 120-127 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 128-143 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 144-159 */ +_S|_SP,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 160-175 */ +_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 176-191 */ +_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U, /* 192-207 */ +_U,_U,_U,_U,_U,_U,_U,_P,_U,_U,_U,_U,_U,_U,_U,_L, /* 208-223 */ +_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L, /* 224-239 */ +_L,_L,_L,_L,_L,_L,_L,_P,_L,_L,_L,_L,_L,_L,_L,_L}; /* 240-255 */ + +/** + * simple_strtoull - convert a string to an unsigned long long + * @cp: The start of the string + * @endp: A pointer to the end of the parsed string will be placed here + * @base: The number base to use + */ +unsigned long long simple_strtoull(const char *cp,char **endp,unsigned int base) +{ + unsigned long long result = 0,value; + + if (!base) { + base = 10; + if (*cp == '0') { + base = 8; + cp++; + if ((toupper(*cp) == 'X') && isxdigit(cp[1])) { + cp++; + base = 16; + } + } + } else if (base == 16) { + if (cp[0] == '0' && toupper(cp[1]) == 'X') + cp += 2; + } + while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp) + ? toupper(*cp) : *cp)-'A'+10) < base) { + result = result*base + value; + cp++; + } + if (endp) + *endp = (char *)cp; + return result; +} + +void * +load_kernel(unsigned long load_addr, int num_words, unsigned long cksum, + void *ign1, void *ign2) +{ + unsigned long long mac64; + + decompress_kernel(load_addr, num_words, cksum); + + mac64 = simple_strtoull((char *)PIBS_MAC_BASE, 0, 16); + memcpy(hold_residual->bi_enetaddr, (char *)&mac64+2, 6); +#ifdef CONFIG_440GX + mac64 = simple_strtoull((char *)(PIBS_MAC_BASE+PIBS_MAC_OFFSET), 0, 16); + memcpy(hold_residual->bi_enet1addr, (char *)&mac64+2, 6); + mac64 = simple_strtoull((char *)(PIBS_MAC_BASE+PIBS_MAC_OFFSET*2), 0, 16); + memcpy(hold_residual->bi_enet2addr, (char *)&mac64+2, 6); + mac64 = simple_strtoull((char *)(PIBS_MAC_BASE+PIBS_MAC_OFFSET*3), 0, 16); + memcpy(hold_residual->bi_enet3addr, (char *)&mac64+2, 6); +#endif + return (void *)hold_residual; +} diff --git a/arch/ppc/boot/simple/prepmap.c b/arch/ppc/boot/simple/prepmap.c new file mode 100644 index 00000000000..c871a4db6e8 --- /dev/null +++ b/arch/ppc/boot/simple/prepmap.c @@ -0,0 +1,12 @@ +/* + * 2004 (C) IBM. This file is licensed under the terms of the GNU General + * Public License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include + +void board_isa_init(void) +{ + ISA_init(0x80000000); +} diff --git a/arch/ppc/boot/simple/qspan_pci.c b/arch/ppc/boot/simple/qspan_pci.c new file mode 100644 index 00000000000..d2966d032a4 --- /dev/null +++ b/arch/ppc/boot/simple/qspan_pci.c @@ -0,0 +1,269 @@ +/* + * LinuxPPC arch/ppc/kernel/qspan_pci.c Dan Malek (dmalek@jlc.net) + * + * QSpan Motorola bus to PCI bridge. The config address register + * is located 0x500 from the base of the bridge control/status registers. + * The data register is located at 0x504. + * This is a two step operation. First, the address register is written, + * then the data register is read/written as required. + * I don't know what to do about interrupts (yet). + */ + +#include +#include +#include +#include + +/* + * When reading the configuration space, if something does not respond + * the bus times out and we get a machine check interrupt. So, the + * good ol' exception tables come to mind to trap it and return some + * value. + * + * On an error we just return a -1, since that is what the caller wants + * returned if nothing is present. I copied this from __get_user_asm, + * with the only difference of returning -1 instead of EFAULT. + * There is an associated hack in the machine check trap code. + * + * The QSPAN is also a big endian device, that is it makes the PCI + * look big endian to us. This presents a problem for the Linux PCI + * functions, which assume little endian. For example, we see the + * first 32-bit word like this: + * ------------------------ + * | Device ID | Vendor ID | + * ------------------------ + * If we read/write as a double word, that's OK. But in our world, + * when read as a word, device ID is at location 0, not location 2 as + * the little endian PCI would believe. We have to switch bits in + * the PCI addresses given to us to get the data to/from the correct + * byte lanes. + * + * The QSPAN only supports 4 bits of "slot" in the dev_fn instead of 5. + * It always forces the MS bit to zero. Therefore, dev_fn values + * greater than 128 are returned as "no device found" errors. + * + * The QSPAN can only perform long word (32-bit) configuration cycles. + * The "offset" must have the two LS bits set to zero. Read operations + * require we read the entire word and then sort out what should be + * returned. Write operations other than long word require that we + * read the long word, update the proper word or byte, then write the + * entire long word back. + * + * PCI Bridge hack. We assume (correctly) that bus 0 is the primary + * PCI bus from the QSPAN. If we are called with a bus number other + * than zero, we create a Type 1 configuration access that a downstream + * PCI bridge will interpret. + */ + +#define __get_pci_config(x, addr, op) \ + __asm__ __volatile__( \ + "1: "op" %0,0(%1)\n" \ + " eieio\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: li %0,-1\n" \ + " b 2b\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 2\n" \ + " .long 1b,3b\n" \ + ".text" \ + : "=r"(x) : "r"(addr)) + +#define QS_CONFIG_ADDR ((volatile uint *)(PCI_CSR_ADDR + 0x500)) +#define QS_CONFIG_DATA ((volatile uint *)(PCI_CSR_ADDR + 0x504)) + +#define mk_config_addr(bus, dev, offset) \ + (((bus)<<16) | ((dev)<<8) | (offset & 0xfc)) + +#define mk_config_type1(bus, dev, offset) \ + mk_config_addr(bus, dev, offset) | 1; + +/* Initialize the QSpan device registers after power up. +*/ +void +qspan_init(void) +{ + uint *qptr; + + + + qptr = (uint *)PCI_CSR_ADDR; + + /* PCI Configuration/status. Upper bits written to clear + * pending interrupt or status. Lower bits enable QSPAN as + * PCI master, enable memory and I/O cycles, and enable PCI + * parity error checking. + * IMPORTANT: The last two bits of this word enable PCI + * master cycles into the QBus. The QSpan is broken and can't + * meet the timing specs of the PQ bus for this to work. Therefore, + * if you don't have external bus arbitration, you can't use + * this function. + */ +#ifdef EXTERNAL_PQ_ARB + qptr[1] = 0xf9000147; +#else + qptr[1] = 0xf9000144; +#endif + + /* PCI Misc configuration. Set PCI latency timer resolution + * of 8 cycles, set cache size to 4 x 32. + */ + qptr[3] = 0; + + /* Set up PCI Target address mapping. Enable, Posted writes, + * 2Gbyte space (processor memory controller determines actual size). + */ + qptr[64] = 0x8f000080; + + /* Map processor 0x80000000 to PCI 0x00000000. + * Processor address bit 1 determines I/O type access (0x80000000) + * or memory type access (0xc0000000). + */ + qptr[65] = 0x80000000; + + /* Enable error logging and clear any pending error status. + */ + qptr[80] = 0x90000000; + + qptr[512] = 0x000c0003; + + /* Set up Qbus slave image. + */ + qptr[960] = 0x01000000; + qptr[961] = 0x000000d1; + qptr[964] = 0x00000000; + qptr[965] = 0x000000d1; + +} + +/* Functions to support PCI bios-like features to read/write configuration + * space. If the function fails for any reason, a -1 (0xffffffff) value + * must be returned. + */ +#define DEVICE_NOT_FOUND (-1) +#define SUCCESSFUL 0 + +int qs_pci_read_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char *val) +{ + uint temp; + u_char *cp; + + if ((bus > 7) || (dev_fn > 127)) { + *val = 0xff; + return DEVICE_NOT_FOUND; + } + + if (bus == 0) + *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); + else + *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); + __get_pci_config(temp, QS_CONFIG_DATA, "lwz"); + + offset ^= 0x03; + cp = ((u_char *)&temp) + (offset & 0x03); + *val = *cp; + return SUCCESSFUL; +} + +int qs_pci_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val) +{ + uint temp; + ushort *sp; + + if ((bus > 7) || (dev_fn > 127)) { + *val = 0xffff; + return DEVICE_NOT_FOUND; + } + + if (bus == 0) + *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); + else + *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); + __get_pci_config(temp, QS_CONFIG_DATA, "lwz"); + offset ^= 0x02; + + sp = ((ushort *)&temp) + ((offset >> 1) & 1); + *val = *sp; + return SUCCESSFUL; +} + +int qs_pci_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val) +{ + if ((bus > 7) || (dev_fn > 127)) { + *val = 0xffffffff; + return DEVICE_NOT_FOUND; + } + if (bus == 0) + *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); + else + *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); + __get_pci_config(*val, QS_CONFIG_DATA, "lwz"); + return SUCCESSFUL; +} + +int qs_pci_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val) +{ + uint temp; + u_char *cp; + + if ((bus > 7) || (dev_fn > 127)) + return DEVICE_NOT_FOUND; + + qs_pci_read_config_dword(bus, dev_fn, offset, &temp); + + offset ^= 0x03; + cp = ((u_char *)&temp) + (offset & 0x03); + *cp = val; + + if (bus == 0) + *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); + else + *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); + *QS_CONFIG_DATA = temp; + + return SUCCESSFUL; +} + +int qs_pci_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val) +{ + uint temp; + ushort *sp; + + if ((bus > 7) || (dev_fn > 127)) + return DEVICE_NOT_FOUND; + + qs_pci_read_config_dword(bus, dev_fn, offset, &temp); + + offset ^= 0x02; + sp = ((ushort *)&temp) + ((offset >> 1) & 1); + *sp = val; + + if (bus == 0) + *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); + else + *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); + *QS_CONFIG_DATA = temp; + + return SUCCESSFUL; +} + +int qs_pci_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val) +{ + if ((bus > 7) || (dev_fn > 127)) + return DEVICE_NOT_FOUND; + + if (bus == 0) + *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); + else + *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); + *(unsigned int *)QS_CONFIG_DATA = val; + + return SUCCESSFUL; +} + diff --git a/arch/ppc/boot/simple/relocate.S b/arch/ppc/boot/simple/relocate.S new file mode 100644 index 00000000000..555a216ccc4 --- /dev/null +++ b/arch/ppc/boot/simple/relocate.S @@ -0,0 +1,216 @@ +/* + * arch/ppc/boot/simple/relocate.S + * + * This is the common part of the loader relocation and initialization + * process. All of the board/processor specific initialization is + * done before we get here. + * + * Author: Tom Rini + * trini@mvista.com + * Derived from arch/ppc/boot/prep/head.S (Cort Dougan, many others). + * + * 2001-2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include + +#define GETSYM(reg, sym) \ + lis reg, sym@h; ori reg, reg, sym@l + + .text + /* We get called from the early initialization code. + * Register 3 has the address where we were loaded, + * Register 4 contains any residual data passed from the + * boot rom. + */ + .globl relocate +relocate: + /* Save r3, r4 for later. + * The r8/r11 are legacy registers so I don't have to + * rewrite the code below :-). + */ + mr r8, r3 + mr r11, r4 + + /* compute the size of the whole image in words. */ + GETSYM(r4,start) + GETSYM(r5,end) + + addi r5,r5,3 /* round up */ + sub r5,r5,r4 /* end - start */ + srwi r5,r5,2 + mr r7,r5 /* Save for later use. */ + + /* + * Check if we need to relocate ourselves to the link addr or were + * we loaded there to begin with. + */ + cmpw cr0,r3,r4 + beq start_ldr /* If 0, we don't need to relocate */ + + /* Move this code somewhere safe. This is max(load + size, end) + * r8 == load address + */ + GETSYM(r4, start) + GETSYM(r5, end) + + sub r6,r5,r4 + add r6,r8,r6 /* r6 == phys(load + size) */ + + cmpw r5,r6 + bgt 1f + b 2f +1: + mr r6, r5 +2: + /* dest is in r6 */ + /* Ensure alignment --- this code is precautionary */ + addi r6,r6,4 + li r5,0x0003 + andc r6,r6,r5 + + /* Find physical address and size of do_relocate */ + GETSYM(r5, __relocate_start) + GETSYM(r4, __relocate_end) + GETSYM(r3, start) + + /* Size to copy */ + sub r4,r4,r5 + srwi r4,r4,2 + + /* Src addr to copy (= __relocate_start - start + where_loaded) */ + sub r3,r5,r3 + add r5,r8,r3 + + /* Save dest */ + mr r3, r6 + + /* Do the copy */ + mtctr r4 +3: lwz r4,0(r5) + stw r4,0(r3) + addi r3,r3,4 + addi r5,r5,4 + bdnz 3b + + GETSYM(r4, __relocate_start) + GETSYM(r5, do_relocate) + + sub r4,r5,r4 /* Get entry point for do_relocate in */ + add r6,r6,r4 /* relocated section */ + + /* This will return to the relocated do_relocate */ + mtlr r6 + b flush_instruction_cache + + .section ".relocate_code","xa" + +do_relocate: + /* We have 2 cases --- start < load, or start > load + * This determines whether we copy from the end, or the start. + * Its easier to have 2 loops than to have paramaterised + * loops. Sigh. + */ + li r6,0 /* Clear checksum */ + mtctr r7 /* Setup for a loop */ + + GETSYM(r4, start) + mr r3,r8 /* Get the load addr */ + + cmpw cr0,r4,r3 /* If we need to copy from the end, do so */ + bgt do_relocate_from_end + +do_relocate_from_start: +1: lwz r5,0(r3) /* Load and decrement */ + stw r5,0(r4) /* Store and decrement */ + addi r3,r3,4 + addi r4,r4,4 + xor r6,r6,r5 /* Update checksum */ + bdnz 1b /* Are we done? */ + b do_relocate_out /* Finished */ + +do_relocate_from_end: + GETSYM(r3, end) + slwi r4,r7,2 + add r4,r8,r4 /* Get the physical end */ +1: lwzu r5,-4(r4) + stwu r5, -4(r3) + xor r6,r6,r5 + bdnz 1b + +do_relocate_out: + GETSYM(r3,start_ldr) + mtlr r3 /* Easiest way to do an absolute jump */ +/* Some boards don't boot up with the I-cache enabled. Do that + * now because the decompress runs much faster that way. + * As a side effect, we have to ensure the data cache is not enabled + * so we can access the serial I/O without trouble. + */ + b flush_instruction_cache + + .previous + +start_ldr: +/* Clear all of BSS and set up stack for C calls */ + lis r3,edata@h + ori r3,r3,edata@l + lis r4,end@h + ori r4,r4,end@l + subi r3,r3,4 + subi r4,r4,4 + li r0,0 +50: stwu r0,4(r3) + cmpw cr0,r3,r4 + bne 50b +90: mr r9,r1 /* Save old stack pointer (in case it matters) */ + lis r1,.stack@h + ori r1,r1,.stack@l + addi r1,r1,4096*2 + subi r1,r1,256 + li r2,0x000F /* Mask pointer to 16-byte boundary */ + andc r1,r1,r2 + + /* + * Exec kernel loader + */ + mr r3,r8 /* Load point */ + mr r4,r7 /* Program length */ + mr r5,r6 /* Checksum */ + mr r6,r11 /* Residual data */ + mr r7,r25 /* Validated OFW interface */ + bl load_kernel + + /* + * Make sure the kernel knows we don't have things set in + * registers. -- Tom + */ + li r4,0 + li r5,0 + li r6,0 + + /* + * Start at the begining. + */ +#ifdef CONFIG_PPC_MULTIPLATFORM + li r9,0xc + mtlr r9 + /* tell kernel we're prep, by putting 0xdeadc0de at KERNELLOAD, + * and tell the kernel to start on the 4th instruction since we + * overwrite the first 3 sometimes (which are 'nop'). + */ + lis r10,0xdeadc0de@h + ori r10,r10,0xdeadc0de@l + li r9,0 + stw r10,0(r9) +#else + li r9,0 + mtlr r9 +#endif + blr + + .comm .stack,4096*2,4 diff --git a/arch/ppc/boot/simple/rw4/ppc_40x.h b/arch/ppc/boot/simple/rw4/ppc_40x.h new file mode 100644 index 00000000000..561fb26f5a9 --- /dev/null +++ b/arch/ppc/boot/simple/rw4/ppc_40x.h @@ -0,0 +1,664 @@ +/*----------------------------------------------------------------------------+ +| This source code has been made available to you by IBM on an AS-IS +| basis. Anyone receiving this source is licensed under IBM +| copyrights to use it in any way he or she deems fit, including +| copying it, modifying it, compiling it, and redistributing it either +| with or without modifications. No license under IBM patents or +| patent applications is to be implied by the copyright license. +| +| Any user of this software should understand that IBM cannot provide +| technical support for this software and will not be responsible for +| any consequences resulting from the use of this software. +| +| Any person who transfers this source code or any derivative work +| must include the IBM copyright notice, this paragraph, and the +| preceding two paragraphs in the transferred software. +| +| COPYRIGHT I B M CORPORATION 1997 +| LICENSED MATERIAL - PROGRAM PROPERTY OF I B M ++----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------+ +| Author: Tony J. Cerreto +| Component: Assembler include file. +| File: ppc_40x.h +| Purpose: Include file containing PPC DCR defines. +| +| Changes: +| Date Author Comment +| --------- ------ -------------------------------------------------------- +| 01-Mar-00 tjc Created ++----------------------------------------------------------------------------*/ +/* added by linguohui*/ +#define MW +/*----------------------------------------------------------------------------+ +| PPC Special purpose registers Numbers ++----------------------------------------------------------------------------*/ +#define ccr0 0x3b3 /* core configuration reg */ +#define ctr 0x009 /* count register */ +#define ctrreg 0x009 /* count register */ +#define dbcr0 0x3f2 /* debug control register 0 */ +#define dbcr1 0x3bd /* debug control register 1 */ +#define dbsr 0x3f0 /* debug status register */ +#define dccr 0x3fa /* data cache control reg. */ +#define dcwr 0x3ba /* data cache write-thru reg */ +#define dear 0x3d5 /* data exception address reg */ +#define esr 0x3d4 /* exception syndrome register */ +#define evpr 0x3d6 /* exception vector prefix reg */ +#define iccr 0x3fb /* instruction cache cntrl re */ +#define icdbdr 0x3d3 /* instr cache dbug data reg */ +#define lrreg 0x008 /* link register */ +#define pid 0x3b1 /* process id reg */ +#define pit 0x3db /* programmable interval time */ +#define pvr 0x11f /* processor version register */ +#define sgr 0x3b9 /* storage guarded reg */ +#define sler 0x3bb /* storage little endian reg */ +#define sprg0 0x110 /* special general purpose 0 */ +#define sprg1 0x111 /* special general purpose 1 */ +#define sprg2 0x112 /* special general purpose 2 */ +#define sprg3 0x113 /* special general purpose 3 */ +#define sprg4 0x114 /* special general purpose 4 */ +#define sprg5 0x115 /* special general purpose 5 */ +#define sprg6 0x116 /* special general purpose 6 */ +#define sprg7 0x117 /* special general purpose 7 */ +#define srr0 0x01a /* save/restore register 0 */ +#define srr1 0x01b /* save/restore register 1 */ +#define srr2 0x3de /* save/restore register 2 */ +#define srr3 0x3df /* save/restore register 3 */ +#define tbhi 0x11D +#define tblo 0x11C +#define tcr 0x3da /* timer control register */ +#define tsr 0x3d8 /* timer status register */ +#define xerreg 0x001 /* fixed point exception */ +#define xer 0x001 /* fixed point exception */ +#define zpr 0x3b0 /* zone protection reg */ + +/*----------------------------------------------------------------------------+ +| Decompression Controller ++----------------------------------------------------------------------------*/ +#define kiar 0x014 /* Decompression cntl addr reg */ +#define kidr 0x015 /* Decompression cntl data reg */ +#define kitor0 0x00 /* index table origin Reg 0 */ +#define kitor1 0x01 /* index table origin Reg 1 */ +#define kitor2 0x02 /* index table origin Reg 2 */ +#define kitor3 0x03 /* index table origin Reg 3 */ +#define kaddr0 0x04 /* addr decode Definition Reg 0 */ +#define kaddr1 0x05 /* addr decode Definition Reg 1 */ +#define kconf 0x40 /* Decompression cntl config reg */ +#define kid 0x41 /* Decompression cntl id reg */ +#define kver 0x42 /* Decompression cntl ver number */ +#define kpear 0x50 /* bus error addr reg (PLB) */ +#define kbear 0x51 /* bus error addr reg (DCP-EBC) */ +#define kesr0 0x52 /* bus error status reg 0 */ + +/*----------------------------------------------------------------------------+ +| Romeo Specific Device Control Register Numbers. ++----------------------------------------------------------------------------*/ +#ifndef VESTA +#define cdbcr 0x3d7 /* cache debug cntrl reg */ + +#define a_latcnt 0x1a9 /* PLB Latency count */ +#define a_tgval 0x1ac /* tone generation value */ +#define a_plb_pr 0x1bf /* PLB priority */ + +#define cic_sel1 0x031 /* select register 1 */ +#define cic_sel2 0x032 /* select register 2 */ + +#define clkgcrst 0x122 /* chip reset register */ + +#define cp_cpmsr 0x100 /*rstatus register */ +#define cp_cpmer 0x101 /* enable register */ + +#define dcp_kiar 0x190 /* indirect address register */ +#define dcp_kidr 0x191 /* indirect data register */ + +#define hsmc_mcgr 0x1c0 /* HSMC global register */ +#define hsmc_mcbesr 0x1c1 /* bus error status register */ +#define hsmc_mcbear 0x1c2 /* bus error address register*/ +#define hsmc_mcbr0 0x1c4 /* SDRAM sub-ctrl bank reg 0 */ +#define hsmc_mccr0 0x1c5 /* SDRAM sub-ctrl ctrl reg 0 */ +#define hsmc_mcbr1 0x1c7 /* SDRAM sub-ctrl bank reg 1 */ +#define hsmc_mccr1 0x1c8 /* SDRAM sub-ctrl ctrl reg 1 */ +#define hsmc_sysr 0x1d1 /* system register */ +#define hsmc_data 0x1d2 /* data register */ +#define hsmc_mccrr 0x1d3 /* refresh register */ + +#define ocm_pbar 0x1E0 /* base address register */ + +#define plb0_pacr0 0x057 /* PLB arbiter control reg */ +#define plb1_pacr1 0x067 /* PLB arbiter control reg */ + +#define v_displb 0x157 /* set left border of display*/ +#define v_disptb 0x158 /* top border of display */ +#define v_osd_la 0x159 /* first link address for OSD*/ +#define v_ptsdlta 0x15E /* PTS delta register */ +#define v_v0base 0x16C /* base mem add for VBI-0 */ +#define v_v1base 0x16D /* base mem add for VBI-1 */ +#define v_osbase 0x16E /* base mem add for OSD data */ +#endif + +/*----------------------------------------------------------------------------+ +| Vesta Device Control Register Numbers. ++----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------+ +| Cross bar switch. ++----------------------------------------------------------------------------*/ +#define cbs0_cr 0x010 /* CBS configuration register */ + +/*----------------------------------------------------------------------------+ +| DCR external master (DCRX). ++----------------------------------------------------------------------------*/ +#define dcrx0_icr 0x020 /* internal control register */ +#define dcrx0_isr 0x021 /* internal status register */ +#define dcrx0_ecr 0x022 /* external control register */ +#define dcrx0_esr 0x023 /* external status register */ +#define dcrx0_tar 0x024 /* target address register */ +#define dcrx0_tdr 0x025 /* target data register */ +#define dcrx0_igr 0x026 /* interrupt generation register */ +#define dcrx0_bcr 0x027 /* buffer control register */ + +/*----------------------------------------------------------------------------+ +| Chip interconnect configuration. ++----------------------------------------------------------------------------*/ +#define cic0_cr 0x030 /* CIC control register */ +#define cic0_vcr 0x033 /* video macro control reg */ +#define cic0_sel3 0x035 /* select register 3 */ + +/*----------------------------------------------------------------------------+ +| Chip interconnect configuration. ++----------------------------------------------------------------------------*/ +#define sgpo0_sgpO 0x036 /* simplified GPIO output */ +#define sgpo0_gpod 0x037 /* simplified GPIO open drain */ +#define sgpo0_gptc 0x038 /* simplified GPIO tristate cntl */ +#define sgpo0_gpi 0x039 /* simplified GPIO input */ + +/*----------------------------------------------------------------------------+ +| Universal interrupt controller. ++----------------------------------------------------------------------------*/ +#define uic0_sr 0x040 /* status register */ +#define uic0_srs 0x041 /* status register set */ +#define uic0_er 0x042 /* enable register */ +#define uic0_cr 0x043 /* critical register */ +#define uic0_pr 0x044 /* parity register */ +#define uic0_tr 0x045 /* triggering register */ +#define uic0_msr 0x046 /* masked status register */ +#define uic0_vr 0x047 /* vector register */ +#define uic0_vcr 0x048 /* enable config register */ + +/*----------------------------------------------------------------------------+ +| PLB 0 and 1. ++----------------------------------------------------------------------------*/ +#define pb0_pesr 0x054 /* PLB error status reg 0 */ +#define pb0_pesrs 0x055 /* PLB error status reg 0 set */ +#define pb0_pear 0x056 /* PLB error address reg */ + +#define pb1_pesr 0x064 /* PLB error status reg 1 */ +#define pb1_pesrs 0x065 /* PLB error status reg 1 set */ +#define pb1_pear 0x066 /* PLB error address reg */ + +/*----------------------------------------------------------------------------+ +| EBIU DCR registers. ++----------------------------------------------------------------------------*/ +#define ebiu0_brcrh0 0x070 /* bus region register 0 high */ +#define ebiu0_brcrh1 0x071 /* bus region register 1 high */ +#define ebiu0_brcrh2 0x072 /* bus region register 2 high */ +#define ebiu0_brcrh3 0x073 /* bus region register 3 high */ +#define ebiu0_brcrh4 0x074 /* bus region register 4 high */ +#define ebiu0_brcrh5 0x075 /* bus region register 5 high */ +#define ebiu0_brcrh6 0x076 /* bus region register 6 high */ +#define ebiu0_brcrh7 0x077 /* bus region register 7 high */ +#define ebiu0_brcr0 0x080 /* bus region register 0 */ +#define ebiu0_brcr1 0x081 /* bus region register 1 */ +#define ebiu0_brcr2 0x082 /* bus region register 2 */ +#define ebiu0_brcr3 0x083 /* bus region register 3 */ +#define ebiu0_brcr4 0x084 /* bus region register 4 */ +#define ebiu0_brcr5 0x085 /* bus region register 5 */ +#define ebiu0_brcr6 0x086 /* bus region register 6 */ +#define ebiu0_brcr7 0x087 /* bus region register 7 */ +#define ebiu0_bear 0x090 /* bus error address register */ +#define ebiu0_besr 0x091 /* bus error syndrome reg */ +#define ebiu0_besr0s 0x093 /* bus error syndrome reg */ +#define ebiu0_biucr 0x09a /* bus interface control reg */ + +/*----------------------------------------------------------------------------+ +| OPB bridge. ++----------------------------------------------------------------------------*/ +#define opbw0_gesr 0x0b0 /* error status reg */ +#define opbw0_gesrs 0x0b1 /* error status reg */ +#define opbw0_gear 0x0b2 /* error address reg */ + +/*----------------------------------------------------------------------------+ +| DMA. ++----------------------------------------------------------------------------*/ +#define dma0_cr0 0x0c0 /* DMA channel control reg 0 */ +#define dma0_ct0 0x0c1 /* DMA count register 0 */ +#define dma0_da0 0x0c2 /* DMA destination addr reg 0 */ +#define dma0_sa0 0x0c3 /* DMA source addr register 0 */ +#define dma0_cc0 0x0c4 /* DMA chained count 0 */ +#define dma0_cr1 0x0c8 /* DMA channel control reg 1 */ +#define dma0_ct1 0x0c9 /* DMA count register 1 */ +#define dma0_da1 0x0ca /* DMA destination addr reg 1 */ +#define dma0_sa1 0x0cb /* DMA source addr register 1 */ +#define dma0_cc1 0x0cc /* DMA chained count 1 */ +#define dma0_cr2 0x0d0 /* DMA channel control reg 2 */ +#define dma0_ct2 0x0d1 /* DMA count register 2 */ +#define dma0_da2 0x0d2 /* DMA destination addr reg 2 */ +#define dma0_sa2 0x0d3 /* DMA source addr register 2 */ +#define dma0_cc2 0x0d4 /* DMA chained count 2 */ +#define dma0_cr3 0x0d8 /* DMA channel control reg 3 */ +#define dma0_ct3 0x0d9 /* DMA count register 3 */ +#define dma0_da3 0x0da /* DMA destination addr reg 3 */ +#define dma0_sa3 0x0db /* DMA source addr register 3 */ +#define dma0_cc3 0x0dc /* DMA chained count 3 */ +#define dma0_sr 0x0e0 /* DMA status register */ +#define dma0_srs 0x0e1 /* DMA status register */ +#define dma0_s1 0x031 /* DMA select1 register */ +#define dma0_s2 0x032 /* DMA select2 register */ + +/*---------------------------------------------------------------------------+ +| Clock and power management. ++----------------------------------------------------------------------------*/ +#define cpm0_fr 0x102 /* force register */ + +/*----------------------------------------------------------------------------+ +| Serial Clock Control. ++----------------------------------------------------------------------------*/ +#define ser0_ccr 0x120 /* serial clock control register */ + +/*----------------------------------------------------------------------------+ +| Audio Clock Control. ++----------------------------------------------------------------------------*/ +#define aud0_apcr 0x121 /* audio clock ctrl register */ + +/*----------------------------------------------------------------------------+ +| DENC. ++----------------------------------------------------------------------------*/ +#define denc0_idr 0x130 /* DENC ID register */ +#define denc0_cr1 0x131 /* control register 1 */ +#define denc0_rr1 0x132 /* microvision 1 (reserved 1) */ +#define denc0_cr2 0x133 /* control register 2 */ +#define denc0_rr2 0x134 /* microvision 2 (reserved 2) */ +#define denc0_rr3 0x135 /* microvision 3 (reserved 3) */ +#define denc0_rr4 0x136 /* microvision 4 (reserved 4) */ +#define denc0_rr5 0x137 /* microvision 5 (reserved 5) */ +#define denc0_ccdr 0x138 /* closed caption data */ +#define denc0_cccr 0x139 /* closed caption control */ +#define denc0_trr 0x13A /* teletext request register */ +#define denc0_tosr 0x13B /* teletext odd field line se */ +#define denc0_tesr 0x13C /* teletext even field line s */ +#define denc0_rlsr 0x13D /* RGB rhift left register */ +#define denc0_vlsr 0x13E /* video level shift register */ +#define denc0_vsr 0x13F /* video scaling register */ + +/*----------------------------------------------------------------------------+ +| Video decoder. Suspect 0x179, 0x169, 0x16a, 0x152 (rc). ++----------------------------------------------------------------------------*/ +#define vid0_ccntl 0x140 /* control decoder operation */ +#define vid0_cmode 0x141 /* video operational mode */ +#define vid0_sstc0 0x142 /* STC high order bits 31:0 */ +#define vid0_sstc1 0x143 /* STC low order bit 32 */ +#define vid0_spts0 0x144 /* PTS high order bits 31:0 */ +#define vid0_spts1 0x145 /* PTS low order bit 32 */ +#define vid0_fifo 0x146 /* FIFO data port */ +#define vid0_fifos 0x147 /* FIFO status */ +#define vid0_cmd 0x148 /* send command to decoder */ +#define vid0_cmdd 0x149 /* port for command params */ +#define vid0_cmdst 0x14A /* command status */ +#define vid0_cmdad 0x14B /* command address */ +#define vid0_procia 0x14C /* instruction store */ +#define vid0_procid 0x14D /* data port for I_Store */ +#define vid0_osdm 0x151 /* OSD mode control */ +#define vid0_hosti 0x152 /* base interrupt register */ +#define vid0_mask 0x153 /* interrupt mask register */ +#define vid0_dispm 0x154 /* operational mode for Disp */ +#define vid0_dispd 0x155 /* setting for 'Sync' delay */ +#define vid0_vbctl 0x156 /* VBI */ +#define vid0_ttxctl 0x157 /* teletext control */ +#define vid0_disptb 0x158 /* display left/top border */ +#define vid0_osdgla 0x159 /* Graphics plane link addr */ +#define vid0_osdila 0x15A /* Image plane link addr */ +#define vid0_rbthr 0x15B /* rate buffer threshold */ +#define vid0_osdcla 0x15C /* Cursor link addr */ +#define vid0_stcca 0x15D /* STC common address */ +#define vid0_ptsctl 0x15F /* PTS Control */ +#define vid0_wprot 0x165 /* write protect for I_Store */ +#define vid0_vcqa 0x167 /* video clip queued block Ad */ +#define vid0_vcql 0x168 /* video clip queued block Le */ +#define vid0_blksz 0x169 /* block size bytes for copy op */ +#define vid0_srcad 0x16a /* copy source address bits 6-31 */ +#define vid0_udbas 0x16B /* base mem add for user data */ +#define vid0_vbibas 0x16C /* base mem add for VBI 0/1 */ +#define vid0_osdibas 0x16D /* Image plane base address */ +#define vid0_osdgbas 0x16E /* Graphic plane base address */ +#define vid0_rbbase 0x16F /* base mem add for video buf */ +#define vid0_dramad 0x170 /* DRAM address */ +#define vid0_dramdt 0x171 /* data port for DRAM access */ +#define vid0_dramcs 0x172 /* DRAM command and statusa */ +#define vid0_vcwa 0x173 /* v clip work address */ +#define vid0_vcwl 0x174 /* v clip work length */ +#define vid0_mseg0 0x175 /* segment address 0 */ +#define vid0_mseg1 0x176 /* segment address 1 */ +#define vid0_mseg2 0x177 /* segment address 2 */ +#define vid0_mseg3 0x178 /* segment address 3 */ +#define vid0_fbbase 0x179 /* frame buffer base memory */ +#define vid0_osdcbas 0x17A /* Cursor base addr */ +#define vid0_lboxtb 0x17B /* top left border */ +#define vid0_trdly 0x17C /* transparency gate delay */ +#define vid0_sbord 0x17D /* left/top small pict. bord. */ +#define vid0_zoffs 0x17E /* hor/ver zoom window */ +#define vid0_rbsz 0x17F /* rate buffer size read */ + +/*----------------------------------------------------------------------------+ +| Transport demultiplexer. ++----------------------------------------------------------------------------*/ +#define xpt0_lr 0x180 /* demux location register */ +#define xpt0_data 0x181 /* demux data register */ +#define xpt0_ir 0x182 /* demux interrupt register */ + +#define xpt0_config1 0x0000 /* configuration 1 */ +#define xpt0_control1 0x0001 /* control 1 */ +#define xpt0_festat 0x0002 /* Front-end status */ +#define xpt0_feimask 0x0003 /* Front_end interrupt Mask */ +#define xpt0_ocmcnfg 0x0004 /* OCM Address */ +#define xpt0_settapi 0x0005 /* Set TAP Interrupt */ + +#define xpt0_pcrhi 0x0010 /* PCR High */ +#define xpt0_pcrlow 0x0011 /* PCR Low */ +#define xpt0_lstchi 0x0012 /* Latched STC High */ +#define xpt0_lstclow 0x0013 /* Latched STC Low */ +#define xpt0_stchi 0x0014 /* STC High */ +#define xpt0_stclow 0x0015 /* STC Low */ +#define xpt0_pwm 0x0016 /* PWM */ +#define xpt0_pcrstct 0x0017 /* PCR-STC Threshold */ +#define xpt0_pcrstcd 0x0018 /* PCR-STC Delta */ +#define xpt0_stccomp 0x0019 /* STC Compare */ +#define xpt0_stccmpd 0x001a /* STC Compare Disarm */ + +#define xpt0_dsstat 0x0048 /* Descrambler Status */ +#define xpt0_dsimask 0x0049 /* Descrambler Interrupt Mask */ + +#define xpt0_vcchng 0x01f0 /* Video Channel Change */ +#define xpt0_acchng 0x01f1 /* Audio Channel Change */ +#define xpt0_axenable 0x01fe /* Aux PID Enables */ +#define xpt0_pcrpid 0x01ff /* PCR PID */ + +#define xpt0_config2 0x1000 /* Configuration 2 */ +#define xpt0_pbuflvl 0x1002 /* Packet Buffer Level */ +#define xpt0_intmask 0x1003 /* Interrupt Mask */ +#define xpt0_plbcnfg 0x1004 /* PLB Configuration */ + +#define xpt0_qint 0x1010 /* Queues Interrupts */ +#define xpt0_qintmsk 0x1011 /* Queues Interrupts Mask */ +#define xpt0_astatus 0x1012 /* Audio Status */ +#define xpt0_aintmask 0x1013 /* Audio Interrupt Mask */ +#define xpt0_vstatus 0x1014 /* Video Status */ +#define xpt0_vintmask 0x1015 /* Video Interrupt Mask */ + +#define xpt0_qbase 0x1020 /* Queue Base */ +#define xpt0_bucketq 0x1021 /* Bucket Queue */ +#define xpt0_qstops 0x1024 /* Queue Stops */ +#define xpt0_qresets 0x1025 /* Queue Resets */ +#define xpt0_sfchng 0x1026 /* Section Filter Change */ + +/*----------------------------------------------------------------------------+ +| Audio decoder. Suspect 0x1ad, 0x1b4, 0x1a3, 0x1a5 (read/write status) ++----------------------------------------------------------------------------*/ +#define aud0_ctrl0 0x1a0 /* control 0 */ +#define aud0_ctrl1 0x1a1 /* control 1 */ +#define aud0_ctrl2 0x1a2 /* control 2 */ +#define aud0_cmd 0x1a3 /* command register */ +#define aud0_isr 0x1a4 /* interrupt status register */ +#define aud0_imr 0x1a5 /* interrupt mask register */ +#define aud0_dsr 0x1a6 /* decoder status register */ +#define aud0_stc 0x1a7 /* system time clock */ +#define aud0_csr 0x1a8 /* channel status register */ +#define aud0_lcnt 0x1a9 /* queued address register 2 */ +#define aud0_pts 0x1aa /* presentation time stamp */ +#define aud0_tgctrl 0x1ab /* tone generation control */ +#define aud0_qlr2 0x1ac /* queued length register 2 */ +#define aud0_auxd 0x1ad /* aux data */ +#define aud0_strmid 0x1ae /* stream ID */ +#define aud0_qar 0x1af /* queued address register */ +#define aud0_dsps 0x1b0 /* DSP status */ +#define aud0_qlr 0x1b1 /* queued len address */ +#define aud0_dspc 0x1b2 /* DSP control */ +#define aud0_wlr2 0x1b3 /* working length register 2 */ +#define aud0_instd 0x1b4 /* instruction download */ +#define aud0_war 0x1b5 /* working address register */ +#define aud0_seg1 0x1b6 /* segment 1 base register */ +#define aud0_seg2 0x1b7 /* segment 2 base register */ +#define aud0_avf 0x1b9 /* audio att value front */ +#define aud0_avr 0x1ba /* audio att value rear */ +#define aud0_avc 0x1bb /* audio att value center */ +#define aud0_seg3 0x1bc /* segment 3 base register */ +#define aud0_offset 0x1bd /* offset address */ +#define aud0_wrl 0x1be /* working length register */ +#define aud0_war2 0x1bf /* working address register 2 */ + +/*----------------------------------------------------------------------------+ +| High speed memory controller 0 and 1. ++----------------------------------------------------------------------------*/ +#define hsmc0_gr 0x1e0 /* HSMC global register */ +#define hsmc0_besr 0x1e1 /* bus error status register */ +#define hsmc0_bear 0x1e2 /* bus error address register */ +#define hsmc0_br0 0x1e4 /* SDRAM sub-ctrl bank reg 0 */ +#define hsmc0_cr0 0x1e5 /* SDRAM sub-ctrl ctrl reg 0 */ +#define hsmc0_br1 0x1e7 /* SDRAM sub-ctrl bank reg 1 */ +#define hsmc0_cr1 0x1e8 /* SDRAM sub-ctrl ctrl reg 1 */ +#define hsmc0_sysr 0x1f1 /* system register */ +#define hsmc0_data 0x1f2 /* data register */ +#define hsmc0_crr 0x1f3 /* refresh register */ + +#define hsmc1_gr 0x1c0 /* HSMC global register */ +#define hsmc1_besr 0x1c1 /* bus error status register */ +#define hsmc1_bear 0x1c2 /* bus error address register */ +#define hsmc1_br0 0x1c4 /* SDRAM sub-ctrl bank reg 0 */ +#define hsmc1_cr0 0x1c5 /* SDRAM sub-ctrl ctrl reg 0 */ +#define hsmc1_br1 0x1c7 /* SDRAM sub-ctrl bank reg 1 */ +#define hsmc1_cr1 0x1c8 /* SDRAM sub-ctrl ctrl reg 1 */ +#define hsmc1_sysr 0x1d1 /* system register */ +#define hsmc1_data 0x1d2 /* data register */ +#define hsmc1_crr 0x1d3 /* refresh register */ + +/*----------------------------------------------------------------------------+ +| Machine State Register bit definitions. ++----------------------------------------------------------------------------*/ +#define msr_ape 0x00100000 +#define msr_apa 0x00080000 +#define msr_we 0x00040000 +#define msr_ce 0x00020000 +#define msr_ile 0x00010000 +#define msr_ee 0x00008000 +#define msr_pr 0x00004000 +#define msr_me 0x00001000 +#define msr_de 0x00000200 +#define msr_ir 0x00000020 +#define msr_dr 0x00000010 +#define msr_le 0x00000001 + +/*----------------------------------------------------------------------------+ +| Used during interrupt processing. ++----------------------------------------------------------------------------*/ +#define stack_reg_image_size 160 + +/*----------------------------------------------------------------------------+ +| Function prolog definition and other Metaware (EABI) defines. ++----------------------------------------------------------------------------*/ +#ifdef MW + +#define r0 0 +#define r1 1 +#define r2 2 +#define r3 3 +#define r4 4 +#define r5 5 +#define r6 6 +#define r7 7 +#define r8 8 +#define r9 9 +#define r10 10 +#define r11 11 +#define r12 12 +#define r13 13 +#define r14 14 +#define r15 15 +#define r16 16 +#define r17 17 +#define r18 18 +#define r19 19 +#define r20 20 +#define r21 21 +#define r22 22 +#define r23 23 +#define r24 24 +#define r25 25 +#define r26 26 +#define r27 27 +#define r28 28 +#define r29 29 +#define r30 30 +#define r31 31 + +#define cr0 0 +#define cr1 1 +#define cr2 2 +#define cr3 3 +#define cr4 4 +#define cr5 5 +#define cr6 6 +#define cr7 7 + +#define function_prolog(func_name) .text; \ + .align 2; \ + .globl func_name; \ + func_name: +#define function_epilog(func_name) .type func_name,@function; \ + .size func_name,.-func_name + +#define function_call(func_name) bl func_name + +#define stack_frame_min 8 +#define stack_frame_bc 0 +#define stack_frame_lr 4 +#define stack_neg_off 0 + +#endif + +/*----------------------------------------------------------------------------+ +| Function prolog definition and other DIAB (Elf) defines. ++----------------------------------------------------------------------------*/ +#ifdef ELF_DIAB + +fprolog: macro f_name + .text + .align 2 + .globl f_name +f_name: + endm + +fepilog: macro f_name + .type f_name,@function + .size f_name,.-f_name + endm + +#define function_prolog(func_name) fprolog func_name +#define function_epilog(func_name) fepilog func_name +#define function_call(func_name) bl func_name + +#define stack_frame_min 8 +#define stack_frame_bc 0 +#define stack_frame_lr 4 +#define stack_neg_off 0 + +#endif + +/*----------------------------------------------------------------------------+ +| Function prolog definition and other Xlc (XCOFF) defines. ++----------------------------------------------------------------------------*/ +#ifdef XCOFF + +.machine "403ga" + +#define r0 0 +#define r1 1 +#define r2 2 +#define r3 3 +#define r4 4 +#define r5 5 +#define r6 6 +#define r7 7 +#define r8 8 +#define r9 9 +#define r10 10 +#define r11 11 +#define r12 12 +#define r13 13 +#define r14 14 +#define r15 15 +#define r16 16 +#define r17 17 +#define r18 18 +#define r19 19 +#define r20 20 +#define r21 21 +#define r22 22 +#define r23 23 +#define r24 24 +#define r25 25 +#define r26 26 +#define r27 27 +#define r28 28 +#define r29 29 +#define r30 30 +#define r31 31 + +#define cr0 0 +#define cr1 1 +#define cr2 2 +#define cr3 3 +#define cr4 4 +#define cr5 5 +#define cr6 6 +#define cr7 7 + +#define function_prolog(func_name) .csect .func_name[PR]; \ + .globl .func_name[PR]; \ + func_name: + +#define function_epilog(func_name) .toc; \ + .csect func_name[DS]; \ + .globl func_name[DS]; \ + .long .func_name[PR]; \ + .long TOC[tc0] + +#define function_call(func_name) .extern .func_name[PR]; \ + stw r2,stack_frame_toc(r1); \ + mfspr r2,sprg0; \ + bl .func_name[PR]; \ + lwz r2,stack_frame_toc(r1) + +#define stack_frame_min 56 +#define stack_frame_bc 0 +#define stack_frame_lr 8 +#define stack_frame_toc 20 +#define stack_neg_off 276 + +#endif +#define function_prolog(func_name) .text; \ + .align 2; \ + .globl func_name; \ + func_name: +#define function_epilog(func_name) .type func_name,@function; \ + .size func_name,.-func_name + +#define function_call(func_name) bl func_name + +/*----------------------------------------------------------------------------+ +| Function prolog definition for GNU ++----------------------------------------------------------------------------*/ +#ifdef _GNU_TOOL + +#define function_prolog(func_name) .globl func_name; \ + func_name: +#define function_epilog(func_name) + +#endif diff --git a/arch/ppc/boot/simple/rw4/rw4_init.S b/arch/ppc/boot/simple/rw4/rw4_init.S new file mode 100644 index 00000000000..b1061962e46 --- /dev/null +++ b/arch/ppc/boot/simple/rw4/rw4_init.S @@ -0,0 +1,78 @@ +#define VESTA +#include "ppc_40x.h" +# + .align 2 + .text +# +# added by linguohui + .extern initb_ebiu0, initb_config, hdw_init_finish + .extern initb_hsmc0, initb_hsmc1, initb_cache +# end added + .globl HdwInit +# +HdwInit: +# +#-----------------------------------------------------------------------* +# If we are not executing from the FLASH get out * +#-----------------------------------------------------------------------* +# SAW keep this or comment out a la Hawthorne? +# r3 contains NIP when used with Linux +# rlwinm r28, r3, 8, 24, 31 # if MSB == 0xFF -> FLASH address +# cmpwi r28, 0xff +# bne locn01 +# +# +#------------------------------------------------------------------------ +# Init_cpu. Bank registers are setup for the IBM STB. +#------------------------------------------------------------------------ +# +# Setup processor core clock to be driven off chip. This is GPI4 bit +# twenty. Setup Open Drain, Output Select, Three-State Control, and +# Three-State Select registers. +# + + + pb0pesr = 0x054 + pb0pear = 0x056 + + mflr r30 + +#----------------------------------------------------------------------------- +# Vectors will be at 0x1F000000 +# Dummy Machine check handler just does RFI before true handler gets installed +#----------------------------------------------------------------------------- +#if 1 /* xuwentao added*/ +#ifdef SDRAM16MB + lis r10,0x0000 + addi r10,r10,0x0000 +#else + lis r10,0x1F00 + addi r10,r10,0x0000 +#endif + + mtspr evpr,r10 #EVPR: 0x0 or 0x1f000000 depending + isync # on SDRAM memory model used. + + lis r10,0xFFFF # clear PB0_PESR because some + ori r10,r10,0xFFFF # transitions from flash,changed by linguohui + mtdcr pb0pesr,r10 # to load RAM image via RiscWatch + lis r10,0x0000 # cause PB0_PESR machine checks + mtdcr pb0pear,r10 + addis r10,r10,0x0000 # clear the + mtxer r10 # XER just in case... +#endif /* xuwentao*/ + + bl initb_ebiu0 # init EBIU + + bl initb_config # config PPC and board + + + + +#------------------------------------------------------------------------ +# EVPR setup moved to top of this function. +#------------------------------------------------------------------------ +# + mtlr r30 + blr + .end diff --git a/arch/ppc/boot/simple/rw4/rw4_init_brd.S b/arch/ppc/boot/simple/rw4/rw4_init_brd.S new file mode 100644 index 00000000000..386afdaad6c --- /dev/null +++ b/arch/ppc/boot/simple/rw4/rw4_init_brd.S @@ -0,0 +1,1125 @@ +/*----------------------------------------------------------------------------+ +| This source code has been made available to you by IBM on an AS-IS +| basis. Anyone receiving this source is licensed under IBM +| copyrights to use it in any way he or she deems fit, including +| copying it, modifying it, compiling it, and redistributing it either +| with or without modifications. No license under IBM patents or +| patent applications is to be implied by the copyright license. +| +| Any user of this software should understand that IBM cannot provide +| technical support for this software and will not be responsible for +| any consequences resulting from the use of this software. +| +| Any person who transfers this source code or any derivative work +| must include the IBM copyright notice, this paragraph, and the +| preceding two paragraphs in the transferred software. +| +| COPYRIGHT I B M CORPORATION 1997 +| LICENSED MATERIAL - PROGRAM PROPERTY OF I B M ++----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------+ +| Author: Tony J. Cerreto +| Component: BSPS +| File: init_brd.s +| Purpose: Vesta Evaluation Board initialization subroutines. The following +| routines are available: +| 1. INITB_EBIU0: Initialize EBIU0. +| 2. INITB_CONFIG: Configure board. +| 3. INITB_HSMC0: Initialize HSMC0 (SDRAM). +| 4. INITB_HSMC1: Initialize HSMC1 (SDRAM). +| 5. INITB_CACHE: Initialize Data and Instruction Cache. +| 6. INITB_DCACHE: Initialize Data Cache. +| 7. INITB_ICACHE: Initialize Instruction Cache. +| 8. INITB_GET_CSPD: Get CPU Speed (Bus Speed and Processor Speed) +| +| Changes: +| Date: Author Comment: +| --------- ------ -------- +| 01-Mar-00 tjc Created +| 04-Mar-00 jfh Modified CIC_SEL3_VAL to support 1284 (Mux3 & GPIO 21-28) +| 04-Mar-00 jfh Modified XILINIX Reg 0 to support 1284 (Mux3 & GPIO 21-28) +| 04-Mar-00 jfh Modified XILINIX Reg 1 to support 1284 (Mux3 & GPIO 21-28) +| 04-Mar-00 jfh Modified XILINIX Reg 4 to support 1284 (Mux3 & GPIO 21-28) +| 19-May-00 rlb Relcoated HSMC0 to 0x1F000000 to support 32MB of contiguous +| SDRAM space. Changed cache ctl regs to reflect this. +| 22-May-00 tjc Changed initb_get_cspd interface and eliminated +| initb_get_bspd routines. +| 26-May-00 tjc Added two nop instructions after all mtxxx/mfxxx +| instructions due to PPC405 bug. ++----------------------------------------------------------------------------*/ +#define VESTA +#include "ppc_40x.h" +#include "stb.h" + +/*----------------------------------------------------------------------------+ +| BOARD CONFIGURATION DEFINES ++----------------------------------------------------------------------------*/ +#define CBS0_CR_VAL 0x00000002 /* CBS control reg value */ +#define CIC0_CR_VAL 0xD0800448 /* CIC control reg value */ +#define CIC0_SEL3_VAL 0x11500000 /* CIC select 3 reg value */ +#define CIC0_VCR_VAL 0x00631700 /* CIC video cntl reg value */ + +/*----------------------------------------------------------------------------+ +| EBIU0 BANK REGISTERS DEFINES ++----------------------------------------------------------------------------*/ +#define EBIU0_BRCRH0_VAL 0x00000000 /* BR High 0 (Extension Reg)*/ +#define EBIU0_BRCRH1_VAL 0x00000000 /* BR High 1 (Extension Reg)*/ +#define EBIU0_BRCRH2_VAL 0x40000000 /* BR High 2 (Extension Reg)*/ +#define EBIU0_BRCRH3_VAL 0x40000000 /* BR High 3 (Extension Reg)*/ +#define EBIU0_BRCRH4_VAL 0x00000000 /* BR High 4 (Extension Reg)*/ +#define EBIU0_BRCRH5_VAL 0x00000000 /* BR High 5 (Extension Reg)*/ +#define EBIU0_BRCRH6_VAL 0x00000000 /* BR High 6 (Extension Reg)*/ +#define EBIU0_BRCRH7_VAL 0x40000000 /* BR High 7 (Extension Reg)*/ + +#define EBIU0_BRCR0_VAL 0xFC58BFFE /* BR 0: 16 bit Flash 4 MB */ +#define EBIU0_BRCR1_VAL 0xFF00BFFE /* BR 1: Ext Connector 1 MB */ +#if 1 +#define EBIU0_BRCR2_VAL 0x207CFFBE /* BR 2: Xilinx 8 MB */ + /* twt == 0x3f */ +#else +#define EBIU0_BRCR2_VAL 0x207CCFBE /* BR 2: Xilinx 8 MB */ + /* twt == 0x0f */ +#endif +#define EBIU0_BRCR3_VAL 0x407CBFBE /* BR 3: IDE Drive 8 MB */ +#define EBIU0_BRCR4_VAL 0xFF00BFFF /* BR 4: Disabled. 0 MB */ +#define EBIU0_BRCR5_VAL 0xFF00BFFF /* BR 5: Disabled. 0 MB */ +#define EBIU0_BRCR6_VAL 0xFF00BFFF /* BR 6: Disabled. 0 MB */ +#define EBIU0_BRCR7_VAL 0xCE3F0003 /* BR 7: Line Mode DMA 2 MB */ + +/*----------------------------------------------------------------------------+ +| GPIO DEFINES ++----------------------------------------------------------------------------*/ +#define STB_GPIO0_OUTPUT (STB_GPIO0_BASE_ADDRESS+ 0x00) +#define STB_GPIO0_TC (STB_GPIO0_BASE_ADDRESS+ 0x04) +#define STB_GPIO0_OS_0_31 (STB_GPIO0_BASE_ADDRESS+ 0x08) +#define STB_GPIO0_OS_32_63 (STB_GPIO0_BASE_ADDRESS+ 0x0C) +#define STB_GPIO0_TS_0_31 (STB_GPIO0_BASE_ADDRESS+ 0x10) +#define STB_GPIO0_TS_32_63 (STB_GPIO0_BASE_ADDRESS+ 0x14) +#define STB_GPIO0_OD (STB_GPIO0_BASE_ADDRESS+ 0x18) +#define STB_GPIO0_INPUT (STB_GPIO0_BASE_ADDRESS+ 0x1C) +#define STB_GPIO0_R1 (STB_GPIO0_BASE_ADDRESS+ 0x20) +#define STB_GPIO0_R2 (STB_GPIO0_BASE_ADDRESS+ 0x24) +#define STB_GPIO0_R3 (STB_GPIO0_BASE_ADDRESS+ 0x28) +#define STB_GPIO0_IS_1_0_31 (STB_GPIO0_BASE_ADDRESS+ 0x30) +#define STB_GPIO0_IS_1_32_63 (STB_GPIO0_BASE_ADDRESS+ 0x34) +#define STB_GPIO0_IS_2_0_31 (STB_GPIO0_BASE_ADDRESS+ 0x38) +#define STB_GPIO0_IS_2_32_63 (STB_GPIO0_BASE_ADDRESS+ 0x3C) +#define STB_GPIO0_IS_3_0_31 (STB_GPIO0_BASE_ADDRESS+ 0x40) +#define STB_GPIO0_IS_3_32_63 (STB_GPIO0_BASE_ADDRESS+ 0x44) +#define STB_GPIO0_SS_1 (STB_GPIO0_BASE_ADDRESS+ 0x50) +#define STB_GPIO0_SS_2 (STB_GPIO0_BASE_ADDRESS+ 0x54) +#define STB_GPIO0_SS_3 (STB_GPIO0_BASE_ADDRESS+ 0x58) + +#define GPIO0_TC_VAL 0x0C020004 /* three-state control val */ +#define GPIO0_OS_0_31_VAL 0x51A00004 /* output select 0-31 val */ +#define GPIO0_OS_32_63_VAL 0x0000002F /* output select 32-63 val */ +#define GPIO0_TS_0_31_VAL 0x51A00000 /* three-state sel 0-31 val*/ +#define GPIO0_TS_32_63_VAL 0x0000000F /* three-state sel 32-63 val*/ +#define GPIO0_OD_VAL 0xC0000004 /* open drain val */ +#define GPIO0_IS_1_0_31_VAL 0x50000151 /* input select 1 0-31 val */ +#define GPIO0_IS_1_32_63_VAL 0x00000000 /* input select 1 32-63 val */ +#define GPIO0_IS_2_0_31_VAL 0x00000000 /* input select 2 0-31 val */ +#define GPIO0_IS_2_32_63_VAL 0x00000000 /* input select 2 32-63 val */ +#define GPIO0_IS_3_0_31_VAL 0x00000440 /* input select 3 0-31 val */ +#define GPIO0_IS_3_32_63_VAL 0x00000000 /* input select 3 32-63 val */ +#define GPIO0_SS_1_VAL 0x00000000 /* sync select 1 val */ +#define GPIO0_SS_2_VAL 0x00000000 /* sync select 2 val */ +#define GPIO0_SS_3_VAL 0x00000000 /* sync select 3 val */ + +/*----------------------------------------------------------------------------+ +| XILINX DEFINES ++----------------------------------------------------------------------------*/ +#define STB_XILINX_LED (STB_FPGA_BASE_ADDRESS+ 0x0100) +#define STB_XILINX1_REG0 (STB_FPGA_BASE_ADDRESS+ 0x40000) +#define STB_XILINX1_REG1 (STB_FPGA_BASE_ADDRESS+ 0x40002) +#define STB_XILINX1_REG2 (STB_FPGA_BASE_ADDRESS+ 0x40004) +#define STB_XILINX1_REG3 (STB_FPGA_BASE_ADDRESS+ 0x40006) +#define STB_XILINX1_REG4 (STB_FPGA_BASE_ADDRESS+ 0x40008) +#define STB_XILINX1_REG5 (STB_FPGA_BASE_ADDRESS+ 0x4000A) +#define STB_XILINX1_REG6 (STB_FPGA_BASE_ADDRESS+ 0x4000C) +#define STB_XILINX1_ID (STB_FPGA_BASE_ADDRESS+ 0x4000E) +#define STB_XILINX1_FLUSH (STB_FPGA_BASE_ADDRESS+ 0x4000E) +#define STB_XILINX2_REG0 (STB_FPGA_BASE_ADDRESS+ 0x80000) +#define STB_XILINX2_REG1 (STB_FPGA_BASE_ADDRESS+ 0x80002) +#define STB_XILINX2_REG2 (STB_FPGA_BASE_ADDRESS+ 0x80004) + +#define XILINX1_R0_VAL 0x2440 /* Xilinx 1 Register 0 Val */ +#define XILINX1_R1_VAL 0x0025 /* Xilinx 1 Register 1 Val */ +#define XILINX1_R2_VAL 0x0441 /* Xilinx 1 Register 2 Val */ +#define XILINX1_R3_VAL 0x0008 /* Xilinx 1 Register 3 Val */ +#define XILINX1_R4_VAL 0x0100 /* Xilinx 1 Register 4 Val */ +#define XILINX1_R5_VAL 0x6810 /* Xilinx 1 Register 5 Val */ +#define XILINX1_R6_VAL 0x0000 /* Xilinx 1 Register 6 Val */ +#if 0 +#define XILINX2_R0_VAL 0x0008 /* Xilinx 2 Register 0 Val */ +#define XILINX2_R1_VAL 0x0000 /* Xilinx 2 Register 1 Val */ +#else +#define XILINX2_R0_VAL 0x0018 /* disable IBM IrDA RxD */ +#define XILINX2_R1_VAL 0x0008 /* enable SICC MAX chip */ +#endif +#define XILINX2_R2_VAL 0x0000 /* Xilinx 2 Register 2 Val */ + +/*----------------------------------------------------------------------------+ +| HSMC BANK REGISTERS DEFINES ++----------------------------------------------------------------------------*/ +#ifdef SDRAM16MB +#define HSMC0_BR0_VAL 0x000D2D55 /* 0x1F000000-007FFFFF R/W */ +#define HSMC0_BR1_VAL 0x008D2D55 /* 0x1F800000-1FFFFFFF R/W */ +#else +#define HSMC0_BR0_VAL 0x1F0D2D55 /* 0x1F000000-007FFFFF R/W */ +#define HSMC0_BR1_VAL 0x1F8D2D55 /* 0x1F800000-1FFFFFFF R/W */ +#endif +#define HSMC1_BR0_VAL 0xA00D2D55 /* 0xA0000000-A07FFFFF R/W */ +#define HSMC1_BR1_VAL 0xA08D2D55 /* 0xA0800000-A0FFFFFF R/W */ + +/*----------------------------------------------------------------------------+ +| CACHE DEFINES ++----------------------------------------------------------------------------*/ +#define DCACHE_NLINES 128 /* no. D-cache lines */ +#define DCACHE_NBYTES 32 /* no. bytes/ D-cache line */ +#define ICACHE_NLINES 256 /* no. I-cache lines */ +#define ICACHE_NBYTES 32 /* no. bytes/ I-cache line */ +#ifdef SDRAM16MB +#define DCACHE_ENABLE 0x80000000 /* D-cache regions to enable*/ +#define ICACHE_ENABLE 0x80000001 /* I-cache regions to enable*/ +#else +#define DCACHE_ENABLE 0x18000000 /* D-cache regions to enable*/ +#define ICACHE_ENABLE 0x18000001 /* I-cache regions to enable*/ +#endif + +/*----------------------------------------------------------------------------+ +| CPU CORE SPEED CALCULATION DEFINES ++----------------------------------------------------------------------------*/ +#define GCS_LCNT 500000 /* CPU speed loop count */ +#define GCS_TROW_BYTES 8 /* no. bytes in table row */ +#define GCS_CTICK_TOL 100 /* allowable clock tick tol */ +#define GCS_NMULT 4 /* no. of core speed mults */ + + /*--------------------------------------------------------------------+ + | No. 13.5Mhz + | Clock Ticks + | based on a + | loop count Bus + | of 100,000 Speed + +--------------------------------------------------------------------*/ +gcs_lookup_table: + .int 50000, 54000000 /* 54.0 Mhz */ + .int 66667, 40500000 /* 40.5 Mhz */ + .int 54545, 49500000 /* 49.5 Mhz */ + .int 46154, 58500000 /* 58.5 Mhz */ + .int 0, 0 /* end of table flag */ + + +/*****************************************************************************+ +| XXXXXXX XXX XXX XXXXXX XXXXXXX XXXXXX XX XX XX XXXX +| XX X XX XX X XX X XX X XX XX XXX XX XXXX XX +| XX X XXX XX XX X XX XX XXXX XX XX XX XX +| XXXX X XX XXXX XXXXX XX XXXX XX XX XX +| XX X XXX XX XX X XX XX XX XXX XXXXXX XX +| XX X XX XX XX XX X XX XX XX XX XX XX XX XX +| XXXXXXX XXX XXX XXXX XXXXXXX XXX XX XX XX XX XX XXXXXXX ++*****************************************************************************/ +/****************************************************************************** +| +| Routine: INITB_EBIU0. +| +| Purpose: Initialize all the EBIU0 Bank Registers +| Parameters: None. +| Returns: None. +| +******************************************************************************/ + function_prolog(initb_ebiu0) + /*--------------------------------------------------------------------+ + | Set EBIU0 Bank 0 + +--------------------------------------------------------------------*/ + lis r10,EBIU0_BRCR0_VAL@h + ori r10,r10,EBIU0_BRCR0_VAL@l + mtdcr ebiu0_brcr0,r10 + lis r10,EBIU0_BRCRH0_VAL@h + ori r10,r10,EBIU0_BRCRH0_VAL@l + mtdcr ebiu0_brcrh0,r10 + + /*--------------------------------------------------------------------+ + | Set EBIU0 Bank 1 + +--------------------------------------------------------------------*/ + lis r10,EBIU0_BRCR1_VAL@h + ori r10,r10,EBIU0_BRCR1_VAL@l + mtdcr ebiu0_brcr1,r10 + lis r10,EBIU0_BRCRH1_VAL@h + ori r10,r10,EBIU0_BRCRH1_VAL@l + mtdcr ebiu0_brcrh1,r10 + + /*--------------------------------------------------------------------+ + | Set EBIU0 Bank 2 + +--------------------------------------------------------------------*/ + lis r10,EBIU0_BRCR2_VAL@h + ori r10,r10,EBIU0_BRCR2_VAL@l + mtdcr ebiu0_brcr2,r10 + lis r10,EBIU0_BRCRH2_VAL@h + ori r10,r10,EBIU0_BRCRH2_VAL@l + mtdcr ebiu0_brcrh2,r10 + + /*--------------------------------------------------------------------+ + | Set EBIU0 Bank 3 + +--------------------------------------------------------------------*/ + lis r10,EBIU0_BRCR3_VAL@h + ori r10,r10,EBIU0_BRCR3_VAL@l + mtdcr ebiu0_brcr3,r10 + lis r10,EBIU0_BRCRH3_VAL@h + ori r10,r10,EBIU0_BRCRH3_VAL@l + mtdcr ebiu0_brcrh3,r10 + + /*--------------------------------------------------------------------+ + | Set EBIU0 Bank 4 + +--------------------------------------------------------------------*/ + lis r10,EBIU0_BRCR4_VAL@h + ori r10,r10,EBIU0_BRCR4_VAL@l + mtdcr ebiu0_brcr4,r10 + lis r10,EBIU0_BRCRH4_VAL@h + ori r10,r10,EBIU0_BRCRH4_VAL@l + mtdcr ebiu0_brcrh4,r10 + + /*--------------------------------------------------------------------+ + | Set EBIU0 Bank 5 + +--------------------------------------------------------------------*/ + lis r10,EBIU0_BRCR5_VAL@h + ori r10,r10,EBIU0_BRCR5_VAL@l + mtdcr ebiu0_brcr5,r10 + lis r10,EBIU0_BRCRH5_VAL@h + ori r10,r10,EBIU0_BRCRH5_VAL@l + mtdcr ebiu0_brcrh5,r10 + + /*--------------------------------------------------------------------+ + | Set EBIU0 Bank 6 + +--------------------------------------------------------------------*/ + lis r10,EBIU0_BRCR6_VAL@h + ori r10,r10,EBIU0_BRCR6_VAL@l + mtdcr ebiu0_brcr6,r10 + lis r10,EBIU0_BRCRH6_VAL@h + ori r10,r10,EBIU0_BRCRH6_VAL@l + mtdcr ebiu0_brcrh6,r10 + + /*--------------------------------------------------------------------+ + | Set EBIU0 Bank 7 + +--------------------------------------------------------------------*/ + lis r10,EBIU0_BRCR7_VAL@h + ori r10,r10,EBIU0_BRCR7_VAL@l + mtdcr ebiu0_brcr7,r10 + lis r10,EBIU0_BRCRH7_VAL@h + ori r10,r10,EBIU0_BRCRH7_VAL@l + mtdcr ebiu0_brcrh7,r10 + + blr + function_epilog(initb_ebiu0) + + +/****************************************************************************** +| +| Routine: INITB_CONFIG +| +| Purpose: Configure the Vesta Evaluation Board. The following items +| will be configured: +| 1. Cross-Bar Switch. +| 2. Chip Interconnect. +| 3. Clear/reset key PPC registers. +| 4. Xilinx and GPIO Registers. +| +| Returns: None. +| +******************************************************************************/ + function_prolog(initb_config) + /*--------------------------------------------------------------------+ + | Init CROSS-BAR SWITCH + +--------------------------------------------------------------------*/ + lis r10,CBS0_CR_VAL@h /* r10 <- CBS Cntl Reg val */ + ori r10,r10,CBS0_CR_VAL@l + mtdcr cbs0_cr,r10 + + /*--------------------------------------------------------------------+ + | Init Chip-Interconnect (CIC) Registers + +--------------------------------------------------------------------*/ + lis r10,CIC0_CR_VAL@h /* r10 <- CIC Cntl Reg val */ + ori r10,r10,CIC0_CR_VAL@l + mtdcr cic0_cr,r10 + + lis r10,CIC0_SEL3_VAL@h /* r10 <- CIC SEL3 Reg val */ + ori r10,r10,CIC0_SEL3_VAL@l + mtdcr cic0_sel3,r10 + + lis r10,CIC0_VCR_VAL@h /* r10 <- CIC Vid C-Reg val */ + ori r10,r10,CIC0_VCR_VAL@l + mtdcr cic0_vcr,r10 + + /*--------------------------------------------------------------------+ + | Clear SGR and DCWR + +--------------------------------------------------------------------*/ + li r10,0x0000 + mtspr sgr,r10 + mtspr dcwr,r10 + + /*--------------------------------------------------------------------+ + | Clear/set up some machine state registers. + +--------------------------------------------------------------------*/ + li r10,0x0000 /* r10 <- 0 */ + mtdcr ebiu0_besr,r10 /* clr Bus Err Syndrome Reg */ + mtspr esr,r10 /* clr Exceptn Syndrome Reg */ + mttcr r10 /* timer control register */ + + mtdcr uic0_er,r10 /* disable all interrupts */ + + /* UIC_IIC0 | UIC_IIC1 | UIC_U0 | UIC_IR_RCV | UIC_IR_XMIT */ + lis r10, 0x00600e00@h + ori r10,r10,0x00600e00@l + mtdcr uic0_pr,r10 + + li r10,0x00000020 /* UIC_EIR1 */ + mtdcr uic0_tr,r10 + + lis r10,0xFFFF /* r10 <- 0xFFFFFFFF */ + ori r10,r10,0xFFFF /* */ + mtdbsr r10 /* clear/reset the dbsr */ + mtdcr uic0_sr,r10 /* clear pending interrupts */ + + li r10,0x1000 /* set Machine Exception bit*/ + oris r10,r10,0x2 /* set Criticl Exception bit*/ + mtmsr r10 /* change MSR */ + + /*--------------------------------------------------------------------+ + | Clear XER. + +--------------------------------------------------------------------*/ + li r10,0x0000 + mtxer r10 + + /*--------------------------------------------------------------------+ + | Init GPIO0 Registers + +--------------------------------------------------------------------*/ + lis r10, STB_GPIO0_TC@h /* Three-state control */ + ori r10,r10,STB_GPIO0_TC@l + lis r11, GPIO0_TC_VAL@h + ori r11,r11,GPIO0_TC_VAL@l + stw r11,0(r10) + + lis r10, STB_GPIO0_OS_0_31@h /* output select 0-31 */ + ori r10,r10,STB_GPIO0_OS_0_31@l + lis r11, GPIO0_OS_0_31_VAL@h + ori r11,r11,GPIO0_OS_0_31_VAL@l + stw r11,0(r10) + + lis r10, STB_GPIO0_OS_32_63@h /* output select 32-63 */ + ori r10,r10,STB_GPIO0_OS_32_63@l + lis r11, GPIO0_OS_32_63_VAL@h + ori r11,r11,GPIO0_OS_32_63_VAL@l + stw r11,0(r10) + + lis r10, STB_GPIO0_TS_0_31@h /* three-state select 0-31 */ + ori r10,r10,STB_GPIO0_TS_0_31@l + lis r11, GPIO0_TS_0_31_VAL@h + ori r11,r11,GPIO0_TS_0_31_VAL@l + stw r11,0(r10) + + lis r10, STB_GPIO0_TS_32_63@h /* three-state select 32-63 */ + ori r10,r10,STB_GPIO0_TS_32_63@l + lis r11, GPIO0_TS_32_63_VAL@h + ori r11,r11,GPIO0_TS_32_63_VAL@l + stw r11,0(r10) + + lis r10, STB_GPIO0_OD@h /* open drain */ + ori r10,r10,STB_GPIO0_OD@l + lis r11, GPIO0_OD_VAL@h + ori r11,r11,GPIO0_OD_VAL@l + stw r11,0(r10) + + lis r10, STB_GPIO0_IS_1_0_31@h /* input select 1, 0-31 */ + ori r10,r10,STB_GPIO0_IS_1_0_31@l + lis r11, GPIO0_IS_1_0_31_VAL@h + ori r11,r11,GPIO0_IS_1_0_31_VAL@l + stw r11,0(r10) + + lis r10, STB_GPIO0_IS_1_32_63@h /* input select 1, 32-63 */ + ori r10,r10,STB_GPIO0_IS_1_32_63@l + lis r11, GPIO0_IS_1_32_63_VAL@h + ori r11,r11,GPIO0_IS_1_32_63_VAL@l + stw r11,0(r10) + + lis r10, STB_GPIO0_IS_2_0_31@h /* input select 2, 0-31 */ + ori r10,r10,STB_GPIO0_IS_2_0_31@l + lis r11, GPIO0_IS_2_0_31_VAL@h + ori r11,r11,GPIO0_IS_2_0_31_VAL@l + stw r11,0(r10) + + lis r10, STB_GPIO0_IS_2_32_63@h /* input select 2, 32-63 */ + ori r10,r10,STB_GPIO0_IS_2_32_63@l + lis r11, GPIO0_IS_2_32_63_VAL@h + ori r11,r11,GPIO0_IS_2_32_63_VAL@l + stw r11,0(r10) + + lis r10, STB_GPIO0_IS_3_0_31@h /* input select 3, 0-31 */ + ori r10,r10,STB_GPIO0_IS_3_0_31@l + lis r11, GPIO0_IS_3_0_31_VAL@h + ori r11,r11,GPIO0_IS_3_0_31_VAL@l + stw r11,0(r10) + + lis r10, STB_GPIO0_IS_3_32_63@h /* input select 3, 32-63 */ + ori r10,r10,STB_GPIO0_IS_3_32_63@l + lis r11, GPIO0_IS_3_32_63_VAL@h + ori r11,r11,GPIO0_IS_3_32_63_VAL@l + stw r11,0(r10) + + lis r10, STB_GPIO0_SS_1@h /* sync select 1 */ + ori r10,r10,STB_GPIO0_SS_1@l + lis r11, GPIO0_SS_1_VAL@h + ori r11,r11,GPIO0_SS_1_VAL@l + stw r11,0(r10) + + lis r10, STB_GPIO0_SS_2@h /* sync select 2 */ + ori r10,r10,STB_GPIO0_SS_2@l + lis r11, GPIO0_SS_2_VAL@h + ori r11,r11,GPIO0_SS_2_VAL@l + stw r11,0(r10) + + lis r10, STB_GPIO0_SS_3@h /* sync select 3 */ + ori r10,r10,STB_GPIO0_SS_3@l + lis r11, GPIO0_SS_3_VAL@h + ori r11,r11,GPIO0_SS_3_VAL@l + stw r11,0(r10) + + /*--------------------------------------------------------------------+ + | Init Xilinx #1 Registers + +--------------------------------------------------------------------*/ + lis r10, STB_XILINX1_REG0@h /* init Xilinx1 Reg 0 */ + ori r10,r10,STB_XILINX1_REG0@l + li r11,XILINX1_R0_VAL + sth r11,0(r10) + + lis r10, STB_XILINX1_REG1@h /* init Xilinx1 Reg 1 */ + ori r10,r10,STB_XILINX1_REG1@l + li r11,XILINX1_R1_VAL + sth r11,0(r10) + + lis r10, STB_XILINX1_REG2@h /* init Xilinx1 Reg 2 */ + ori r10,r10,STB_XILINX1_REG2@l + li r11,XILINX1_R2_VAL + sth r11,0(r10) + + lis r10, STB_XILINX1_REG3@h /* init Xilinx1 Reg 3 */ + ori r10,r10,STB_XILINX1_REG3@l + li r11,XILINX1_R3_VAL + sth r11,0(r10) + + lis r10, STB_XILINX1_REG4@h /* init Xilinx1 Reg 4 */ + ori r10,r10,STB_XILINX1_REG4@l + li r11,XILINX1_R4_VAL + sth r11,0(r10) + + lis r10, STB_XILINX1_REG5@h /* init Xilinx1 Reg 5 */ + ori r10,r10,STB_XILINX1_REG5@l + li r11,XILINX1_R5_VAL + sth r11,0(r10) + + lis r10, STB_XILINX1_REG6@h /* init Xilinx1 Reg 6 */ + ori r10,r10,STB_XILINX1_REG6@l + li r11,XILINX1_R6_VAL + sth r11,0(r10) + + lis r10, STB_XILINX1_FLUSH@h /* latch registers in Xilinx*/ + ori r10,r10,STB_XILINX1_FLUSH@l + li r11,0x0000 + sth r11,0(r10) + + /*--------------------------------------------------------------------+ + | Init Xilinx #2 Registers + +--------------------------------------------------------------------*/ + lis r10, STB_XILINX2_REG0@h /* init Xilinx2 Reg 0 */ + ori r10,r10,STB_XILINX2_REG0@l + li r11,XILINX2_R0_VAL + sth r11,0(r10) + + lis r10, STB_XILINX2_REG1@h /* init Xilinx2 Reg 1 */ + ori r10,r10,STB_XILINX2_REG1@l + li r11,XILINX2_R1_VAL + sth r11,0(r10) + + lis r10, STB_XILINX2_REG2@h /* init Xilinx2 Reg 2 */ + ori r10,r10,STB_XILINX2_REG2@l + li r11,XILINX2_R2_VAL + sth r11,0(r10) + + blr + function_epilog(initb_config) + + +/****************************************************************************** +| +| Routine: INITB_HSMC0. +| +| Purpose: Initialize the HSMC0 Registers for SDRAM +| Parameters: None. +| Returns: R3 = 0: Successful +| = -1: Unsuccessful, SDRAM did not reset properly. +| +******************************************************************************/ + function_prolog(initb_hsmc0) + mflr r0 /* Save return addr */ + + /*--------------------------------------------------------------------+ + | Set Global SDRAM Controller to recommended default + +--------------------------------------------------------------------*/ + lis r10,0x6C00 + ori r10,r10,0x0000 + mtdcr hsmc0_gr,r10 + + /*--------------------------------------------------------------------+ + | Set HSMC0 Data Register to recommended default + +--------------------------------------------------------------------*/ + lis r10,0x0037 + ori r10,r10,0x0000 + mtdcr hsmc0_data,r10 + + /*--------------------------------------------------------------------+ + | Init HSMC0 Bank Register 0 + +--------------------------------------------------------------------*/ + lis r10,HSMC0_BR0_VAL@h + ori r10,r10,HSMC0_BR0_VAL@l + mtdcr hsmc0_br0,r10 + + /*--------------------------------------------------------------------+ + | Init HSMC0 Bank Register 1 + +--------------------------------------------------------------------*/ + lis r10,HSMC0_BR1_VAL@h + ori r10,r10,HSMC0_BR1_VAL@l + mtdcr hsmc0_br1,r10 + + /*--------------------------------------------------------------------+ + | Set HSMC0 Control Reg 0 + +--------------------------------------------------------------------*/ + lis r10,0x8077 /* PRECHARGE ALL DEVICE BKS */ + ori r10,r10,0x0000 + mtdcr hsmc0_cr0,r10 + li r3,0x0000 + bl hsmc_cr_wait /* wait for op completion */ + cmpwi cr0,r3,0x0000 + bne cr0,hsmc0_err + + lis r10,0x8078 /* AUTO-REFRESH */ + ori r10,r10,0x0000 + mtdcr hsmc0_cr0,r10 + li r3,0x0000 + bl hsmc_cr_wait /* wait for op completion */ + cmpwi cr0,r3,0x0000 + bne cr0,hsmc0_err + + lis r10,0x8070 /* PROG MODE W/DATA REG VAL */ + ori r10,r10,0x8000 + mtdcr hsmc0_cr0,r10 + li r3,0x0000 + bl hsmc_cr_wait /* wait for op completion */ + cmpwi cr0,r3,0x0000 + bne hsmc0_err + + /*--------------------------------------------------------------------+ + | Set HSMC0 Control Reg 1 + +--------------------------------------------------------------------*/ + lis r10,0x8077 /* PRECHARGE ALL DEVICE BKS */ + ori r10,r10,0x0000 + mtdcr hsmc0_cr1,r10 + li r3,0x0001 + bl hsmc_cr_wait /* wait for op completion */ + cmpwi cr0,r3,0x0000 + bne cr0,hsmc0_err + + lis r10,0x8078 /* AUTO-REFRESH */ + ori r10,r10,0x0000 + mtdcr hsmc0_cr1,r10 + li r3,0x0001 + bl hsmc_cr_wait /* wait for op completion */ + cmpwi cr0,r3,0x0000 + bne cr0,hsmc0_err + + lis r10,0x8070 /* PROG MODE W/DATA REG VAL */ + ori r10,r10,0x8000 + mtdcr hsmc0_cr1,r10 + li r3,0x0001 + bl hsmc_cr_wait /* wait for op completion */ + cmpwi cr0,r3,0x0000 + bne cr0,hsmc0_err + + /*--------------------------------------------------------------------+ + | Set HSMC0 Refresh Register + +--------------------------------------------------------------------*/ + lis r10,0x0FE1 + ori r10,r10,0x0000 + mtdcr hsmc0_crr,r10 + li r3,0 + +hsmc0_err: + mtlr r0 + blr + function_epilog(initb_hsmc0) + + +/****************************************************************************** +| +| Routine: INITB_HSMC1. +| +| Purpose: Initialize the HSMC1 Registers for SDRAM +| Parameters: None. +| Returns: R3 = 0: Successful +| = -1: Unsuccessful, SDRAM did not reset properly. +| +******************************************************************************/ + function_prolog(initb_hsmc1) + mflr r0 /* Save return addr */ + + /*--------------------------------------------------------------------+ + | Set Global SDRAM Controller to recommended default + +--------------------------------------------------------------------*/ + lis r10,0x6C00 + ori r10,r10,0x0000 + mtdcr hsmc1_gr,r10 + + /*--------------------------------------------------------------------+ + | Set HSMC1 Data Register to recommended default + +--------------------------------------------------------------------*/ + lis r10,0x0037 + ori r10,r10,0x0000 + mtdcr hsmc1_data,r10 + + /*--------------------------------------------------------------------+ + | Init HSMC1 Bank Register 0 + +--------------------------------------------------------------------*/ + lis r10,HSMC1_BR0_VAL@h + ori r10,r10,HSMC1_BR0_VAL@l + mtdcr hsmc1_br0,r10 + + /*--------------------------------------------------------------------+ + | Init HSMC1 Bank Register 1 + +--------------------------------------------------------------------*/ + lis r10,HSMC1_BR1_VAL@h + ori r10,r10,HSMC1_BR1_VAL@l + mtdcr hsmc1_br1,r10 + + /*--------------------------------------------------------------------+ + | Set HSMC1 Control Reg 0 + +--------------------------------------------------------------------*/ + lis r10,0x8077 /* PRECHARGE ALL DEVICE BANKS */ + ori r10,r10,0x0000 + mtdcr hsmc1_cr0,r10 + li r3,0x0002 + bl hsmc_cr_wait /* wait for operation completion */ + cmpwi cr0,r3,0x0000 + bne hsmc1_err + + lis r10,0x8078 /* AUTO-REFRESH */ + ori r10,r10,0x0000 + mtdcr hsmc1_cr0,r10 + li r3,0x0002 + bl hsmc_cr_wait /* wait for operation completion */ + cmpwi cr0,r3,0x0000 + bne hsmc1_err + + lis r10,0x8070 /* PROGRAM MODE W/DATA REG VALUE */ + ori r10,r10,0x8000 + mtdcr hsmc1_cr0,r10 + li r3,0x0002 + bl hsmc_cr_wait /* wait for operation completion */ + cmpwi cr0,r3,0x0000 + bne hsmc1_err + + /*--------------------------------------------------------------------+ + | Set HSMC1 Control Reg 1 + +--------------------------------------------------------------------*/ + lis r10,0x8077 /* PRECHARGE ALL DEVICE BKS */ + ori r10,r10,0x0000 + mtdcr hsmc1_cr1,r10 + li r3,0x0003 + bl hsmc_cr_wait /* wait for op completion */ + cmpwi cr0,r3,0x0000 + bne hsmc1_err + + lis r10,0x8078 /* AUTO-REFRESH */ + ori r10,r10,0x0000 + mtdcr hsmc1_cr1,r10 + li r3,0x0003 + bl hsmc_cr_wait /* wait for op completion */ + cmpwi cr0,r3,0x0000 + bne hsmc1_err + + lis r10,0x8070 /* PROG MODE W/DATA REG VAL */ + ori r10,r10,0x8000 + mtdcr hsmc1_cr1,r10 + li r3,0x0003 + bl hsmc_cr_wait /* wait for op completion */ + cmpwi cr0,r3,0x0000 + bne hsmc1_err + + /*--------------------------------------------------------------------+ + | Set HSMC1 Refresh Register + +--------------------------------------------------------------------*/ + lis r10,0x0FE1 + ori r10,r10,0x0000 + mtdcr hsmc1_crr,r10 + xor r3,r3,r3 + +hsmc1_err: + mtlr r0 + blr + function_epilog(initb_hsmc1) + + +/****************************************************************************** +| +| Routine: INITB_CACHE +| +| Purpose: This routine will enable Data and Instruction Cache. +| The Data Cache is an 8K two-way set associative and the +| Instruction Cache is an 16K two-way set associative cache. +| +| Parameters: None. +| +| Returns: None. +| +******************************************************************************/ + function_prolog(initb_cache) + mflr r0 /* Save return addr */ + + bl initb_Dcache /* enable D-Cache */ + bl initb_Icache /* enable I-Cache */ + + mtlr r0 + blr + function_epilog(initb_cache) + + +/****************************************************************************** +| +| Routine: INITB_DCACHE +| +| Purpose: This routine will invalidate all data in the Data Cache and +| then enable D-Cache. If cache is enabled already, the D-Cache +| will be flushed before the data is invalidated. +| +| Parameters: None. +| +| Returns: None. +| +******************************************************************************/ + function_prolog(initb_Dcache) + /*--------------------------------------------------------------------+ + | Flush Data Cache if enabled + +--------------------------------------------------------------------*/ + mfdccr r10 /* r10 <- DCCR */ + isync /* ensure prev insts done */ + cmpwi r10,0x00 + beq ic_dcinv /* D-cache off, invalidate */ + + /*--------------------------------------------------------------------+ + | Data Cache enabled, force known memory addresses to be Cached + +--------------------------------------------------------------------*/ + lis r10,HSMC0_BR0_VAL@h /* r10 <- first memory loc */ + andis. r10,r10,0xFFF0 + li r11,DCACHE_NLINES /* r11 <- # A-way addresses */ + addi r11,r11,DCACHE_NLINES /* r11 <- # B-way addresses */ + mtctr r11 /* set loop counter */ + +ic_dcload: + lwz r12,0(r10) /* force cache of address */ + addi r10,r10,DCACHE_NBYTES /* r10 <- next memory loc */ + bdnz ic_dcload + sync /* ensure prev insts done */ + isync + + /*--------------------------------------------------------------------+ + | Flush the known memory addresses from Cache + +--------------------------------------------------------------------*/ + lis r10,HSMC0_BR0_VAL@h /* r10 <- first memory loc */ + andis. r10,r10,0xFFF0 + mtctr r11 /* set loop counter */ + +ic_dcflush: + dcbf 0,r10 /* flush D-cache line */ + addi r10,r10,DCACHE_NBYTES /* r10 <- next memory loc */ + bdnz ic_dcflush + sync /* ensure prev insts done */ + isync + + /*--------------------------------------------------------------------+ + | Disable then invalidate Data Cache + +--------------------------------------------------------------------*/ + li r10,0 /* r10 <- 0 */ + mtdccr r10 /* disable the D-Cache */ + isync /* ensure prev insts done */ + +ic_dcinv: + li r10,0 /* r10 <- line address */ + li r11,DCACHE_NLINES /* r11 <- # lines in cache */ + mtctr r11 /* set loop counter */ + +ic_dcloop: + dccci 0,r10 /* invalidate A/B cache lns */ + addi r10,r10,DCACHE_NBYTES /* bump to next line */ + bdnz ic_dcloop + sync /* ensure prev insts done */ + isync + + /*--------------------------------------------------------------------+ + | Enable Data Cache + +--------------------------------------------------------------------*/ + lis r10,DCACHE_ENABLE@h /* r10 <- D-cache enable msk*/ + ori r10,r10,DCACHE_ENABLE@l + mtdccr r10 + sync /* ensure prev insts done */ + isync + + blr + function_epilog(initb_Dcache) + + +/****************************************************************************** +| +| Routine: INITB_ICACHE +| +| Purpose: This routine will invalidate all data in the Instruction +| Cache then enable I-Cache. +| +| Parameters: None. +| +| Returns: None. +| +******************************************************************************/ + function_prolog(initb_Icache) + /*--------------------------------------------------------------------+ + | Invalidate Instruction Cache + +--------------------------------------------------------------------*/ + li r10,0 /* r10 <- lines address */ + iccci 0,r10 /* invalidate all I-cache */ + sync /* ensure prev insts done */ + isync + + /*--------------------------------------------------------------------+ + | Enable Instruction Cache + +--------------------------------------------------------------------*/ + lis r10,ICACHE_ENABLE@h /* r10 <- I-cache enable msk*/ + ori r10,r10,ICACHE_ENABLE@l + mticcr r10 + sync /* ensure prev insts done */ + isync + + blr + function_epilog(initb_Icache) + +#if 0 +/****************************************************************************** +| +| Routine: INITB_GET_CSPD +| +| Purpose: Determine the CPU Core Speed. The 13.5 Mhz Time Base +| Counter (TBC) is used to measure a conditional branch +| instruction. +| +| Parameters: R3 = Address of Bus Speed +| R4 = Address of Core Speed +| +| Returns: (R3) = >0: Bus Speed. +| 0: Bus Speed not found in Look-Up Table. +| (R4) = >0: Core Speed. +| 0: Core Speed not found in Look-Up Table. +| +| Note: 1. This routine assumes the bdnz branch instruction takes +| two instruction cycles to complete. +| 2. This routine must be called before interrupts are enabled. +| +******************************************************************************/ + function_prolog(initb_get_cspd) + mflr r0 /* Save return address */ + /*--------------------------------------------------------------------+ + | Set-up timed loop + +--------------------------------------------------------------------*/ + lis r9,gcs_time_loop@h /* r9 <- addr loop instr */ + ori r9,r9,gcs_time_loop@l + lis r10,GCS_LCNT@h /* r10 <- loop count */ + ori r10,r10,GCS_LCNT@l + mtctr r10 /* ctr <- loop count */ + lis r11,STB_TIMERS_TBC@h /* r11 <- TBC register addr */ + ori r11,r11,STB_TIMERS_TBC@l + li r12,0 /* r12 <- 0 */ + + /*--------------------------------------------------------------------+ + | Cache timed-loop instruction + +--------------------------------------------------------------------*/ + icbt 0,r9 + sync + isync + + /*--------------------------------------------------------------------+ + | Get number of 13.5 Mhz cycles to execute time-loop + +--------------------------------------------------------------------*/ + stw r12,0(r11) /* reset TBC */ +gcs_time_loop: + bdnz+ gcs_time_loop /* force branch pred taken */ + lwz r5,0(r11) /* r5 <- num 13.5 Mhz ticks */ + li r6,5 /* LUT based on 1/5th the...*/ + divw r5,r5,r6 /*..loop count used */ + sync + isync + + /*--------------------------------------------------------------------+ + | Look-up core speed based on TBC value + +--------------------------------------------------------------------*/ + lis r6,gcs_lookup_table@h /* r6 <- pts at core spd LUT*/ + ori r6,r6,gcs_lookup_table@l + bl gcs_cspd_lookup /* find core speed in LUT */ + + mtlr r0 /* set return address */ + blr + function_epilog(initb_get_cspd) + +#endif +/*****************************************************************************+ +| XXXX XX XX XXXXXX XXXXXXX XXXXXX XX XX XX XXXX +| XX XXX XX X XX X XX X XX XX XXX XX XXXX XX +| XX XXXX XX XX XX X XX XX XXXX XX XX XX XX +| XX XX XXXX XX XXXX XXXXX XX XXXX XX XX XX +| XX XX XXX XX XX X XX XX XX XXX XXXXXX XX +| XX XX XX XX XX X XX XX XX XX XX XX XX XX +| XXXX XX XX XXXX XXXXXXX XXX XX XX XX XX XX XXXXXXX ++*****************************************************************************/ +/****************************************************************************** +| +| Routine: HSMC_CR_WAIT +| +| Purpose: Wait for the HSMC Control Register (bits 12-16) to be reset +| after an auto-refresh, pre-charge or program mode register +| command execution. +| +| Parameters: R3 = HSMC Control Register ID. +| 0: HSMC0 CR0 +| 1: HSMC0 CR1 +| 2: HSMC1 CR0 +| 3: HSMC1 CR1 +| +| Returns: R3 = 0: Successful +| -1: Unsuccessful +| +******************************************************************************/ +hsmc_cr_wait: + + li r11,10 /* r11 <- retry counter */ + mtctr r11 /* set retry counter */ + mr r11,r3 /* r11 <- HSMC CR reg id */ + +hsmc_cr_rep: + bdz hsmc_cr_err /* branch if max retries hit*/ + + /*--------------------------------------------------------------------+ + | GET HSMCx_CRx value based on HSMC Control Register ID + +--------------------------------------------------------------------*/ +try_hsmc0_cr0: /* CHECK IF ID=HSMC0 CR0 REG*/ + cmpwi cr0,r11,0x0000 + bne cr0,try_hsmc0_cr1 + mfdcr r10,hsmc0_cr0 /* r11 <- HSMC0 CR0 value */ + b hsmc_cr_read + +try_hsmc0_cr1: /* CHECK IF ID=HSMC0 CR1 REG*/ + cmpwi cr0,r11,0x0001 + bne cr0,try_hsmc1_cr0 + mfdcr r10,hsmc0_cr1 /* r10 <- HSMC0 CR1 value */ + b hsmc_cr_read + +try_hsmc1_cr0: /* CHECK IF ID=HSMC1 CR0 REG*/ + cmpwi cr0,r11,0x0002 + bne cr0,try_hsmc1_cr1 + mfdcr r10,hsmc1_cr0 /* r10 <- HSMC1 CR0 value */ + b hsmc_cr_read + +try_hsmc1_cr1: /* CHECK IF ID=HSMC1 CR1 REG*/ + cmpwi cr0,r11,0x0003 + bne cr0,hsmc_cr_err + mfdcr r10,hsmc1_cr1 /* r10 <- HSMC1 CR1 value */ + + /*--------------------------------------------------------------------+ + | Check if HSMC CR register was reset after command execution + +--------------------------------------------------------------------*/ +hsmc_cr_read: + lis r12,0x000F /* create "AND" mask */ + ori r12,r12,0x8000 + and. r10,r10,r12 /* r10 <- HSMC CR bits 12-16*/ + bne cr0,hsmc_cr_rep /* wait for bits to reset */ + li r3,0 /* set return code = success*/ + b hsmc_cr_done + +hsmc_cr_err: /* ERROR: SDRAM didn't reset*/ + li r3,-1 /* set RC=unsuccessful */ + +hsmc_cr_done: + blr + +#if 0 +/****************************************************************************** +| +| Routine: GCS_CSPD_LOOKUP +| +| Purpose: Uses the number of 13.5 Mhz clock ticks found after executing +| the branch instruction time loop to look-up the CPU Core Speed +| in the Core Speed Look-up Table. +| +| Parameters: R3 = Address of Bus Speed +| R4 = Address of Core Speed +| R5 = Number of 13.5 Mhz clock ticks found in time loop. +| R6 = Pointer to Core-Speed Look-Up Table +| +| Returns: (R3) = >0: Bus Speed. +| 0: Bus Speed not found in Look-Up Table. +| (R4) = >0: Core Speed. +| 0: Core Speed not found in Look-Up Table. +| +| Note: Core Speed = Bus Speed * Mult Factor (1-4x). +| +******************************************************************************/ +gcs_cspd_lookup: + + li r9,1 /* r9 <- core speed mult */ + /*--------------------------------------------------------------------+ + | Get theoritical number 13.5 Mhz ticks for a given Bus Speed from + | Look-up Table. Check all mult factors to determine if calculated + | value matches theoretical value (within a tolerance). + +--------------------------------------------------------------------*/ +gcs_cspd_loop: + lwz r10,0(r6) /* r10 <- no. ticks from LUT*/ + divw r10,r10,r9 /* r10 <- div mult (1-4x) */ + subi r11,r10,GCS_CTICK_TOL /* r11 <- no. tks low range */ + addi r12,r10,GCS_CTICK_TOL /* r12 <- no. tks high range*/ + + cmpw cr0,r5,r11 /* calc value within range? */ + blt gcs_cspd_retry /* less than low range */ + cmpw cr0,r5,r12 + bgt gcs_cspd_retry /* greater than high range */ + b gcs_cspd_fnd /* calc value within range */ + + /*--------------------------------------------------------------------+ + | SO FAR CORE SPEED NOT FOUND: Check next mult factor + +--------------------------------------------------------------------*/ +gcs_cspd_retry: + addi r9,r9,1 /* bump mult factor (1-4x) */ + cmpwi cr0,r9,GCS_NMULT + ble gcs_cspd_loop + + /*--------------------------------------------------------------------+ + | SO FAR CORE SPEED NOT FOUND: Point at next Bus Speed in LUT + +--------------------------------------------------------------------*/ + li r9,1 /* reset mult factor */ + addi r6,r6,GCS_TROW_BYTES /* point at next table entry*/ + lwz r10,0(r6) + cmpwi cr0,r10,0 /* check for EOT flag */ + bne gcs_cspd_loop + + /*--------------------------------------------------------------------+ + | COMPUTE CORE SPEED AND GET BUS SPEED FROM LOOK-UP TABLE + +--------------------------------------------------------------------*/ +gcs_cspd_fnd: + lwz r5,4(r6) /* r5 <- Bus Speed in LUT */ + mullw r6,r5,r9 /* r6 <- Core speed */ + stw r5,0(r3) /* (r3) <- Bus Speed */ + stw r6,0(r4) /* (r4) <- Core Speed */ + + blr +#endif diff --git a/arch/ppc/boot/simple/rw4/stb.h b/arch/ppc/boot/simple/rw4/stb.h new file mode 100644 index 00000000000..fd98ee0f843 --- /dev/null +++ b/arch/ppc/boot/simple/rw4/stb.h @@ -0,0 +1,239 @@ +/*----------------------------------------------------------------------------+ +| This source code has been made available to you by IBM on an AS-IS +| basis. Anyone receiving this source is licensed under IBM +| copyrights to use it in any way he or she deems fit, including +| copying it, modifying it, compiling it, and redistributing it either +| with or without modifications. No license under IBM patents or +| patent applications is to be implied by the copyright license. +| +| Any user of this software should understand that IBM cannot provide +| technical support for this software and will not be responsible for +| any consequences resulting from the use of this software. +| +| Any person who transfers this source code or any derivative work +| must include the IBM copyright notice, this paragraph, and the +| preceding two paragraphs in the transferred software. +| +| COPYRIGHT I B M CORPORATION 1999 +| LICENSED MATERIAL - PROGRAM PROPERTY OF I B M ++----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------+ +| Author: Maciej P. Tyrlik +| Component: Include file. +| File: stb.h +| Purpose: Common Set-tob-box definitions. +| Changes: +| Date: Comment: +| ----- -------- +| 14-Jan-97 Created for ElPaso pass 1 MPT +| 13-May-97 Added function prototype and global variables MPT +| 08-Dec-98 Added RAW IR task information MPT +| 19-Jan-99 Port to Romeo MPT +| 19-May-00 Changed SDRAM to 32MB contiguous 0x1F000000 - 0x20FFFFFF RLB ++----------------------------------------------------------------------------*/ + +#ifndef _stb_h_ +#define _stb_h_ + +/*----------------------------------------------------------------------------+ +| Read/write from I/O macros. ++----------------------------------------------------------------------------*/ +#define inbyte(port) (*((unsigned char volatile *)(port))) +#define outbyte(port,data) *(unsigned char volatile *)(port)=\ + (unsigned char)(data) + +#define inshort(port) (*((unsigned short volatile *)(port))) +#define outshort(port,data) *(unsigned short volatile *)(port)=\ + (unsigned short)(data) + +#define inword(port) (*((unsigned long volatile *)(port))) +#define outword(port,data) *(unsigned long volatile *)(port)=\ + (unsigned long)(data) + +/*----------------------------------------------------------------------------+ +| STB interrupts. ++----------------------------------------------------------------------------*/ +#define STB_XP_TP_INT 0 +#define STB_XP_APP_INT 1 +#define STB_AUD_INT 2 +#define STB_VID_INT 3 +#define STB_DMA0_INT 4 +#define STB_DMA1_INT 5 +#define STB_DMA2_INT 6 +#define STB_DMA3_INT 7 +#define STB_SCI_INT 8 +#define STB_I2C1_INT 9 +#define STB_I2C2_INT 10 +#define STB_GPT_PWM0 11 +#define STB_GPT_PWM1 12 +#define STB_SCP_INT 13 +#define STB_SSP_INT 14 +#define STB_GPT_PWM2 15 +#define STB_EXT5_INT 16 +#define STB_EXT6_INT 17 +#define STB_EXT7_INT 18 +#define STB_EXT8_INT 19 +#define STB_SCC_INT 20 +#define STB_SICC_RECV_INT 21 +#define STB_SICC_TRAN_INT 22 +#define STB_PPU_INT 23 +#define STB_DCRX_INT 24 +#define STB_EXT0_INT 25 +#define STB_EXT1_INT 26 +#define STB_EXT2_INT 27 +#define STB_EXT3_INT 28 +#define STB_EXT4_INT 29 +#define STB_REDWOOD_ENET_INT STB_EXT1_INT + +/*----------------------------------------------------------------------------+ +| STB tasks, task stack sizes, and task priorities. The actual task priority +| is 1 more than the specified number since priority 0 is reserved (system +| internaly adds 1 to supplied priority number). ++----------------------------------------------------------------------------*/ +#define STB_IDLE_TASK_SS (5* 1024) +#define STB_IDLE_TASK_PRIO 0 +#define STB_LEDTEST_SS (2* 1024) +#define STB_LEDTEST_PRIO 0 +#define STB_CURSOR_TASK_SS (10* 1024) +#define STB_CURSOR_TASK_PRIO 7 +#define STB_MPEG_TASK_SS (10* 1024) +#define STB_MPEG_TASK_PRIO 9 +#define STB_DEMUX_TASK_SS (10* 1024) +#define STB_DEMUX_TASK_PRIO 20 +#define RAW_STB_IR_TASK_SS (10* 1024) +#define RAW_STB_IR_TASK_PRIO 20 + +#define STB_SERIAL_ER_TASK_SS (10* 1024) +#define STB_SERIAL_ER_TASK_PRIO 1 +#define STB_CA_TASK_SS (10* 1024) +#define STB_CA_TASK_PRIO 8 + +#define INIT_DEFAULT_VIDEO_SS (10* 1024) +#define INIT_DEFAULT_VIDEO_PRIO 8 +#define INIT_DEFAULT_SERVI_SS (10* 1024) +#define INIT_DEFAULT_SERVI_PRIO 8 +#define INIT_DEFAULT_POST_SS (10* 1024) +#define INIT_DEFAULT_POST_PRIO 8 +#define INIT_DEFAULT_INTER_SS (10* 1024) +#define INIT_DEFAULT_INTER_PRIO 8 +#define INIT_DEFAULT_BR_SS (10* 1024) +#define INIT_DEFAULT_BR_PRIO 8 +#define INITIAL_TASK_STACK_SIZE (32* 1024) + +#ifdef VESTA +/*----------------------------------------------------------------------------+ +| Vesta Overall Address Map (all addresses are double mapped, bit 0 of the +| address is not decoded. Numbers below are dependent on board configuration. +| FLASH, SDRAM, DRAM numbers can be affected by actual board setup. +| +| FFE0,0000 - FFFF,FFFF FLASH +| F200,0000 - F210,FFFF FPGA logic +| Ethernet = F200,0000 +| LED Display = F200,0100 +| Xilinx #1 Regs = F204,0000 +| Xilinx #2 Regs = F208,0000 +| Spare = F20C,0000 +| IDE CS0 = F210,0000 +| F410,0000 - F410,FFFF IDE CS1 +| C000,0000 - C7FF,FFFF OBP +| C000,0000 - C000,0014 SICC (16550 + infra red) +| C001,0000 - C001,0018 PPU (Parallel Port) +| C002,0000 - C002,001B SC0 (Smart Card 0) +| C003,0000 - C003,000F I2C0 +| C004,0000 - C004,0009 SCC (16550 UART) +| C005,0000 - C005,0124 GPT (Timers) +| C006,0000 - C006,0058 GPIO0 +| C007,0000 - C007,001b SC1 (Smart Card 1) +| C008,0000 - C008,FFFF Unused +| C009,0000 - C009,FFFF Unused +| C00A,0000 - C00A,FFFF Unused +| C00B,0000 - C00B,000F I2C1 +| C00C,0000 - C00C,0006 SCP +| C00D,0000 - C00D,0010 SSP +| A000,0000 - A0FF,FFFF SDRAM1 (16M) +| 0000,0000 - 00FF,FFFF SDRAM0 (16M) ++----------------------------------------------------------------------------*/ +#define STB_FLASH_BASE_ADDRESS 0xFFE00000 +#define STB_FPGA_BASE_ADDRESS 0xF2000000 +#define STB_SICC_BASE_ADDRESS 0xC0000000 +#define STB_PPU_BASE_ADDR 0xC0010000 +#define STB_SC0_BASE_ADDRESS 0xC0020000 +#define STB_I2C1_BASE_ADDRESS 0xC0030000 +#define STB_SCC_BASE_ADDRESS 0xC0040000 +#define STB_TIMERS_BASE_ADDRESS 0xC0050000 +#define STB_GPIO0_BASE_ADDRESS 0xC0060000 +#define STB_SC1_BASE_ADDRESS 0xC0070000 +#define STB_I2C2_BASE_ADDRESS 0xC00B0000 +#define STB_SCP_BASE_ADDRESS 0xC00C0000 +#define STB_SSP_BASE_ADDRESS 0xC00D0000 +/*----------------------------------------------------------------------------+ +|The following are used by the IBM RTOS SW. +|15-May-00 Changed these values to reflect movement of base addresses in +|order to support 32MB of contiguous SDRAM space. +|Points to the cacheable region since these values are used in IBM RTOS +|to establish the vector address. ++----------------------------------------------------------------------------*/ +#define STB_SDRAM1_BASE_ADDRESS 0x20000000 +#define STB_SDRAM1_SIZE 0x01000000 +#define STB_SDRAM0_BASE_ADDRESS 0x1F000000 +#define STB_SDRAM0_SIZE 0x01000000 + +#else +/*----------------------------------------------------------------------------+ +| ElPaso Overall Address Map (all addresses are double mapped, bit 0 of the +| address is not decoded. Numbers below are dependent on board configuration. +| FLASH, SDRAM, DRAM numbers can be affected by actual board setup. OPB +| devices are inside the ElPaso chip. +| FFE0,0000 - FFFF,FFFF FLASH +| F144,0000 - F104,FFFF FPGA logic +| F140,0000 - F100,0000 ethernet (through FPGA logic) +| C000,0000 - C7FF,FFFF OBP +| C000,0000 - C000,0014 SICC (16550+ infra red) +| C001,0000 - C001,0016 PPU (parallel port) +| C002,0000 - C002,001B SC (smart card) +| C003,0000 - C003,000F I2C 1 +| C004,0000 - C004,0009 SCC (16550 UART) +| C005,0000 - C005,0124 Timers +| C006,0000 - C006,0058 GPIO0 +| C007,0000 - C007,0058 GPIO1 +| C008,0000 - C008,0058 GPIO2 +| C009,0000 - C009,0058 GPIO3 +| C00A,0000 - C00A,0058 GPIO4 +| C00B,0000 - C00B,000F I2C 2 +| C00C,0000 - C00C,0006 SCP +| C00D,0000 - C00D,0006 SSP +| A000,0000 - A0FF,FFFF SDRAM 16M +| 0000,0000 - 00FF,FFFF DRAM 16M ++----------------------------------------------------------------------------*/ +#define STB_FLASH_BASE_ADDRESS 0xFFE00000 +#define STB_FPGA_BASE_ADDRESS 0xF1440000 +#define STB_ENET_BASE_ADDRESS 0xF1400000 +#define STB_SICC_BASE_ADDRESS 0xC0000000 +#define STB_PPU_BASE_ADDR 0xC0010000 +#define STB_SC_BASE_ADDRESS 0xC0020000 +#define STB_I2C1_BASE_ADDRESS 0xC0030000 +#define STB_SCC_BASE_ADDRESS 0xC0040000 +#define STB_TIMERS_BASE_ADDRESS 0xC0050000 +#define STB_GPIO0_BASE_ADDRESS 0xC0060000 +#define STB_GPIO1_BASE_ADDRESS 0xC0070000 +#define STB_GPIO2_BASE_ADDRESS 0xC0080000 +#define STB_GPIO3_BASE_ADDRESS 0xC0090000 +#define STB_GPIO4_BASE_ADDRESS 0xC00A0000 +#define STB_I2C2_BASE_ADDRESS 0xC00B0000 +#define STB_SCP_BASE_ADDRESS 0xC00C0000 +#define STB_SSP_BASE_ADDRESS 0xC00D0000 +#define STB_SDRAM_BASE_ADDRESS 0xA0000000 +#endif + +/*----------------------------------------------------------------------------+ +| Other common defines. ++----------------------------------------------------------------------------*/ +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#endif /* _stb_h_ */ diff --git a/arch/ppc/boot/utils/addRamDisk.c b/arch/ppc/boot/utils/addRamDisk.c new file mode 100644 index 00000000000..93400dfcce7 --- /dev/null +++ b/arch/ppc/boot/utils/addRamDisk.c @@ -0,0 +1,203 @@ +#include +#include +#include +#include +#include +#include +#include + +#define ElfHeaderSize (64 * 1024) +#define ElfPages (ElfHeaderSize / 4096) +#define KERNELBASE (0xc0000000) + +void get4k(FILE *file, char *buf ) +{ + unsigned j; + unsigned num = fread(buf, 1, 4096, file); + for ( j=num; j<4096; ++j ) + buf[j] = 0; +} + +void put4k(FILE *file, char *buf ) +{ + fwrite(buf, 1, 4096, file); +} + +void death(const char *msg, FILE *fdesc, const char *fname) +{ + printf(msg); + fclose(fdesc); + unlink(fname); + exit(1); +} + +int main(int argc, char **argv) +{ + char inbuf[4096]; + FILE *ramDisk = NULL; + FILE *inputVmlinux = NULL; + FILE *outputVmlinux = NULL; + unsigned i = 0; + u_int32_t ramFileLen = 0; + u_int32_t ramLen = 0; + u_int32_t roundR = 0; + u_int32_t kernelLen = 0; + u_int32_t actualKernelLen = 0; + u_int32_t round = 0; + u_int32_t roundedKernelLen = 0; + u_int32_t ramStartOffs = 0; + u_int32_t ramPages = 0; + u_int32_t roundedKernelPages = 0; + u_int32_t hvReleaseData = 0; + u_int32_t eyeCatcher = 0xc8a5d9c4; + u_int32_t naca = 0; + u_int32_t xRamDisk = 0; + u_int32_t xRamDiskSize = 0; + if ( argc < 2 ) { + printf("Name of RAM disk file missing.\n"); + exit(1); + } + + if ( argc < 3 ) { + printf("Name of vmlinux file missing.\n"); + exit(1); + } + + if ( argc < 4 ) { + printf("Name of vmlinux output file missing.\n"); + exit(1); + } + + ramDisk = fopen(argv[1], "r"); + if ( ! ramDisk ) { + printf("RAM disk file \"%s\" failed to open.\n", argv[1]); + exit(1); + } + inputVmlinux = fopen(argv[2], "r"); + if ( ! inputVmlinux ) { + printf("vmlinux file \"%s\" failed to open.\n", argv[2]); + exit(1); + } + outputVmlinux = fopen(argv[3], "w+"); + if ( ! outputVmlinux ) { + printf("output vmlinux file \"%s\" failed to open.\n", argv[3]); + exit(1); + } + fseek(ramDisk, 0, SEEK_END); + ramFileLen = ftell(ramDisk); + fseek(ramDisk, 0, SEEK_SET); + printf("%s file size = %d\n", argv[1], ramFileLen); + + ramLen = ramFileLen; + + roundR = 4096 - (ramLen % 4096); + if ( roundR ) { + printf("Rounding RAM disk file up to a multiple of 4096, adding %d\n", roundR); + ramLen += roundR; + } + + printf("Rounded RAM disk size is %d\n", ramLen); + fseek(inputVmlinux, 0, SEEK_END); + kernelLen = ftell(inputVmlinux); + fseek(inputVmlinux, 0, SEEK_SET); + printf("kernel file size = %d\n", kernelLen); + if ( kernelLen == 0 ) { + printf("You must have a linux kernel specified as argv[2]\n"); + exit(1); + } + + actualKernelLen = kernelLen - ElfHeaderSize; + + printf("actual kernel length (minus ELF header) = %d\n", actualKernelLen); + + round = actualKernelLen % 4096; + roundedKernelLen = actualKernelLen; + if ( round ) + roundedKernelLen += (4096 - round); + + printf("actual kernel length rounded up to a 4k multiple = %d\n", roundedKernelLen); + + ramStartOffs = roundedKernelLen; + ramPages = ramLen / 4096; + + printf("RAM disk pages to copy = %d\n", ramPages); + + // Copy 64K ELF header + for (i=0; i<(ElfPages); ++i) { + get4k( inputVmlinux, inbuf ); + put4k( outputVmlinux, inbuf ); + } + + roundedKernelPages = roundedKernelLen / 4096; + + fseek(inputVmlinux, ElfHeaderSize, SEEK_SET); + + for ( i=0; i +#include +#include +#include +#include + +void xlate( char * inb, char * trb, unsigned len ) +{ + unsigned i; + for ( i=0; i> 4; + char c2 = c & 0xf; + if ( c1 > 9 ) + c1 = c1 + 'A' - 10; + else + c1 = c1 + '0'; + if ( c2 > 9 ) + c2 = c2 + 'A' - 10; + else + c2 = c2 + '0'; + *trb++ = c1; + *trb++ = c2; + } + *trb = 0; +} + +#define ElfHeaderSize (64 * 1024) +#define ElfPages (ElfHeaderSize / 4096) +#define KERNELBASE (0xc0000000) + +void get4k( /*istream *inf*/FILE *file, char *buf ) +{ + unsigned j; + unsigned num = fread(buf, 1, 4096, file); + for ( j=num; j<4096; ++j ) + buf[j] = 0; +} + +void put4k( /*ostream *outf*/FILE *file, char *buf ) +{ + fwrite(buf, 1, 4096, file); +} + +int main(int argc, char **argv) +{ + char inbuf[4096]; + FILE *ramDisk = NULL; + FILE *inputVmlinux = NULL; + FILE *outputVmlinux = NULL; + unsigned i = 0; + unsigned long ramFileLen = 0; + unsigned long ramLen = 0; + unsigned long roundR = 0; + unsigned long kernelLen = 0; + unsigned long actualKernelLen = 0; + unsigned long round = 0; + unsigned long roundedKernelLen = 0; + unsigned long ramStartOffs = 0; + unsigned long ramPages = 0; + unsigned long roundedKernelPages = 0; + if ( argc < 2 ) { + printf("Name of System Map file missing.\n"); + exit(1); + } + + if ( argc < 3 ) { + printf("Name of vmlinux file missing.\n"); + exit(1); + } + + if ( argc < 4 ) { + printf("Name of vmlinux output file missing.\n"); + exit(1); + } + + ramDisk = fopen(argv[1], "r"); + if ( ! ramDisk ) { + printf("System Map file \"%s\" failed to open.\n", argv[1]); + exit(1); + } + inputVmlinux = fopen(argv[2], "r"); + if ( ! inputVmlinux ) { + printf("vmlinux file \"%s\" failed to open.\n", argv[2]); + exit(1); + } + outputVmlinux = fopen(argv[3], "w"); + if ( ! outputVmlinux ) { + printf("output vmlinux file \"%s\" failed to open.\n", argv[3]); + exit(1); + } + fseek(ramDisk, 0, SEEK_END); + ramFileLen = ftell(ramDisk); + fseek(ramDisk, 0, SEEK_SET); + printf("%s file size = %ld\n", argv[1], ramFileLen); + + ramLen = ramFileLen; + + roundR = 4096 - (ramLen % 4096); + if ( roundR ) { + printf("Rounding System Map file up to a multiple of 4096, adding %ld\n", roundR); + ramLen += roundR; + } + + printf("Rounded System Map size is %ld\n", ramLen); + fseek(inputVmlinux, 0, SEEK_END); + kernelLen = ftell(inputVmlinux); + fseek(inputVmlinux, 0, SEEK_SET); + printf("kernel file size = %ld\n", kernelLen); + if ( kernelLen == 0 ) { + printf("You must have a linux kernel specified as argv[2]\n"); + exit(1); + } + + actualKernelLen = kernelLen - ElfHeaderSize; + + printf("actual kernel length (minus ELF header) = %ld\n", actualKernelLen); + + round = actualKernelLen % 4096; + roundedKernelLen = actualKernelLen; + if ( round ) + roundedKernelLen += (4096 - round); + + printf("actual kernel length rounded up to a 4k multiple = %ld\n", roundedKernelLen); + + ramStartOffs = roundedKernelLen; + ramPages = ramLen / 4096; + + printf("System map pages to copy = %ld\n", ramPages); + + // Copy 64K ELF header + for (i=0; i<(ElfPages); ++i) { + get4k( inputVmlinux, inbuf ); + put4k( outputVmlinux, inbuf ); + } + + + + roundedKernelPages = roundedKernelLen / 4096; + + fseek(inputVmlinux, ElfHeaderSize, SEEK_SET); + + { + for ( i=0; i +#include +#include +#include +#include + +char arch[] = "PowerPC"; + +#define N_DESCR 6 +unsigned int descr[N_DESCR] = { +#if 1 + /* values for IBM RS/6000 machines */ + 0xffffffff, /* real-mode = true */ + 0x00c00000, /* real-base, i.e. where we expect OF to be */ + 0xffffffff, /* real-size */ + 0xffffffff, /* virt-base */ + 0xffffffff, /* virt-size */ + 0x4000, /* load-base */ +#else + /* values for longtrail CHRP */ + 0, /* real-mode = false */ + 0xffffffff, /* real-base */ + 0xffffffff, /* real-size */ + 0xffffffff, /* virt-base */ + 0xffffffff, /* virt-size */ + 0x00600000, /* load-base */ +#endif +}; + +unsigned char buf[512]; + +#define GET_16BE(off) ((buf[off] << 8) + (buf[(off)+1])) +#define GET_32BE(off) ((GET_16BE(off) << 16) + GET_16BE((off)+2)) + +#define PUT_16BE(off, v) (buf[off] = ((v) >> 8) & 0xff, \ + buf[(off) + 1] = (v) & 0xff) +#define PUT_32BE(off, v) (PUT_16BE((off), (v) >> 16), \ + PUT_16BE((off) + 2, (v))) + +/* Structure of an ELF file */ +#define E_IDENT 0 /* ELF header */ +#define E_PHOFF 28 +#define E_PHENTSIZE 42 +#define E_PHNUM 44 +#define E_HSIZE 52 /* size of ELF header */ + +#define EI_MAGIC 0 /* offsets in E_IDENT area */ +#define EI_CLASS 4 +#define EI_DATA 5 + +#define PH_TYPE 0 /* ELF program header */ +#define PH_OFFSET 4 +#define PH_FILESZ 16 +#define PH_HSIZE 32 /* size of program header */ + +#define PT_NOTE 4 /* Program header type = note */ + +#define ELFCLASS32 1 +#define ELFDATA2MSB 2 + +unsigned char elf_magic[4] = { 0x7f, 'E', 'L', 'F' }; + +int main(int ac, char **av) +{ + int fd, n, i; + int ph, ps, np; + int nnote, ns; + + if (ac != 2) { + fprintf(stderr, "Usage: %s elf-file\n", av[0]); + exit(1); + } + fd = open(av[1], O_RDWR); + if (fd < 0) { + perror(av[1]); + exit(1); + } + + nnote = strlen(arch) + 1 + (N_DESCR + 3) * 4; + + n = read(fd, buf, sizeof(buf)); + if (n < 0) { + perror("read"); + exit(1); + } + + if (n < E_HSIZE || memcmp(&buf[E_IDENT+EI_MAGIC], elf_magic, 4) != 0) + goto notelf; + + if (buf[E_IDENT+EI_CLASS] != ELFCLASS32 + || buf[E_IDENT+EI_DATA] != ELFDATA2MSB) { + fprintf(stderr, "%s is not a big-endian 32-bit ELF image\n", + av[1]); + exit(1); + } + + ph = GET_32BE(E_PHOFF); + ps = GET_16BE(E_PHENTSIZE); + np = GET_16BE(E_PHNUM); + if (ph < E_HSIZE || ps < PH_HSIZE || np < 1) + goto notelf; + if (ph + (np + 1) * ps + nnote > n) + goto nospace; + + for (i = 0; i < np; ++i) { + if (GET_32BE(ph + PH_TYPE) == PT_NOTE) { + fprintf(stderr, "%s already has a note entry\n", + av[1]); + exit(0); + } + ph += ps; + } + + /* XXX check that the area we want to use is all zeroes */ + for (i = 0; i < ps + nnote; ++i) + if (buf[ph + i] != 0) + goto nospace; + + /* fill in the program header entry */ + ns = ph + ps; + PUT_32BE(ph + PH_TYPE, PT_NOTE); + PUT_32BE(ph + PH_OFFSET, ns); + PUT_32BE(ph + PH_FILESZ, nnote); + + /* fill in the note area we point to */ + /* XXX we should probably make this a proper section */ + PUT_32BE(ns, strlen(arch) + 1); + PUT_32BE(ns + 4, N_DESCR * 4); + PUT_32BE(ns + 8, 0x1275); + strcpy(&buf[ns + 12], arch); + ns += 12 + strlen(arch) + 1; + for (i = 0; i < N_DESCR; ++i) + PUT_32BE(ns + i * 4, descr[i]); + + /* Update the number of program headers */ + PUT_16BE(E_PHNUM, np + 1); + + /* write back */ + lseek(fd, (long) 0, SEEK_SET); + i = write(fd, buf, n); + if (i < 0) { + perror("write"); + exit(1); + } + if (i < n) { + fprintf(stderr, "%s: write truncated\n", av[1]); + exit(1); + } + + exit(0); + + notelf: + fprintf(stderr, "%s does not appear to be an ELF file\n", av[0]); + exit(1); + + nospace: + fprintf(stderr, "sorry, I can't find space in %s to put the note\n", + av[0]); + exit(1); +} diff --git a/arch/ppc/boot/utils/elf.pl b/arch/ppc/boot/utils/elf.pl new file mode 100644 index 00000000000..d3e9d9d5b84 --- /dev/null +++ b/arch/ppc/boot/utils/elf.pl @@ -0,0 +1,33 @@ +# +# ELF header field numbers +# + +$e_ident = 0; # Identification bytes / magic number +$e_type = 1; # ELF file type +$e_machine = 2; # Target machine type +$e_version = 3; # File version +$e_entry = 4; # Start address +$e_phoff = 5; # Program header file offset +$e_shoff = 6; # Section header file offset +$e_flags = 7; # File flags +$e_ehsize = 8; # Size of ELF header +$e_phentsize = 9; # Size of program header +$e_phnum = 10; # Number of program header entries +$e_shentsize = 11; # Size of section header +$e_shnum = 12; # Number of section header entries +$e_shstrndx = 13; # Section header table string index + +# +# Section header field numbers +# + +$sh_name = 0; # Section name +$sh_type = 1; # Section header type +$sh_flags = 2; # Section header flags +$sh_addr = 3; # Virtual address +$sh_offset = 4; # File offset +$sh_size = 5; # Section size +$sh_link = 6; # Miscellaneous info +$sh_info = 7; # More miscellaneous info +$sh_addralign = 8; # Memory alignment +$sh_entsize = 9; # Entry size if this is a table diff --git a/arch/ppc/boot/utils/hack-coff.c b/arch/ppc/boot/utils/hack-coff.c new file mode 100644 index 00000000000..5e5a6573a1e --- /dev/null +++ b/arch/ppc/boot/utils/hack-coff.c @@ -0,0 +1,84 @@ +/* + * hack-coff.c - hack the header of an xcoff file to fill in + * a few fields needed by the Open Firmware xcoff loader on + * Power Macs but not initialized by objcopy. + * + * Copyright (C) Paul Mackerras 1997. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include "rs6000.h" + +#define AOUT_MAGIC 0x010b + +#define get_16be(x) ((((unsigned char *)(x))[0] << 8) \ + + ((unsigned char *)(x))[1]) +#define put_16be(x, v) (((unsigned char *)(x))[0] = (v) >> 8, \ + ((unsigned char *)(x))[1] = (v) & 0xff) +#define get_32be(x) ((((unsigned char *)(x))[0] << 24) \ + + (((unsigned char *)(x))[1] << 16) \ + + (((unsigned char *)(x))[2] << 8) \ + + ((unsigned char *)(x))[3]) + +int +main(int ac, char **av) +{ + int fd; + int i, nsect; + int aoutsz; + struct external_filehdr fhdr; + AOUTHDR aout; + struct external_scnhdr shdr; + + if (ac != 2) { + fprintf(stderr, "Usage: hack-coff coff-file\n"); + exit(1); + } + if ((fd = open(av[1], 2)) == -1) { + perror(av[2]); + exit(1); + } + if (read(fd, &fhdr, sizeof(fhdr)) != sizeof(fhdr)) + goto readerr; + i = get_16be(fhdr.f_magic); + if (i != U802TOCMAGIC && i != U802WRMAGIC && i != U802ROMAGIC) { + fprintf(stderr, "%s: not an xcoff file\n", av[1]); + exit(1); + } + aoutsz = get_16be(fhdr.f_opthdr); + if (read(fd, &aout, aoutsz) != aoutsz) + goto readerr; + nsect = get_16be(fhdr.f_nscns); + for (i = 0; i < nsect; ++i) { + if (read(fd, &shdr, sizeof(shdr)) != sizeof(shdr)) + goto readerr; + if (strcmp(shdr.s_name, ".text") == 0) { + put_16be(aout.o_snentry, i+1); + put_16be(aout.o_sntext, i+1); + } else if (strcmp(shdr.s_name, ".data") == 0) { + put_16be(aout.o_sndata, i+1); + } else if (strcmp(shdr.s_name, ".bss") == 0) { + put_16be(aout.o_snbss, i+1); + } + } + put_16be(aout.magic, AOUT_MAGIC); + if (lseek(fd, (long) sizeof(struct external_filehdr), 0) == -1 + || write(fd, &aout, aoutsz) != aoutsz) { + fprintf(stderr, "%s: write error\n", av[1]); + exit(1); + } + close(fd); + exit(0); + +readerr: + fprintf(stderr, "%s: read error or file too short\n", av[1]); + exit(1); +} diff --git a/arch/ppc/boot/utils/mkbugboot.c b/arch/ppc/boot/utils/mkbugboot.c new file mode 100644 index 00000000000..886122283f3 --- /dev/null +++ b/arch/ppc/boot/utils/mkbugboot.c @@ -0,0 +1,187 @@ +/* + * arch/ppc/boot/utils/mkbugboot.c + * + * Makes a Motorola PPCBUG ROM bootable image which can be flashed + * into one of the FLASH banks on a Motorola PowerPlus board. + * + * Author: Matt Porter + * + * 2001 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#define ELF_HEADER_SIZE 65536 + +#include +#include +#include +#include +#include +#include +#include +#ifdef __sun__ +#include +#else +#include +#endif + +#ifdef __i386__ +#define cpu_to_be32(x) le32_to_cpu(x) +#define cpu_to_be16(x) le16_to_cpu(x) +#else +#define cpu_to_be32(x) (x) +#define cpu_to_be16(x) (x) +#endif + +#define cpu_to_le32(x) le32_to_cpu((x)) +unsigned long le32_to_cpu(unsigned long x) +{ + return (((x & 0x000000ffU) << 24) | + ((x & 0x0000ff00U) << 8) | + ((x & 0x00ff0000U) >> 8) | + ((x & 0xff000000U) >> 24)); +} + +#define cpu_to_le16(x) le16_to_cpu((x)) +unsigned short le16_to_cpu(unsigned short x) +{ + return (((x & 0x00ff) << 8) | + ((x & 0xff00) >> 8)); +} + +/* size of read buffer */ +#define SIZE 0x1000 + +/* PPCBUG ROM boot header */ +typedef struct bug_boot_header { + uint8_t magic_word[4]; /* "BOOT" */ + uint32_t entry_offset; /* Offset from top of header to code */ + uint32_t routine_length; /* Length of code */ + uint8_t routine_name[8]; /* Name of the boot code */ +} bug_boot_header_t; + +#define HEADER_SIZE sizeof(bug_boot_header_t) + +uint32_t copy_image(int32_t in_fd, int32_t out_fd) +{ + uint8_t buf[SIZE]; + int n; + uint32_t image_size = 0; + uint8_t zero = 0; + + lseek(in_fd, ELF_HEADER_SIZE, SEEK_SET); + + /* Copy an image while recording its size */ + while ( (n = read(in_fd, buf, SIZE)) > 0 ) + { + image_size = image_size + n; + write(out_fd, buf, n); + } + + /* BUG romboot requires that our size is divisible by 2 */ + /* align image to 2 byte boundary */ + if (image_size % 2) + { + image_size++; + write(out_fd, &zero, 1); + } + + return image_size; +} + +void write_bugboot_header(int32_t out_fd, uint32_t boot_size) +{ + uint8_t header_block[HEADER_SIZE]; + bug_boot_header_t *bbh = (bug_boot_header_t *)&header_block[0]; + + memset(header_block, 0, HEADER_SIZE); + + /* Fill in the PPCBUG ROM boot header */ + strncpy(bbh->magic_word, "BOOT", 4); /* PPCBUG magic word */ + bbh->entry_offset = cpu_to_be32(HEADER_SIZE); /* Entry address */ + bbh->routine_length= cpu_to_be32(HEADER_SIZE+boot_size+2); /* Routine length */ + strncpy(bbh->routine_name, "LINUXROM", 8); /* Routine name */ + + /* Output the header and bootloader to the file */ + write(out_fd, header_block, HEADER_SIZE); +} + +uint16_t calc_checksum(int32_t bug_fd) +{ + uint32_t checksum_var = 0; + uint8_t buf[2]; + int n; + + /* Checksum loop */ + while ( (n = read(bug_fd, buf, 2) ) ) + { + checksum_var = checksum_var + *(uint16_t *)buf; + + /* If we carry out, mask it and add one to the checksum */ + if (checksum_var >> 16) + checksum_var = (checksum_var & 0x0000ffff) + 1; + } + + return checksum_var; +} + +int main(int argc, char *argv[]) +{ + int32_t image_fd, bugboot_fd; + int argptr = 1; + uint32_t kernel_size = 0; + uint16_t checksum = 0; + uint8_t bugbootname[256]; + + if ( (argc != 3) ) + { + fprintf(stderr, "usage: %s \n",argv[0]); + exit(-1); + } + + /* Get file args */ + + /* kernel image file */ + if ((image_fd = open( argv[argptr] , 0)) < 0) + exit(-1); + argptr++; + + /* bugboot file */ + if ( !strcmp( argv[argptr], "-" ) ) + bugboot_fd = 1; /* stdout */ + else + if ((bugboot_fd = creat( argv[argptr] , 0755)) < 0) + exit(-1); + else + strcpy(bugbootname, argv[argptr]); + argptr++; + + /* Set file position after ROM header block where zImage will be written */ + lseek(bugboot_fd, HEADER_SIZE, SEEK_SET); + + /* Copy kernel image into bugboot image */ + kernel_size = copy_image(image_fd, bugboot_fd); + close(image_fd); + + /* Set file position to beginning where header/romboot will be written */ + lseek(bugboot_fd, 0, SEEK_SET); + + /* Write out BUG header/romboot */ + write_bugboot_header(bugboot_fd, kernel_size); + + /* Close bugboot file */ + close(bugboot_fd); + + /* Reopen it as read/write */ + bugboot_fd = open(bugbootname, O_RDWR); + + /* Calculate checksum */ + checksum = calc_checksum(bugboot_fd); + + /* Write out the calculated checksum */ + write(bugboot_fd, &checksum, 2); + + return 0; +} diff --git a/arch/ppc/boot/utils/mknote.c b/arch/ppc/boot/utils/mknote.c new file mode 100644 index 00000000000..b9fbb2cbfc8 --- /dev/null +++ b/arch/ppc/boot/utils/mknote.c @@ -0,0 +1,44 @@ +/* + * Copyright (C) Cort Dougan 1999. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Generate a note section as per the CHRP specification. + * + */ + +#include +#include + +#define PL(x) printf("%c%c%c%c", ((x)>>24)&0xff, ((x)>>16)&0xff, ((x)>>8)&0xff, (x)&0xff ); + +int main(void) +{ +/* header */ + /* namesz */ + PL(strlen("PowerPC")+1); + /* descrsz */ + PL(6*4); + /* type */ + PL(0x1275); + /* name */ + printf("PowerPC"); printf("%c", 0); + +/* descriptor */ + /* real-mode */ + PL(0xffffffff); + /* real-base */ + PL(0x00c00000); + /* real-size */ + PL(0xffffffff); + /* virt-base */ + PL(0xffffffff); + /* virt-size */ + PL(0xffffffff); + /* load-base */ + PL(0x4000); + return 0; +} diff --git a/arch/ppc/boot/utils/mkprep.c b/arch/ppc/boot/utils/mkprep.c new file mode 100644 index 00000000000..f6d5a2f2fcf --- /dev/null +++ b/arch/ppc/boot/utils/mkprep.c @@ -0,0 +1,293 @@ +/* + * Makes a prep bootable image which can be dd'd onto + * a disk device to make a bootdisk. Will take + * as input a elf executable, strip off the header + * and write out a boot image as: + * 1) default - strips elf header + * suitable as a network boot image + * 2) -pbp - strips elf header and writes out prep boot partition image + * cat or dd onto disk for booting + * 3) -asm - strips elf header and writes out as asm data + * useful for generating data for a compressed image + * -- Cort + * + * Modified for x86 hosted builds by Matt Porter + * Modified for Sparc hosted builds by Peter Wahl + */ + +#include +#include +#include +#include +#include +#include +#include + +#define cpu_to_le32(x) le32_to_cpu((x)) +unsigned long le32_to_cpu(unsigned long x) +{ + return (((x & 0x000000ffU) << 24) | + ((x & 0x0000ff00U) << 8) | + ((x & 0x00ff0000U) >> 8) | + ((x & 0xff000000U) >> 24)); +} + + +#define cpu_to_le16(x) le16_to_cpu((x)) +unsigned short le16_to_cpu(unsigned short x) +{ + return (((x & 0x00ff) << 8) | + ((x & 0xff00) >> 8)); +} + +#define cpu_to_be32(x) (x) +#define be32_to_cpu(x) (x) +#define cpu_to_be16(x) (x) +#define be16_to_cpu(x) (x) + +/* size of read buffer */ +#define SIZE 0x1000 + + +typedef unsigned long dword_t; +typedef unsigned short word_t; +typedef unsigned char byte_t; +typedef byte_t block_t[512]; +typedef byte_t page_t[4096]; + + +/* + * Partition table entry + * - from the PReP spec + */ +typedef struct partition_entry { + byte_t boot_indicator; + byte_t starting_head; + byte_t starting_sector; + byte_t starting_cylinder; + + byte_t system_indicator; + byte_t ending_head; + byte_t ending_sector; + byte_t ending_cylinder; + + dword_t beginning_sector; + dword_t number_of_sectors; +} partition_entry_t; + +#define BootActive 0x80 +#define SystemPrep 0x41 + +void copy_image(int , int); +void write_prep_partition(int , int ); +void write_asm_data( int in, int out ); + +unsigned int elfhdr_size = 65536; + +int main(int argc, char *argv[]) +{ + int in_fd, out_fd; + int argptr = 1; + unsigned int prep = 0; + unsigned int asmoutput = 0; + + if ( (argc < 3) || (argc > 4) ) + { + fprintf(stderr, "usage: %s [-pbp] [-asm] \n",argv[0]); + exit(-1); + } + + /* needs to handle args more elegantly -- but this is a small/simple program */ + + /* check for -pbp */ + if ( !strcmp( argv[argptr], "-pbp" ) ) + { + prep = 1; + argptr++; + } + + /* check for -asm */ + if ( !strcmp( argv[argptr], "-asm" ) ) + { + asmoutput = 1; + argptr++; + } + + /* input file */ + if ( !strcmp( argv[argptr], "-" ) ) + in_fd = 0; /* stdin */ + else + if ((in_fd = open( argv[argptr] , 0)) < 0) + exit(-1); + argptr++; + + /* output file */ + if ( !strcmp( argv[argptr], "-" ) ) + out_fd = 1; /* stdout */ + else + if ((out_fd = creat( argv[argptr] , 0755)) < 0) + exit(-1); + argptr++; + + /* skip elf header in input file */ + /*if ( !prep )*/ + lseek(in_fd, elfhdr_size, SEEK_SET); + + /* write prep partition if necessary */ + if ( prep ) + write_prep_partition( in_fd, out_fd ); + + /* write input image to bootimage */ + if ( asmoutput ) + write_asm_data( in_fd, out_fd ); + else + copy_image(in_fd, out_fd); + + return 0; +} + +void write_prep_partition(int in, int out) +{ + unsigned char block[512]; + partition_entry_t pe; + dword_t *entry = (dword_t *)&block[0]; + dword_t *length = (dword_t *)&block[sizeof(long)]; + struct stat info; + + if (fstat(in, &info) < 0) + { + fprintf(stderr,"info failed\n"); + exit(-1); + } + + bzero( block, sizeof block ); + + /* set entry point and boot image size skipping over elf header */ +#ifdef __i386__ + *entry = 0x400/*+65536*/; + *length = info.st_size-elfhdr_size+0x400; +#else + *entry = cpu_to_le32(0x400/*+65536*/); + *length = cpu_to_le32(info.st_size-elfhdr_size+0x400); +#endif /* __i386__ */ + + /* sets magic number for msdos partition (used by linux) */ + block[510] = 0x55; + block[511] = 0xAA; + + /* + * Build a "PReP" partition table entry in the boot record + * - "PReP" may only look at the system_indicator + */ + pe.boot_indicator = BootActive; + pe.system_indicator = SystemPrep; + /* + * The first block of the diskette is used by this "boot record" which + * actually contains the partition table. (The first block of the + * partition contains the boot image, but I digress...) We'll set up + * one partition on the diskette and it shall contain the rest of the + * diskette. + */ + pe.starting_head = 0; /* zero-based */ + pe.starting_sector = 2; /* one-based */ + pe.starting_cylinder = 0; /* zero-based */ + pe.ending_head = 1; /* assumes two heads */ + pe.ending_sector = 18; /* assumes 18 sectors/track */ + pe.ending_cylinder = 79; /* assumes 80 cylinders/diskette */ + + /* + * The "PReP" software ignores the above fields and just looks at + * the next two. + * - size of the diskette is (assumed to be) + * (2 tracks/cylinder)(18 sectors/tracks)(80 cylinders/diskette) + * - unlike the above sector numbers, the beginning sector is zero-based! + */ +#if 0 + pe.beginning_sector = cpu_to_le32(1); +#else + /* This has to be 0 on the PowerStack? */ +#ifdef __i386__ + pe.beginning_sector = 0; +#else + pe.beginning_sector = cpu_to_le32(0); +#endif /* __i386__ */ +#endif + +#ifdef __i386__ + pe.number_of_sectors = 2*18*80-1; +#else + pe.number_of_sectors = cpu_to_le32(2*18*80-1); +#endif /* __i386__ */ + + memcpy(&block[0x1BE], &pe, sizeof(pe)); + + write( out, block, sizeof(block) ); + write( out, entry, sizeof(*entry) ); + write( out, length, sizeof(*length) ); + /* set file position to 2nd sector where image will be written */ + lseek( out, 0x400, SEEK_SET ); +} + + + +void +copy_image(int in, int out) +{ + char buf[SIZE]; + int n; + + while ( (n = read(in, buf, SIZE)) > 0 ) + write(out, buf, n); +} + + +void +write_asm_data( int in, int out ) +{ + int i, cnt, pos, len; + unsigned int cksum, val; + unsigned char *lp; + unsigned char buf[SIZE]; + unsigned char str[256]; + + write( out, "\t.data\n\t.globl input_data\ninput_data:\n", + strlen( "\t.data\n\t.globl input_data\ninput_data:\n" ) ); + pos = 0; + cksum = 0; + while ((len = read(in, buf, sizeof(buf))) > 0) + { + cnt = 0; + lp = (unsigned char *)buf; + len = (len + 3) & ~3; /* Round up to longwords */ + for (i = 0; i < len; i += 4) + { + if (cnt == 0) + { + write( out, "\t.long\t", strlen( "\t.long\t" ) ); + } + sprintf( str, "0x%02X%02X%02X%02X", lp[0], lp[1], lp[2], lp[3]); + write( out, str, strlen(str) ); + val = *(unsigned long *)lp; + cksum ^= val; + lp += 4; + if (++cnt == 4) + { + cnt = 0; + sprintf( str, " # %x \n", pos+i-12); + write( out, str, strlen(str) ); + } else + { + write( out, ",", 1 ); + } + } + if (cnt) + { + write( out, "0\n", 2 ); + } + pos += len; + } + sprintf(str, "\t.globl input_len\ninput_len:\t.long\t0x%x\n", pos); + write( out, str, strlen(str) ); + + fprintf(stderr, "cksum = %x\n", cksum); +} diff --git a/arch/ppc/boot/utils/mktree.c b/arch/ppc/boot/utils/mktree.c new file mode 100644 index 00000000000..2be22e28f2b --- /dev/null +++ b/arch/ppc/boot/utils/mktree.c @@ -0,0 +1,152 @@ +/* + * Makes a tree bootable image for IBM Evaluation boards. + * Basically, just take a zImage, skip the ELF header, and stuff + * a 32 byte header on the front. + * + * We use htonl, which is a network macro, to make sure we're doing + * The Right Thing on an LE machine. It's non-obvious, but it should + * work on anything BSD'ish. + */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef __sun__ +#include +#else +#include +#endif + +/* This gets tacked on the front of the image. There are also a few + * bytes allocated after the _start label used by the boot rom (see + * head.S for details). + */ +typedef struct boot_block { + uint32_t bb_magic; /* 0x0052504F */ + uint32_t bb_dest; /* Target address of the image */ + uint32_t bb_num_512blocks; /* Size, rounded-up, in 512 byte blks */ + uint32_t bb_debug_flag; /* Run debugger or image after load */ + uint32_t bb_entry_point; /* The image address to start */ + uint32_t bb_checksum; /* 32 bit checksum including header */ + uint32_t reserved[2]; +} boot_block_t; + +#define IMGBLK 512 +char tmpbuf[IMGBLK]; + +int main(int argc, char *argv[]) +{ + int in_fd, out_fd; + int nblks, i; + uint cksum, *cp; + struct stat st; + boot_block_t bt; + + if (argc < 3) { + fprintf(stderr, "usage: %s [entry-point]\n",argv[0]); + exit(1); + } + + if (stat(argv[1], &st) < 0) { + perror("stat"); + exit(2); + } + + nblks = (st.st_size + IMGBLK) / IMGBLK; + + bt.bb_magic = htonl(0x0052504F); + + /* If we have the optional entry point parameter, use it */ + if (argc == 4) + bt.bb_dest = bt.bb_entry_point = htonl(strtoul(argv[3], NULL, 0)); + else + bt.bb_dest = bt.bb_entry_point = htonl(0x500000); + + /* We know these from the linker command. + * ...and then move it up into memory a little more so the + * relocation can happen. + */ + bt.bb_num_512blocks = htonl(nblks); + bt.bb_debug_flag = 0; + + bt.bb_checksum = 0; + + /* To be neat and tidy :-). + */ + bt.reserved[0] = 0; + bt.reserved[1] = 0; + + if ((in_fd = open(argv[1], O_RDONLY)) < 0) { + perror("zImage open"); + exit(3); + } + + if ((out_fd = open(argv[2], (O_RDWR | O_CREAT | O_TRUNC), 0666)) < 0) { + perror("bootfile open"); + exit(3); + } + + cksum = 0; + cp = (void *)&bt; + for (i=0; i 0) { + if (read(in_fd, tmpbuf, IMGBLK) < 0) { + perror("zImage read"); + exit(5); + } + cp = (uint *)tmpbuf; + for (i=0; i + * Copyright (c) 1998-1999 TiVo, Inc. + * PowerPC 403GCX modifications. + * Copyright (c) 1999 Grant Erickson + * PowerPC 403GCX/405GP modifications. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +struct aligninfo { + unsigned char len; + unsigned char flags; +}; + +#if defined(CONFIG_4xx) || defined(CONFIG_POWER4) || defined(CONFIG_BOOKE) +#define OPCD(inst) (((inst) & 0xFC000000) >> 26) +#define RS(inst) (((inst) & 0x03E00000) >> 21) +#define RA(inst) (((inst) & 0x001F0000) >> 16) +#define IS_XFORM(code) ((code) == 31) +#endif + +#define INVALID { 0, 0 } + +#define LD 1 /* load */ +#define ST 2 /* store */ +#define SE 4 /* sign-extend value */ +#define F 8 /* to/from fp regs */ +#define U 0x10 /* update index register */ +#define M 0x20 /* multiple load/store */ +#define S 0x40 /* single-precision fp, or byte-swap value */ +#define SX 0x40 /* byte count in XER */ +#define HARD 0x80 /* string, stwcx. */ + +#define DCBZ 0x5f /* 8xx/82xx dcbz faults when cache not enabled */ + +/* + * The PowerPC stores certain bits of the instruction that caused the + * alignment exception in the DSISR register. This array maps those + * bits to information about the operand length and what the + * instruction would do. + */ +static struct aligninfo aligninfo[128] = { + { 4, LD }, /* 00 0 0000: lwz / lwarx */ + INVALID, /* 00 0 0001 */ + { 4, ST }, /* 00 0 0010: stw */ + INVALID, /* 00 0 0011 */ + { 2, LD }, /* 00 0 0100: lhz */ + { 2, LD+SE }, /* 00 0 0101: lha */ + { 2, ST }, /* 00 0 0110: sth */ + { 4, LD+M }, /* 00 0 0111: lmw */ + { 4, LD+F+S }, /* 00 0 1000: lfs */ + { 8, LD+F }, /* 00 0 1001: lfd */ + { 4, ST+F+S }, /* 00 0 1010: stfs */ + { 8, ST+F }, /* 00 0 1011: stfd */ + INVALID, /* 00 0 1100 */ + INVALID, /* 00 0 1101: ld/ldu/lwa */ + INVALID, /* 00 0 1110 */ + INVALID, /* 00 0 1111: std/stdu */ + { 4, LD+U }, /* 00 1 0000: lwzu */ + INVALID, /* 00 1 0001 */ + { 4, ST+U }, /* 00 1 0010: stwu */ + INVALID, /* 00 1 0011 */ + { 2, LD+U }, /* 00 1 0100: lhzu */ + { 2, LD+SE+U }, /* 00 1 0101: lhau */ + { 2, ST+U }, /* 00 1 0110: sthu */ + { 4, ST+M }, /* 00 1 0111: stmw */ + { 4, LD+F+S+U }, /* 00 1 1000: lfsu */ + { 8, LD+F+U }, /* 00 1 1001: lfdu */ + { 4, ST+F+S+U }, /* 00 1 1010: stfsu */ + { 8, ST+F+U }, /* 00 1 1011: stfdu */ + INVALID, /* 00 1 1100 */ + INVALID, /* 00 1 1101 */ + INVALID, /* 00 1 1110 */ + INVALID, /* 00 1 1111 */ + INVALID, /* 01 0 0000: ldx */ + INVALID, /* 01 0 0001 */ + INVALID, /* 01 0 0010: stdx */ + INVALID, /* 01 0 0011 */ + INVALID, /* 01 0 0100 */ + INVALID, /* 01 0 0101: lwax */ + INVALID, /* 01 0 0110 */ + INVALID, /* 01 0 0111 */ + { 4, LD+M+HARD+SX }, /* 01 0 1000: lswx */ + { 4, LD+M+HARD }, /* 01 0 1001: lswi */ + { 4, ST+M+HARD+SX }, /* 01 0 1010: stswx */ + { 4, ST+M+HARD }, /* 01 0 1011: stswi */ + INVALID, /* 01 0 1100 */ + INVALID, /* 01 0 1101 */ + INVALID, /* 01 0 1110 */ + INVALID, /* 01 0 1111 */ + INVALID, /* 01 1 0000: ldux */ + INVALID, /* 01 1 0001 */ + INVALID, /* 01 1 0010: stdux */ + INVALID, /* 01 1 0011 */ + INVALID, /* 01 1 0100 */ + INVALID, /* 01 1 0101: lwaux */ + INVALID, /* 01 1 0110 */ + INVALID, /* 01 1 0111 */ + INVALID, /* 01 1 1000 */ + INVALID, /* 01 1 1001 */ + INVALID, /* 01 1 1010 */ + INVALID, /* 01 1 1011 */ + INVALID, /* 01 1 1100 */ + INVALID, /* 01 1 1101 */ + INVALID, /* 01 1 1110 */ + INVALID, /* 01 1 1111 */ + INVALID, /* 10 0 0000 */ + INVALID, /* 10 0 0001 */ + { 0, ST+HARD }, /* 10 0 0010: stwcx. */ + INVALID, /* 10 0 0011 */ + INVALID, /* 10 0 0100 */ + INVALID, /* 10 0 0101 */ + INVALID, /* 10 0 0110 */ + INVALID, /* 10 0 0111 */ + { 4, LD+S }, /* 10 0 1000: lwbrx */ + INVALID, /* 10 0 1001 */ + { 4, ST+S }, /* 10 0 1010: stwbrx */ + INVALID, /* 10 0 1011 */ + { 2, LD+S }, /* 10 0 1100: lhbrx */ + INVALID, /* 10 0 1101 */ + { 2, ST+S }, /* 10 0 1110: sthbrx */ + INVALID, /* 10 0 1111 */ + INVALID, /* 10 1 0000 */ + INVALID, /* 10 1 0001 */ + INVALID, /* 10 1 0010 */ + INVALID, /* 10 1 0011 */ + INVALID, /* 10 1 0100 */ + INVALID, /* 10 1 0101 */ + INVALID, /* 10 1 0110 */ + INVALID, /* 10 1 0111 */ + INVALID, /* 10 1 1000 */ + INVALID, /* 10 1 1001 */ + INVALID, /* 10 1 1010 */ + INVALID, /* 10 1 1011 */ + INVALID, /* 10 1 1100 */ + INVALID, /* 10 1 1101 */ + INVALID, /* 10 1 1110 */ + { 0, ST+HARD }, /* 10 1 1111: dcbz */ + { 4, LD }, /* 11 0 0000: lwzx */ + INVALID, /* 11 0 0001 */ + { 4, ST }, /* 11 0 0010: stwx */ + INVALID, /* 11 0 0011 */ + { 2, LD }, /* 11 0 0100: lhzx */ + { 2, LD+SE }, /* 11 0 0101: lhax */ + { 2, ST }, /* 11 0 0110: sthx */ + INVALID, /* 11 0 0111 */ + { 4, LD+F+S }, /* 11 0 1000: lfsx */ + { 8, LD+F }, /* 11 0 1001: lfdx */ + { 4, ST+F+S }, /* 11 0 1010: stfsx */ + { 8, ST+F }, /* 11 0 1011: stfdx */ + INVALID, /* 11 0 1100 */ + INVALID, /* 11 0 1101: lmd */ + INVALID, /* 11 0 1110 */ + INVALID, /* 11 0 1111: stmd */ + { 4, LD+U }, /* 11 1 0000: lwzux */ + INVALID, /* 11 1 0001 */ + { 4, ST+U }, /* 11 1 0010: stwux */ + INVALID, /* 11 1 0011 */ + { 2, LD+U }, /* 11 1 0100: lhzux */ + { 2, LD+SE+U }, /* 11 1 0101: lhaux */ + { 2, ST+U }, /* 11 1 0110: sthux */ + INVALID, /* 11 1 0111 */ + { 4, LD+F+S+U }, /* 11 1 1000: lfsux */ + { 8, LD+F+U }, /* 11 1 1001: lfdux */ + { 4, ST+F+S+U }, /* 11 1 1010: stfsux */ + { 8, ST+F+U }, /* 11 1 1011: stfdux */ + INVALID, /* 11 1 1100 */ + INVALID, /* 11 1 1101 */ + INVALID, /* 11 1 1110 */ + INVALID, /* 11 1 1111 */ +}; + +#define SWAP(a, b) (t = (a), (a) = (b), (b) = t) + +int +fix_alignment(struct pt_regs *regs) +{ + int instr, nb, flags; +#if defined(CONFIG_4xx) || defined(CONFIG_POWER4) || defined(CONFIG_BOOKE) + int opcode, f1, f2, f3; +#endif + int i, t; + int reg, areg; + int offset, nb0; + unsigned char __user *addr; + unsigned char *rptr; + union { + long l; + float f; + double d; + unsigned char v[8]; + } data; + + CHECK_FULL_REGS(regs); + +#if defined(CONFIG_4xx) || defined(CONFIG_POWER4) || defined(CONFIG_BOOKE) + /* The 4xx-family & Book-E processors have no DSISR register, + * so we emulate it. + * The POWER4 has a DSISR register but doesn't set it on + * an alignment fault. -- paulus + */ + + if (__get_user(instr, (unsigned int __user *) regs->nip)) + return 0; + opcode = OPCD(instr); + reg = RS(instr); + areg = RA(instr); + + if (!IS_XFORM(opcode)) { + f1 = 0; + f2 = (instr & 0x04000000) >> 26; + f3 = (instr & 0x78000000) >> 27; + } else { + f1 = (instr & 0x00000006) >> 1; + f2 = (instr & 0x00000040) >> 6; + f3 = (instr & 0x00000780) >> 7; + } + + instr = ((f1 << 5) | (f2 << 4) | f3); +#else + reg = (regs->dsisr >> 5) & 0x1f; /* source/dest register */ + areg = regs->dsisr & 0x1f; /* register to update */ + instr = (regs->dsisr >> 10) & 0x7f; +#endif + + nb = aligninfo[instr].len; + if (nb == 0) { + long __user *p; + int i; + + if (instr != DCBZ) + return 0; /* too hard or invalid instruction */ + /* + * The dcbz (data cache block zero) instruction + * gives an alignment fault if used on non-cacheable + * memory. We handle the fault mainly for the + * case when we are running with the cache disabled + * for debugging. + */ + p = (long __user *) (regs->dar & -L1_CACHE_BYTES); + if (user_mode(regs) + && !access_ok(VERIFY_WRITE, p, L1_CACHE_BYTES)) + return -EFAULT; + for (i = 0; i < L1_CACHE_BYTES / sizeof(long); ++i) + if (__put_user(0, p+i)) + return -EFAULT; + return 1; + } + + flags = aligninfo[instr].flags; + if ((flags & (LD|ST)) == 0) + return 0; + + /* For the 4xx-family & Book-E processors, the 'dar' field of the + * pt_regs structure is overloaded and is really from the DEAR. + */ + + addr = (unsigned char __user *)regs->dar; + + if (flags & M) { + /* lmw, stmw, lswi/x, stswi/x */ + nb0 = 0; + if (flags & HARD) { + if (flags & SX) { + nb = regs->xer & 127; + if (nb == 0) + return 1; + } else { + if (__get_user(instr, + (unsigned int __user *)regs->nip)) + return 0; + nb = (instr >> 11) & 0x1f; + if (nb == 0) + nb = 32; + } + if (nb + reg * 4 > 128) { + nb0 = nb + reg * 4 - 128; + nb = 128 - reg * 4; + } + } else { + /* lwm, stmw */ + nb = (32 - reg) * 4; + } + rptr = (unsigned char *) ®s->gpr[reg]; + if (flags & LD) { + for (i = 0; i < nb; ++i) + if (__get_user(rptr[i], addr+i)) + return -EFAULT; + if (nb0 > 0) { + rptr = (unsigned char *) ®s->gpr[0]; + addr += nb; + for (i = 0; i < nb0; ++i) + if (__get_user(rptr[i], addr+i)) + return -EFAULT; + } + for (; (i & 3) != 0; ++i) + rptr[i] = 0; + } else { + for (i = 0; i < nb; ++i) + if (__put_user(rptr[i], addr+i)) + return -EFAULT; + if (nb0 > 0) { + rptr = (unsigned char *) ®s->gpr[0]; + addr += nb; + for (i = 0; i < nb0; ++i) + if (__put_user(rptr[i], addr+i)) + return -EFAULT; + } + } + return 1; + } + + offset = 0; + if (nb < 4) { + /* read/write the least significant bits */ + data.l = 0; + offset = 4 - nb; + } + + /* Verify the address of the operand */ + if (user_mode(regs)) { + if (!access_ok((flags & ST? VERIFY_WRITE: VERIFY_READ), addr, nb)) + return -EFAULT; /* bad address */ + } + + if (flags & F) { + preempt_disable(); + if (regs->msr & MSR_FP) + giveup_fpu(current); + preempt_enable(); + } + + /* If we read the operand, copy it in, else get register values */ + if (flags & LD) { + for (i = 0; i < nb; ++i) + if (__get_user(data.v[offset+i], addr+i)) + return -EFAULT; + } else if (flags & F) { + data.d = current->thread.fpr[reg]; + } else { + data.l = regs->gpr[reg]; + } + + switch (flags & ~U) { + case LD+SE: /* sign extend */ + if (data.v[2] >= 0x80) + data.v[0] = data.v[1] = -1; + break; + + case LD+S: /* byte-swap */ + case ST+S: + if (nb == 2) { + SWAP(data.v[2], data.v[3]); + } else { + SWAP(data.v[0], data.v[3]); + SWAP(data.v[1], data.v[2]); + } + break; + + /* Single-precision FP load and store require conversions... */ + case LD+F+S: + preempt_disable(); + enable_kernel_fp(); + cvt_fd(&data.f, &data.d, ¤t->thread.fpscr); + preempt_enable(); + break; + case ST+F+S: + preempt_disable(); + enable_kernel_fp(); + cvt_df(&data.d, &data.f, ¤t->thread.fpscr); + preempt_enable(); + break; + } + + if (flags & ST) { + for (i = 0; i < nb; ++i) + if (__put_user(data.v[offset+i], addr+i)) + return -EFAULT; + } else if (flags & F) { + current->thread.fpr[reg] = data.d; + } else { + regs->gpr[reg] = data.l; + } + + if (flags & U) + regs->gpr[areg] = regs->dar; + + return 1; +} diff --git a/arch/ppc/kernel/asm-offsets.c b/arch/ppc/kernel/asm-offsets.c new file mode 100644 index 00000000000..d9ad1d776d0 --- /dev/null +++ b/arch/ppc/kernel/asm-offsets.c @@ -0,0 +1,146 @@ +/* + * This program is used to generate definitions needed by + * assembly language modules. + * + * We use the technique used in the OSF Mach kernel code: + * generate asm statements containing #defines, + * compile this file to assembler, and then extract the + * #defines from the assembly-language output. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEFINE(sym, val) \ + asm volatile("\n->" #sym " %0 " #val : : "i" (val)) + +#define BLANK() asm volatile("\n->" : : ) + +int +main(void) +{ + DEFINE(THREAD, offsetof(struct task_struct, thread)); + DEFINE(THREAD_INFO, offsetof(struct task_struct, thread_info)); + DEFINE(MM, offsetof(struct task_struct, mm)); + DEFINE(PTRACE, offsetof(struct task_struct, ptrace)); + DEFINE(KSP, offsetof(struct thread_struct, ksp)); + DEFINE(PGDIR, offsetof(struct thread_struct, pgdir)); + DEFINE(LAST_SYSCALL, offsetof(struct thread_struct, last_syscall)); + DEFINE(PT_REGS, offsetof(struct thread_struct, regs)); + DEFINE(THREAD_FPEXC_MODE, offsetof(struct thread_struct, fpexc_mode)); + DEFINE(THREAD_FPR0, offsetof(struct thread_struct, fpr[0])); + DEFINE(THREAD_FPSCR, offsetof(struct thread_struct, fpscr)); +#if defined(CONFIG_4xx) || defined(CONFIG_BOOKE) + DEFINE(THREAD_DBCR0, offsetof(struct thread_struct, dbcr0)); + DEFINE(PT_PTRACED, PT_PTRACED); +#endif +#ifdef CONFIG_ALTIVEC + DEFINE(THREAD_VR0, offsetof(struct thread_struct, vr[0])); + DEFINE(THREAD_VRSAVE, offsetof(struct thread_struct, vrsave)); + DEFINE(THREAD_VSCR, offsetof(struct thread_struct, vscr)); + DEFINE(THREAD_USED_VR, offsetof(struct thread_struct, used_vr)); +#endif /* CONFIG_ALTIVEC */ +#ifdef CONFIG_SPE + DEFINE(THREAD_EVR0, offsetof(struct thread_struct, evr[0])); + DEFINE(THREAD_ACC, offsetof(struct thread_struct, acc)); + DEFINE(THREAD_SPEFSCR, offsetof(struct thread_struct, spefscr)); + DEFINE(THREAD_USED_SPE, offsetof(struct thread_struct, used_spe)); +#endif /* CONFIG_SPE */ + /* Interrupt register frame */ + DEFINE(STACK_FRAME_OVERHEAD, STACK_FRAME_OVERHEAD); + DEFINE(INT_FRAME_SIZE, STACK_FRAME_OVERHEAD + sizeof(struct pt_regs)); + /* in fact we only use gpr0 - gpr9 and gpr20 - gpr23 */ + DEFINE(GPR0, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[0])); + DEFINE(GPR1, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[1])); + DEFINE(GPR2, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[2])); + DEFINE(GPR3, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[3])); + DEFINE(GPR4, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[4])); + DEFINE(GPR5, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[5])); + DEFINE(GPR6, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[6])); + DEFINE(GPR7, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[7])); + DEFINE(GPR8, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[8])); + DEFINE(GPR9, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[9])); + DEFINE(GPR10, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[10])); + DEFINE(GPR11, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[11])); + DEFINE(GPR12, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[12])); + DEFINE(GPR13, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[13])); + DEFINE(GPR14, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[14])); + DEFINE(GPR15, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[15])); + DEFINE(GPR16, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[16])); + DEFINE(GPR17, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[17])); + DEFINE(GPR18, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[18])); + DEFINE(GPR19, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[19])); + DEFINE(GPR20, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[20])); + DEFINE(GPR21, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[21])); + DEFINE(GPR22, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[22])); + DEFINE(GPR23, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[23])); + DEFINE(GPR24, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[24])); + DEFINE(GPR25, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[25])); + DEFINE(GPR26, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[26])); + DEFINE(GPR27, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[27])); + DEFINE(GPR28, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[28])); + DEFINE(GPR29, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[29])); + DEFINE(GPR30, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[30])); + DEFINE(GPR31, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[31])); + /* Note: these symbols include _ because they overlap with special + * register names + */ + DEFINE(_NIP, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, nip)); + DEFINE(_MSR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, msr)); + DEFINE(_CTR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, ctr)); + DEFINE(_LINK, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, link)); + DEFINE(_CCR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, ccr)); + DEFINE(_MQ, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, mq)); + DEFINE(_XER, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, xer)); + DEFINE(_DAR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, dar)); + DEFINE(_DSISR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, dsisr)); + /* The PowerPC 400-class & Book-E processors have neither the DAR nor the DSISR + * SPRs. Hence, we overload them to hold the similar DEAR and ESR SPRs + * for such processors. For critical interrupts we use them to + * hold SRR0 and SRR1. + */ + DEFINE(_DEAR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, dar)); + DEFINE(_ESR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, dsisr)); + DEFINE(ORIG_GPR3, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, orig_gpr3)); + DEFINE(RESULT, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, result)); + DEFINE(TRAP, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, trap)); + DEFINE(CLONE_VM, CLONE_VM); + DEFINE(CLONE_UNTRACED, CLONE_UNTRACED); + DEFINE(MM_PGD, offsetof(struct mm_struct, pgd)); + + /* About the CPU features table */ + DEFINE(CPU_SPEC_ENTRY_SIZE, sizeof(struct cpu_spec)); + DEFINE(CPU_SPEC_PVR_MASK, offsetof(struct cpu_spec, pvr_mask)); + DEFINE(CPU_SPEC_PVR_VALUE, offsetof(struct cpu_spec, pvr_value)); + DEFINE(CPU_SPEC_FEATURES, offsetof(struct cpu_spec, cpu_features)); + DEFINE(CPU_SPEC_SETUP, offsetof(struct cpu_spec, cpu_setup)); + + DEFINE(TI_TASK, offsetof(struct thread_info, task)); + DEFINE(TI_EXECDOMAIN, offsetof(struct thread_info, exec_domain)); + DEFINE(TI_FLAGS, offsetof(struct thread_info, flags)); + DEFINE(TI_LOCAL_FLAGS, offsetof(struct thread_info, local_flags)); + DEFINE(TI_CPU, offsetof(struct thread_info, cpu)); + DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count)); + + DEFINE(pbe_address, offsetof(struct pbe, address)); + DEFINE(pbe_orig_address, offsetof(struct pbe, orig_address)); + DEFINE(pbe_next, offsetof(struct pbe, next)); + + DEFINE(NUM_USER_SEGMENTS, TASK_SIZE>>28); + return 0; +} diff --git a/arch/ppc/kernel/bitops.c b/arch/ppc/kernel/bitops.c new file mode 100644 index 00000000000..7f53d193968 --- /dev/null +++ b/arch/ppc/kernel/bitops.c @@ -0,0 +1,126 @@ +/* + * Copyright (C) 1996 Paul Mackerras. + */ + +#include +#include + +/* + * If the bitops are not inlined in bitops.h, they are defined here. + * -- paulus + */ +#if !__INLINE_BITOPS +void set_bit(int nr, volatile void * addr) +{ + unsigned long old; + unsigned long mask = 1 << (nr & 0x1f); + unsigned long *p = ((unsigned long *)addr) + (nr >> 5); + + __asm__ __volatile__(SMP_WMB "\n\ +1: lwarx %0,0,%3 \n\ + or %0,%0,%2 \n" + PPC405_ERR77(0,%3) +" stwcx. %0,0,%3 \n\ + bne 1b" + SMP_MB + : "=&r" (old), "=m" (*p) + : "r" (mask), "r" (p), "m" (*p) + : "cc" ); +} + +void clear_bit(int nr, volatile void *addr) +{ + unsigned long old; + unsigned long mask = 1 << (nr & 0x1f); + unsigned long *p = ((unsigned long *)addr) + (nr >> 5); + + __asm__ __volatile__(SMP_WMB "\n\ +1: lwarx %0,0,%3 \n\ + andc %0,%0,%2 \n" + PPC405_ERR77(0,%3) +" stwcx. %0,0,%3 \n\ + bne 1b" + SMP_MB + : "=&r" (old), "=m" (*p) + : "r" (mask), "r" (p), "m" (*p) + : "cc"); +} + +void change_bit(int nr, volatile void *addr) +{ + unsigned long old; + unsigned long mask = 1 << (nr & 0x1f); + unsigned long *p = ((unsigned long *)addr) + (nr >> 5); + + __asm__ __volatile__(SMP_WMB "\n\ +1: lwarx %0,0,%3 \n\ + xor %0,%0,%2 \n" + PPC405_ERR77(0,%3) +" stwcx. %0,0,%3 \n\ + bne 1b" + SMP_MB + : "=&r" (old), "=m" (*p) + : "r" (mask), "r" (p), "m" (*p) + : "cc"); +} + +int test_and_set_bit(int nr, volatile void *addr) +{ + unsigned int old, t; + unsigned int mask = 1 << (nr & 0x1f); + volatile unsigned int *p = ((volatile unsigned int *)addr) + (nr >> 5); + + __asm__ __volatile__(SMP_WMB "\n\ +1: lwarx %0,0,%4 \n\ + or %1,%0,%3 \n" + PPC405_ERR77(0,%4) +" stwcx. %1,0,%4 \n\ + bne 1b" + SMP_MB + : "=&r" (old), "=&r" (t), "=m" (*p) + : "r" (mask), "r" (p), "m" (*p) + : "cc"); + + return (old & mask) != 0; +} + +int test_and_clear_bit(int nr, volatile void *addr) +{ + unsigned int old, t; + unsigned int mask = 1 << (nr & 0x1f); + volatile unsigned int *p = ((volatile unsigned int *)addr) + (nr >> 5); + + __asm__ __volatile__(SMP_WMB "\n\ +1: lwarx %0,0,%4 \n\ + andc %1,%0,%3 \n" + PPC405_ERR77(0,%4) +" stwcx. %1,0,%4 \n\ + bne 1b" + SMP_MB + : "=&r" (old), "=&r" (t), "=m" (*p) + : "r" (mask), "r" (p), "m" (*p) + : "cc"); + + return (old & mask) != 0; +} + +int test_and_change_bit(int nr, volatile void *addr) +{ + unsigned int old, t; + unsigned int mask = 1 << (nr & 0x1f); + volatile unsigned int *p = ((volatile unsigned int *)addr) + (nr >> 5); + + __asm__ __volatile__(SMP_WMB "\n\ +1: lwarx %0,0,%4 \n\ + xor %1,%0,%3 \n" + PPC405_ERR77(0,%4) +" stwcx. %1,0,%4 \n\ + bne 1b" + SMP_MB + : "=&r" (old), "=&r" (t), "=m" (*p) + : "r" (mask), "r" (p), "m" (*p) + : "cc"); + + return (old & mask) != 0; +} +#endif /* !__INLINE_BITOPS */ diff --git a/arch/ppc/kernel/cpu_setup_6xx.S b/arch/ppc/kernel/cpu_setup_6xx.S new file mode 100644 index 00000000000..74f781b486a --- /dev/null +++ b/arch/ppc/kernel/cpu_setup_6xx.S @@ -0,0 +1,440 @@ +/* + * This file contains low level CPU setup functions. + * Copyright (C) 2003 Benjamin Herrenschmidt (benh@kernel.crashing.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +_GLOBAL(__setup_cpu_601) + blr +_GLOBAL(__setup_cpu_603) + b setup_common_caches +_GLOBAL(__setup_cpu_604) + mflr r4 + bl setup_common_caches + bl setup_604_hid0 + mtlr r4 + blr +_GLOBAL(__setup_cpu_750) + mflr r4 + bl setup_common_caches + bl setup_750_7400_hid0 + mtlr r4 + blr +_GLOBAL(__setup_cpu_750cx) + mflr r4 + bl setup_common_caches + bl setup_750_7400_hid0 + bl setup_750cx + mtlr r4 + blr +_GLOBAL(__setup_cpu_750fx) + mflr r4 + bl setup_common_caches + bl setup_750_7400_hid0 + bl setup_750fx + mtlr r4 + blr +_GLOBAL(__setup_cpu_7400) + mflr r4 + bl setup_7400_workarounds + bl setup_common_caches + bl setup_750_7400_hid0 + mtlr r4 + blr +_GLOBAL(__setup_cpu_7410) + mflr r4 + bl setup_7410_workarounds + bl setup_common_caches + bl setup_750_7400_hid0 + li r3,0 + mtspr SPRN_L2CR2,r3 + mtlr r4 + blr +_GLOBAL(__setup_cpu_745x) + mflr r4 + bl setup_common_caches + bl setup_745x_specifics + mtlr r4 + blr + +/* Enable caches for 603's, 604, 750 & 7400 */ +setup_common_caches: + mfspr r11,SPRN_HID0 + andi. r0,r11,HID0_DCE + ori r11,r11,HID0_ICE|HID0_DCE + ori r8,r11,HID0_ICFI + bne 1f /* don't invalidate the D-cache */ + ori r8,r8,HID0_DCI /* unless it wasn't enabled */ +1: sync + mtspr SPRN_HID0,r8 /* enable and invalidate caches */ + sync + mtspr SPRN_HID0,r11 /* enable caches */ + sync + isync + blr + +/* 604, 604e, 604ev, ... + * Enable superscalar execution & branch history table + */ +setup_604_hid0: + mfspr r11,SPRN_HID0 + ori r11,r11,HID0_SIED|HID0_BHTE + ori r8,r11,HID0_BTCD + sync + mtspr SPRN_HID0,r8 /* flush branch target address cache */ + sync /* on 604e/604r */ + mtspr SPRN_HID0,r11 + sync + isync + blr + +/* 7400 <= rev 2.7 and 7410 rev = 1.0 suffer from some + * erratas we work around here. + * Moto MPC710CE.pdf describes them, those are errata + * #3, #4 and #5 + * Note that we assume the firmware didn't choose to + * apply other workarounds (there are other ones documented + * in the .pdf). It appear that Apple firmware only works + * around #3 and with the same fix we use. We may want to + * check if the CPU is using 60x bus mode in which case + * the workaround for errata #4 is useless. Also, we may + * want to explicitely clear HID0_NOPDST as this is not + * needed once we have applied workaround #5 (though it's + * not set by Apple's firmware at least). + */ +setup_7400_workarounds: + mfpvr r3 + rlwinm r3,r3,0,20,31 + cmpwi 0,r3,0x0207 + ble 1f + blr +setup_7410_workarounds: + mfpvr r3 + rlwinm r3,r3,0,20,31 + cmpwi 0,r3,0x0100 + bnelr +1: + mfspr r11,SPRN_MSSSR0 + /* Errata #3: Set L1OPQ_SIZE to 0x10 */ + rlwinm r11,r11,0,9,6 + oris r11,r11,0x0100 + /* Errata #4: Set L2MQ_SIZE to 1 (check for MPX mode first ?) */ + oris r11,r11,0x0002 + /* Errata #5: Set DRLT_SIZE to 0x01 */ + rlwinm r11,r11,0,5,2 + oris r11,r11,0x0800 + sync + mtspr SPRN_MSSSR0,r11 + sync + isync + blr + +/* 740/750/7400/7410 + * Enable Store Gathering (SGE), Address Brodcast (ABE), + * Branch History Table (BHTE), Branch Target ICache (BTIC) + * Dynamic Power Management (DPM), Speculative (SPD) + * Clear Instruction cache throttling (ICTC) + */ +setup_750_7400_hid0: + mfspr r11,SPRN_HID0 + ori r11,r11,HID0_SGE | HID0_ABE | HID0_BHTE | HID0_BTIC +BEGIN_FTR_SECTION + oris r11,r11,HID0_DPM@h /* enable dynamic power mgmt */ +END_FTR_SECTION_IFCLR(CPU_FTR_NO_DPM) + li r3,HID0_SPD + andc r11,r11,r3 /* clear SPD: enable speculative */ + li r3,0 + mtspr SPRN_ICTC,r3 /* Instruction Cache Throttling off */ + isync + mtspr SPRN_HID0,r11 + sync + isync + blr + +/* 750cx specific + * Looks like we have to disable NAP feature for some PLL settings... + * (waiting for confirmation) + */ +setup_750cx: + mfspr r10, SPRN_HID1 + rlwinm r10,r10,4,28,31 + cmpwi cr0,r10,7 + cmpwi cr1,r10,9 + cmpwi cr2,r10,11 + cror 4*cr0+eq,4*cr0+eq,4*cr1+eq + cror 4*cr0+eq,4*cr0+eq,4*cr2+eq + bnelr + lwz r6,CPU_SPEC_FEATURES(r5) + li r7,CPU_FTR_CAN_NAP + andc r6,r6,r7 + stw r6,CPU_SPEC_FEATURES(r5) + blr + +/* 750fx specific + */ +setup_750fx: + blr + +/* MPC 745x + * Enable Store Gathering (SGE), Branch Folding (FOLD) + * Branch History Table (BHTE), Branch Target ICache (BTIC) + * Dynamic Power Management (DPM), Speculative (SPD) + * Ensure our data cache instructions really operate. + * Timebase has to be running or we wouldn't have made it here, + * just ensure we don't disable it. + * Clear Instruction cache throttling (ICTC) + * Enable L2 HW prefetch + */ +setup_745x_specifics: + /* We check for the presence of an L3 cache setup by + * the firmware. If any, we disable NAP capability as + * it's known to be bogus on rev 2.1 and earlier + */ + mfspr r11,SPRN_L3CR + andis. r11,r11,L3CR_L3E@h + beq 1f + lwz r6,CPU_SPEC_FEATURES(r5) + andi. r0,r6,CPU_FTR_L3_DISABLE_NAP + beq 1f + li r7,CPU_FTR_CAN_NAP + andc r6,r6,r7 + stw r6,CPU_SPEC_FEATURES(r5) +1: + mfspr r11,SPRN_HID0 + + /* All of the bits we have to set..... + */ + ori r11,r11,HID0_SGE | HID0_FOLD | HID0_BHTE | HID0_LRSTK | HID0_BTIC +BEGIN_FTR_SECTION + xori r11,r11,HID0_BTIC +END_FTR_SECTION_IFSET(CPU_FTR_NO_BTIC) +BEGIN_FTR_SECTION + oris r11,r11,HID0_DPM@h /* enable dynamic power mgmt */ +END_FTR_SECTION_IFCLR(CPU_FTR_NO_DPM) + + /* All of the bits we have to clear.... + */ + li r3,HID0_SPD | HID0_NOPDST | HID0_NOPTI + andc r11,r11,r3 /* clear SPD: enable speculative */ + li r3,0 + + mtspr SPRN_ICTC,r3 /* Instruction Cache Throttling off */ + isync + mtspr SPRN_HID0,r11 + sync + isync + + /* Enable L2 HW prefetch + */ + mfspr r3,SPRN_MSSCR0 + ori r3,r3,3 + sync + mtspr SPRN_MSSCR0,r3 + sync + isync + blr + +/* Definitions for the table use to save CPU states */ +#define CS_HID0 0 +#define CS_HID1 4 +#define CS_HID2 8 +#define CS_MSSCR0 12 +#define CS_MSSSR0 16 +#define CS_ICTRL 20 +#define CS_LDSTCR 24 +#define CS_LDSTDB 28 +#define CS_SIZE 32 + + .data + .balign L1_CACHE_LINE_SIZE +cpu_state_storage: + .space CS_SIZE + .balign L1_CACHE_LINE_SIZE,0 + .text + +/* Called in normal context to backup CPU 0 state. This + * does not include cache settings. This function is also + * called for machine sleep. This does not include the MMU + * setup, BATs, etc... but rather the "special" registers + * like HID0, HID1, MSSCR0, etc... + */ +_GLOBAL(__save_cpu_setup) + /* Some CR fields are volatile, we back it up all */ + mfcr r7 + + /* Get storage ptr */ + lis r5,cpu_state_storage@h + ori r5,r5,cpu_state_storage@l + + /* Save HID0 (common to all CONFIG_6xx cpus) */ + mfspr r3,SPRN_HID0 + stw r3,CS_HID0(r5) + + /* Now deal with CPU type dependent registers */ + mfspr r3,SPRN_PVR + srwi r3,r3,16 + cmplwi cr0,r3,0x8000 /* 7450 */ + cmplwi cr1,r3,0x000c /* 7400 */ + cmplwi cr2,r3,0x800c /* 7410 */ + cmplwi cr3,r3,0x8001 /* 7455 */ + cmplwi cr4,r3,0x8002 /* 7457 */ + cmplwi cr5,r3,0x8003 /* 7447A */ + cmplwi cr6,r3,0x7000 /* 750FX */ + /* cr1 is 7400 || 7410 */ + cror 4*cr1+eq,4*cr1+eq,4*cr2+eq + /* cr0 is 74xx */ + cror 4*cr0+eq,4*cr0+eq,4*cr3+eq + cror 4*cr0+eq,4*cr0+eq,4*cr4+eq + cror 4*cr0+eq,4*cr0+eq,4*cr1+eq + cror 4*cr0+eq,4*cr0+eq,4*cr5+eq + bne 1f + /* Backup 74xx specific regs */ + mfspr r4,SPRN_MSSCR0 + stw r4,CS_MSSCR0(r5) + mfspr r4,SPRN_MSSSR0 + stw r4,CS_MSSSR0(r5) + beq cr1,1f + /* Backup 745x specific registers */ + mfspr r4,SPRN_HID1 + stw r4,CS_HID1(r5) + mfspr r4,SPRN_ICTRL + stw r4,CS_ICTRL(r5) + mfspr r4,SPRN_LDSTCR + stw r4,CS_LDSTCR(r5) + mfspr r4,SPRN_LDSTDB + stw r4,CS_LDSTDB(r5) +1: + bne cr6,1f + /* Backup 750FX specific registers */ + mfspr r4,SPRN_HID1 + stw r4,CS_HID1(r5) + /* If rev 2.x, backup HID2 */ + mfspr r3,SPRN_PVR + andi. r3,r3,0xff00 + cmpwi cr0,r3,0x0200 + bne 1f + mfspr r4,SPRN_HID2 + stw r4,CS_HID2(r5) +1: + mtcr r7 + blr + +/* Called with no MMU context (typically MSR:IR/DR off) to + * restore CPU state as backed up by the previous + * function. This does not include cache setting + */ +_GLOBAL(__restore_cpu_setup) + /* Some CR fields are volatile, we back it up all */ + mfcr r7 + + /* Get storage ptr */ + lis r5,(cpu_state_storage-KERNELBASE)@h + ori r5,r5,cpu_state_storage@l + + /* Restore HID0 */ + lwz r3,CS_HID0(r5) + sync + isync + mtspr SPRN_HID0,r3 + sync + isync + + /* Now deal with CPU type dependent registers */ + mfspr r3,SPRN_PVR + srwi r3,r3,16 + cmplwi cr0,r3,0x8000 /* 7450 */ + cmplwi cr1,r3,0x000c /* 7400 */ + cmplwi cr2,r3,0x800c /* 7410 */ + cmplwi cr3,r3,0x8001 /* 7455 */ + cmplwi cr4,r3,0x8002 /* 7457 */ + cmplwi cr5,r3,0x8003 /* 7447A */ + cmplwi cr6,r3,0x7000 /* 750FX */ + /* cr1 is 7400 || 7410 */ + cror 4*cr1+eq,4*cr1+eq,4*cr2+eq + /* cr0 is 74xx */ + cror 4*cr0+eq,4*cr0+eq,4*cr3+eq + cror 4*cr0+eq,4*cr0+eq,4*cr4+eq + cror 4*cr0+eq,4*cr0+eq,4*cr1+eq + cror 4*cr0+eq,4*cr0+eq,4*cr5+eq + bne 2f + /* Restore 74xx specific regs */ + lwz r4,CS_MSSCR0(r5) + sync + mtspr SPRN_MSSCR0,r4 + sync + isync + lwz r4,CS_MSSSR0(r5) + sync + mtspr SPRN_MSSSR0,r4 + sync + isync + bne cr2,1f + /* Clear 7410 L2CR2 */ + li r4,0 + mtspr SPRN_L2CR2,r4 +1: beq cr1,2f + /* Restore 745x specific registers */ + lwz r4,CS_HID1(r5) + sync + mtspr SPRN_HID1,r4 + isync + sync + lwz r4,CS_ICTRL(r5) + sync + mtspr SPRN_ICTRL,r4 + isync + sync + lwz r4,CS_LDSTCR(r5) + sync + mtspr SPRN_LDSTCR,r4 + isync + sync + lwz r4,CS_LDSTDB(r5) + sync + mtspr SPRN_LDSTDB,r4 + isync + sync +2: bne cr6,1f + /* Restore 750FX specific registers + * that is restore HID2 on rev 2.x and PLL config & switch + * to PLL 0 on all + */ + /* If rev 2.x, restore HID2 with low voltage bit cleared */ + mfspr r3,SPRN_PVR + andi. r3,r3,0xff00 + cmpwi cr0,r3,0x0200 + bne 4f + lwz r4,CS_HID2(r5) + rlwinm r4,r4,0,19,17 + mtspr SPRN_HID2,r4 + sync +4: + lwz r4,CS_HID1(r5) + rlwinm r5,r4,0,16,14 + mtspr SPRN_HID1,r5 + /* Wait for PLL to stabilize */ + mftbl r5 +3: mftbl r6 + sub r6,r6,r5 + cmplwi cr0,r6,10000 + ble 3b + /* Setup final PLL */ + mtspr SPRN_HID1,r4 +1: + mtcr r7 + blr + diff --git a/arch/ppc/kernel/cpu_setup_power4.S b/arch/ppc/kernel/cpu_setup_power4.S new file mode 100644 index 00000000000..f2ea1a990f1 --- /dev/null +++ b/arch/ppc/kernel/cpu_setup_power4.S @@ -0,0 +1,201 @@ +/* + * This file contains low level CPU setup functions. + * Copyright (C) 2003 Benjamin Herrenschmidt (benh@kernel.crashing.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +_GLOBAL(__970_cpu_preinit) + /* + * Deal only with PPC970 and PPC970FX. + */ + mfspr r0,SPRN_PVR + srwi r0,r0,16 + cmpwi cr0,r0,0x39 + cmpwi cr1,r0,0x3c + cror 4*cr0+eq,4*cr0+eq,4*cr1+eq + bnelr + + /* Make sure HID4:rm_ci is off before MMU is turned off, that large + * pages are enabled with HID4:61 and clear HID5:DCBZ_size and + * HID5:DCBZ32_ill + */ + li r0,0 + mfspr r11,SPRN_HID4 + rldimi r11,r0,40,23 /* clear bit 23 (rm_ci) */ + rldimi r11,r0,2,61 /* clear bit 61 (lg_pg_en) */ + sync + mtspr SPRN_HID4,r11 + isync + sync + mfspr r11,SPRN_HID5 + rldimi r11,r0,6,56 /* clear bits 56 & 57 (DCBZ*) */ + sync + mtspr SPRN_HID5,r11 + isync + sync + + /* Setup some basic HID1 features */ + mfspr r0,SPRN_HID1 + li r11,0x1200 /* enable i-fetch cacheability */ + sldi r11,r11,44 /* and prefetch */ + or r0,r0,r11 + mtspr SPRN_HID1,r0 + mtspr SPRN_HID1,r0 + isync + + /* Clear HIOR */ + li r0,0 + sync + mtspr SPRN_HIOR,0 /* Clear interrupt prefix */ + isync + blr + +_GLOBAL(__setup_cpu_power4) + blr +_GLOBAL(__setup_cpu_ppc970) + mfspr r0,SPRN_HID0 + li r11,5 /* clear DOZE and SLEEP */ + rldimi r0,r11,52,8 /* set NAP and DPM */ + mtspr SPRN_HID0,r0 + mfspr r0,SPRN_HID0 + mfspr r0,SPRN_HID0 + mfspr r0,SPRN_HID0 + mfspr r0,SPRN_HID0 + mfspr r0,SPRN_HID0 + mfspr r0,SPRN_HID0 + sync + isync + blr + +/* Definitions for the table use to save CPU states */ +#define CS_HID0 0 +#define CS_HID1 8 +#define CS_HID4 16 +#define CS_HID5 24 +#define CS_SIZE 32 + + .data + .balign L1_CACHE_LINE_SIZE +cpu_state_storage: + .space CS_SIZE + .balign L1_CACHE_LINE_SIZE,0 + .text + +/* Called in normal context to backup CPU 0 state. This + * does not include cache settings. This function is also + * called for machine sleep. This does not include the MMU + * setup, BATs, etc... but rather the "special" registers + * like HID0, HID1, HID4, etc... + */ +_GLOBAL(__save_cpu_setup) + /* Some CR fields are volatile, we back it up all */ + mfcr r7 + + /* Get storage ptr */ + lis r5,cpu_state_storage@h + ori r5,r5,cpu_state_storage@l + + /* We only deal with 970 for now */ + mfspr r0,SPRN_PVR + srwi r0,r0,16 + cmpwi cr0,r0,0x39 + cmpwi cr1,r0,0x3c + cror 4*cr0+eq,4*cr0+eq,4*cr1+eq + bne 1f + + /* Save HID0,1,4 and 5 */ + mfspr r3,SPRN_HID0 + std r3,CS_HID0(r5) + mfspr r3,SPRN_HID1 + std r3,CS_HID1(r5) + mfspr r3,SPRN_HID4 + std r3,CS_HID4(r5) + mfspr r3,SPRN_HID5 + std r3,CS_HID5(r5) + +1: + mtcr r7 + blr + +/* Called with no MMU context (typically MSR:IR/DR off) to + * restore CPU state as backed up by the previous + * function. This does not include cache setting + */ +_GLOBAL(__restore_cpu_setup) + /* Some CR fields are volatile, we back it up all */ + mfcr r7 + + /* Get storage ptr */ + lis r5,(cpu_state_storage-KERNELBASE)@h + ori r5,r5,cpu_state_storage@l + + /* We only deal with 970 for now */ + mfspr r0,SPRN_PVR + srwi r0,r0,16 + cmpwi cr0,r0,0x39 + cmpwi cr1,r0,0x3c + cror 4*cr0+eq,4*cr0+eq,4*cr1+eq + bne 1f + + /* Clear interrupt prefix */ + li r0,0 + sync + mtspr SPRN_HIOR,0 + isync + + /* Restore HID0 */ + ld r3,CS_HID0(r5) + sync + isync + mtspr SPRN_HID0,r3 + mfspr r3,SPRN_HID0 + mfspr r3,SPRN_HID0 + mfspr r3,SPRN_HID0 + mfspr r3,SPRN_HID0 + mfspr r3,SPRN_HID0 + mfspr r3,SPRN_HID0 + sync + isync + + /* Restore HID1 */ + ld r3,CS_HID1(r5) + sync + isync + mtspr SPRN_HID1,r3 + mtspr SPRN_HID1,r3 + sync + isync + + /* Restore HID4 */ + ld r3,CS_HID4(r5) + sync + isync + mtspr SPRN_HID4,r3 + sync + isync + + /* Restore HID5 */ + ld r3,CS_HID5(r5) + sync + isync + mtspr SPRN_HID5,r3 + sync + isync +1: + mtcr r7 + blr + diff --git a/arch/ppc/kernel/cputable.c b/arch/ppc/kernel/cputable.c new file mode 100644 index 00000000000..8aa5e8c6900 --- /dev/null +++ b/arch/ppc/kernel/cputable.c @@ -0,0 +1,922 @@ +/* + * arch/ppc/kernel/cputable.c + * + * Copyright (C) 2001 Ben. Herrenschmidt (benh@kernel.crashing.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +struct cpu_spec* cur_cpu_spec[NR_CPUS]; + +extern void __setup_cpu_601(unsigned long offset, int cpu_nr, struct cpu_spec* spec); +extern void __setup_cpu_603(unsigned long offset, int cpu_nr, struct cpu_spec* spec); +extern void __setup_cpu_604(unsigned long offset, int cpu_nr, struct cpu_spec* spec); +extern void __setup_cpu_750(unsigned long offset, int cpu_nr, struct cpu_spec* spec); +extern void __setup_cpu_750cx(unsigned long offset, int cpu_nr, struct cpu_spec* spec); +extern void __setup_cpu_750fx(unsigned long offset, int cpu_nr, struct cpu_spec* spec); +extern void __setup_cpu_7400(unsigned long offset, int cpu_nr, struct cpu_spec* spec); +extern void __setup_cpu_7410(unsigned long offset, int cpu_nr, struct cpu_spec* spec); +extern void __setup_cpu_745x(unsigned long offset, int cpu_nr, struct cpu_spec* spec); +extern void __setup_cpu_power3(unsigned long offset, int cpu_nr, struct cpu_spec* spec); +extern void __setup_cpu_power4(unsigned long offset, int cpu_nr, struct cpu_spec* spec); +extern void __setup_cpu_ppc970(unsigned long offset, int cpu_nr, struct cpu_spec* spec); +extern void __setup_cpu_generic(unsigned long offset, int cpu_nr, struct cpu_spec* spec); + +#define CLASSIC_PPC (!defined(CONFIG_8xx) && !defined(CONFIG_4xx) && \ + !defined(CONFIG_POWER3) && !defined(CONFIG_POWER4) && \ + !defined(CONFIG_BOOKE)) + +/* This table only contains "desktop" CPUs, it need to be filled with embedded + * ones as well... + */ +#define COMMON_PPC (PPC_FEATURE_32 | PPC_FEATURE_HAS_FPU | \ + PPC_FEATURE_HAS_MMU) + +/* We only set the altivec features if the kernel was compiled with altivec + * support + */ +#ifdef CONFIG_ALTIVEC +#define CPU_FTR_ALTIVEC_COMP CPU_FTR_ALTIVEC +#define PPC_FEATURE_ALTIVEC_COMP PPC_FEATURE_HAS_ALTIVEC +#else +#define CPU_FTR_ALTIVEC_COMP 0 +#define PPC_FEATURE_ALTIVEC_COMP 0 +#endif + +/* We only set the spe features if the kernel was compiled with + * spe support + */ +#ifdef CONFIG_SPE +#define PPC_FEATURE_SPE_COMP PPC_FEATURE_HAS_SPE +#else +#define PPC_FEATURE_SPE_COMP 0 +#endif + +/* We need to mark all pages as being coherent if we're SMP or we + * have a 74[45]x and an MPC107 host bridge. + */ +#if defined(CONFIG_SMP) || defined(CONFIG_MPC10X_BRIDGE) +#define CPU_FTR_COMMON CPU_FTR_NEED_COHERENT +#else +#define CPU_FTR_COMMON 0 +#endif + +/* The powersave features NAP & DOZE seems to confuse BDI when + debugging. So if a BDI is used, disable theses + */ +#ifndef CONFIG_BDI_SWITCH +#define CPU_FTR_MAYBE_CAN_DOZE CPU_FTR_CAN_DOZE +#define CPU_FTR_MAYBE_CAN_NAP CPU_FTR_CAN_NAP +#else +#define CPU_FTR_MAYBE_CAN_DOZE 0 +#define CPU_FTR_MAYBE_CAN_NAP 0 +#endif + +struct cpu_spec cpu_specs[] = { +#if CLASSIC_PPC + { /* 601 */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x00010000, + .cpu_name = "601", + .cpu_features = CPU_FTR_COMMON | CPU_FTR_601 | + CPU_FTR_HPTE_TABLE, + .cpu_user_features = COMMON_PPC | PPC_FEATURE_601_INSTR | + PPC_FEATURE_UNIFIED_CACHE, + .icache_bsize = 32, + .dcache_bsize = 32, + .cpu_setup = __setup_cpu_601 + }, + { /* 603 */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x00030000, + .cpu_name = "603", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | + CPU_FTR_USE_TB | CPU_FTR_MAYBE_CAN_NAP, + .cpu_user_features = COMMON_PPC, + .icache_bsize = 32, + .dcache_bsize = 32, + .cpu_setup = __setup_cpu_603 + }, + { /* 603e */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x00060000, + .cpu_name = "603e", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | + CPU_FTR_USE_TB | CPU_FTR_MAYBE_CAN_NAP, + .cpu_user_features = COMMON_PPC, + .icache_bsize = 32, + .dcache_bsize = 32, + .cpu_setup = __setup_cpu_603 + }, + { /* 603ev */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x00070000, + .cpu_name = "603ev", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | + CPU_FTR_USE_TB | CPU_FTR_MAYBE_CAN_NAP, + .cpu_user_features = COMMON_PPC, + .icache_bsize = 32, + .dcache_bsize = 32, + .cpu_setup = __setup_cpu_603 + }, + { /* 604 */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x00040000, + .cpu_name = "604", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | + CPU_FTR_604_PERF_MON | CPU_FTR_HPTE_TABLE, + .cpu_user_features = COMMON_PPC, + .icache_bsize = 32, + .dcache_bsize = 32, + .num_pmcs = 2, + .cpu_setup = __setup_cpu_604 + }, + { /* 604e */ + .pvr_mask = 0xfffff000, + .pvr_value = 0x00090000, + .cpu_name = "604e", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | + CPU_FTR_604_PERF_MON | CPU_FTR_HPTE_TABLE, + .cpu_user_features = COMMON_PPC, + .icache_bsize = 32, + .dcache_bsize = 32, + .num_pmcs = 4, + .cpu_setup = __setup_cpu_604 + }, + { /* 604r */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x00090000, + .cpu_name = "604r", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | + CPU_FTR_604_PERF_MON | CPU_FTR_HPTE_TABLE, + .cpu_user_features = COMMON_PPC, + .icache_bsize = 32, + .dcache_bsize = 32, + .num_pmcs = 4, + .cpu_setup = __setup_cpu_604 + }, + { /* 604ev */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x000a0000, + .cpu_name = "604ev", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | + CPU_FTR_604_PERF_MON | CPU_FTR_HPTE_TABLE, + .cpu_user_features = COMMON_PPC, + .icache_bsize = 32, + .dcache_bsize = 32, + .num_pmcs = 4, + .cpu_setup = __setup_cpu_604 + }, + { /* 740/750 (0x4202, don't support TAU ?) */ + .pvr_mask = 0xffffffff, + .pvr_value = 0x00084202, + .cpu_name = "740/750", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | + CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_HPTE_TABLE | + CPU_FTR_MAYBE_CAN_NAP, + .cpu_user_features = COMMON_PPC, + .icache_bsize = 32, + .dcache_bsize = 32, + .num_pmcs = 4, + .cpu_setup = __setup_cpu_750 + }, + { /* 745/755 */ + .pvr_mask = 0xfffff000, + .pvr_value = 0x00083000, + .cpu_name = "745/755", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | + CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_TAU | + CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP, + .cpu_user_features = COMMON_PPC, + .icache_bsize = 32, + .dcache_bsize = 32, + .num_pmcs = 4, + .cpu_setup = __setup_cpu_750 + }, + { /* 750CX (80100 and 8010x?) */ + .pvr_mask = 0xfffffff0, + .pvr_value = 0x00080100, + .cpu_name = "750CX", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | + CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_TAU | + CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP, + .cpu_user_features = COMMON_PPC, + .icache_bsize = 32, + .dcache_bsize = 32, + .num_pmcs = 4, + .cpu_setup = __setup_cpu_750cx + }, + { /* 750CX (82201 and 82202) */ + .pvr_mask = 0xfffffff0, + .pvr_value = 0x00082200, + .cpu_name = "750CX", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | + CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_TAU | + CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP, + .cpu_user_features = COMMON_PPC, + .icache_bsize = 32, + .dcache_bsize = 32, + .num_pmcs = 4, + .cpu_setup = __setup_cpu_750cx + }, + { /* 750CXe (82214) */ + .pvr_mask = 0xfffffff0, + .pvr_value = 0x00082210, + .cpu_name = "750CXe", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | + CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_TAU | + CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP, + .cpu_user_features = COMMON_PPC, + .icache_bsize = 32, + .dcache_bsize = 32, + .num_pmcs = 4, + .cpu_setup = __setup_cpu_750cx + }, + { /* 750FX rev 1.x */ + .pvr_mask = 0xffffff00, + .pvr_value = 0x70000100, + .cpu_name = "750FX", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | + CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_TAU | + CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | + CPU_FTR_DUAL_PLL_750FX | CPU_FTR_NO_DPM, + .cpu_user_features = COMMON_PPC, + .icache_bsize = 32, + .dcache_bsize = 32, + .num_pmcs = 4, + .cpu_setup = __setup_cpu_750 + }, + { /* 750FX rev 2.0 must disable HID0[DPM] */ + .pvr_mask = 0xffffffff, + .pvr_value = 0x70000200, + .cpu_name = "750FX", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | + CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_TAU | + CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | + CPU_FTR_NO_DPM, + .cpu_user_features = COMMON_PPC, + .icache_bsize = 32, + .dcache_bsize = 32, + .num_pmcs = 4, + .cpu_setup = __setup_cpu_750 + }, + { /* 750FX (All revs except 2.0) */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x70000000, + .cpu_name = "750FX", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | + CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_TAU | + CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | + CPU_FTR_DUAL_PLL_750FX | CPU_FTR_HAS_HIGH_BATS, + .cpu_user_features = COMMON_PPC, + .icache_bsize = 32, + .dcache_bsize = 32, + .num_pmcs = 4, + .cpu_setup = __setup_cpu_750fx + }, + { /* 750GX */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x70020000, + .cpu_name = "750GX", + .cpu_features = CPU_FTR_SPLIT_ID_CACHE | + CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | + CPU_FTR_L2CR | CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | + CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_DUAL_PLL_750FX | + CPU_FTR_HAS_HIGH_BATS, + .cpu_user_features = COMMON_PPC, + .icache_bsize = 32, + .dcache_bsize = 32, + .num_pmcs = 4, + .cpu_setup = __setup_cpu_750fx + }, + { /* 740/750 (L2CR bit need fixup for 740) */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x00080000, + .cpu_name = "740/750", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | + CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_TAU | + CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP, + .cpu_user_features = COMMON_PPC, + .icache_bsize = 32, + .dcache_bsize = 32, + .num_pmcs = 4, + .cpu_setup = __setup_cpu_750 + }, + { /* 7400 rev 1.1 ? (no TAU) */ + .pvr_mask = 0xffffffff, + .pvr_value = 0x000c1101, + .cpu_name = "7400 (1.1)", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | + CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | + CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP, + .cpu_user_features = COMMON_PPC | PPC_FEATURE_ALTIVEC_COMP, + .icache_bsize = 32, + .dcache_bsize = 32, + .num_pmcs = 4, + .cpu_setup = __setup_cpu_7400 + }, + { /* 7400 */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x000c0000, + .cpu_name = "7400", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | + CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_TAU | + CPU_FTR_ALTIVEC_COMP | CPU_FTR_HPTE_TABLE | + CPU_FTR_MAYBE_CAN_NAP, + .cpu_user_features = COMMON_PPC | PPC_FEATURE_ALTIVEC_COMP, + .icache_bsize = 32, + .dcache_bsize = 32, + .num_pmcs = 4, + .cpu_setup = __setup_cpu_7400 + }, + { /* 7410 */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x800c0000, + .cpu_name = "7410", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | + CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_TAU | + CPU_FTR_ALTIVEC_COMP | CPU_FTR_HPTE_TABLE | + CPU_FTR_MAYBE_CAN_NAP, + .cpu_user_features = COMMON_PPC | PPC_FEATURE_ALTIVEC_COMP, + .icache_bsize = 32, + .dcache_bsize = 32, + .num_pmcs = 4, + .cpu_setup = __setup_cpu_7410 + }, + { /* 7450 2.0 - no doze/nap */ + .pvr_mask = 0xffffffff, + .pvr_value = 0x80000200, + .cpu_name = "7450", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | + CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | CPU_FTR_L3CR | + CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | + CPU_FTR_NEED_COHERENT, + .cpu_user_features = COMMON_PPC | PPC_FEATURE_ALTIVEC_COMP, + .icache_bsize = 32, + .dcache_bsize = 32, + .num_pmcs = 6, + .cpu_setup = __setup_cpu_745x + }, + { /* 7450 2.1 */ + .pvr_mask = 0xffffffff, + .pvr_value = 0x80000201, + .cpu_name = "7450", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | + CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | + CPU_FTR_ALTIVEC_COMP | CPU_FTR_L3CR | + CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | + CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_L3_DISABLE_NAP | + CPU_FTR_NEED_COHERENT, + .cpu_user_features = COMMON_PPC | PPC_FEATURE_ALTIVEC_COMP, + .icache_bsize = 32, + .dcache_bsize = 32, + .num_pmcs = 6, + .cpu_setup = __setup_cpu_745x + }, + { /* 7450 2.3 and newer */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x80000000, + .cpu_name = "7450", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | + CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | + CPU_FTR_ALTIVEC_COMP | CPU_FTR_L3CR | + CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | + CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_NEED_COHERENT, + .cpu_user_features = COMMON_PPC | PPC_FEATURE_ALTIVEC_COMP, + .icache_bsize = 32, + .dcache_bsize = 32, + .num_pmcs = 6, + .cpu_setup = __setup_cpu_745x + }, + { /* 7455 rev 1.x */ + .pvr_mask = 0xffffff00, + .pvr_value = 0x80010100, + .cpu_name = "7455", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | + CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | CPU_FTR_L3CR | + CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | + CPU_FTR_HAS_HIGH_BATS | CPU_FTR_NEED_COHERENT, + .cpu_user_features = COMMON_PPC | PPC_FEATURE_ALTIVEC_COMP, + .icache_bsize = 32, + .dcache_bsize = 32, + .num_pmcs = 6, + .cpu_setup = __setup_cpu_745x + }, + { /* 7455 rev 2.0 */ + .pvr_mask = 0xffffffff, + .pvr_value = 0x80010200, + .cpu_name = "7455", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | + CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | + CPU_FTR_ALTIVEC_COMP | CPU_FTR_L3CR | + CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | + CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_L3_DISABLE_NAP | + CPU_FTR_NEED_COHERENT | CPU_FTR_HAS_HIGH_BATS, + .cpu_user_features = COMMON_PPC | PPC_FEATURE_ALTIVEC_COMP, + .icache_bsize = 32, + .dcache_bsize = 32, + .num_pmcs = 6, + .cpu_setup = __setup_cpu_745x + }, + { /* 7455 others */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x80010000, + .cpu_name = "7455", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | + CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | + CPU_FTR_ALTIVEC_COMP | CPU_FTR_L3CR | + CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | + CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_HAS_HIGH_BATS | + CPU_FTR_NEED_COHERENT, + .cpu_user_features = COMMON_PPC | PPC_FEATURE_ALTIVEC_COMP, + .icache_bsize = 32, + .dcache_bsize = 32, + .num_pmcs = 6, + .cpu_setup = __setup_cpu_745x + }, + { /* 7447/7457 Rev 1.0 */ + .pvr_mask = 0xffffffff, + .pvr_value = 0x80020100, + .cpu_name = "7447/7457", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | + CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | + CPU_FTR_ALTIVEC_COMP | CPU_FTR_L3CR | + CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | + CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_HAS_HIGH_BATS | + CPU_FTR_NEED_COHERENT | CPU_FTR_NO_BTIC, + .cpu_user_features = COMMON_PPC | PPC_FEATURE_ALTIVEC_COMP, + .icache_bsize = 32, + .dcache_bsize = 32, + .num_pmcs = 6, + .cpu_setup = __setup_cpu_745x + }, + { /* 7447/7457 Rev 1.1 */ + .pvr_mask = 0xffffffff, + .pvr_value = 0x80020101, + .cpu_name = "7447/7457", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | + CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | + CPU_FTR_ALTIVEC_COMP | CPU_FTR_L3CR | + CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | + CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_HAS_HIGH_BATS | + CPU_FTR_NEED_COHERENT | CPU_FTR_NO_BTIC, + .cpu_user_features = COMMON_PPC | PPC_FEATURE_ALTIVEC_COMP, + .icache_bsize = 32, + .dcache_bsize = 32, + .num_pmcs = 6, + .cpu_setup = __setup_cpu_745x + }, + { /* 7447/7457 Rev 1.2 and later */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x80020000, + .cpu_name = "7447/7457", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | + CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | + CPU_FTR_ALTIVEC_COMP | CPU_FTR_L3CR | + CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | + CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_HAS_HIGH_BATS | + CPU_FTR_NEED_COHERENT, + .cpu_user_features = COMMON_PPC | PPC_FEATURE_ALTIVEC_COMP, + .icache_bsize = 32, + .dcache_bsize = 32, + .num_pmcs = 6, + .cpu_setup = __setup_cpu_745x + }, + { /* 7447A */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x80030000, + .cpu_name = "7447A", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | + CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | + CPU_FTR_ALTIVEC_COMP | CPU_FTR_HPTE_TABLE | + CPU_FTR_SPEC7450 | CPU_FTR_NAP_DISABLE_L2_PR | + CPU_FTR_HAS_HIGH_BATS | CPU_FTR_NEED_COHERENT, + .cpu_user_features = COMMON_PPC | PPC_FEATURE_ALTIVEC_COMP, + .icache_bsize = 32, + .dcache_bsize = 32, + .num_pmcs = 6, + .cpu_setup = __setup_cpu_745x + }, + { /* 82xx (8240, 8245, 8260 are all 603e cores) */ + .pvr_mask = 0x7fff0000, + .pvr_value = 0x00810000, + .cpu_name = "82xx", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | + CPU_FTR_USE_TB, + .cpu_user_features = COMMON_PPC, + .icache_bsize = 32, + .dcache_bsize = 32, + .cpu_setup = __setup_cpu_603 + }, + { /* All G2_LE (603e core, plus some) have the same pvr */ + .pvr_mask = 0x7fff0000, + .pvr_value = 0x00820000, + .cpu_name = "G2_LE", + .cpu_features = CPU_FTR_SPLIT_ID_CACHE | + CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | + CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_HAS_HIGH_BATS, + .cpu_user_features = COMMON_PPC, + .icache_bsize = 32, + .dcache_bsize = 32, + .cpu_setup = __setup_cpu_603 + }, + { /* e300 (a 603e core, plus some) on 83xx */ + .pvr_mask = 0x7fff0000, + .pvr_value = 0x00830000, + .cpu_name = "e300", + .cpu_features = CPU_FTR_SPLIT_ID_CACHE | + CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | + CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_HAS_HIGH_BATS, + .cpu_user_features = COMMON_PPC, + .icache_bsize = 32, + .dcache_bsize = 32, + .cpu_setup = __setup_cpu_603 + }, + { /* default match, we assume split I/D cache & TB (non-601)... */ + .pvr_mask = 0x00000000, + .pvr_value = 0x00000000, + .cpu_name = "(generic PPC)", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | + CPU_FTR_HPTE_TABLE, + .cpu_user_features = COMMON_PPC, + .icache_bsize = 32, + .dcache_bsize = 32, + .cpu_setup = __setup_cpu_generic + }, +#endif /* CLASSIC_PPC */ +#ifdef CONFIG_PPC64BRIDGE + { /* Power3 */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x00400000, + .cpu_name = "Power3 (630)", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | + CPU_FTR_HPTE_TABLE, + .cpu_user_features = COMMON_PPC | PPC_FEATURE_64, + .icache_bsize = 128, + .dcache_bsize = 128, + .num_pmcs = 8, + .cpu_setup = __setup_cpu_power3 + }, + { /* Power3+ */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x00410000, + .cpu_name = "Power3 (630+)", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | + CPU_FTR_HPTE_TABLE, + .cpu_user_features = COMMON_PPC | PPC_FEATURE_64, + .icache_bsize = 128, + .dcache_bsize = 128, + .num_pmcs = 8, + .cpu_setup = __setup_cpu_power3 + }, + { /* I-star */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x00360000, + .cpu_name = "I-star", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | + CPU_FTR_HPTE_TABLE, + .cpu_user_features = COMMON_PPC | PPC_FEATURE_64, + .icache_bsize = 128, + .dcache_bsize = 128, + .num_pmcs = 8, + .cpu_setup = __setup_cpu_power3 + }, + { /* S-star */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x00370000, + .cpu_name = "S-star", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | + CPU_FTR_HPTE_TABLE, + .cpu_user_features = COMMON_PPC | PPC_FEATURE_64, + .icache_bsize = 128, + .dcache_bsize = 128, + .num_pmcs = 8, + .cpu_setup = __setup_cpu_power3 + }, +#endif /* CONFIG_PPC64BRIDGE */ +#ifdef CONFIG_POWER4 + { /* Power4 */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x00350000, + .cpu_name = "Power4", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | + CPU_FTR_HPTE_TABLE, + .cpu_user_features = COMMON_PPC | PPC_FEATURE_64, + .icache_bsize = 128, + .dcache_bsize = 128, + .num_pmcs = 8, + .cpu_setup = __setup_cpu_power4 + }, + { /* PPC970 */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x00390000, + .cpu_name = "PPC970", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | + CPU_FTR_HPTE_TABLE | + CPU_FTR_ALTIVEC_COMP | CPU_FTR_MAYBE_CAN_NAP, + .cpu_user_features = COMMON_PPC | PPC_FEATURE_64 | + PPC_FEATURE_ALTIVEC_COMP, + .icache_bsize = 128, + .dcache_bsize = 128, + .num_pmcs = 8, + .cpu_setup = __setup_cpu_ppc970 + }, + { /* PPC970FX */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x003c0000, + .cpu_name = "PPC970FX", + .cpu_features = CPU_FTR_COMMON | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | + CPU_FTR_HPTE_TABLE | + CPU_FTR_ALTIVEC_COMP | CPU_FTR_MAYBE_CAN_NAP, + .cpu_user_features = COMMON_PPC | PPC_FEATURE_64 | + PPC_FEATURE_ALTIVEC_COMP, + .icache_bsize = 128, + .dcache_bsize = 128, + .num_pmcs = 8, + .cpu_setup = __setup_cpu_ppc970 + }, +#endif /* CONFIG_POWER4 */ +#ifdef CONFIG_8xx + { /* 8xx */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x00500000, + .cpu_name = "8xx", + /* CPU_FTR_MAYBE_CAN_DOZE is possible, + * if the 8xx code is there.... */ + .cpu_features = CPU_FTR_SPLIT_ID_CACHE | + CPU_FTR_USE_TB, + .cpu_user_features = PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU, + .icache_bsize = 16, + .dcache_bsize = 16, + }, +#endif /* CONFIG_8xx */ +#ifdef CONFIG_40x + { /* 403GC */ + .pvr_mask = 0xffffff00, + .pvr_value = 0x00200200, + .cpu_name = "403GC", + .cpu_features = CPU_FTR_SPLIT_ID_CACHE | + CPU_FTR_USE_TB, + .cpu_user_features = PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU, + .icache_bsize = 16, + .dcache_bsize = 16, + }, + { /* 403GCX */ + .pvr_mask = 0xffffff00, + .pvr_value = 0x00201400, + .cpu_name = "403GCX", + .cpu_features = CPU_FTR_SPLIT_ID_CACHE | + CPU_FTR_USE_TB, + .cpu_user_features = PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU, + .icache_bsize = 16, + .dcache_bsize = 16, + }, + { /* 403G ?? */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x00200000, + .cpu_name = "403G ??", + .cpu_features = CPU_FTR_SPLIT_ID_CACHE | + CPU_FTR_USE_TB, + .cpu_user_features = PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU, + .icache_bsize = 16, + .dcache_bsize = 16, + }, + { /* 405GP */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x40110000, + .cpu_name = "405GP", + .cpu_features = CPU_FTR_SPLIT_ID_CACHE | + CPU_FTR_USE_TB, + .cpu_user_features = PPC_FEATURE_32 | + PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC, + .icache_bsize = 32, + .dcache_bsize = 32, + }, + { /* STB 03xxx */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x40130000, + .cpu_name = "STB03xxx", + .cpu_features = CPU_FTR_SPLIT_ID_CACHE | + CPU_FTR_USE_TB, + .cpu_user_features = PPC_FEATURE_32 | + PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC, + .icache_bsize = 32, + .dcache_bsize = 32, + }, + { /* STB 04xxx */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x41810000, + .cpu_name = "STB04xxx", + .cpu_features = CPU_FTR_SPLIT_ID_CACHE | + CPU_FTR_USE_TB, + .cpu_user_features = PPC_FEATURE_32 | + PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC, + .icache_bsize = 32, + .dcache_bsize = 32, + }, + { /* NP405L */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x41610000, + .cpu_name = "NP405L", + .cpu_features = CPU_FTR_SPLIT_ID_CACHE | + CPU_FTR_USE_TB, + .cpu_user_features = PPC_FEATURE_32 | + PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC, + .icache_bsize = 32, + .dcache_bsize = 32, + }, + { /* NP4GS3 */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x40B10000, + .cpu_name = "NP4GS3", + .cpu_features = CPU_FTR_SPLIT_ID_CACHE | + CPU_FTR_USE_TB, + .cpu_user_features = PPC_FEATURE_32 | + PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC, + .icache_bsize = 32, + .dcache_bsize = 32, + }, + { /* NP405H */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x41410000, + .cpu_name = "NP405H", + .cpu_features = CPU_FTR_SPLIT_ID_CACHE | + CPU_FTR_USE_TB, + .cpu_user_features = PPC_FEATURE_32 | + PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC, + .icache_bsize = 32, + .dcache_bsize = 32, + }, + { /* 405GPr */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x50910000, + .cpu_name = "405GPr", + .cpu_features = CPU_FTR_SPLIT_ID_CACHE | + CPU_FTR_USE_TB, + .cpu_user_features = PPC_FEATURE_32 | + PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC, + .icache_bsize = 32, + .dcache_bsize = 32, + }, + { /* STBx25xx */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x51510000, + .cpu_name = "STBx25xx", + .cpu_features = CPU_FTR_SPLIT_ID_CACHE | + CPU_FTR_USE_TB, + .cpu_user_features = PPC_FEATURE_32 | + PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC, + .icache_bsize = 32, + .dcache_bsize = 32, + }, + { /* 405LP */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x41F10000, + .cpu_name = "405LP", + .cpu_features = CPU_FTR_SPLIT_ID_CACHE | + CPU_FTR_USE_TB, + .cpu_user_features = PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU, + .icache_bsize = 32, + .dcache_bsize = 32, + }, + { /* Xilinx Virtex-II Pro */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x20010000, + .cpu_name = "Virtex-II Pro", + .cpu_features = CPU_FTR_SPLIT_ID_CACHE | + CPU_FTR_USE_TB, + .cpu_user_features = PPC_FEATURE_32 | + PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC, + .icache_bsize = 32, + .dcache_bsize = 32, + }, + +#endif /* CONFIG_40x */ +#ifdef CONFIG_44x + { /* 440GP Rev. B */ + .pvr_mask = 0xf0000fff, + .pvr_value = 0x40000440, + .cpu_name = "440GP Rev. B", + .cpu_features = CPU_FTR_SPLIT_ID_CACHE | + CPU_FTR_USE_TB, + .cpu_user_features = PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU, + .icache_bsize = 32, + .dcache_bsize = 32, + }, + { /* 440GP Rev. C */ + .pvr_mask = 0xf0000fff, + .pvr_value = 0x40000481, + .cpu_name = "440GP Rev. C", + .cpu_features = CPU_FTR_SPLIT_ID_CACHE | + CPU_FTR_USE_TB, + .cpu_user_features = PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU, + .icache_bsize = 32, + .dcache_bsize = 32, + }, + { /* 440GX Rev. A */ + .pvr_mask = 0xf0000fff, + .pvr_value = 0x50000850, + .cpu_name = "440GX Rev. A", + .cpu_features = CPU_FTR_SPLIT_ID_CACHE | + CPU_FTR_USE_TB, + .cpu_user_features = PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU, + .icache_bsize = 32, + .dcache_bsize = 32, + }, + { /* 440GX Rev. B */ + .pvr_mask = 0xf0000fff, + .pvr_value = 0x50000851, + .cpu_name = "440GX Rev. B", + .cpu_features = CPU_FTR_SPLIT_ID_CACHE | + CPU_FTR_USE_TB, + .cpu_user_features = PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU, + .icache_bsize = 32, + .dcache_bsize = 32, + }, + { /* 440GX Rev. C */ + .pvr_mask = 0xf0000fff, + .pvr_value = 0x50000892, + .cpu_name = "440GX Rev. C", + .cpu_features = CPU_FTR_SPLIT_ID_CACHE | + CPU_FTR_USE_TB, + .cpu_user_features = PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU, + .icache_bsize = 32, + .dcache_bsize = 32, + }, +#endif /* CONFIG_44x */ +#ifdef CONFIG_E500 + { /* e500 */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x80200000, + .cpu_name = "e500", + /* xxx - galak: add CPU_FTR_MAYBE_CAN_DOZE */ + .cpu_features = CPU_FTR_SPLIT_ID_CACHE | + CPU_FTR_USE_TB, + .cpu_user_features = PPC_FEATURE_32 | + PPC_FEATURE_HAS_MMU | PPC_FEATURE_SPE_COMP | + PPC_FEATURE_HAS_EFP_SINGLE, + .icache_bsize = 32, + .dcache_bsize = 32, + .num_pmcs = 4, + }, +#endif +#if !CLASSIC_PPC + { /* default match */ + .pvr_mask = 0x00000000, + .pvr_value = 0x00000000, + .cpu_name = "(generic PPC)", + .cpu_features = CPU_FTR_COMMON, + .cpu_user_features = PPC_FEATURE_32, + .icache_bsize = 32, + .dcache_bsize = 32, + } +#endif /* !CLASSIC_PPC */ +}; diff --git a/arch/ppc/kernel/dma-mapping.c b/arch/ppc/kernel/dma-mapping.c new file mode 100644 index 00000000000..e0c631cf96b --- /dev/null +++ b/arch/ppc/kernel/dma-mapping.c @@ -0,0 +1,447 @@ +/* + * PowerPC version derived from arch/arm/mm/consistent.c + * Copyright (C) 2001 Dan Malek (dmalek@jlc.net) + * + * Copyright (C) 2000 Russell King + * + * Consistent memory allocators. Used for DMA devices that want to + * share uncached memory with the processor core. The function return + * is the virtual address and 'dma_handle' is the physical address. + * Mostly stolen from the ARM port, with some changes for PowerPC. + * -- Dan + * + * Reorganized to get rid of the arch-specific consistent_* functions + * and provide non-coherent implementations for the DMA API. -Matt + * + * Added in_interrupt() safe dma_alloc_coherent()/dma_free_coherent() + * implementation. This is pulled straight from ARM and barely + * modified. -Matt + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int map_page(unsigned long va, phys_addr_t pa, int flags); + +#include + +/* + * This address range defaults to a value that is safe for all + * platforms which currently set CONFIG_NOT_COHERENT_CACHE. It + * can be further configured for specific applications under + * the "Advanced Setup" menu. -Matt + */ +#define CONSISTENT_BASE (CONFIG_CONSISTENT_START) +#define CONSISTENT_END (CONFIG_CONSISTENT_START + CONFIG_CONSISTENT_SIZE) +#define CONSISTENT_OFFSET(x) (((unsigned long)(x) - CONSISTENT_BASE) >> PAGE_SHIFT) + +/* + * This is the page table (2MB) covering uncached, DMA consistent allocations + */ +static pte_t *consistent_pte; +static DEFINE_SPINLOCK(consistent_lock); + +/* + * VM region handling support. + * + * This should become something generic, handling VM region allocations for + * vmalloc and similar (ioremap, module space, etc). + * + * I envisage vmalloc()'s supporting vm_struct becoming: + * + * struct vm_struct { + * struct vm_region region; + * unsigned long flags; + * struct page **pages; + * unsigned int nr_pages; + * unsigned long phys_addr; + * }; + * + * get_vm_area() would then call vm_region_alloc with an appropriate + * struct vm_region head (eg): + * + * struct vm_region vmalloc_head = { + * .vm_list = LIST_HEAD_INIT(vmalloc_head.vm_list), + * .vm_start = VMALLOC_START, + * .vm_end = VMALLOC_END, + * }; + * + * However, vmalloc_head.vm_start is variable (typically, it is dependent on + * the amount of RAM found at boot time.) I would imagine that get_vm_area() + * would have to initialise this each time prior to calling vm_region_alloc(). + */ +struct vm_region { + struct list_head vm_list; + unsigned long vm_start; + unsigned long vm_end; +}; + +static struct vm_region consistent_head = { + .vm_list = LIST_HEAD_INIT(consistent_head.vm_list), + .vm_start = CONSISTENT_BASE, + .vm_end = CONSISTENT_END, +}; + +static struct vm_region * +vm_region_alloc(struct vm_region *head, size_t size, int gfp) +{ + unsigned long addr = head->vm_start, end = head->vm_end - size; + unsigned long flags; + struct vm_region *c, *new; + + new = kmalloc(sizeof(struct vm_region), gfp); + if (!new) + goto out; + + spin_lock_irqsave(&consistent_lock, flags); + + list_for_each_entry(c, &head->vm_list, vm_list) { + if ((addr + size) < addr) + goto nospc; + if ((addr + size) <= c->vm_start) + goto found; + addr = c->vm_end; + if (addr > end) + goto nospc; + } + + found: + /* + * Insert this entry _before_ the one we found. + */ + list_add_tail(&new->vm_list, &c->vm_list); + new->vm_start = addr; + new->vm_end = addr + size; + + spin_unlock_irqrestore(&consistent_lock, flags); + return new; + + nospc: + spin_unlock_irqrestore(&consistent_lock, flags); + kfree(new); + out: + return NULL; +} + +static struct vm_region *vm_region_find(struct vm_region *head, unsigned long addr) +{ + struct vm_region *c; + + list_for_each_entry(c, &head->vm_list, vm_list) { + if (c->vm_start == addr) + goto out; + } + c = NULL; + out: + return c; +} + +/* + * Allocate DMA-coherent memory space and return both the kernel remapped + * virtual and bus address for that space. + */ +void * +__dma_alloc_coherent(size_t size, dma_addr_t *handle, int gfp) +{ + struct page *page; + struct vm_region *c; + unsigned long order; + u64 mask = 0x00ffffff, limit; /* ISA default */ + + if (!consistent_pte) { + printk(KERN_ERR "%s: not initialised\n", __func__); + dump_stack(); + return NULL; + } + + size = PAGE_ALIGN(size); + limit = (mask + 1) & ~mask; + if ((limit && size >= limit) || size >= (CONSISTENT_END - CONSISTENT_BASE)) { + printk(KERN_WARNING "coherent allocation too big (requested %#x mask %#Lx)\n", + size, mask); + return NULL; + } + + order = get_order(size); + + if (mask != 0xffffffff) + gfp |= GFP_DMA; + + page = alloc_pages(gfp, order); + if (!page) + goto no_page; + + /* + * Invalidate any data that might be lurking in the + * kernel direct-mapped region for device DMA. + */ + { + unsigned long kaddr = (unsigned long)page_address(page); + memset(page_address(page), 0, size); + flush_dcache_range(kaddr, kaddr + size); + } + + /* + * Allocate a virtual address in the consistent mapping region. + */ + c = vm_region_alloc(&consistent_head, size, + gfp & ~(__GFP_DMA | __GFP_HIGHMEM)); + if (c) { + unsigned long vaddr = c->vm_start; + pte_t *pte = consistent_pte + CONSISTENT_OFFSET(vaddr); + struct page *end = page + (1 << order); + + /* + * Set the "dma handle" + */ + *handle = page_to_bus(page); + + do { + BUG_ON(!pte_none(*pte)); + + set_page_count(page, 1); + SetPageReserved(page); + set_pte_at(&init_mm, vaddr, + pte, mk_pte(page, pgprot_noncached(PAGE_KERNEL))); + page++; + pte++; + vaddr += PAGE_SIZE; + } while (size -= PAGE_SIZE); + + /* + * Free the otherwise unused pages. + */ + while (page < end) { + set_page_count(page, 1); + __free_page(page); + page++; + } + + return (void *)c->vm_start; + } + + if (page) + __free_pages(page, order); + no_page: + return NULL; +} +EXPORT_SYMBOL(__dma_alloc_coherent); + +/* + * free a page as defined by the above mapping. + */ +void __dma_free_coherent(size_t size, void *vaddr) +{ + struct vm_region *c; + unsigned long flags, addr; + pte_t *ptep; + + size = PAGE_ALIGN(size); + + spin_lock_irqsave(&consistent_lock, flags); + + c = vm_region_find(&consistent_head, (unsigned long)vaddr); + if (!c) + goto no_area; + + if ((c->vm_end - c->vm_start) != size) { + printk(KERN_ERR "%s: freeing wrong coherent size (%ld != %d)\n", + __func__, c->vm_end - c->vm_start, size); + dump_stack(); + size = c->vm_end - c->vm_start; + } + + ptep = consistent_pte + CONSISTENT_OFFSET(c->vm_start); + addr = c->vm_start; + do { + pte_t pte = ptep_get_and_clear(&init_mm, addr, ptep); + unsigned long pfn; + + ptep++; + addr += PAGE_SIZE; + + if (!pte_none(pte) && pte_present(pte)) { + pfn = pte_pfn(pte); + + if (pfn_valid(pfn)) { + struct page *page = pfn_to_page(pfn); + ClearPageReserved(page); + + __free_page(page); + continue; + } + } + + printk(KERN_CRIT "%s: bad page in kernel page table\n", + __func__); + } while (size -= PAGE_SIZE); + + flush_tlb_kernel_range(c->vm_start, c->vm_end); + + list_del(&c->vm_list); + + spin_unlock_irqrestore(&consistent_lock, flags); + + kfree(c); + return; + + no_area: + spin_unlock_irqrestore(&consistent_lock, flags); + printk(KERN_ERR "%s: trying to free invalid coherent area: %p\n", + __func__, vaddr); + dump_stack(); +} +EXPORT_SYMBOL(__dma_free_coherent); + +/* + * Initialise the consistent memory allocation. + */ +static int __init dma_alloc_init(void) +{ + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + int ret = 0; + + spin_lock(&init_mm.page_table_lock); + + do { + pgd = pgd_offset(&init_mm, CONSISTENT_BASE); + pmd = pmd_alloc(&init_mm, pgd, CONSISTENT_BASE); + if (!pmd) { + printk(KERN_ERR "%s: no pmd tables\n", __func__); + ret = -ENOMEM; + break; + } + WARN_ON(!pmd_none(*pmd)); + + pte = pte_alloc_kernel(&init_mm, pmd, CONSISTENT_BASE); + if (!pte) { + printk(KERN_ERR "%s: no pte tables\n", __func__); + ret = -ENOMEM; + break; + } + + consistent_pte = pte; + } while (0); + + spin_unlock(&init_mm.page_table_lock); + + return ret; +} + +core_initcall(dma_alloc_init); + +/* + * make an area consistent. + */ +void __dma_sync(void *vaddr, size_t size, int direction) +{ + unsigned long start = (unsigned long)vaddr; + unsigned long end = start + size; + + switch (direction) { + case DMA_NONE: + BUG(); + case DMA_FROM_DEVICE: /* invalidate only */ + invalidate_dcache_range(start, end); + break; + case DMA_TO_DEVICE: /* writeback only */ + clean_dcache_range(start, end); + break; + case DMA_BIDIRECTIONAL: /* writeback and invalidate */ + flush_dcache_range(start, end); + break; + } +} +EXPORT_SYMBOL(__dma_sync); + +#ifdef CONFIG_HIGHMEM +/* + * __dma_sync_page() implementation for systems using highmem. + * In this case, each page of a buffer must be kmapped/kunmapped + * in order to have a virtual address for __dma_sync(). This must + * not sleep so kmap_atmomic()/kunmap_atomic() are used. + * + * Note: yes, it is possible and correct to have a buffer extend + * beyond the first page. + */ +static inline void __dma_sync_page_highmem(struct page *page, + unsigned long offset, size_t size, int direction) +{ + size_t seg_size = min((size_t)PAGE_SIZE, size) - offset; + size_t cur_size = seg_size; + unsigned long flags, start, seg_offset = offset; + int nr_segs = PAGE_ALIGN(size + (PAGE_SIZE - offset))/PAGE_SIZE; + int seg_nr = 0; + + local_irq_save(flags); + + do { + start = (unsigned long)kmap_atomic(page + seg_nr, + KM_PPC_SYNC_PAGE) + seg_offset; + + /* Sync this buffer segment */ + __dma_sync((void *)start, seg_size, direction); + kunmap_atomic((void *)start, KM_PPC_SYNC_PAGE); + seg_nr++; + + /* Calculate next buffer segment size */ + seg_size = min((size_t)PAGE_SIZE, size - cur_size); + + /* Add the segment size to our running total */ + cur_size += seg_size; + seg_offset = 0; + } while (seg_nr < nr_segs); + + local_irq_restore(flags); +} +#endif /* CONFIG_HIGHMEM */ + +/* + * __dma_sync_page makes memory consistent. identical to __dma_sync, but + * takes a struct page instead of a virtual address + */ +void __dma_sync_page(struct page *page, unsigned long offset, + size_t size, int direction) +{ +#ifdef CONFIG_HIGHMEM + __dma_sync_page_highmem(page, offset, size, direction); +#else + unsigned long start = (unsigned long)page_address(page) + offset; + __dma_sync((void *)start, size, direction); +#endif +} +EXPORT_SYMBOL(__dma_sync_page); diff --git a/arch/ppc/kernel/entry.S b/arch/ppc/kernel/entry.S new file mode 100644 index 00000000000..035217d6c0f --- /dev/null +++ b/arch/ppc/kernel/entry.S @@ -0,0 +1,969 @@ +/* + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * Rewritten by Cort Dougan (cort@fsmlabs.com) for PReP + * Copyright (C) 1996 Cort Dougan + * Adapted for Power Macintosh by Paul Mackerras. + * Low-level exception handlers and MMU support + * rewritten by Paul Mackerras. + * Copyright (C) 1996 Paul Mackerras. + * MPC8xx modifications Copyright (C) 1997 Dan Malek (dmalek@jlc.net). + * + * This file contains the system call entry code, context switch + * code, and exception/interrupt return code for PowerPC. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef SHOW_SYSCALLS +#undef SHOW_SYSCALLS_TASK + +/* + * MSR_KERNEL is > 0x10000 on 4xx/Book-E since it include MSR_CE. + */ +#if MSR_KERNEL >= 0x10000 +#define LOAD_MSR_KERNEL(r, x) lis r,(x)@h; ori r,r,(x)@l +#else +#define LOAD_MSR_KERNEL(r, x) li r,(x) +#endif + +#ifdef CONFIG_BOOKE +#include "head_booke.h" + .globl mcheck_transfer_to_handler +mcheck_transfer_to_handler: + mtspr MCHECK_SPRG,r8 + BOOKE_LOAD_MCHECK_STACK + lwz r0,GPR10-INT_FRAME_SIZE(r8) + stw r0,GPR10(r11) + lwz r0,GPR11-INT_FRAME_SIZE(r8) + stw r0,GPR11(r11) + mfspr r8,MCHECK_SPRG + b transfer_to_handler_full + + .globl crit_transfer_to_handler +crit_transfer_to_handler: + mtspr CRIT_SPRG,r8 + BOOKE_LOAD_CRIT_STACK + lwz r0,GPR10-INT_FRAME_SIZE(r8) + stw r0,GPR10(r11) + lwz r0,GPR11-INT_FRAME_SIZE(r8) + stw r0,GPR11(r11) + mfspr r8,CRIT_SPRG + /* fall through */ +#endif + +#ifdef CONFIG_40x + .globl crit_transfer_to_handler +crit_transfer_to_handler: + lwz r0,crit_r10@l(0) + stw r0,GPR10(r11) + lwz r0,crit_r11@l(0) + stw r0,GPR11(r11) + /* fall through */ +#endif + +/* + * This code finishes saving the registers to the exception frame + * and jumps to the appropriate handler for the exception, turning + * on address translation. + * Note that we rely on the caller having set cr0.eq iff the exception + * occurred in kernel mode (i.e. MSR:PR = 0). + */ + .globl transfer_to_handler_full +transfer_to_handler_full: + SAVE_NVGPRS(r11) + /* fall through */ + + .globl transfer_to_handler +transfer_to_handler: + stw r2,GPR2(r11) + stw r12,_NIP(r11) + stw r9,_MSR(r11) + andi. r2,r9,MSR_PR + mfctr r12 + mfspr r2,SPRN_XER + stw r12,_CTR(r11) + stw r2,_XER(r11) + mfspr r12,SPRN_SPRG3 + addi r2,r12,-THREAD + tovirt(r2,r2) /* set r2 to current */ + beq 2f /* if from user, fix up THREAD.regs */ + addi r11,r1,STACK_FRAME_OVERHEAD + stw r11,PT_REGS(r12) +#if defined(CONFIG_40x) || defined(CONFIG_BOOKE) + /* Check to see if the dbcr0 register is set up to debug. Use the + single-step bit to do this. */ + lwz r12,THREAD_DBCR0(r12) + andis. r12,r12,DBCR0_IC@h + beq+ 3f + /* From user and task is ptraced - load up global dbcr0 */ + li r12,-1 /* clear all pending debug events */ + mtspr SPRN_DBSR,r12 + lis r11,global_dbcr0@ha + tophys(r11,r11) + addi r11,r11,global_dbcr0@l + lwz r12,0(r11) + mtspr SPRN_DBCR0,r12 + lwz r12,4(r11) + addi r12,r12,-1 + stw r12,4(r11) +#endif + b 3f +2: /* if from kernel, check interrupted DOZE/NAP mode and + * check for stack overflow + */ +#ifdef CONFIG_6xx + mfspr r11,SPRN_HID0 + mtcr r11 +BEGIN_FTR_SECTION + bt- 8,power_save_6xx_restore /* Check DOZE */ +END_FTR_SECTION_IFSET(CPU_FTR_CAN_DOZE) +BEGIN_FTR_SECTION + bt- 9,power_save_6xx_restore /* Check NAP */ +END_FTR_SECTION_IFSET(CPU_FTR_CAN_NAP) +#endif /* CONFIG_6xx */ + .globl transfer_to_handler_cont +transfer_to_handler_cont: + lwz r11,THREAD_INFO-THREAD(r12) + cmplw r1,r11 /* if r1 <= current->thread_info */ + ble- stack_ovf /* then the kernel stack overflowed */ +3: + mflr r9 + lwz r11,0(r9) /* virtual address of handler */ + lwz r9,4(r9) /* where to go when done */ + FIX_SRR1(r10,r12) + mtspr SPRN_SRR0,r11 + mtspr SPRN_SRR1,r10 + mtlr r9 + SYNC + RFI /* jump to handler, enable MMU */ + +/* + * On kernel stack overflow, load up an initial stack pointer + * and call StackOverflow(regs), which should not return. + */ +stack_ovf: + /* sometimes we use a statically-allocated stack, which is OK. */ + lis r11,_end@h + ori r11,r11,_end@l + cmplw r1,r11 + ble 3b /* r1 <= &_end is OK */ + SAVE_NVGPRS(r11) + addi r3,r1,STACK_FRAME_OVERHEAD + lis r1,init_thread_union@ha + addi r1,r1,init_thread_union@l + addi r1,r1,THREAD_SIZE-STACK_FRAME_OVERHEAD + lis r9,StackOverflow@ha + addi r9,r9,StackOverflow@l + LOAD_MSR_KERNEL(r10,MSR_KERNEL) + FIX_SRR1(r10,r12) + mtspr SPRN_SRR0,r9 + mtspr SPRN_SRR1,r10 + SYNC + RFI + +/* + * Handle a system call. + */ + .stabs "arch/ppc/kernel/",N_SO,0,0,0f + .stabs "entry.S",N_SO,0,0,0f +0: + +_GLOBAL(DoSyscall) + stw r0,THREAD+LAST_SYSCALL(r2) + stw r3,ORIG_GPR3(r1) + li r12,0 + stw r12,RESULT(r1) + lwz r11,_CCR(r1) /* Clear SO bit in CR */ + rlwinm r11,r11,0,4,2 + stw r11,_CCR(r1) +#ifdef SHOW_SYSCALLS + bl do_show_syscall +#endif /* SHOW_SYSCALLS */ + rlwinm r10,r1,0,0,18 /* current_thread_info() */ + lwz r11,TI_LOCAL_FLAGS(r10) + rlwinm r11,r11,0,~_TIFL_FORCE_NOERROR + stw r11,TI_LOCAL_FLAGS(r10) + lwz r11,TI_FLAGS(r10) + andi. r11,r11,_TIF_SYSCALL_TRACE + bne- syscall_dotrace +syscall_dotrace_cont: + cmplwi 0,r0,NR_syscalls + lis r10,sys_call_table@h + ori r10,r10,sys_call_table@l + slwi r0,r0,2 + bge- 66f + lwzx r10,r10,r0 /* Fetch system call handler [ptr] */ + mtlr r10 + addi r9,r1,STACK_FRAME_OVERHEAD + blrl /* Call handler */ + .globl ret_from_syscall +ret_from_syscall: +#ifdef SHOW_SYSCALLS + bl do_show_syscall_exit +#endif + mr r6,r3 + li r11,-_LAST_ERRNO + cmplw 0,r3,r11 + rlwinm r12,r1,0,0,18 /* current_thread_info() */ + blt+ 30f + lwz r11,TI_LOCAL_FLAGS(r12) + andi. r11,r11,_TIFL_FORCE_NOERROR + bne 30f + neg r3,r3 + lwz r10,_CCR(r1) /* Set SO bit in CR */ + oris r10,r10,0x1000 + stw r10,_CCR(r1) + + /* disable interrupts so current_thread_info()->flags can't change */ +30: LOAD_MSR_KERNEL(r10,MSR_KERNEL) /* doesn't include MSR_EE */ + SYNC + MTMSRD(r10) + lwz r9,TI_FLAGS(r12) + andi. r0,r9,(_TIF_SYSCALL_TRACE|_TIF_SIGPENDING|_TIF_NEED_RESCHED) + bne- syscall_exit_work +syscall_exit_cont: +#if defined(CONFIG_4xx) || defined(CONFIG_BOOKE) + /* If the process has its own DBCR0 value, load it up. The single + step bit tells us that dbcr0 should be loaded. */ + lwz r0,THREAD+THREAD_DBCR0(r2) + andis. r10,r0,DBCR0_IC@h + bnel- load_dbcr0 +#endif + stwcx. r0,0,r1 /* to clear the reservation */ + lwz r4,_LINK(r1) + lwz r5,_CCR(r1) + mtlr r4 + mtcr r5 + lwz r7,_NIP(r1) + lwz r8,_MSR(r1) + FIX_SRR1(r8, r0) + lwz r2,GPR2(r1) + lwz r1,GPR1(r1) + mtspr SPRN_SRR0,r7 + mtspr SPRN_SRR1,r8 + SYNC + RFI + +66: li r3,-ENOSYS + b ret_from_syscall + + .globl ret_from_fork +ret_from_fork: + REST_NVGPRS(r1) + bl schedule_tail + li r3,0 + b ret_from_syscall + +/* Traced system call support */ +syscall_dotrace: + SAVE_NVGPRS(r1) + li r0,0xc00 + stw r0,TRAP(r1) + bl do_syscall_trace + lwz r0,GPR0(r1) /* Restore original registers */ + lwz r3,GPR3(r1) + lwz r4,GPR4(r1) + lwz r5,GPR5(r1) + lwz r6,GPR6(r1) + lwz r7,GPR7(r1) + lwz r8,GPR8(r1) + REST_NVGPRS(r1) + b syscall_dotrace_cont + +syscall_exit_work: + stw r6,RESULT(r1) /* Save result */ + stw r3,GPR3(r1) /* Update return value */ + andi. r0,r9,_TIF_SYSCALL_TRACE + beq 5f + ori r10,r10,MSR_EE + SYNC + MTMSRD(r10) /* re-enable interrupts */ + lwz r4,TRAP(r1) + andi. r4,r4,1 + beq 4f + SAVE_NVGPRS(r1) + li r4,0xc00 + stw r4,TRAP(r1) +4: + bl do_syscall_trace + REST_NVGPRS(r1) +2: + lwz r3,GPR3(r1) + LOAD_MSR_KERNEL(r10,MSR_KERNEL) /* doesn't include MSR_EE */ + SYNC + MTMSRD(r10) /* disable interrupts again */ + rlwinm r12,r1,0,0,18 /* current_thread_info() */ + lwz r9,TI_FLAGS(r12) +5: + andi. r0,r9,_TIF_NEED_RESCHED + bne 1f + lwz r5,_MSR(r1) + andi. r5,r5,MSR_PR + beq syscall_exit_cont + andi. r0,r9,_TIF_SIGPENDING + beq syscall_exit_cont + b do_user_signal +1: + ori r10,r10,MSR_EE + SYNC + MTMSRD(r10) /* re-enable interrupts */ + bl schedule + b 2b + +#ifdef SHOW_SYSCALLS +do_show_syscall: +#ifdef SHOW_SYSCALLS_TASK + lis r11,show_syscalls_task@ha + lwz r11,show_syscalls_task@l(r11) + cmp 0,r2,r11 + bnelr +#endif + stw r31,GPR31(r1) + mflr r31 + lis r3,7f@ha + addi r3,r3,7f@l + lwz r4,GPR0(r1) + lwz r5,GPR3(r1) + lwz r6,GPR4(r1) + lwz r7,GPR5(r1) + lwz r8,GPR6(r1) + lwz r9,GPR7(r1) + bl printk + lis r3,77f@ha + addi r3,r3,77f@l + lwz r4,GPR8(r1) + mr r5,r2 + bl printk + lwz r0,GPR0(r1) + lwz r3,GPR3(r1) + lwz r4,GPR4(r1) + lwz r5,GPR5(r1) + lwz r6,GPR6(r1) + lwz r7,GPR7(r1) + lwz r8,GPR8(r1) + mtlr r31 + lwz r31,GPR31(r1) + blr + +do_show_syscall_exit: +#ifdef SHOW_SYSCALLS_TASK + lis r11,show_syscalls_task@ha + lwz r11,show_syscalls_task@l(r11) + cmp 0,r2,r11 + bnelr +#endif + stw r31,GPR31(r1) + mflr r31 + stw r3,RESULT(r1) /* Save result */ + mr r4,r3 + lis r3,79f@ha + addi r3,r3,79f@l + bl printk + lwz r3,RESULT(r1) + mtlr r31 + lwz r31,GPR31(r1) + blr + +7: .string "syscall %d(%x, %x, %x, %x, %x, " +77: .string "%x), current=%p\n" +79: .string " -> %x\n" + .align 2,0 + +#ifdef SHOW_SYSCALLS_TASK + .data + .globl show_syscalls_task +show_syscalls_task: + .long -1 + .text +#endif +#endif /* SHOW_SYSCALLS */ + +/* + * The sigsuspend and rt_sigsuspend system calls can call do_signal + * and thus put the process into the stopped state where we might + * want to examine its user state with ptrace. Therefore we need + * to save all the nonvolatile registers (r13 - r31) before calling + * the C code. + */ + .globl ppc_sigsuspend +ppc_sigsuspend: + SAVE_NVGPRS(r1) + lwz r0,TRAP(r1) + rlwinm r0,r0,0,0,30 /* clear LSB to indicate full */ + stw r0,TRAP(r1) /* register set saved */ + b sys_sigsuspend + + .globl ppc_rt_sigsuspend +ppc_rt_sigsuspend: + SAVE_NVGPRS(r1) + lwz r0,TRAP(r1) + rlwinm r0,r0,0,0,30 + stw r0,TRAP(r1) + b sys_rt_sigsuspend + + .globl ppc_fork +ppc_fork: + SAVE_NVGPRS(r1) + lwz r0,TRAP(r1) + rlwinm r0,r0,0,0,30 /* clear LSB to indicate full */ + stw r0,TRAP(r1) /* register set saved */ + b sys_fork + + .globl ppc_vfork +ppc_vfork: + SAVE_NVGPRS(r1) + lwz r0,TRAP(r1) + rlwinm r0,r0,0,0,30 /* clear LSB to indicate full */ + stw r0,TRAP(r1) /* register set saved */ + b sys_vfork + + .globl ppc_clone +ppc_clone: + SAVE_NVGPRS(r1) + lwz r0,TRAP(r1) + rlwinm r0,r0,0,0,30 /* clear LSB to indicate full */ + stw r0,TRAP(r1) /* register set saved */ + b sys_clone + + .globl ppc_swapcontext +ppc_swapcontext: + SAVE_NVGPRS(r1) + lwz r0,TRAP(r1) + rlwinm r0,r0,0,0,30 /* clear LSB to indicate full */ + stw r0,TRAP(r1) /* register set saved */ + b sys_swapcontext + +/* + * Top-level page fault handling. + * This is in assembler because if do_page_fault tells us that + * it is a bad kernel page fault, we want to save the non-volatile + * registers before calling bad_page_fault. + */ + .globl handle_page_fault +handle_page_fault: + stw r4,_DAR(r1) + addi r3,r1,STACK_FRAME_OVERHEAD + bl do_page_fault + cmpwi r3,0 + beq+ ret_from_except + SAVE_NVGPRS(r1) + lwz r0,TRAP(r1) + clrrwi r0,r0,1 + stw r0,TRAP(r1) + mr r5,r3 + addi r3,r1,STACK_FRAME_OVERHEAD + lwz r4,_DAR(r1) + bl bad_page_fault + b ret_from_except_full + +/* + * This routine switches between two different tasks. The process + * state of one is saved on its kernel stack. Then the state + * of the other is restored from its kernel stack. The memory + * management hardware is updated to the second process's state. + * Finally, we can return to the second process. + * On entry, r3 points to the THREAD for the current task, r4 + * points to the THREAD for the new task. + * + * This routine is always called with interrupts disabled. + * + * Note: there are two ways to get to the "going out" portion + * of this code; either by coming in via the entry (_switch) + * or via "fork" which must set up an environment equivalent + * to the "_switch" path. If you change this , you'll have to + * change the fork code also. + * + * The code which creates the new task context is in 'copy_thread' + * in arch/ppc/kernel/process.c + */ +_GLOBAL(_switch) + stwu r1,-INT_FRAME_SIZE(r1) + mflr r0 + stw r0,INT_FRAME_SIZE+4(r1) + /* r3-r12 are caller saved -- Cort */ + SAVE_NVGPRS(r1) + stw r0,_NIP(r1) /* Return to switch caller */ + mfmsr r11 + li r0,MSR_FP /* Disable floating-point */ +#ifdef CONFIG_ALTIVEC +BEGIN_FTR_SECTION + oris r0,r0,MSR_VEC@h /* Disable altivec */ + mfspr r12,SPRN_VRSAVE /* save vrsave register value */ + stw r12,THREAD+THREAD_VRSAVE(r2) +END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) +#endif /* CONFIG_ALTIVEC */ +#ifdef CONFIG_SPE + oris r0,r0,MSR_SPE@h /* Disable SPE */ + mfspr r12,SPRN_SPEFSCR /* save spefscr register value */ + stw r12,THREAD+THREAD_SPEFSCR(r2) +#endif /* CONFIG_SPE */ + and. r0,r0,r11 /* FP or altivec or SPE enabled? */ + beq+ 1f + andc r11,r11,r0 + MTMSRD(r11) + isync +1: stw r11,_MSR(r1) + mfcr r10 + stw r10,_CCR(r1) + stw r1,KSP(r3) /* Set old stack pointer */ + +#ifdef CONFIG_SMP + /* We need a sync somewhere here to make sure that if the + * previous task gets rescheduled on another CPU, it sees all + * stores it has performed on this one. + */ + sync +#endif /* CONFIG_SMP */ + + tophys(r0,r4) + CLR_TOP32(r0) + mtspr SPRN_SPRG3,r0 /* Update current THREAD phys addr */ + lwz r1,KSP(r4) /* Load new stack pointer */ + + /* save the old current 'last' for return value */ + mr r3,r2 + addi r2,r4,-THREAD /* Update current */ + +#ifdef CONFIG_ALTIVEC +BEGIN_FTR_SECTION + lwz r0,THREAD+THREAD_VRSAVE(r2) + mtspr SPRN_VRSAVE,r0 /* if G4, restore VRSAVE reg */ +END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) +#endif /* CONFIG_ALTIVEC */ +#ifdef CONFIG_SPE + lwz r0,THREAD+THREAD_SPEFSCR(r2) + mtspr SPRN_SPEFSCR,r0 /* restore SPEFSCR reg */ +#endif /* CONFIG_SPE */ + + lwz r0,_CCR(r1) + mtcrf 0xFF,r0 + /* r3-r12 are destroyed -- Cort */ + REST_NVGPRS(r1) + + lwz r4,_NIP(r1) /* Return to _switch caller in new task */ + mtlr r4 + addi r1,r1,INT_FRAME_SIZE + blr + + .globl sigreturn_exit +sigreturn_exit: + subi r1,r3,STACK_FRAME_OVERHEAD + rlwinm r12,r1,0,0,18 /* current_thread_info() */ + lwz r9,TI_FLAGS(r12) + andi. r0,r9,_TIF_SYSCALL_TRACE + bnel- do_syscall_trace + /* fall through */ + + .globl ret_from_except_full +ret_from_except_full: + REST_NVGPRS(r1) + /* fall through */ + + .globl ret_from_except +ret_from_except: + /* Hard-disable interrupts so that current_thread_info()->flags + * can't change between when we test it and when we return + * from the interrupt. */ + LOAD_MSR_KERNEL(r10,MSR_KERNEL) + SYNC /* Some chip revs have problems here... */ + MTMSRD(r10) /* disable interrupts */ + + lwz r3,_MSR(r1) /* Returning to user mode? */ + andi. r0,r3,MSR_PR + beq resume_kernel + +user_exc_return: /* r10 contains MSR_KERNEL here */ + /* Check current_thread_info()->flags */ + rlwinm r9,r1,0,0,18 + lwz r9,TI_FLAGS(r9) + andi. r0,r9,(_TIF_SIGPENDING|_TIF_NEED_RESCHED) + bne do_work + +restore_user: +#if defined(CONFIG_4xx) || defined(CONFIG_BOOKE) + /* Check whether this process has its own DBCR0 value. The single + step bit tells us that dbcr0 should be loaded. */ + lwz r0,THREAD+THREAD_DBCR0(r2) + andis. r10,r0,DBCR0_IC@h + bnel- load_dbcr0 +#endif + +#ifdef CONFIG_PREEMPT + b restore + +/* N.B. the only way to get here is from the beq following ret_from_except. */ +resume_kernel: + /* check current_thread_info->preempt_count */ + rlwinm r9,r1,0,0,18 + lwz r0,TI_PREEMPT(r9) + cmpwi 0,r0,0 /* if non-zero, just restore regs and return */ + bne restore + lwz r0,TI_FLAGS(r9) + andi. r0,r0,_TIF_NEED_RESCHED + beq+ restore + andi. r0,r3,MSR_EE /* interrupts off? */ + beq restore /* don't schedule if so */ +1: bl preempt_schedule_irq + rlwinm r9,r1,0,0,18 + lwz r3,TI_FLAGS(r9) + andi. r0,r3,_TIF_NEED_RESCHED + bne- 1b +#else +resume_kernel: +#endif /* CONFIG_PREEMPT */ + + /* interrupts are hard-disabled at this point */ +restore: + lwz r0,GPR0(r1) + lwz r2,GPR2(r1) + REST_4GPRS(3, r1) + REST_2GPRS(7, r1) + + lwz r10,_XER(r1) + lwz r11,_CTR(r1) + mtspr SPRN_XER,r10 + mtctr r11 + + PPC405_ERR77(0,r1) + stwcx. r0,0,r1 /* to clear the reservation */ + +#if !(defined(CONFIG_4xx) || defined(CONFIG_BOOKE)) + lwz r9,_MSR(r1) + andi. r10,r9,MSR_RI /* check if this exception occurred */ + beql nonrecoverable /* at a bad place (MSR:RI = 0) */ + + lwz r10,_CCR(r1) + lwz r11,_LINK(r1) + mtcrf 0xFF,r10 + mtlr r11 + + /* + * Once we put values in SRR0 and SRR1, we are in a state + * where exceptions are not recoverable, since taking an + * exception will trash SRR0 and SRR1. Therefore we clear the + * MSR:RI bit to indicate this. If we do take an exception, + * we can't return to the point of the exception but we + * can restart the exception exit path at the label + * exc_exit_restart below. -- paulus + */ + LOAD_MSR_KERNEL(r10,MSR_KERNEL & ~MSR_RI) + SYNC + MTMSRD(r10) /* clear the RI bit */ + .globl exc_exit_restart +exc_exit_restart: + lwz r9,_MSR(r1) + lwz r12,_NIP(r1) + FIX_SRR1(r9,r10) + mtspr SPRN_SRR0,r12 + mtspr SPRN_SRR1,r9 + REST_4GPRS(9, r1) + lwz r1,GPR1(r1) + .globl exc_exit_restart_end +exc_exit_restart_end: + SYNC + RFI + +#else /* !(CONFIG_4xx || CONFIG_BOOKE) */ + /* + * This is a bit different on 4xx/Book-E because it doesn't have + * the RI bit in the MSR. + * The TLB miss handler checks if we have interrupted + * the exception exit path and restarts it if so + * (well maybe one day it will... :). + */ + lwz r11,_LINK(r1) + mtlr r11 + lwz r10,_CCR(r1) + mtcrf 0xff,r10 + REST_2GPRS(9, r1) + .globl exc_exit_restart +exc_exit_restart: + lwz r11,_NIP(r1) + lwz r12,_MSR(r1) +exc_exit_start: + mtspr SPRN_SRR0,r11 + mtspr SPRN_SRR1,r12 + REST_2GPRS(11, r1) + lwz r1,GPR1(r1) + .globl exc_exit_restart_end +exc_exit_restart_end: + PPC405_ERR77_SYNC + rfi + b . /* prevent prefetch past rfi */ + +/* + * Returning from a critical interrupt in user mode doesn't need + * to be any different from a normal exception. For a critical + * interrupt in the kernel, we just return (without checking for + * preemption) since the interrupt may have happened at some crucial + * place (e.g. inside the TLB miss handler), and because we will be + * running with r1 pointing into critical_stack, not the current + * process's kernel stack (and therefore current_thread_info() will + * give the wrong answer). + * We have to restore various SPRs that may have been in use at the + * time of the critical interrupt. + * + */ + .globl ret_from_crit_exc +ret_from_crit_exc: + REST_NVGPRS(r1) + lwz r3,_MSR(r1) + andi. r3,r3,MSR_PR + LOAD_MSR_KERNEL(r10,MSR_KERNEL) + bne user_exc_return + + lwz r0,GPR0(r1) + lwz r2,GPR2(r1) + REST_4GPRS(3, r1) + REST_2GPRS(7, r1) + + lwz r10,_XER(r1) + lwz r11,_CTR(r1) + mtspr SPRN_XER,r10 + mtctr r11 + + PPC405_ERR77(0,r1) + stwcx. r0,0,r1 /* to clear the reservation */ + + lwz r11,_LINK(r1) + mtlr r11 + lwz r10,_CCR(r1) + mtcrf 0xff,r10 +#ifdef CONFIG_40x + /* avoid any possible TLB misses here by turning off MSR.DR, we + * assume the instructions here are mapped by a pinned TLB entry */ + li r10,MSR_IR + mtmsr r10 + isync + tophys(r1, r1) +#endif + lwz r9,_DEAR(r1) + lwz r10,_ESR(r1) + mtspr SPRN_DEAR,r9 + mtspr SPRN_ESR,r10 + lwz r11,_NIP(r1) + lwz r12,_MSR(r1) + mtspr SPRN_CSRR0,r11 + mtspr SPRN_CSRR1,r12 + lwz r9,GPR9(r1) + lwz r12,GPR12(r1) + lwz r10,GPR10(r1) + lwz r11,GPR11(r1) + lwz r1,GPR1(r1) + PPC405_ERR77_SYNC + rfci + b . /* prevent prefetch past rfci */ + +#ifdef CONFIG_BOOKE +/* + * Return from a machine check interrupt, similar to a critical + * interrupt. + */ + .globl ret_from_mcheck_exc +ret_from_mcheck_exc: + REST_NVGPRS(r1) + lwz r3,_MSR(r1) + andi. r3,r3,MSR_PR + LOAD_MSR_KERNEL(r10,MSR_KERNEL) + bne user_exc_return + + lwz r0,GPR0(r1) + lwz r2,GPR2(r1) + REST_4GPRS(3, r1) + REST_2GPRS(7, r1) + + lwz r10,_XER(r1) + lwz r11,_CTR(r1) + mtspr SPRN_XER,r10 + mtctr r11 + + stwcx. r0,0,r1 /* to clear the reservation */ + + lwz r11,_LINK(r1) + mtlr r11 + lwz r10,_CCR(r1) + mtcrf 0xff,r10 + lwz r9,_DEAR(r1) + lwz r10,_ESR(r1) + mtspr SPRN_DEAR,r9 + mtspr SPRN_ESR,r10 + lwz r11,_NIP(r1) + lwz r12,_MSR(r1) + mtspr SPRN_MCSRR0,r11 + mtspr SPRN_MCSRR1,r12 + lwz r9,GPR9(r1) + lwz r12,GPR12(r1) + lwz r10,GPR10(r1) + lwz r11,GPR11(r1) + lwz r1,GPR1(r1) + RFMCI +#endif /* CONFIG_BOOKE */ + +/* + * Load the DBCR0 value for a task that is being ptraced, + * having first saved away the global DBCR0. Note that r0 + * has the dbcr0 value to set upon entry to this. + */ +load_dbcr0: + mfmsr r10 /* first disable debug exceptions */ + rlwinm r10,r10,0,~MSR_DE + mtmsr r10 + isync + mfspr r10,SPRN_DBCR0 + lis r11,global_dbcr0@ha + addi r11,r11,global_dbcr0@l + stw r10,0(r11) + mtspr SPRN_DBCR0,r0 + lwz r10,4(r11) + addi r10,r10,1 + stw r10,4(r11) + li r11,-1 + mtspr SPRN_DBSR,r11 /* clear all pending debug events */ + blr + + .comm global_dbcr0,8 +#endif /* !(CONFIG_4xx || CONFIG_BOOKE) */ + +do_work: /* r10 contains MSR_KERNEL here */ + andi. r0,r9,_TIF_NEED_RESCHED + beq do_user_signal + +do_resched: /* r10 contains MSR_KERNEL here */ + ori r10,r10,MSR_EE + SYNC + MTMSRD(r10) /* hard-enable interrupts */ + bl schedule +recheck: + LOAD_MSR_KERNEL(r10,MSR_KERNEL) + SYNC + MTMSRD(r10) /* disable interrupts */ + rlwinm r9,r1,0,0,18 + lwz r9,TI_FLAGS(r9) + andi. r0,r9,_TIF_NEED_RESCHED + bne- do_resched + andi. r0,r9,_TIF_SIGPENDING + beq restore_user +do_user_signal: /* r10 contains MSR_KERNEL here */ + ori r10,r10,MSR_EE + SYNC + MTMSRD(r10) /* hard-enable interrupts */ + /* save r13-r31 in the exception frame, if not already done */ + lwz r3,TRAP(r1) + andi. r0,r3,1 + beq 2f + SAVE_NVGPRS(r1) + rlwinm r3,r3,0,0,30 + stw r3,TRAP(r1) +2: li r3,0 + addi r4,r1,STACK_FRAME_OVERHEAD + bl do_signal + REST_NVGPRS(r1) + b recheck + +/* + * We come here when we are at the end of handling an exception + * that occurred at a place where taking an exception will lose + * state information, such as the contents of SRR0 and SRR1. + */ +nonrecoverable: + lis r10,exc_exit_restart_end@ha + addi r10,r10,exc_exit_restart_end@l + cmplw r12,r10 + bge 3f + lis r11,exc_exit_restart@ha + addi r11,r11,exc_exit_restart@l + cmplw r12,r11 + blt 3f + lis r10,ee_restarts@ha + lwz r12,ee_restarts@l(r10) + addi r12,r12,1 + stw r12,ee_restarts@l(r10) + mr r12,r11 /* restart at exc_exit_restart */ + blr +3: /* OK, we can't recover, kill this process */ + /* but the 601 doesn't implement the RI bit, so assume it's OK */ +BEGIN_FTR_SECTION + blr +END_FTR_SECTION_IFSET(CPU_FTR_601) + lwz r3,TRAP(r1) + andi. r0,r3,1 + beq 4f + SAVE_NVGPRS(r1) + rlwinm r3,r3,0,0,30 + stw r3,TRAP(r1) +4: addi r3,r1,STACK_FRAME_OVERHEAD + bl nonrecoverable_exception + /* shouldn't return */ + b 4b + + .comm ee_restarts,4 + +/* + * PROM code for specific machines follows. Put it + * here so it's easy to add arch-specific sections later. + * -- Cort + */ +#ifdef CONFIG_PPC_OF +/* + * On CHRP, the Run-Time Abstraction Services (RTAS) have to be + * called with the MMU off. + */ +_GLOBAL(enter_rtas) + stwu r1,-INT_FRAME_SIZE(r1) + mflr r0 + stw r0,INT_FRAME_SIZE+4(r1) + lis r4,rtas_data@ha + lwz r4,rtas_data@l(r4) + lis r6,1f@ha /* physical return address for rtas */ + addi r6,r6,1f@l + tophys(r6,r6) + tophys(r7,r1) + lis r8,rtas_entry@ha + lwz r8,rtas_entry@l(r8) + mfmsr r9 + stw r9,8(r1) + LOAD_MSR_KERNEL(r0,MSR_KERNEL) + SYNC /* disable interrupts so SRR0/1 */ + MTMSRD(r0) /* don't get trashed */ + li r9,MSR_KERNEL & ~(MSR_IR|MSR_DR) + mtlr r6 + CLR_TOP32(r7) + mtspr SPRN_SPRG2,r7 + mtspr SPRN_SRR0,r8 + mtspr SPRN_SRR1,r9 + RFI +1: tophys(r9,r1) + lwz r8,INT_FRAME_SIZE+4(r9) /* get return address */ + lwz r9,8(r9) /* original msr value */ + FIX_SRR1(r9,r0) + addi r1,r1,INT_FRAME_SIZE + li r0,0 + mtspr SPRN_SPRG2,r0 + mtspr SPRN_SRR0,r8 + mtspr SPRN_SRR1,r9 + RFI /* return to caller */ + + .globl machine_check_in_rtas +machine_check_in_rtas: + twi 31,0,0 + /* XXX load up BATs and panic */ + +#endif /* CONFIG_PPC_OF */ diff --git a/arch/ppc/kernel/find_name.c b/arch/ppc/kernel/find_name.c new file mode 100644 index 00000000000..3c0fa8e0c07 --- /dev/null +++ b/arch/ppc/kernel/find_name.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include +/* + * Finds a given address in the System.map and prints it out + * with its neighbors. -- Cort + */ + +int main(int argc, char **argv) +{ + unsigned long addr, cmp, i; + FILE *f; + char s[256], last[256]; + + if ( argc < 2 ) + { + fprintf(stderr, "Usage: %s
\n", argv[0]); + return -1; + } + + for ( i = 1 ; argv[i] ; i++ ) + { + sscanf( argv[i], "%0lx", &addr ); + /* adjust if addr is relative to kernelbase */ + if ( addr < PAGE_OFFSET ) + addr += PAGE_OFFSET; + + if ( (f = fopen( "System.map", "r" )) == NULL ) + { + perror("fopen()\n"); + exit(-1); + } + + while ( !feof(f) ) + { + fgets(s, 255 , f); + sscanf( s, "%0lx", &cmp ); + if ( addr < cmp ) + break; + strcpy( last, s); + } + + printf( "%s%s", last, s ); + } + fclose(f); + return 0; +} diff --git a/arch/ppc/kernel/head.S b/arch/ppc/kernel/head.S new file mode 100644 index 00000000000..1a89a71e0ac --- /dev/null +++ b/arch/ppc/kernel/head.S @@ -0,0 +1,1710 @@ +/* + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Rewritten by Cort Dougan (cort@cs.nmt.edu) for PReP + * Copyright (C) 1996 Cort Dougan + * Adapted for Power Macintosh by Paul Mackerras. + * Low-level exception handlers and MMU support + * rewritten by Paul Mackerras. + * Copyright (C) 1996 Paul Mackerras. + * MPC8xx modifications Copyright (C) 1997 Dan Malek (dmalek@jlc.net). + * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). + * + * This file contains the low-level support and setup for the + * PowerPC platform, including trap and interrupt dispatch. + * (The PPC 8xx embedded CPUs use head_8xx.S instead.) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_APUS +#include +#endif + +#ifdef CONFIG_PPC64BRIDGE +#define LOAD_BAT(n, reg, RA, RB) \ + ld RA,(n*32)+0(reg); \ + ld RB,(n*32)+8(reg); \ + mtspr SPRN_IBAT##n##U,RA; \ + mtspr SPRN_IBAT##n##L,RB; \ + ld RA,(n*32)+16(reg); \ + ld RB,(n*32)+24(reg); \ + mtspr SPRN_DBAT##n##U,RA; \ + mtspr SPRN_DBAT##n##L,RB; \ + +#else /* CONFIG_PPC64BRIDGE */ + +/* 601 only have IBAT; cr0.eq is set on 601 when using this macro */ +#define LOAD_BAT(n, reg, RA, RB) \ + /* see the comment for clear_bats() -- Cort */ \ + li RA,0; \ + mtspr SPRN_IBAT##n##U,RA; \ + mtspr SPRN_DBAT##n##U,RA; \ + lwz RA,(n*16)+0(reg); \ + lwz RB,(n*16)+4(reg); \ + mtspr SPRN_IBAT##n##U,RA; \ + mtspr SPRN_IBAT##n##L,RB; \ + beq 1f; \ + lwz RA,(n*16)+8(reg); \ + lwz RB,(n*16)+12(reg); \ + mtspr SPRN_DBAT##n##U,RA; \ + mtspr SPRN_DBAT##n##L,RB; \ +1: +#endif /* CONFIG_PPC64BRIDGE */ + + .text + .stabs "arch/ppc/kernel/",N_SO,0,0,0f + .stabs "head.S",N_SO,0,0,0f +0: + .globl _stext +_stext: + +/* + * _start is defined this way because the XCOFF loader in the OpenFirmware + * on the powermac expects the entry point to be a procedure descriptor. + */ + .text + .globl _start +_start: + /* + * These are here for legacy reasons, the kernel used to + * need to look like a coff function entry for the pmac + * but we're always started by some kind of bootloader now. + * -- Cort + */ + nop /* used by __secondary_hold on prep (mtx) and chrp smp */ + nop /* used by __secondary_hold on prep (mtx) and chrp smp */ + nop + +/* PMAC + * Enter here with the kernel text, data and bss loaded starting at + * 0, running with virtual == physical mapping. + * r5 points to the prom entry point (the client interface handler + * address). Address translation is turned on, with the prom + * managing the hash table. Interrupts are disabled. The stack + * pointer (r1) points to just below the end of the half-meg region + * from 0x380000 - 0x400000, which is mapped in already. + * + * If we are booted from MacOS via BootX, we enter with the kernel + * image loaded somewhere, and the following values in registers: + * r3: 'BooX' (0x426f6f58) + * r4: virtual address of boot_infos_t + * r5: 0 + * + * APUS + * r3: 'APUS' + * r4: physical address of memory base + * Linux/m68k style BootInfo structure at &_end. + * + * PREP + * This is jumped to on prep systems right after the kernel is relocated + * to its proper place in memory by the boot loader. The expected layout + * of the regs is: + * r3: ptr to residual data + * r4: initrd_start or if no initrd then 0 + * r5: initrd_end - unused if r4 is 0 + * r6: Start of command line string + * r7: End of command line string + * + * This just gets a minimal mmu environment setup so we can call + * start_here() to do the real work. + * -- Cort + */ + + .globl __start +__start: +/* + * We have to do any OF calls before we map ourselves to KERNELBASE, + * because OF may have I/O devices mapped into that area + * (particularly on CHRP). + */ + mr r31,r3 /* save parameters */ + mr r30,r4 + mr r29,r5 + mr r28,r6 + mr r27,r7 + li r24,0 /* cpu # */ + +/* + * early_init() does the early machine identification and does + * the necessary low-level setup and clears the BSS + * -- Cort + */ + bl early_init + +/* + * On POWER4, we first need to tweak some CPU configuration registers + * like real mode cache inhibit or exception base + */ +#ifdef CONFIG_POWER4 + bl __970_cpu_preinit +#endif /* CONFIG_POWER4 */ + +#ifdef CONFIG_APUS +/* On APUS the __va/__pa constants need to be set to the correct + * values before continuing. + */ + mr r4,r30 + bl fix_mem_constants +#endif /* CONFIG_APUS */ + +/* Switch MMU off, clear BATs and flush TLB. At this point, r3 contains + * the physical address we are running at, returned by early_init() + */ + bl mmu_off +__after_mmu_off: +#ifndef CONFIG_POWER4 + bl clear_bats + bl flush_tlbs + + bl initial_bats +#if !defined(CONFIG_APUS) && defined(CONFIG_BOOTX_TEXT) + bl setup_disp_bat +#endif +#else /* CONFIG_POWER4 */ + bl reloc_offset + bl initial_mm_power4 +#endif /* CONFIG_POWER4 */ + +/* + * Call setup_cpu for CPU 0 and initialize 6xx Idle + */ + bl reloc_offset + li r24,0 /* cpu# */ + bl call_setup_cpu /* Call setup_cpu for this CPU */ +#ifdef CONFIG_6xx + bl reloc_offset + bl init_idle_6xx +#endif /* CONFIG_6xx */ +#ifdef CONFIG_POWER4 + bl reloc_offset + bl init_idle_power4 +#endif /* CONFIG_POWER4 */ + + +#ifndef CONFIG_APUS +/* + * We need to run with _start at physical address 0. + * On CHRP, we are loaded at 0x10000 since OF on CHRP uses + * the exception vectors at 0 (and therefore this copy + * overwrites OF's exception vectors with our own). + * If the MMU is already turned on, we copy stuff to KERNELBASE, + * otherwise we copy it to 0. + */ + bl reloc_offset + mr r26,r3 + addis r4,r3,KERNELBASE@h /* current address of _start */ + cmpwi 0,r4,0 /* are we already running at 0? */ + bne relocate_kernel +#endif /* CONFIG_APUS */ +/* + * we now have the 1st 16M of ram mapped with the bats. + * prep needs the mmu to be turned on here, but pmac already has it on. + * this shouldn't bother the pmac since it just gets turned on again + * as we jump to our code at KERNELBASE. -- Cort + * Actually no, pmac doesn't have it on any more. BootX enters with MMU + * off, and in other cases, we now turn it off before changing BATs above. + */ +turn_on_mmu: + mfmsr r0 + ori r0,r0,MSR_DR|MSR_IR + mtspr SPRN_SRR1,r0 + lis r0,start_here@h + ori r0,r0,start_here@l + mtspr SPRN_SRR0,r0 + SYNC + RFI /* enables MMU */ + +/* + * We need __secondary_hold as a place to hold the other cpus on + * an SMP machine, even when we are running a UP kernel. + */ + . = 0xc0 /* for prep bootloader */ + li r3,1 /* MTX only has 1 cpu */ + .globl __secondary_hold +__secondary_hold: + /* tell the master we're here */ + stw r3,4(0) +#ifdef CONFIG_SMP +100: lwz r4,0(0) + /* wait until we're told to start */ + cmpw 0,r4,r3 + bne 100b + /* our cpu # was at addr 0 - go */ + mr r24,r3 /* cpu # */ + b __secondary_start +#else + b . +#endif /* CONFIG_SMP */ + +/* + * Exception entry code. This code runs with address translation + * turned off, i.e. using physical addresses. + * We assume sprg3 has the physical address of the current + * task's thread_struct. + */ +#define EXCEPTION_PROLOG \ + mtspr SPRN_SPRG0,r10; \ + mtspr SPRN_SPRG1,r11; \ + mfcr r10; \ + EXCEPTION_PROLOG_1; \ + EXCEPTION_PROLOG_2 + +#define EXCEPTION_PROLOG_1 \ + mfspr r11,SPRN_SRR1; /* check whether user or kernel */ \ + andi. r11,r11,MSR_PR; \ + tophys(r11,r1); /* use tophys(r1) if kernel */ \ + beq 1f; \ + mfspr r11,SPRN_SPRG3; \ + lwz r11,THREAD_INFO-THREAD(r11); \ + addi r11,r11,THREAD_SIZE; \ + tophys(r11,r11); \ +1: subi r11,r11,INT_FRAME_SIZE /* alloc exc. frame */ + + +#define EXCEPTION_PROLOG_2 \ + CLR_TOP32(r11); \ + stw r10,_CCR(r11); /* save registers */ \ + stw r12,GPR12(r11); \ + stw r9,GPR9(r11); \ + mfspr r10,SPRN_SPRG0; \ + stw r10,GPR10(r11); \ + mfspr r12,SPRN_SPRG1; \ + stw r12,GPR11(r11); \ + mflr r10; \ + stw r10,_LINK(r11); \ + mfspr r12,SPRN_SRR0; \ + mfspr r9,SPRN_SRR1; \ + stw r1,GPR1(r11); \ + stw r1,0(r11); \ + tovirt(r1,r11); /* set new kernel sp */ \ + li r10,MSR_KERNEL & ~(MSR_IR|MSR_DR); /* can take exceptions */ \ + MTMSRD(r10); /* (except for mach check in rtas) */ \ + stw r0,GPR0(r11); \ + SAVE_4GPRS(3, r11); \ + SAVE_2GPRS(7, r11) + +/* + * Note: code which follows this uses cr0.eq (set if from kernel), + * r11, r12 (SRR0), and r9 (SRR1). + * + * Note2: once we have set r1 we are in a position to take exceptions + * again, and we could thus set MSR:RI at that point. + */ + +/* + * Exception vectors. + */ +#define EXCEPTION(n, label, hdlr, xfer) \ + . = n; \ +label: \ + EXCEPTION_PROLOG; \ + addi r3,r1,STACK_FRAME_OVERHEAD; \ + xfer(n, hdlr) + +#define EXC_XFER_TEMPLATE(n, hdlr, trap, copyee, tfer, ret) \ + li r10,trap; \ + stw r10,TRAP(r11); \ + li r10,MSR_KERNEL; \ + copyee(r10, r9); \ + bl tfer; \ +i##n: \ + .long hdlr; \ + .long ret + +#define COPY_EE(d, s) rlwimi d,s,0,16,16 +#define NOCOPY(d, s) + +#define EXC_XFER_STD(n, hdlr) \ + EXC_XFER_TEMPLATE(n, hdlr, n, NOCOPY, transfer_to_handler_full, \ + ret_from_except_full) + +#define EXC_XFER_LITE(n, hdlr) \ + EXC_XFER_TEMPLATE(n, hdlr, n+1, NOCOPY, transfer_to_handler, \ + ret_from_except) + +#define EXC_XFER_EE(n, hdlr) \ + EXC_XFER_TEMPLATE(n, hdlr, n, COPY_EE, transfer_to_handler_full, \ + ret_from_except_full) + +#define EXC_XFER_EE_LITE(n, hdlr) \ + EXC_XFER_TEMPLATE(n, hdlr, n+1, COPY_EE, transfer_to_handler, \ + ret_from_except) + +/* System reset */ +/* core99 pmac starts the seconary here by changing the vector, and + putting it back to what it was (UnknownException) when done. */ +#if defined(CONFIG_GEMINI) && defined(CONFIG_SMP) + . = 0x100 + b __secondary_start_gemini +#else + EXCEPTION(0x100, Reset, UnknownException, EXC_XFER_STD) +#endif + +/* Machine check */ +/* + * On CHRP, this is complicated by the fact that we could get a + * machine check inside RTAS, and we have no guarantee that certain + * critical registers will have the values we expect. The set of + * registers that might have bad values includes all the GPRs + * and all the BATs. We indicate that we are in RTAS by putting + * a non-zero value, the address of the exception frame to use, + * in SPRG2. The machine check handler checks SPRG2 and uses its + * value if it is non-zero. If we ever needed to free up SPRG2, + * we could use a field in the thread_info or thread_struct instead. + * (Other exception handlers assume that r1 is a valid kernel stack + * pointer when we take an exception from supervisor mode.) + * -- paulus. + */ + . = 0x200 + mtspr SPRN_SPRG0,r10 + mtspr SPRN_SPRG1,r11 + mfcr r10 +#ifdef CONFIG_PPC_CHRP + mfspr r11,SPRN_SPRG2 + cmpwi 0,r11,0 + bne 7f +#endif /* CONFIG_PPC_CHRP */ + EXCEPTION_PROLOG_1 +7: EXCEPTION_PROLOG_2 + addi r3,r1,STACK_FRAME_OVERHEAD +#ifdef CONFIG_PPC_CHRP + mfspr r4,SPRN_SPRG2 + cmpwi cr1,r4,0 + bne cr1,1f +#endif + EXC_XFER_STD(0x200, MachineCheckException) +#ifdef CONFIG_PPC_CHRP +1: b machine_check_in_rtas +#endif + +/* Data access exception. */ + . = 0x300 +#ifdef CONFIG_PPC64BRIDGE + b DataAccess +DataAccessCont: +#else +DataAccess: + EXCEPTION_PROLOG +#endif /* CONFIG_PPC64BRIDGE */ + mfspr r10,SPRN_DSISR + andis. r0,r10,0xa470 /* weird error? */ + bne 1f /* if not, try to put a PTE */ + mfspr r4,SPRN_DAR /* into the hash table */ + rlwinm r3,r10,32-15,21,21 /* DSISR_STORE -> _PAGE_RW */ + bl hash_page +1: stw r10,_DSISR(r11) + mr r5,r10 + mfspr r4,SPRN_DAR + EXC_XFER_EE_LITE(0x300, handle_page_fault) + +#ifdef CONFIG_PPC64BRIDGE +/* SLB fault on data access. */ + . = 0x380 + b DataSegment +#endif /* CONFIG_PPC64BRIDGE */ + +/* Instruction access exception. */ + . = 0x400 +#ifdef CONFIG_PPC64BRIDGE + b InstructionAccess +InstructionAccessCont: +#else +InstructionAccess: + EXCEPTION_PROLOG +#endif /* CONFIG_PPC64BRIDGE */ + andis. r0,r9,0x4000 /* no pte found? */ + beq 1f /* if so, try to put a PTE */ + li r3,0 /* into the hash table */ + mr r4,r12 /* SRR0 is fault address */ + bl hash_page +1: mr r4,r12 + mr r5,r9 + EXC_XFER_EE_LITE(0x400, handle_page_fault) + +#ifdef CONFIG_PPC64BRIDGE +/* SLB fault on instruction access. */ + . = 0x480 + b InstructionSegment +#endif /* CONFIG_PPC64BRIDGE */ + +/* External interrupt */ + EXCEPTION(0x500, HardwareInterrupt, do_IRQ, EXC_XFER_LITE) + +/* Alignment exception */ + . = 0x600 +Alignment: + EXCEPTION_PROLOG + mfspr r4,SPRN_DAR + stw r4,_DAR(r11) + mfspr r5,SPRN_DSISR + stw r5,_DSISR(r11) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_EE(0x600, AlignmentException) + +/* Program check exception */ + EXCEPTION(0x700, ProgramCheck, ProgramCheckException, EXC_XFER_STD) + +/* Floating-point unavailable */ + . = 0x800 +FPUnavailable: + EXCEPTION_PROLOG + bne load_up_fpu /* if from user, just load it up */ + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_EE_LITE(0x800, KernelFP) + +/* Decrementer */ + EXCEPTION(0x900, Decrementer, timer_interrupt, EXC_XFER_LITE) + + EXCEPTION(0xa00, Trap_0a, UnknownException, EXC_XFER_EE) + EXCEPTION(0xb00, Trap_0b, UnknownException, EXC_XFER_EE) + +/* System call */ + . = 0xc00 +SystemCall: + EXCEPTION_PROLOG + EXC_XFER_EE_LITE(0xc00, DoSyscall) + +/* Single step - not used on 601 */ + EXCEPTION(0xd00, SingleStep, SingleStepException, EXC_XFER_STD) + EXCEPTION(0xe00, Trap_0e, UnknownException, EXC_XFER_EE) + +/* + * The Altivec unavailable trap is at 0x0f20. Foo. + * We effectively remap it to 0x3000. + * We include an altivec unavailable exception vector even if + * not configured for Altivec, so that you can't panic a + * non-altivec kernel running on a machine with altivec just + * by executing an altivec instruction. + */ + . = 0xf00 + b Trap_0f + + . = 0xf20 + b AltiVecUnavailable + +Trap_0f: + EXCEPTION_PROLOG + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_EE(0xf00, UnknownException) + +/* + * Handle TLB miss for instruction on 603/603e. + * Note: we get an alternate set of r0 - r3 to use automatically. + */ + . = 0x1000 +InstructionTLBMiss: +/* + * r0: stored ctr + * r1: linux style pte ( later becomes ppc hardware pte ) + * r2: ptr to linux-style pte + * r3: scratch + */ + mfctr r0 + /* Get PTE (linux-style) and check access */ + mfspr r3,SPRN_IMISS + lis r1,KERNELBASE@h /* check if kernel address */ + cmplw 0,r3,r1 + mfspr r2,SPRN_SPRG3 + li r1,_PAGE_USER|_PAGE_PRESENT /* low addresses tested as user */ + lwz r2,PGDIR(r2) + blt+ 112f + lis r2,swapper_pg_dir@ha /* if kernel address, use */ + addi r2,r2,swapper_pg_dir@l /* kernel page table */ + mfspr r1,SPRN_SRR1 /* and MSR_PR bit from SRR1 */ + rlwinm r1,r1,32-12,29,29 /* shift MSR_PR to _PAGE_USER posn */ +112: tophys(r2,r2) + rlwimi r2,r3,12,20,29 /* insert top 10 bits of address */ + lwz r2,0(r2) /* get pmd entry */ + rlwinm. r2,r2,0,0,19 /* extract address of pte page */ + beq- InstructionAddressInvalid /* return if no mapping */ + rlwimi r2,r3,22,20,29 /* insert next 10 bits of address */ + lwz r3,0(r2) /* get linux-style pte */ + andc. r1,r1,r3 /* check access & ~permission */ + bne- InstructionAddressInvalid /* return if access not permitted */ + ori r3,r3,_PAGE_ACCESSED /* set _PAGE_ACCESSED in pte */ + /* + * NOTE! We are assuming this is not an SMP system, otherwise + * we would need to update the pte atomically with lwarx/stwcx. + */ + stw r3,0(r2) /* update PTE (accessed bit) */ + /* Convert linux-style PTE to low word of PPC-style PTE */ + rlwinm r1,r3,32-10,31,31 /* _PAGE_RW -> PP lsb */ + rlwinm r2,r3,32-7,31,31 /* _PAGE_DIRTY -> PP lsb */ + and r1,r1,r2 /* writable if _RW and _DIRTY */ + rlwimi r3,r3,32-1,30,30 /* _PAGE_USER -> PP msb */ + rlwimi r3,r3,32-1,31,31 /* _PAGE_USER -> PP lsb */ + ori r1,r1,0xe14 /* clear out reserved bits and M */ + andc r1,r3,r1 /* PP = user? (rw&dirty? 2: 3): 0 */ + mtspr SPRN_RPA,r1 + mfspr r3,SPRN_IMISS + tlbli r3 + mfspr r3,SPRN_SRR1 /* Need to restore CR0 */ + mtcrf 0x80,r3 + rfi +InstructionAddressInvalid: + mfspr r3,SPRN_SRR1 + rlwinm r1,r3,9,6,6 /* Get load/store bit */ + + addis r1,r1,0x2000 + mtspr SPRN_DSISR,r1 /* (shouldn't be needed) */ + mtctr r0 /* Restore CTR */ + andi. r2,r3,0xFFFF /* Clear upper bits of SRR1 */ + or r2,r2,r1 + mtspr SPRN_SRR1,r2 + mfspr r1,SPRN_IMISS /* Get failing address */ + rlwinm. r2,r2,0,31,31 /* Check for little endian access */ + rlwimi r2,r2,1,30,30 /* change 1 -> 3 */ + xor r1,r1,r2 + mtspr SPRN_DAR,r1 /* Set fault address */ + mfmsr r0 /* Restore "normal" registers */ + xoris r0,r0,MSR_TGPR>>16 + mtcrf 0x80,r3 /* Restore CR0 */ + mtmsr r0 + b InstructionAccess + +/* + * Handle TLB miss for DATA Load operation on 603/603e + */ + . = 0x1100 +DataLoadTLBMiss: +/* + * r0: stored ctr + * r1: linux style pte ( later becomes ppc hardware pte ) + * r2: ptr to linux-style pte + * r3: scratch + */ + mfctr r0 + /* Get PTE (linux-style) and check access */ + mfspr r3,SPRN_DMISS + lis r1,KERNELBASE@h /* check if kernel address */ + cmplw 0,r3,r1 + mfspr r2,SPRN_SPRG3 + li r1,_PAGE_USER|_PAGE_PRESENT /* low addresses tested as user */ + lwz r2,PGDIR(r2) + blt+ 112f + lis r2,swapper_pg_dir@ha /* if kernel address, use */ + addi r2,r2,swapper_pg_dir@l /* kernel page table */ + mfspr r1,SPRN_SRR1 /* and MSR_PR bit from SRR1 */ + rlwinm r1,r1,32-12,29,29 /* shift MSR_PR to _PAGE_USER posn */ +112: tophys(r2,r2) + rlwimi r2,r3,12,20,29 /* insert top 10 bits of address */ + lwz r2,0(r2) /* get pmd entry */ + rlwinm. r2,r2,0,0,19 /* extract address of pte page */ + beq- DataAddressInvalid /* return if no mapping */ + rlwimi r2,r3,22,20,29 /* insert next 10 bits of address */ + lwz r3,0(r2) /* get linux-style pte */ + andc. r1,r1,r3 /* check access & ~permission */ + bne- DataAddressInvalid /* return if access not permitted */ + ori r3,r3,_PAGE_ACCESSED /* set _PAGE_ACCESSED in pte */ + /* + * NOTE! We are assuming this is not an SMP system, otherwise + * we would need to update the pte atomically with lwarx/stwcx. + */ + stw r3,0(r2) /* update PTE (accessed bit) */ + /* Convert linux-style PTE to low word of PPC-style PTE */ + rlwinm r1,r3,32-10,31,31 /* _PAGE_RW -> PP lsb */ + rlwinm r2,r3,32-7,31,31 /* _PAGE_DIRTY -> PP lsb */ + and r1,r1,r2 /* writable if _RW and _DIRTY */ + rlwimi r3,r3,32-1,30,30 /* _PAGE_USER -> PP msb */ + rlwimi r3,r3,32-1,31,31 /* _PAGE_USER -> PP lsb */ + ori r1,r1,0xe14 /* clear out reserved bits and M */ + andc r1,r3,r1 /* PP = user? (rw&dirty? 2: 3): 0 */ + mtspr SPRN_RPA,r1 + mfspr r3,SPRN_DMISS + tlbld r3 + mfspr r3,SPRN_SRR1 /* Need to restore CR0 */ + mtcrf 0x80,r3 + rfi +DataAddressInvalid: + mfspr r3,SPRN_SRR1 + rlwinm r1,r3,9,6,6 /* Get load/store bit */ + addis r1,r1,0x2000 + mtspr SPRN_DSISR,r1 + mtctr r0 /* Restore CTR */ + andi. r2,r3,0xFFFF /* Clear upper bits of SRR1 */ + mtspr SPRN_SRR1,r2 + mfspr r1,SPRN_DMISS /* Get failing address */ + rlwinm. r2,r2,0,31,31 /* Check for little endian access */ + beq 20f /* Jump if big endian */ + xori r1,r1,3 +20: mtspr SPRN_DAR,r1 /* Set fault address */ + mfmsr r0 /* Restore "normal" registers */ + xoris r0,r0,MSR_TGPR>>16 + mtcrf 0x80,r3 /* Restore CR0 */ + mtmsr r0 + b DataAccess + +/* + * Handle TLB miss for DATA Store on 603/603e + */ + . = 0x1200 +DataStoreTLBMiss: +/* + * r0: stored ctr + * r1: linux style pte ( later becomes ppc hardware pte ) + * r2: ptr to linux-style pte + * r3: scratch + */ + mfctr r0 + /* Get PTE (linux-style) and check access */ + mfspr r3,SPRN_DMISS + lis r1,KERNELBASE@h /* check if kernel address */ + cmplw 0,r3,r1 + mfspr r2,SPRN_SPRG3 + li r1,_PAGE_RW|_PAGE_USER|_PAGE_PRESENT /* access flags */ + lwz r2,PGDIR(r2) + blt+ 112f + lis r2,swapper_pg_dir@ha /* if kernel address, use */ + addi r2,r2,swapper_pg_dir@l /* kernel page table */ + mfspr r1,SPRN_SRR1 /* and MSR_PR bit from SRR1 */ + rlwinm r1,r1,32-12,29,29 /* shift MSR_PR to _PAGE_USER posn */ +112: tophys(r2,r2) + rlwimi r2,r3,12,20,29 /* insert top 10 bits of address */ + lwz r2,0(r2) /* get pmd entry */ + rlwinm. r2,r2,0,0,19 /* extract address of pte page */ + beq- DataAddressInvalid /* return if no mapping */ + rlwimi r2,r3,22,20,29 /* insert next 10 bits of address */ + lwz r3,0(r2) /* get linux-style pte */ + andc. r1,r1,r3 /* check access & ~permission */ + bne- DataAddressInvalid /* return if access not permitted */ + ori r3,r3,_PAGE_ACCESSED|_PAGE_DIRTY + /* + * NOTE! We are assuming this is not an SMP system, otherwise + * we would need to update the pte atomically with lwarx/stwcx. + */ + stw r3,0(r2) /* update PTE (accessed/dirty bits) */ + /* Convert linux-style PTE to low word of PPC-style PTE */ + rlwimi r3,r3,32-1,30,30 /* _PAGE_USER -> PP msb */ + li r1,0xe15 /* clear out reserved bits and M */ + andc r1,r3,r1 /* PP = user? 2: 0 */ + mtspr SPRN_RPA,r1 + mfspr r3,SPRN_DMISS + tlbld r3 + mfspr r3,SPRN_SRR1 /* Need to restore CR0 */ + mtcrf 0x80,r3 + rfi + +#ifndef CONFIG_ALTIVEC +#define AltivecAssistException UnknownException +#endif + + EXCEPTION(0x1300, Trap_13, InstructionBreakpoint, EXC_XFER_EE) + EXCEPTION(0x1400, SMI, SMIException, EXC_XFER_EE) + EXCEPTION(0x1500, Trap_15, UnknownException, EXC_XFER_EE) +#ifdef CONFIG_POWER4 + EXCEPTION(0x1600, Trap_16, UnknownException, EXC_XFER_EE) + EXCEPTION(0x1700, Trap_17, AltivecAssistException, EXC_XFER_EE) + EXCEPTION(0x1800, Trap_18, TAUException, EXC_XFER_STD) +#else /* !CONFIG_POWER4 */ + EXCEPTION(0x1600, Trap_16, AltivecAssistException, EXC_XFER_EE) + EXCEPTION(0x1700, Trap_17, TAUException, EXC_XFER_STD) + EXCEPTION(0x1800, Trap_18, UnknownException, EXC_XFER_EE) +#endif /* CONFIG_POWER4 */ + EXCEPTION(0x1900, Trap_19, UnknownException, EXC_XFER_EE) + EXCEPTION(0x1a00, Trap_1a, UnknownException, EXC_XFER_EE) + EXCEPTION(0x1b00, Trap_1b, UnknownException, EXC_XFER_EE) + EXCEPTION(0x1c00, Trap_1c, UnknownException, EXC_XFER_EE) + EXCEPTION(0x1d00, Trap_1d, UnknownException, EXC_XFER_EE) + EXCEPTION(0x1e00, Trap_1e, UnknownException, EXC_XFER_EE) + EXCEPTION(0x1f00, Trap_1f, UnknownException, EXC_XFER_EE) + EXCEPTION(0x2000, RunMode, RunModeException, EXC_XFER_EE) + EXCEPTION(0x2100, Trap_21, UnknownException, EXC_XFER_EE) + EXCEPTION(0x2200, Trap_22, UnknownException, EXC_XFER_EE) + EXCEPTION(0x2300, Trap_23, UnknownException, EXC_XFER_EE) + EXCEPTION(0x2400, Trap_24, UnknownException, EXC_XFER_EE) + EXCEPTION(0x2500, Trap_25, UnknownException, EXC_XFER_EE) + EXCEPTION(0x2600, Trap_26, UnknownException, EXC_XFER_EE) + EXCEPTION(0x2700, Trap_27, UnknownException, EXC_XFER_EE) + EXCEPTION(0x2800, Trap_28, UnknownException, EXC_XFER_EE) + EXCEPTION(0x2900, Trap_29, UnknownException, EXC_XFER_EE) + EXCEPTION(0x2a00, Trap_2a, UnknownException, EXC_XFER_EE) + EXCEPTION(0x2b00, Trap_2b, UnknownException, EXC_XFER_EE) + EXCEPTION(0x2c00, Trap_2c, UnknownException, EXC_XFER_EE) + EXCEPTION(0x2d00, Trap_2d, UnknownException, EXC_XFER_EE) + EXCEPTION(0x2e00, Trap_2e, UnknownException, EXC_XFER_EE) + EXCEPTION(0x2f00, MOLTrampoline, UnknownException, EXC_XFER_EE_LITE) + + .globl mol_trampoline + .set mol_trampoline, i0x2f00 + + . = 0x3000 + +AltiVecUnavailable: + EXCEPTION_PROLOG +#ifdef CONFIG_ALTIVEC + bne load_up_altivec /* if from user, just load it up */ +#endif /* CONFIG_ALTIVEC */ + EXC_XFER_EE_LITE(0xf20, AltivecUnavailException) + +#ifdef CONFIG_PPC64BRIDGE +DataAccess: + EXCEPTION_PROLOG + b DataAccessCont + +InstructionAccess: + EXCEPTION_PROLOG + b InstructionAccessCont + +DataSegment: + EXCEPTION_PROLOG + addi r3,r1,STACK_FRAME_OVERHEAD + mfspr r4,SPRN_DAR + stw r4,_DAR(r11) + EXC_XFER_STD(0x380, UnknownException) + +InstructionSegment: + EXCEPTION_PROLOG + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_STD(0x480, UnknownException) +#endif /* CONFIG_PPC64BRIDGE */ + +/* + * This task wants to use the FPU now. + * On UP, disable FP for the task which had the FPU previously, + * and save its floating-point registers in its thread_struct. + * Load up this task's FP registers from its thread_struct, + * enable the FPU for the current task and return to the task. + */ +load_up_fpu: + mfmsr r5 + ori r5,r5,MSR_FP +#ifdef CONFIG_PPC64BRIDGE + clrldi r5,r5,1 /* turn off 64-bit mode */ +#endif /* CONFIG_PPC64BRIDGE */ + SYNC + MTMSRD(r5) /* enable use of fpu now */ + isync +/* + * For SMP, we don't do lazy FPU switching because it just gets too + * horrendously complex, especially when a task switches from one CPU + * to another. Instead we call giveup_fpu in switch_to. + */ +#ifndef CONFIG_SMP + tophys(r6,0) /* get __pa constant */ + addis r3,r6,last_task_used_math@ha + lwz r4,last_task_used_math@l(r3) + cmpwi 0,r4,0 + beq 1f + add r4,r4,r6 + addi r4,r4,THREAD /* want last_task_used_math->thread */ + SAVE_32FPRS(0, r4) + mffs fr0 + stfd fr0,THREAD_FPSCR-4(r4) + lwz r5,PT_REGS(r4) + add r5,r5,r6 + lwz r4,_MSR-STACK_FRAME_OVERHEAD(r5) + li r10,MSR_FP|MSR_FE0|MSR_FE1 + andc r4,r4,r10 /* disable FP for previous task */ + stw r4,_MSR-STACK_FRAME_OVERHEAD(r5) +1: +#endif /* CONFIG_SMP */ + /* enable use of FP after return */ + mfspr r5,SPRN_SPRG3 /* current task's THREAD (phys) */ + lwz r4,THREAD_FPEXC_MODE(r5) + ori r9,r9,MSR_FP /* enable FP for current */ + or r9,r9,r4 + lfd fr0,THREAD_FPSCR-4(r5) + mtfsf 0xff,fr0 + REST_32FPRS(0, r5) +#ifndef CONFIG_SMP + subi r4,r5,THREAD + sub r4,r4,r6 + stw r4,last_task_used_math@l(r3) +#endif /* CONFIG_SMP */ + /* restore registers and return */ + /* we haven't used ctr or xer or lr */ + /* fall through to fast_exception_return */ + + .globl fast_exception_return +fast_exception_return: + andi. r10,r9,MSR_RI /* check for recoverable interrupt */ + beq 1f /* if not, we've got problems */ +2: REST_4GPRS(3, r11) + lwz r10,_CCR(r11) + REST_GPR(1, r11) + mtcr r10 + lwz r10,_LINK(r11) + mtlr r10 + REST_GPR(10, r11) + mtspr SPRN_SRR1,r9 + mtspr SPRN_SRR0,r12 + REST_GPR(9, r11) + REST_GPR(12, r11) + lwz r11,GPR11(r11) + SYNC + RFI + +/* check if the exception happened in a restartable section */ +1: lis r3,exc_exit_restart_end@ha + addi r3,r3,exc_exit_restart_end@l + cmplw r12,r3 + bge 3f + lis r4,exc_exit_restart@ha + addi r4,r4,exc_exit_restart@l + cmplw r12,r4 + blt 3f + lis r3,fee_restarts@ha + tophys(r3,r3) + lwz r5,fee_restarts@l(r3) + addi r5,r5,1 + stw r5,fee_restarts@l(r3) + mr r12,r4 /* restart at exc_exit_restart */ + b 2b + + .comm fee_restarts,4 + +/* aargh, a nonrecoverable interrupt, panic */ +/* aargh, we don't know which trap this is */ +/* but the 601 doesn't implement the RI bit, so assume it's OK */ +3: +BEGIN_FTR_SECTION + b 2b +END_FTR_SECTION_IFSET(CPU_FTR_601) + li r10,-1 + stw r10,TRAP(r11) + addi r3,r1,STACK_FRAME_OVERHEAD + li r10,MSR_KERNEL + bl transfer_to_handler_full + .long nonrecoverable_exception + .long ret_from_except + +/* + * FP unavailable trap from kernel - print a message, but let + * the task use FP in the kernel until it returns to user mode. + */ +KernelFP: + lwz r3,_MSR(r1) + ori r3,r3,MSR_FP + stw r3,_MSR(r1) /* enable use of FP after return */ + lis r3,86f@h + ori r3,r3,86f@l + mr r4,r2 /* current */ + lwz r5,_NIP(r1) + bl printk + b ret_from_except +86: .string "floating point used in kernel (task=%p, pc=%x)\n" + .align 4,0 + +#ifdef CONFIG_ALTIVEC +/* Note that the AltiVec support is closely modeled after the FP + * support. Changes to one are likely to be applicable to the + * other! */ +load_up_altivec: +/* + * Disable AltiVec for the task which had AltiVec previously, + * and save its AltiVec registers in its thread_struct. + * Enables AltiVec for use in the kernel on return. + * On SMP we know the AltiVec units are free, since we give it up every + * switch. -- Kumar + */ + mfmsr r5 + oris r5,r5,MSR_VEC@h + MTMSRD(r5) /* enable use of AltiVec now */ + isync +/* + * For SMP, we don't do lazy AltiVec switching because it just gets too + * horrendously complex, especially when a task switches from one CPU + * to another. Instead we call giveup_altivec in switch_to. + */ +#ifndef CONFIG_SMP + tophys(r6,0) + addis r3,r6,last_task_used_altivec@ha + lwz r4,last_task_used_altivec@l(r3) + cmpwi 0,r4,0 + beq 1f + add r4,r4,r6 + addi r4,r4,THREAD /* want THREAD of last_task_used_altivec */ + SAVE_32VR(0,r10,r4) + mfvscr vr0 + li r10,THREAD_VSCR + stvx vr0,r10,r4 + lwz r5,PT_REGS(r4) + add r5,r5,r6 + lwz r4,_MSR-STACK_FRAME_OVERHEAD(r5) + lis r10,MSR_VEC@h + andc r4,r4,r10 /* disable altivec for previous task */ + stw r4,_MSR-STACK_FRAME_OVERHEAD(r5) +1: +#endif /* CONFIG_SMP */ + /* enable use of AltiVec after return */ + oris r9,r9,MSR_VEC@h + mfspr r5,SPRN_SPRG3 /* current task's THREAD (phys) */ + li r4,1 + li r10,THREAD_VSCR + stw r4,THREAD_USED_VR(r5) + lvx vr0,r10,r5 + mtvscr vr0 + REST_32VR(0,r10,r5) +#ifndef CONFIG_SMP + subi r4,r5,THREAD + sub r4,r4,r6 + stw r4,last_task_used_altivec@l(r3) +#endif /* CONFIG_SMP */ + /* restore registers and return */ + /* we haven't used ctr or xer or lr */ + b fast_exception_return + +/* + * AltiVec unavailable trap from kernel - print a message, but let + * the task use AltiVec in the kernel until it returns to user mode. + */ +KernelAltiVec: + lwz r3,_MSR(r1) + oris r3,r3,MSR_VEC@h + stw r3,_MSR(r1) /* enable use of AltiVec after return */ + lis r3,87f@h + ori r3,r3,87f@l + mr r4,r2 /* current */ + lwz r5,_NIP(r1) + bl printk + b ret_from_except +87: .string "AltiVec used in kernel (task=%p, pc=%x) \n" + .align 4,0 + +/* + * giveup_altivec(tsk) + * Disable AltiVec for the task given as the argument, + * and save the AltiVec registers in its thread_struct. + * Enables AltiVec for use in the kernel on return. + */ + + .globl giveup_altivec +giveup_altivec: + mfmsr r5 + oris r5,r5,MSR_VEC@h + SYNC + MTMSRD(r5) /* enable use of AltiVec now */ + isync + cmpwi 0,r3,0 + beqlr- /* if no previous owner, done */ + addi r3,r3,THREAD /* want THREAD of task */ + lwz r5,PT_REGS(r3) + cmpwi 0,r5,0 + SAVE_32VR(0, r4, r3) + mfvscr vr0 + li r4,THREAD_VSCR + stvx vr0,r4,r3 + beq 1f + lwz r4,_MSR-STACK_FRAME_OVERHEAD(r5) + lis r3,MSR_VEC@h + andc r4,r4,r3 /* disable AltiVec for previous task */ + stw r4,_MSR-STACK_FRAME_OVERHEAD(r5) +1: +#ifndef CONFIG_SMP + li r5,0 + lis r4,last_task_used_altivec@ha + stw r5,last_task_used_altivec@l(r4) +#endif /* CONFIG_SMP */ + blr +#endif /* CONFIG_ALTIVEC */ + +/* + * giveup_fpu(tsk) + * Disable FP for the task given as the argument, + * and save the floating-point registers in its thread_struct. + * Enables the FPU for use in the kernel on return. + */ + .globl giveup_fpu +giveup_fpu: + mfmsr r5 + ori r5,r5,MSR_FP + SYNC_601 + ISYNC_601 + MTMSRD(r5) /* enable use of fpu now */ + SYNC_601 + isync + cmpwi 0,r3,0 + beqlr- /* if no previous owner, done */ + addi r3,r3,THREAD /* want THREAD of task */ + lwz r5,PT_REGS(r3) + cmpwi 0,r5,0 + SAVE_32FPRS(0, r3) + mffs fr0 + stfd fr0,THREAD_FPSCR-4(r3) + beq 1f + lwz r4,_MSR-STACK_FRAME_OVERHEAD(r5) + li r3,MSR_FP|MSR_FE0|MSR_FE1 + andc r4,r4,r3 /* disable FP for previous task */ + stw r4,_MSR-STACK_FRAME_OVERHEAD(r5) +1: +#ifndef CONFIG_SMP + li r5,0 + lis r4,last_task_used_math@ha + stw r5,last_task_used_math@l(r4) +#endif /* CONFIG_SMP */ + blr + +/* + * This code is jumped to from the startup code to copy + * the kernel image to physical address 0. + */ +relocate_kernel: + addis r9,r26,klimit@ha /* fetch klimit */ + lwz r25,klimit@l(r9) + addis r25,r25,-KERNELBASE@h + li r3,0 /* Destination base address */ + li r6,0 /* Destination offset */ + li r5,0x4000 /* # bytes of memory to copy */ + bl copy_and_flush /* copy the first 0x4000 bytes */ + addi r0,r3,4f@l /* jump to the address of 4f */ + mtctr r0 /* in copy and do the rest. */ + bctr /* jump to the copy */ +4: mr r5,r25 + bl copy_and_flush /* copy the rest */ + b turn_on_mmu + +/* + * Copy routine used to copy the kernel to start at physical address 0 + * and flush and invalidate the caches as needed. + * r3 = dest addr, r4 = source addr, r5 = copy limit, r6 = start offset + * on exit, r3, r4, r5 are unchanged, r6 is updated to be >= r5. + */ +copy_and_flush: + addi r5,r5,-4 + addi r6,r6,-4 +4: li r0,L1_CACHE_LINE_SIZE/4 + mtctr r0 +3: addi r6,r6,4 /* copy a cache line */ + lwzx r0,r6,r4 + stwx r0,r6,r3 + bdnz 3b + dcbst r6,r3 /* write it to memory */ + sync + icbi r6,r3 /* flush the icache line */ + cmplw 0,r6,r5 + blt 4b + sync /* additional sync needed on g4 */ + isync + addi r5,r5,4 + addi r6,r6,4 + blr + +#ifdef CONFIG_APUS +/* + * On APUS the physical base address of the kernel is not known at compile + * time, which means the __pa/__va constants used are incorrect. In the + * __init section is recorded the virtual addresses of instructions using + * these constants, so all that has to be done is fix these before + * continuing the kernel boot. + * + * r4 = The physical address of the kernel base. + */ +fix_mem_constants: + mr r10,r4 + addis r10,r10,-KERNELBASE@h /* virt_to_phys constant */ + neg r11,r10 /* phys_to_virt constant */ + + lis r12,__vtop_table_begin@h + ori r12,r12,__vtop_table_begin@l + add r12,r12,r10 /* table begin phys address */ + lis r13,__vtop_table_end@h + ori r13,r13,__vtop_table_end@l + add r13,r13,r10 /* table end phys address */ + subi r12,r12,4 + subi r13,r13,4 +1: lwzu r14,4(r12) /* virt address of instruction */ + add r14,r14,r10 /* phys address of instruction */ + lwz r15,0(r14) /* instruction, now insert top */ + rlwimi r15,r10,16,16,31 /* half of vp const in low half */ + stw r15,0(r14) /* of instruction and restore. */ + dcbst r0,r14 /* write it to memory */ + sync + icbi r0,r14 /* flush the icache line */ + cmpw r12,r13 + bne 1b + sync /* additional sync needed on g4 */ + isync + +/* + * Map the memory where the exception handlers will + * be copied to when hash constants have been patched. + */ +#ifdef CONFIG_APUS_FAST_EXCEPT + lis r8,0xfff0 +#else + lis r8,0 +#endif + ori r8,r8,0x2 /* 128KB, supervisor */ + mtspr SPRN_DBAT3U,r8 + mtspr SPRN_DBAT3L,r8 + + lis r12,__ptov_table_begin@h + ori r12,r12,__ptov_table_begin@l + add r12,r12,r10 /* table begin phys address */ + lis r13,__ptov_table_end@h + ori r13,r13,__ptov_table_end@l + add r13,r13,r10 /* table end phys address */ + subi r12,r12,4 + subi r13,r13,4 +1: lwzu r14,4(r12) /* virt address of instruction */ + add r14,r14,r10 /* phys address of instruction */ + lwz r15,0(r14) /* instruction, now insert top */ + rlwimi r15,r11,16,16,31 /* half of pv const in low half*/ + stw r15,0(r14) /* of instruction and restore. */ + dcbst r0,r14 /* write it to memory */ + sync + icbi r0,r14 /* flush the icache line */ + cmpw r12,r13 + bne 1b + + sync /* additional sync needed on g4 */ + isync /* No speculative loading until now */ + blr + +/*********************************************************************** + * Please note that on APUS the exception handlers are located at the + * physical address 0xfff0000. For this reason, the exception handlers + * cannot use relative branches to access the code below. + ***********************************************************************/ +#endif /* CONFIG_APUS */ + +#ifdef CONFIG_SMP +#ifdef CONFIG_GEMINI + .globl __secondary_start_gemini +__secondary_start_gemini: + mfspr r4,SPRN_HID0 + ori r4,r4,HID0_ICFI + li r3,0 + ori r3,r3,HID0_ICE + andc r4,r4,r3 + mtspr SPRN_HID0,r4 + sync + bl gemini_prom_init + b __secondary_start +#endif /* CONFIG_GEMINI */ + .globl __secondary_start_psurge +__secondary_start_psurge: + li r24,1 /* cpu # */ + b __secondary_start_psurge99 + .globl __secondary_start_psurge2 +__secondary_start_psurge2: + li r24,2 /* cpu # */ + b __secondary_start_psurge99 + .globl __secondary_start_psurge3 +__secondary_start_psurge3: + li r24,3 /* cpu # */ + b __secondary_start_psurge99 +__secondary_start_psurge99: + /* we come in here with IR=0 and DR=1, and DBAT 0 + set to map the 0xf0000000 - 0xffffffff region */ + mfmsr r0 + rlwinm r0,r0,0,28,26 /* clear DR (0x10) */ + SYNC + mtmsr r0 + isync + + .globl __secondary_start +__secondary_start: +#ifdef CONFIG_PPC64BRIDGE + mfmsr r0 + clrldi r0,r0,1 /* make sure it's in 32-bit mode */ + SYNC + MTMSRD(r0) + isync +#endif + /* Copy some CPU settings from CPU 0 */ + bl __restore_cpu_setup + + lis r3,-KERNELBASE@h + mr r4,r24 + bl identify_cpu + bl call_setup_cpu /* Call setup_cpu for this CPU */ +#ifdef CONFIG_6xx + lis r3,-KERNELBASE@h + bl init_idle_6xx +#endif /* CONFIG_6xx */ +#ifdef CONFIG_POWER4 + lis r3,-KERNELBASE@h + bl init_idle_power4 +#endif /* CONFIG_POWER4 */ + + /* get current_thread_info and current */ + lis r1,secondary_ti@ha + tophys(r1,r1) + lwz r1,secondary_ti@l(r1) + tophys(r2,r1) + lwz r2,TI_TASK(r2) + + /* stack */ + addi r1,r1,THREAD_SIZE-STACK_FRAME_OVERHEAD + li r0,0 + tophys(r3,r1) + stw r0,0(r3) + + /* load up the MMU */ + bl load_up_mmu + + /* ptr to phys current thread */ + tophys(r4,r2) + addi r4,r4,THREAD /* phys address of our thread_struct */ + CLR_TOP32(r4) + mtspr SPRN_SPRG3,r4 + li r3,0 + mtspr SPRN_SPRG2,r3 /* 0 => not in RTAS */ + + /* enable MMU and jump to start_secondary */ + li r4,MSR_KERNEL + FIX_SRR1(r4,r5) + lis r3,start_secondary@h + ori r3,r3,start_secondary@l + mtspr SPRN_SRR0,r3 + mtspr SPRN_SRR1,r4 + SYNC + RFI +#endif /* CONFIG_SMP */ + +/* + * Those generic dummy functions are kept for CPUs not + * included in CONFIG_6xx + */ +_GLOBAL(__setup_cpu_power3) + blr +_GLOBAL(__setup_cpu_generic) + blr + +#if !defined(CONFIG_6xx) && !defined(CONFIG_POWER4) +_GLOBAL(__save_cpu_setup) + blr +_GLOBAL(__restore_cpu_setup) + blr +#endif /* !defined(CONFIG_6xx) && !defined(CONFIG_POWER4) */ + + +/* + * Load stuff into the MMU. Intended to be called with + * IR=0 and DR=0. + */ +load_up_mmu: + sync /* Force all PTE updates to finish */ + isync + tlbia /* Clear all TLB entries */ + sync /* wait for tlbia/tlbie to finish */ + TLBSYNC /* ... on all CPUs */ + /* Load the SDR1 register (hash table base & size) */ + lis r6,_SDR1@ha + tophys(r6,r6) + lwz r6,_SDR1@l(r6) + mtspr SPRN_SDR1,r6 +#ifdef CONFIG_PPC64BRIDGE + /* clear the ASR so we only use the pseudo-segment registers. */ + li r6,0 + mtasr r6 +#endif /* CONFIG_PPC64BRIDGE */ + li r0,16 /* load up segment register values */ + mtctr r0 /* for context 0 */ + lis r3,0x2000 /* Ku = 1, VSID = 0 */ + li r4,0 +3: mtsrin r3,r4 + addi r3,r3,0x111 /* increment VSID */ + addis r4,r4,0x1000 /* address of next segment */ + bdnz 3b +#ifndef CONFIG_POWER4 +/* Load the BAT registers with the values set up by MMU_init. + MMU_init takes care of whether we're on a 601 or not. */ + mfpvr r3 + srwi r3,r3,16 + cmpwi r3,1 + lis r3,BATS@ha + addi r3,r3,BATS@l + tophys(r3,r3) + LOAD_BAT(0,r3,r4,r5) + LOAD_BAT(1,r3,r4,r5) + LOAD_BAT(2,r3,r4,r5) + LOAD_BAT(3,r3,r4,r5) +#endif /* CONFIG_POWER4 */ + blr + +/* + * This is where the main kernel code starts. + */ +start_here: + /* ptr to current */ + lis r2,init_task@h + ori r2,r2,init_task@l + /* Set up for using our exception vectors */ + /* ptr to phys current thread */ + tophys(r4,r2) + addi r4,r4,THREAD /* init task's THREAD */ + CLR_TOP32(r4) + mtspr SPRN_SPRG3,r4 + li r3,0 + mtspr SPRN_SPRG2,r3 /* 0 => not in RTAS */ + + /* stack */ + lis r1,init_thread_union@ha + addi r1,r1,init_thread_union@l + li r0,0 + stwu r0,THREAD_SIZE-STACK_FRAME_OVERHEAD(r1) +/* + * Do early bootinfo parsing, platform-specific initialization, + * and set up the MMU. + */ + mr r3,r31 + mr r4,r30 + mr r5,r29 + mr r6,r28 + mr r7,r27 + bl machine_init + bl MMU_init + +#ifdef CONFIG_APUS + /* Copy exception code to exception vector base on APUS. */ + lis r4,KERNELBASE@h +#ifdef CONFIG_APUS_FAST_EXCEPT + lis r3,0xfff0 /* Copy to 0xfff00000 */ +#else + lis r3,0 /* Copy to 0x00000000 */ +#endif + li r5,0x4000 /* # bytes of memory to copy */ + li r6,0 + bl copy_and_flush /* copy the first 0x4000 bytes */ +#endif /* CONFIG_APUS */ + +/* + * Go back to running unmapped so we can load up new values + * for SDR1 (hash table pointer) and the segment registers + * and change to using our exception vectors. + */ + lis r4,2f@h + ori r4,r4,2f@l + tophys(r4,r4) + li r3,MSR_KERNEL & ~(MSR_IR|MSR_DR) + FIX_SRR1(r3,r5) + mtspr SPRN_SRR0,r4 + mtspr SPRN_SRR1,r3 + SYNC + RFI +/* Load up the kernel context */ +2: bl load_up_mmu + +#ifdef CONFIG_BDI_SWITCH + /* Add helper information for the Abatron bdiGDB debugger. + * We do this here because we know the mmu is disabled, and + * will be enabled for real in just a few instructions. + */ + lis r5, abatron_pteptrs@h + ori r5, r5, abatron_pteptrs@l + stw r5, 0xf0(r0) /* This much match your Abatron config */ + lis r6, swapper_pg_dir@h + ori r6, r6, swapper_pg_dir@l + tophys(r5, r5) + stw r6, 0(r5) +#endif /* CONFIG_BDI_SWITCH */ + +/* Now turn on the MMU for real! */ + li r4,MSR_KERNEL + FIX_SRR1(r4,r5) + lis r3,start_kernel@h + ori r3,r3,start_kernel@l + mtspr SPRN_SRR0,r3 + mtspr SPRN_SRR1,r4 + SYNC + RFI + +/* + * Set up the segment registers for a new context. + */ +_GLOBAL(set_context) + mulli r3,r3,897 /* multiply context by skew factor */ + rlwinm r3,r3,4,8,27 /* VSID = (context & 0xfffff) << 4 */ + addis r3,r3,0x6000 /* Set Ks, Ku bits */ + li r0,NUM_USER_SEGMENTS + mtctr r0 + +#ifdef CONFIG_BDI_SWITCH + /* Context switch the PTE pointer for the Abatron BDI2000. + * The PGDIR is passed as second argument. + */ + lis r5, KERNELBASE@h + lwz r5, 0xf0(r5) + stw r4, 0x4(r5) +#endif + li r4,0 + isync +3: +#ifdef CONFIG_PPC64BRIDGE + slbie r4 +#endif /* CONFIG_PPC64BRIDGE */ + mtsrin r3,r4 + addi r3,r3,0x111 /* next VSID */ + rlwinm r3,r3,0,8,3 /* clear out any overflow from VSID field */ + addis r4,r4,0x1000 /* address of next segment */ + bdnz 3b + sync + isync + blr + +/* + * An undocumented "feature" of 604e requires that the v bit + * be cleared before changing BAT values. + * + * Also, newer IBM firmware does not clear bat3 and 4 so + * this makes sure it's done. + * -- Cort + */ +clear_bats: + li r10,0 + mfspr r9,SPRN_PVR + rlwinm r9,r9,16,16,31 /* r9 = 1 for 601, 4 for 604 */ + cmpwi r9, 1 + beq 1f + + mtspr SPRN_DBAT0U,r10 + mtspr SPRN_DBAT0L,r10 + mtspr SPRN_DBAT1U,r10 + mtspr SPRN_DBAT1L,r10 + mtspr SPRN_DBAT2U,r10 + mtspr SPRN_DBAT2L,r10 + mtspr SPRN_DBAT3U,r10 + mtspr SPRN_DBAT3L,r10 +1: + mtspr SPRN_IBAT0U,r10 + mtspr SPRN_IBAT0L,r10 + mtspr SPRN_IBAT1U,r10 + mtspr SPRN_IBAT1L,r10 + mtspr SPRN_IBAT2U,r10 + mtspr SPRN_IBAT2L,r10 + mtspr SPRN_IBAT3U,r10 + mtspr SPRN_IBAT3L,r10 +BEGIN_FTR_SECTION + /* Here's a tweak: at this point, CPU setup have + * not been called yet, so HIGH_BAT_EN may not be + * set in HID0 for the 745x processors. However, it + * seems that doesn't affect our ability to actually + * write to these SPRs. + */ + mtspr SPRN_DBAT4U,r10 + mtspr SPRN_DBAT4L,r10 + mtspr SPRN_DBAT5U,r10 + mtspr SPRN_DBAT5L,r10 + mtspr SPRN_DBAT6U,r10 + mtspr SPRN_DBAT6L,r10 + mtspr SPRN_DBAT7U,r10 + mtspr SPRN_DBAT7L,r10 + mtspr SPRN_IBAT4U,r10 + mtspr SPRN_IBAT4L,r10 + mtspr SPRN_IBAT5U,r10 + mtspr SPRN_IBAT5L,r10 + mtspr SPRN_IBAT6U,r10 + mtspr SPRN_IBAT6L,r10 + mtspr SPRN_IBAT7U,r10 + mtspr SPRN_IBAT7L,r10 +END_FTR_SECTION_IFSET(CPU_FTR_HAS_HIGH_BATS) + blr + +flush_tlbs: + lis r10, 0x40 +1: addic. r10, r10, -0x1000 + tlbie r10 + blt 1b + sync + blr + +mmu_off: + addi r4, r3, __after_mmu_off - _start + mfmsr r3 + andi. r0,r3,MSR_DR|MSR_IR /* MMU enabled? */ + beqlr + andc r3,r3,r0 + mtspr SPRN_SRR0,r4 + mtspr SPRN_SRR1,r3 + sync + RFI + +#ifndef CONFIG_POWER4 +/* + * Use the first pair of BAT registers to map the 1st 16MB + * of RAM to KERNELBASE. From this point on we can't safely + * call OF any more. + */ +initial_bats: + lis r11,KERNELBASE@h +#ifndef CONFIG_PPC64BRIDGE + mfspr r9,SPRN_PVR + rlwinm r9,r9,16,16,31 /* r9 = 1 for 601, 4 for 604 */ + cmpwi 0,r9,1 + bne 4f + ori r11,r11,4 /* set up BAT registers for 601 */ + li r8,0x7f /* valid, block length = 8MB */ + oris r9,r11,0x800000@h /* set up BAT reg for 2nd 8M */ + oris r10,r8,0x800000@h /* set up BAT reg for 2nd 8M */ + mtspr SPRN_IBAT0U,r11 /* N.B. 601 has valid bit in */ + mtspr SPRN_IBAT0L,r8 /* lower BAT register */ + mtspr SPRN_IBAT1U,r9 + mtspr SPRN_IBAT1L,r10 + isync + blr +#endif /* CONFIG_PPC64BRIDGE */ + +4: tophys(r8,r11) +#ifdef CONFIG_SMP + ori r8,r8,0x12 /* R/W access, M=1 */ +#else + ori r8,r8,2 /* R/W access */ +#endif /* CONFIG_SMP */ +#ifdef CONFIG_APUS + ori r11,r11,BL_8M<<2|0x2 /* set up 8MB BAT registers for 604 */ +#else + ori r11,r11,BL_256M<<2|0x2 /* set up BAT registers for 604 */ +#endif /* CONFIG_APUS */ + +#ifdef CONFIG_PPC64BRIDGE + /* clear out the high 32 bits in the BAT */ + clrldi r11,r11,32 + clrldi r8,r8,32 +#endif /* CONFIG_PPC64BRIDGE */ + mtspr SPRN_DBAT0L,r8 /* N.B. 6xx (not 601) have valid */ + mtspr SPRN_DBAT0U,r11 /* bit in upper BAT register */ + mtspr SPRN_IBAT0L,r8 + mtspr SPRN_IBAT0U,r11 + isync + blr + +#if !defined(CONFIG_APUS) && defined(CONFIG_BOOTX_TEXT) +setup_disp_bat: + /* + * setup the display bat prepared for us in prom.c + */ + mflr r8 + bl reloc_offset + mtlr r8 + addis r8,r3,disp_BAT@ha + addi r8,r8,disp_BAT@l + lwz r11,0(r8) + lwz r8,4(r8) + mfspr r9,SPRN_PVR + rlwinm r9,r9,16,16,31 /* r9 = 1 for 601, 4 for 604 */ + cmpwi 0,r9,1 + beq 1f + mtspr SPRN_DBAT3L,r8 + mtspr SPRN_DBAT3U,r11 + blr +1: mtspr SPRN_IBAT3L,r8 + mtspr SPRN_IBAT3U,r11 + blr + +#endif /* !defined(CONFIG_APUS) && defined(CONFIG_BOOTX_TEXT) */ + +#else /* CONFIG_POWER4 */ +/* + * Load up the SDR1 and segment register values now + * since we don't have the BATs. + * Also make sure we are running in 32-bit mode. + */ + +initial_mm_power4: + addis r14,r3,_SDR1@ha /* get the value from _SDR1 */ + lwz r14,_SDR1@l(r14) /* assume hash table below 4GB */ + mtspr SPRN_SDR1,r14 + slbia + lis r4,0x2000 /* set pseudo-segment reg 12 */ + ori r5,r4,0x0ccc + mtsr 12,r5 +#if 0 + ori r5,r4,0x0888 /* set pseudo-segment reg 8 */ + mtsr 8,r5 /* (for access to serial port) */ +#endif +#ifdef CONFIG_BOOTX_TEXT + ori r5,r4,0x0999 /* set pseudo-segment reg 9 */ + mtsr 9,r5 /* (for access to screen) */ +#endif + mfmsr r0 + clrldi r0,r0,1 + sync + mtmsr r0 + isync + blr + +#endif /* CONFIG_POWER4 */ + +#ifdef CONFIG_8260 +/* Jump into the system reset for the rom. + * We first disable the MMU, and then jump to the ROM reset address. + * + * r3 is the board info structure, r4 is the location for starting. + * I use this for building a small kernel that can load other kernels, + * rather than trying to write or rely on a rom monitor that can tftp load. + */ + .globl m8260_gorom +m8260_gorom: + mfmsr r0 + rlwinm r0,r0,0,17,15 /* clear MSR_EE in r0 */ + sync + mtmsr r0 + sync + mfspr r11, SPRN_HID0 + lis r10, 0 + ori r10,r10,HID0_ICE|HID0_DCE + andc r11, r11, r10 + mtspr SPRN_HID0, r11 + isync + li r5, MSR_ME|MSR_RI + lis r6,2f@h + addis r6,r6,-KERNELBASE@h + ori r6,r6,2f@l + mtspr SPRN_SRR0,r6 + mtspr SPRN_SRR1,r5 + isync + sync + rfi +2: + mtlr r4 + blr +#endif + + +/* + * We put a few things here that have to be page-aligned. + * This stuff goes at the beginning of the data segment, + * which is page-aligned. + */ + .data + .globl sdata +sdata: + .globl empty_zero_page +empty_zero_page: + .space 4096 + + .globl swapper_pg_dir +swapper_pg_dir: + .space 4096 + +/* + * This space gets a copy of optional info passed to us by the bootstrap + * Used to pass parameters into the kernel like root=/dev/sda1, etc. + */ + .globl cmd_line +cmd_line: + .space 512 + + .globl intercept_table +intercept_table: + .long 0, 0, i0x200, i0x300, i0x400, 0, i0x600, i0x700 + .long i0x800, 0, 0, 0, 0, i0xd00, 0, 0 + .long 0, 0, 0, i0x1300, 0, 0, 0, 0 + .long 0, 0, 0, 0, 0, 0, 0, 0 + .long 0, 0, 0, 0, 0, 0, 0, 0 + .long 0, 0, 0, 0, 0, 0, 0, 0 + +/* Room for two PTE pointers, usually the kernel and current user pointers + * to their respective root page table. + */ +abatron_pteptrs: + .space 8 diff --git a/arch/ppc/kernel/head_44x.S b/arch/ppc/kernel/head_44x.S new file mode 100644 index 00000000000..9ed8165a3d6 --- /dev/null +++ b/arch/ppc/kernel/head_44x.S @@ -0,0 +1,753 @@ +/* + * arch/ppc/kernel/head_44x.S + * + * Kernel execution entry point code. + * + * Copyright (c) 1995-1996 Gary Thomas + * Initial PowerPC version. + * Copyright (c) 1996 Cort Dougan + * Rewritten for PReP + * Copyright (c) 1996 Paul Mackerras + * Low-level exception handers, MMU support, and rewrite. + * Copyright (c) 1997 Dan Malek + * PowerPC 8xx modifications. + * Copyright (c) 1998-1999 TiVo, Inc. + * PowerPC 403GCX modifications. + * Copyright (c) 1999 Grant Erickson + * PowerPC 403GCX/405GP modifications. + * Copyright 2000 MontaVista Software Inc. + * PPC405 modifications + * PowerPC 403GCX/405GP modifications. + * Author: MontaVista Software, Inc. + * frank_rowand@mvista.com or source@mvista.com + * debbie_chu@mvista.com + * Copyright 2002-2005 MontaVista Software, Inc. + * PowerPC 44x support, Matt Porter + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "head_booke.h" + + +/* As with the other PowerPC ports, it is expected that when code + * execution begins here, the following registers contain valid, yet + * optional, information: + * + * r3 - Board info structure pointer (DRAM, frequency, MAC address, etc.) + * r4 - Starting address of the init RAM disk + * r5 - Ending address of the init RAM disk + * r6 - Start of kernel command line string (e.g. "mem=128") + * r7 - End of kernel command line string + * + */ + .text +_GLOBAL(_stext) +_GLOBAL(_start) + /* + * Reserve a word at a fixed location to store the address + * of abatron_pteptrs + */ + nop +/* + * Save parameters we are passed + */ + mr r31,r3 + mr r30,r4 + mr r29,r5 + mr r28,r6 + mr r27,r7 + li r24,0 /* CPU number */ + +/* + * Set up the initial MMU state + * + * We are still executing code at the virtual address + * mappings set by the firmware for the base of RAM. + * + * We first invalidate all TLB entries but the one + * we are running from. We then load the KERNELBASE + * mappings so we can begin to use kernel addresses + * natively and so the interrupt vector locations are + * permanently pinned (necessary since Book E + * implementations always have translation enabled). + * + * TODO: Use the known TLB entry we are running from to + * determine which physical region we are located + * in. This can be used to determine where in RAM + * (on a shared CPU system) or PCI memory space + * (on a DRAMless system) we are located. + * For now, we assume a perfect world which means + * we are located at the base of DRAM (physical 0). + */ + +/* + * Search TLB for entry that we are currently using. + * Invalidate all entries but the one we are using. + */ + /* Load our current PID->MMUCR TID and MSR IS->MMUCR STS */ + mfspr r3,SPRN_PID /* Get PID */ + mfmsr r4 /* Get MSR */ + andi. r4,r4,MSR_IS@l /* TS=1? */ + beq wmmucr /* If not, leave STS=0 */ + oris r3,r3,PPC44x_MMUCR_STS@h /* Set STS=1 */ +wmmucr: mtspr SPRN_MMUCR,r3 /* Put MMUCR */ + sync + + bl invstr /* Find our address */ +invstr: mflr r5 /* Make it accessible */ + tlbsx r23,0,r5 /* Find entry we are in */ + li r4,0 /* Start at TLB entry 0 */ + li r3,0 /* Set PAGEID inval value */ +1: cmpw r23,r4 /* Is this our entry? */ + beq skpinv /* If so, skip the inval */ + tlbwe r3,r4,PPC44x_TLB_PAGEID /* If not, inval the entry */ +skpinv: addi r4,r4,1 /* Increment */ + cmpwi r4,64 /* Are we done? */ + bne 1b /* If not, repeat */ + isync /* If so, context change */ + +/* + * Configure and load pinned entry into TLB slot 63. + */ + + lis r3,KERNELBASE@h /* Load the kernel virtual address */ + ori r3,r3,KERNELBASE@l + + /* Kernel is at the base of RAM */ + li r4, 0 /* Load the kernel physical address */ + + /* Load the kernel PID = 0 */ + li r0,0 + mtspr SPRN_PID,r0 + sync + + /* Initialize MMUCR */ + li r5,0 + mtspr SPRN_MMUCR,r5 + sync + + /* pageid fields */ + clrrwi r3,r3,10 /* Mask off the effective page number */ + ori r3,r3,PPC44x_TLB_VALID | PPC44x_TLB_256M + + /* xlat fields */ + clrrwi r4,r4,10 /* Mask off the real page number */ + /* ERPN is 0 for first 4GB page */ + + /* attrib fields */ + /* Added guarded bit to protect against speculative loads/stores */ + li r5,0 + ori r5,r5,(PPC44x_TLB_SW | PPC44x_TLB_SR | PPC44x_TLB_SX | PPC44x_TLB_G) + + li r0,63 /* TLB slot 63 */ + + tlbwe r3,r0,PPC44x_TLB_PAGEID /* Load the pageid fields */ + tlbwe r4,r0,PPC44x_TLB_XLAT /* Load the translation fields */ + tlbwe r5,r0,PPC44x_TLB_ATTRIB /* Load the attrib/access fields */ + + /* Force context change */ + mfmsr r0 + mtspr SPRN_SRR1, r0 + lis r0,3f@h + ori r0,r0,3f@l + mtspr SPRN_SRR0,r0 + sync + rfi + + /* If necessary, invalidate original entry we used */ +3: cmpwi r23,63 + beq 4f + li r6,0 + tlbwe r6,r23,PPC44x_TLB_PAGEID + isync + +4: +#ifdef CONFIG_SERIAL_TEXT_DEBUG + /* + * Add temporary UART mapping for early debug. This + * mapping must be identical to that used by the early + * bootloader code since the same asm/serial.h parameters + * are used for polled operation. + */ + /* pageid fields */ + lis r3,UART0_IO_BASE@h + ori r3,r3,PPC44x_TLB_VALID | PPC44x_TLB_256M + + /* xlat fields */ + lis r4,UART0_PHYS_IO_BASE@h /* RPN depends on SoC */ + ori r4,r4,0x0001 /* ERPN is 1 for second 4GB page */ + + /* attrib fields */ + li r5,0 + ori r5,r5,(PPC44x_TLB_SW | PPC44x_TLB_SR | PPC44x_TLB_I | PPC44x_TLB_G) + + li r0,1 /* TLB slot 1 */ + + tlbwe r3,r0,PPC44x_TLB_PAGEID /* Load the pageid fields */ + tlbwe r4,r0,PPC44x_TLB_XLAT /* Load the translation fields */ + tlbwe r5,r0,PPC44x_TLB_ATTRIB /* Load the attrib/access fields */ + + /* Force context change */ + isync +#endif /* CONFIG_SERIAL_TEXT_DEBUG */ + + /* Establish the interrupt vector offsets */ + SET_IVOR(0, CriticalInput); + SET_IVOR(1, MachineCheck); + SET_IVOR(2, DataStorage); + SET_IVOR(3, InstructionStorage); + SET_IVOR(4, ExternalInput); + SET_IVOR(5, Alignment); + SET_IVOR(6, Program); + SET_IVOR(7, FloatingPointUnavailable); + SET_IVOR(8, SystemCall); + SET_IVOR(9, AuxillaryProcessorUnavailable); + SET_IVOR(10, Decrementer); + SET_IVOR(11, FixedIntervalTimer); + SET_IVOR(12, WatchdogTimer); + SET_IVOR(13, DataTLBError); + SET_IVOR(14, InstructionTLBError); + SET_IVOR(15, Debug); + + /* Establish the interrupt vector base */ + lis r4,interrupt_base@h /* IVPR only uses the high 16-bits */ + mtspr SPRN_IVPR,r4 + + /* + * This is where the main kernel code starts. + */ + + /* ptr to current */ + lis r2,init_task@h + ori r2,r2,init_task@l + + /* ptr to current thread */ + addi r4,r2,THREAD /* init task's THREAD */ + mtspr SPRN_SPRG3,r4 + + /* stack */ + lis r1,init_thread_union@h + ori r1,r1,init_thread_union@l + li r0,0 + stwu r0,THREAD_SIZE-STACK_FRAME_OVERHEAD(r1) + + bl early_init + +/* + * Decide what sort of machine this is and initialize the MMU. + */ + mr r3,r31 + mr r4,r30 + mr r5,r29 + mr r6,r28 + mr r7,r27 + bl machine_init + bl MMU_init + + /* Setup PTE pointers for the Abatron bdiGDB */ + lis r6, swapper_pg_dir@h + ori r6, r6, swapper_pg_dir@l + lis r5, abatron_pteptrs@h + ori r5, r5, abatron_pteptrs@l + lis r4, KERNELBASE@h + ori r4, r4, KERNELBASE@l + stw r5, 0(r4) /* Save abatron_pteptrs at a fixed location */ + stw r6, 0(r5) + + /* Let's move on */ + lis r4,start_kernel@h + ori r4,r4,start_kernel@l + lis r3,MSR_KERNEL@h + ori r3,r3,MSR_KERNEL@l + mtspr SPRN_SRR0,r4 + mtspr SPRN_SRR1,r3 + rfi /* change context and jump to start_kernel */ + +/* + * Interrupt vector entry code + * + * The Book E MMUs are always on so we don't need to handle + * interrupts in real mode as with previous PPC processors. In + * this case we handle interrupts in the kernel virtual address + * space. + * + * Interrupt vectors are dynamically placed relative to the + * interrupt prefix as determined by the address of interrupt_base. + * The interrupt vectors offsets are programmed using the labels + * for each interrupt vector entry. + * + * Interrupt vectors must be aligned on a 16 byte boundary. + * We align on a 32 byte cache line boundary for good measure. + */ + +interrupt_base: + /* Critical Input Interrupt */ + CRITICAL_EXCEPTION(0x0100, CriticalInput, UnknownException) + + /* Machine Check Interrupt */ +#ifdef CONFIG_440A + MCHECK_EXCEPTION(0x0200, MachineCheck, MachineCheckException) +#else + CRITICAL_EXCEPTION(0x0200, MachineCheck, MachineCheckException) +#endif + + /* Data Storage Interrupt */ + START_EXCEPTION(DataStorage) + mtspr SPRN_SPRG0, r10 /* Save some working registers */ + mtspr SPRN_SPRG1, r11 + mtspr SPRN_SPRG4W, r12 + mtspr SPRN_SPRG5W, r13 + mfcr r11 + mtspr SPRN_SPRG7W, r11 + + /* + * Check if it was a store fault, if not then bail + * because a user tried to access a kernel or + * read-protected page. Otherwise, get the + * offending address and handle it. + */ + mfspr r10, SPRN_ESR + andis. r10, r10, ESR_ST@h + beq 2f + + mfspr r10, SPRN_DEAR /* Get faulting address */ + + /* If we are faulting a kernel address, we have to use the + * kernel page tables. + */ + andis. r11, r10, 0x8000 + beq 3f + lis r11, swapper_pg_dir@h + ori r11, r11, swapper_pg_dir@l + + mfspr r12,SPRN_MMUCR + rlwinm r12,r12,0,0,23 /* Clear TID */ + + b 4f + + /* Get the PGD for the current thread */ +3: + mfspr r11,SPRN_SPRG3 + lwz r11,PGDIR(r11) + + /* Load PID into MMUCR TID */ + mfspr r12,SPRN_MMUCR /* Get MMUCR */ + mfspr r13,SPRN_PID /* Get PID */ + rlwimi r12,r13,0,24,31 /* Set TID */ + +4: + mtspr SPRN_MMUCR,r12 + + rlwinm r12, r10, 13, 19, 29 /* Compute pgdir/pmd offset */ + lwzx r11, r12, r11 /* Get pgd/pmd entry */ + rlwinm. r12, r11, 0, 0, 20 /* Extract pt base address */ + beq 2f /* Bail if no table */ + + rlwimi r12, r10, 23, 20, 28 /* Compute pte address */ + lwz r11, 4(r12) /* Get pte entry */ + + andi. r13, r11, _PAGE_RW /* Is it writeable? */ + beq 2f /* Bail if not */ + + /* Update 'changed'. + */ + ori r11, r11, _PAGE_DIRTY|_PAGE_ACCESSED|_PAGE_HWWRITE + stw r11, 4(r12) /* Update Linux page table */ + + li r13, PPC44x_TLB_SR@l /* Set SR */ + rlwimi r13, r11, 29, 29, 29 /* SX = _PAGE_HWEXEC */ + rlwimi r13, r11, 0, 30, 30 /* SW = _PAGE_RW */ + rlwimi r13, r11, 29, 28, 28 /* UR = _PAGE_USER */ + rlwimi r12, r11, 31, 26, 26 /* (_PAGE_USER>>1)->r12 */ + rlwimi r12, r11, 29, 30, 30 /* (_PAGE_USER>>3)->r12 */ + and r12, r12, r11 /* HWEXEC/RW & USER */ + rlwimi r13, r12, 0, 26, 26 /* UX = HWEXEC & USER */ + rlwimi r13, r12, 3, 27, 27 /* UW = RW & USER */ + + rlwimi r11,r13,0,26,31 /* Insert static perms */ + + rlwinm r11,r11,0,20,15 /* Clear U0-U3 */ + + /* find the TLB index that caused the fault. It has to be here. */ + tlbsx r10, 0, r10 + + tlbwe r11, r10, PPC44x_TLB_ATTRIB /* Write ATTRIB */ + + /* Done...restore registers and get out of here. + */ + mfspr r11, SPRN_SPRG7R + mtcr r11 + mfspr r13, SPRN_SPRG5R + mfspr r12, SPRN_SPRG4R + + mfspr r11, SPRN_SPRG1 + mfspr r10, SPRN_SPRG0 + rfi /* Force context change */ + +2: + /* + * The bailout. Restore registers to pre-exception conditions + * and call the heavyweights to help us out. + */ + mfspr r11, SPRN_SPRG7R + mtcr r11 + mfspr r13, SPRN_SPRG5R + mfspr r12, SPRN_SPRG4R + + mfspr r11, SPRN_SPRG1 + mfspr r10, SPRN_SPRG0 + b data_access + + /* Instruction Storage Interrupt */ + INSTRUCTION_STORAGE_EXCEPTION + + /* External Input Interrupt */ + EXCEPTION(0x0500, ExternalInput, do_IRQ, EXC_XFER_LITE) + + /* Alignment Interrupt */ + ALIGNMENT_EXCEPTION + + /* Program Interrupt */ + PROGRAM_EXCEPTION + + /* Floating Point Unavailable Interrupt */ + EXCEPTION(0x2010, FloatingPointUnavailable, UnknownException, EXC_XFER_EE) + + /* System Call Interrupt */ + START_EXCEPTION(SystemCall) + NORMAL_EXCEPTION_PROLOG + EXC_XFER_EE_LITE(0x0c00, DoSyscall) + + /* Auxillary Processor Unavailable Interrupt */ + EXCEPTION(0x2020, AuxillaryProcessorUnavailable, UnknownException, EXC_XFER_EE) + + /* Decrementer Interrupt */ + DECREMENTER_EXCEPTION + + /* Fixed Internal Timer Interrupt */ + /* TODO: Add FIT support */ + EXCEPTION(0x1010, FixedIntervalTimer, UnknownException, EXC_XFER_EE) + + /* Watchdog Timer Interrupt */ + /* TODO: Add watchdog support */ + CRITICAL_EXCEPTION(0x1020, WatchdogTimer, UnknownException) + + /* Data TLB Error Interrupt */ + START_EXCEPTION(DataTLBError) + mtspr SPRN_SPRG0, r10 /* Save some working registers */ + mtspr SPRN_SPRG1, r11 + mtspr SPRN_SPRG4W, r12 + mtspr SPRN_SPRG5W, r13 + mfcr r11 + mtspr SPRN_SPRG7W, r11 + mfspr r10, SPRN_DEAR /* Get faulting address */ + + /* If we are faulting a kernel address, we have to use the + * kernel page tables. + */ + andis. r11, r10, 0x8000 + beq 3f + lis r11, swapper_pg_dir@h + ori r11, r11, swapper_pg_dir@l + + mfspr r12,SPRN_MMUCR + rlwinm r12,r12,0,0,23 /* Clear TID */ + + b 4f + + /* Get the PGD for the current thread */ +3: + mfspr r11,SPRN_SPRG3 + lwz r11,PGDIR(r11) + + /* Load PID into MMUCR TID */ + mfspr r12,SPRN_MMUCR + mfspr r13,SPRN_PID /* Get PID */ + rlwimi r12,r13,0,24,31 /* Set TID */ + +4: + mtspr SPRN_MMUCR,r12 + + rlwinm r12, r10, 13, 19, 29 /* Compute pgdir/pmd offset */ + lwzx r11, r12, r11 /* Get pgd/pmd entry */ + rlwinm. r12, r11, 0, 0, 20 /* Extract pt base address */ + beq 2f /* Bail if no table */ + + rlwimi r12, r10, 23, 20, 28 /* Compute pte address */ + lwz r11, 4(r12) /* Get pte entry */ + andi. r13, r11, _PAGE_PRESENT /* Is the page present? */ + beq 2f /* Bail if not present */ + + ori r11, r11, _PAGE_ACCESSED + stw r11, 4(r12) + + /* Jump to common tlb load */ + b finish_tlb_load + +2: + /* The bailout. Restore registers to pre-exception conditions + * and call the heavyweights to help us out. + */ + mfspr r11, SPRN_SPRG7R + mtcr r11 + mfspr r13, SPRN_SPRG5R + mfspr r12, SPRN_SPRG4R + mfspr r11, SPRN_SPRG1 + mfspr r10, SPRN_SPRG0 + b data_access + + /* Instruction TLB Error Interrupt */ + /* + * Nearly the same as above, except we get our + * information from different registers and bailout + * to a different point. + */ + START_EXCEPTION(InstructionTLBError) + mtspr SPRN_SPRG0, r10 /* Save some working registers */ + mtspr SPRN_SPRG1, r11 + mtspr SPRN_SPRG4W, r12 + mtspr SPRN_SPRG5W, r13 + mfcr r11 + mtspr SPRN_SPRG7W, r11 + mfspr r10, SPRN_SRR0 /* Get faulting address */ + + /* If we are faulting a kernel address, we have to use the + * kernel page tables. + */ + andis. r11, r10, 0x8000 + beq 3f + lis r11, swapper_pg_dir@h + ori r11, r11, swapper_pg_dir@l + + mfspr r12,SPRN_MMUCR + rlwinm r12,r12,0,0,23 /* Clear TID */ + + b 4f + + /* Get the PGD for the current thread */ +3: + mfspr r11,SPRN_SPRG3 + lwz r11,PGDIR(r11) + + /* Load PID into MMUCR TID */ + mfspr r12,SPRN_MMUCR + mfspr r13,SPRN_PID /* Get PID */ + rlwimi r12,r13,0,24,31 /* Set TID */ + +4: + mtspr SPRN_MMUCR,r12 + + rlwinm r12, r10, 13, 19, 29 /* Compute pgdir/pmd offset */ + lwzx r11, r12, r11 /* Get pgd/pmd entry */ + rlwinm. r12, r11, 0, 0, 20 /* Extract pt base address */ + beq 2f /* Bail if no table */ + + rlwimi r12, r10, 23, 20, 28 /* Compute pte address */ + lwz r11, 4(r12) /* Get pte entry */ + andi. r13, r11, _PAGE_PRESENT /* Is the page present? */ + beq 2f /* Bail if not present */ + + ori r11, r11, _PAGE_ACCESSED + stw r11, 4(r12) + + /* Jump to common TLB load point */ + b finish_tlb_load + +2: + /* The bailout. Restore registers to pre-exception conditions + * and call the heavyweights to help us out. + */ + mfspr r11, SPRN_SPRG7R + mtcr r11 + mfspr r13, SPRN_SPRG5R + mfspr r12, SPRN_SPRG4R + mfspr r11, SPRN_SPRG1 + mfspr r10, SPRN_SPRG0 + b InstructionStorage + + /* Debug Interrupt */ + DEBUG_EXCEPTION + +/* + * Local functions + */ + /* + * Data TLB exceptions will bail out to this point + * if they can't resolve the lightweight TLB fault. + */ +data_access: + NORMAL_EXCEPTION_PROLOG + mfspr r5,SPRN_ESR /* Grab the ESR, save it, pass arg3 */ + stw r5,_ESR(r11) + mfspr r4,SPRN_DEAR /* Grab the DEAR, save it, pass arg2 */ + EXC_XFER_EE_LITE(0x0300, handle_page_fault) + +/* + + * Both the instruction and data TLB miss get to this + * point to load the TLB. + * r10 - EA of fault + * r11 - available to use + * r12 - Pointer to the 64-bit PTE + * r13 - available to use + * MMUCR - loaded with proper value when we get here + * Upon exit, we reload everything and RFI. + */ +finish_tlb_load: + /* + * We set execute, because we don't have the granularity to + * properly set this at the page level (Linux problem). + * If shared is set, we cause a zero PID->TID load. + * Many of these bits are software only. Bits we don't set + * here we (properly should) assume have the appropriate value. + */ + + /* Load the next available TLB index */ + lis r13, tlb_44x_index@ha + lwz r13, tlb_44x_index@l(r13) + /* Load the TLB high watermark */ + lis r11, tlb_44x_hwater@ha + lwz r11, tlb_44x_hwater@l(r11) + + /* Increment, rollover, and store TLB index */ + addi r13, r13, 1 + cmpw 0, r13, r11 /* reserve entries */ + ble 7f + li r13, 0 +7: + /* Store the next available TLB index */ + lis r11, tlb_44x_index@ha + stw r13, tlb_44x_index@l(r11) + + lwz r11, 0(r12) /* Get MS word of PTE */ + lwz r12, 4(r12) /* Get LS word of PTE */ + rlwimi r11, r12, 0, 0 , 19 /* Insert RPN */ + tlbwe r11, r13, PPC44x_TLB_XLAT /* Write XLAT */ + + /* + * Create PAGEID. This is the faulting address, + * page size, and valid flag. + */ + li r11, PPC44x_TLB_VALID | PPC44x_TLB_4K + rlwimi r10, r11, 0, 20, 31 /* Insert valid and page size */ + tlbwe r10, r13, PPC44x_TLB_PAGEID /* Write PAGEID */ + + li r10, PPC44x_TLB_SR@l /* Set SR */ + rlwimi r10, r12, 0, 30, 30 /* Set SW = _PAGE_RW */ + rlwimi r10, r12, 29, 29, 29 /* SX = _PAGE_HWEXEC */ + rlwimi r10, r12, 29, 28, 28 /* UR = _PAGE_USER */ + rlwimi r11, r12, 31, 26, 26 /* (_PAGE_USER>>1)->r12 */ + and r11, r12, r11 /* HWEXEC & USER */ + rlwimi r10, r11, 0, 26, 26 /* UX = HWEXEC & USER */ + + rlwimi r12, r10, 0, 26, 31 /* Insert static perms */ + rlwinm r12, r12, 0, 20, 15 /* Clear U0-U3 */ + tlbwe r12, r13, PPC44x_TLB_ATTRIB /* Write ATTRIB */ + + /* Done...restore registers and get out of here. + */ + mfspr r11, SPRN_SPRG7R + mtcr r11 + mfspr r13, SPRN_SPRG5R + mfspr r12, SPRN_SPRG4R + mfspr r11, SPRN_SPRG1 + mfspr r10, SPRN_SPRG0 + rfi /* Force context change */ + +/* + * Global functions + */ + +/* + * extern void giveup_altivec(struct task_struct *prev) + * + * The 44x core does not have an AltiVec unit. + */ +_GLOBAL(giveup_altivec) + blr + +/* + * extern void giveup_fpu(struct task_struct *prev) + * + * The 44x core does not have an FPU. + */ +_GLOBAL(giveup_fpu) + blr + +/* + * extern void abort(void) + * + * At present, this routine just applies a system reset. + */ +_GLOBAL(abort) + mfspr r13,SPRN_DBCR0 + oris r13,r13,DBCR0_RST_SYSTEM@h + mtspr SPRN_DBCR0,r13 + +_GLOBAL(set_context) + +#ifdef CONFIG_BDI_SWITCH + /* Context switch the PTE pointer for the Abatron BDI2000. + * The PGDIR is the second parameter. + */ + lis r5, abatron_pteptrs@h + ori r5, r5, abatron_pteptrs@l + stw r4, 0x4(r5) +#endif + mtspr SPRN_PID,r3 + isync /* Force context change */ + blr + +/* + * We put a few things here that have to be page-aligned. This stuff + * goes at the beginning of the data segment, which is page-aligned. + */ + .data +_GLOBAL(sdata) +_GLOBAL(empty_zero_page) + .space 4096 + +/* + * To support >32-bit physical addresses, we use an 8KB pgdir. + */ +_GLOBAL(swapper_pg_dir) + .space 8192 + +/* Reserved 4k for the critical exception stack & 4k for the machine + * check stack per CPU for kernel mode exceptions */ + .section .bss + .align 12 +exception_stack_bottom: + .space BOOKE_EXCEPTION_STACK_SIZE +_GLOBAL(exception_stack_top) + +/* + * This space gets a copy of optional info passed to us by the bootstrap + * which is used to pass parameters into the kernel like root=/dev/sda1, etc. + */ +_GLOBAL(cmd_line) + .space 512 + +/* + * Room for two PTE pointers, usually the kernel and current user pointers + * to their respective root page table. + */ +abatron_pteptrs: + .space 8 + + diff --git a/arch/ppc/kernel/head_4xx.S b/arch/ppc/kernel/head_4xx.S new file mode 100644 index 00000000000..6f5d380e234 --- /dev/null +++ b/arch/ppc/kernel/head_4xx.S @@ -0,0 +1,1010 @@ +/* + * Copyright (c) 1995-1996 Gary Thomas + * Initial PowerPC version. + * Copyright (c) 1996 Cort Dougan + * Rewritten for PReP + * Copyright (c) 1996 Paul Mackerras + * Low-level exception handers, MMU support, and rewrite. + * Copyright (c) 1997 Dan Malek + * PowerPC 8xx modifications. + * Copyright (c) 1998-1999 TiVo, Inc. + * PowerPC 403GCX modifications. + * Copyright (c) 1999 Grant Erickson + * PowerPC 403GCX/405GP modifications. + * Copyright 2000 MontaVista Software Inc. + * PPC405 modifications + * PowerPC 403GCX/405GP modifications. + * Author: MontaVista Software, Inc. + * frank_rowand@mvista.com or source@mvista.com + * debbie_chu@mvista.com + * + * + * Module name: head_4xx.S + * + * Description: + * Kernel execution entry point code. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* As with the other PowerPC ports, it is expected that when code + * execution begins here, the following registers contain valid, yet + * optional, information: + * + * r3 - Board info structure pointer (DRAM, frequency, MAC address, etc.) + * r4 - Starting address of the init RAM disk + * r5 - Ending address of the init RAM disk + * r6 - Start of kernel command line string (e.g. "mem=96m") + * r7 - End of kernel command line string + * + * This is all going to change RSN when we add bi_recs....... -- Dan + */ + .text +_GLOBAL(_stext) +_GLOBAL(_start) + + /* Save parameters we are passed. + */ + mr r31,r3 + mr r30,r4 + mr r29,r5 + mr r28,r6 + mr r27,r7 + + /* We have to turn on the MMU right away so we get cache modes + * set correctly. + */ + bl initial_mmu + +/* We now have the lower 16 Meg mapped into TLB entries, and the caches + * ready to work. + */ +turn_on_mmu: + lis r0,MSR_KERNEL@h + ori r0,r0,MSR_KERNEL@l + mtspr SPRN_SRR1,r0 + lis r0,start_here@h + ori r0,r0,start_here@l + mtspr SPRN_SRR0,r0 + SYNC + rfi /* enables MMU */ + b . /* prevent prefetch past rfi */ + +/* + * This area is used for temporarily saving registers during the + * critical exception prolog. + */ + . = 0xc0 +crit_save: +_GLOBAL(crit_r10) + .space 4 +_GLOBAL(crit_r11) + .space 4 + +/* + * Exception vector entry code. This code runs with address translation + * turned off (i.e. using physical addresses). We assume SPRG3 has the + * physical address of the current task thread_struct. + * Note that we have to have decremented r1 before we write to any fields + * of the exception frame, since a critical interrupt could occur at any + * time, and it will write to the area immediately below the current r1. + */ +#define NORMAL_EXCEPTION_PROLOG \ + mtspr SPRN_SPRG0,r10; /* save two registers to work with */\ + mtspr SPRN_SPRG1,r11; \ + mtspr SPRN_SPRG2,r1; \ + mfcr r10; /* save CR in r10 for now */\ + mfspr r11,SPRN_SRR1; /* check whether user or kernel */\ + andi. r11,r11,MSR_PR; \ + beq 1f; \ + mfspr r1,SPRN_SPRG3; /* if from user, start at top of */\ + lwz r1,THREAD_INFO-THREAD(r1); /* this thread's kernel stack */\ + addi r1,r1,THREAD_SIZE; \ +1: subi r1,r1,INT_FRAME_SIZE; /* Allocate an exception frame */\ + tophys(r11,r1); \ + stw r10,_CCR(r11); /* save various registers */\ + stw r12,GPR12(r11); \ + stw r9,GPR9(r11); \ + mfspr r10,SPRN_SPRG0; \ + stw r10,GPR10(r11); \ + mfspr r12,SPRN_SPRG1; \ + stw r12,GPR11(r11); \ + mflr r10; \ + stw r10,_LINK(r11); \ + mfspr r10,SPRN_SPRG2; \ + mfspr r12,SPRN_SRR0; \ + stw r10,GPR1(r11); \ + mfspr r9,SPRN_SRR1; \ + stw r10,0(r11); \ + rlwinm r9,r9,0,14,12; /* clear MSR_WE (necessary?) */\ + stw r0,GPR0(r11); \ + SAVE_4GPRS(3, r11); \ + SAVE_2GPRS(7, r11) + +/* + * Exception prolog for critical exceptions. This is a little different + * from the normal exception prolog above since a critical exception + * can potentially occur at any point during normal exception processing. + * Thus we cannot use the same SPRG registers as the normal prolog above. + * Instead we use a couple of words of memory at low physical addresses. + * This is OK since we don't support SMP on these processors. + */ +#define CRITICAL_EXCEPTION_PROLOG \ + stw r10,crit_r10@l(0); /* save two registers to work with */\ + stw r11,crit_r11@l(0); \ + mfcr r10; /* save CR in r10 for now */\ + mfspr r11,SPRN_SRR3; /* check whether user or kernel */\ + andi. r11,r11,MSR_PR; \ + lis r11,critical_stack_top@h; \ + ori r11,r11,critical_stack_top@l; \ + beq 1f; \ + /* COMING FROM USER MODE */ \ + mfspr r11,SPRN_SPRG3; /* if from user, start at top of */\ + lwz r11,THREAD_INFO-THREAD(r11); /* this thread's kernel stack */\ + addi r11,r11,THREAD_SIZE; \ +1: subi r11,r11,INT_FRAME_SIZE; /* Allocate an exception frame */\ + tophys(r11,r11); \ + stw r10,_CCR(r11); /* save various registers */\ + stw r12,GPR12(r11); \ + stw r9,GPR9(r11); \ + mflr r10; \ + stw r10,_LINK(r11); \ + mfspr r12,SPRN_DEAR; /* save DEAR and ESR in the frame */\ + stw r12,_DEAR(r11); /* since they may have had stuff */\ + mfspr r9,SPRN_ESR; /* in them at the point where the */\ + stw r9,_ESR(r11); /* exception was taken */\ + mfspr r12,SPRN_SRR2; \ + stw r1,GPR1(r11); \ + mfspr r9,SPRN_SRR3; \ + stw r1,0(r11); \ + tovirt(r1,r11); \ + rlwinm r9,r9,0,14,12; /* clear MSR_WE (necessary?) */\ + stw r0,GPR0(r11); \ + SAVE_4GPRS(3, r11); \ + SAVE_2GPRS(7, r11) + + /* + * State at this point: + * r9 saved in stack frame, now saved SRR3 & ~MSR_WE + * r10 saved in crit_r10 and in stack frame, trashed + * r11 saved in crit_r11 and in stack frame, + * now phys stack/exception frame pointer + * r12 saved in stack frame, now saved SRR2 + * CR saved in stack frame, CR0.EQ = !SRR3.PR + * LR, DEAR, ESR in stack frame + * r1 saved in stack frame, now virt stack/excframe pointer + * r0, r3-r8 saved in stack frame + */ + +/* + * Exception vectors. + */ +#define START_EXCEPTION(n, label) \ + . = n; \ +label: + +#define EXCEPTION(n, label, hdlr, xfer) \ + START_EXCEPTION(n, label); \ + NORMAL_EXCEPTION_PROLOG; \ + addi r3,r1,STACK_FRAME_OVERHEAD; \ + xfer(n, hdlr) + +#define CRITICAL_EXCEPTION(n, label, hdlr) \ + START_EXCEPTION(n, label); \ + CRITICAL_EXCEPTION_PROLOG; \ + addi r3,r1,STACK_FRAME_OVERHEAD; \ + EXC_XFER_TEMPLATE(hdlr, n+2, (MSR_KERNEL & ~(MSR_ME|MSR_DE|MSR_CE)), \ + NOCOPY, crit_transfer_to_handler, \ + ret_from_crit_exc) + +#define EXC_XFER_TEMPLATE(hdlr, trap, msr, copyee, tfer, ret) \ + li r10,trap; \ + stw r10,TRAP(r11); \ + lis r10,msr@h; \ + ori r10,r10,msr@l; \ + copyee(r10, r9); \ + bl tfer; \ + .long hdlr; \ + .long ret + +#define COPY_EE(d, s) rlwimi d,s,0,16,16 +#define NOCOPY(d, s) + +#define EXC_XFER_STD(n, hdlr) \ + EXC_XFER_TEMPLATE(hdlr, n, MSR_KERNEL, NOCOPY, transfer_to_handler_full, \ + ret_from_except_full) + +#define EXC_XFER_LITE(n, hdlr) \ + EXC_XFER_TEMPLATE(hdlr, n+1, MSR_KERNEL, NOCOPY, transfer_to_handler, \ + ret_from_except) + +#define EXC_XFER_EE(n, hdlr) \ + EXC_XFER_TEMPLATE(hdlr, n, MSR_KERNEL, COPY_EE, transfer_to_handler_full, \ + ret_from_except_full) + +#define EXC_XFER_EE_LITE(n, hdlr) \ + EXC_XFER_TEMPLATE(hdlr, n+1, MSR_KERNEL, COPY_EE, transfer_to_handler, \ + ret_from_except) + + +/* + * 0x0100 - Critical Interrupt Exception + */ + CRITICAL_EXCEPTION(0x0100, CriticalInterrupt, UnknownException) + +/* + * 0x0200 - Machine Check Exception + */ + CRITICAL_EXCEPTION(0x0200, MachineCheck, MachineCheckException) + +/* + * 0x0300 - Data Storage Exception + * This happens for just a few reasons. U0 set (but we don't do that), + * or zone protection fault (user violation, write to protected page). + * If this is just an update of modified status, we do that quickly + * and exit. Otherwise, we call heavywight functions to do the work. + */ + START_EXCEPTION(0x0300, DataStorage) + mtspr SPRN_SPRG0, r10 /* Save some working registers */ + mtspr SPRN_SPRG1, r11 +#ifdef CONFIG_403GCX + stw r12, 0(r0) + stw r9, 4(r0) + mfcr r11 + mfspr r12, SPRN_PID + stw r11, 8(r0) + stw r12, 12(r0) +#else + mtspr SPRN_SPRG4, r12 + mtspr SPRN_SPRG5, r9 + mfcr r11 + mfspr r12, SPRN_PID + mtspr SPRN_SPRG7, r11 + mtspr SPRN_SPRG6, r12 +#endif + + /* First, check if it was a zone fault (which means a user + * tried to access a kernel or read-protected page - always + * a SEGV). All other faults here must be stores, so no + * need to check ESR_DST as well. */ + mfspr r10, SPRN_ESR + andis. r10, r10, ESR_DIZ@h + bne 2f + + mfspr r10, SPRN_DEAR /* Get faulting address */ + + /* If we are faulting a kernel address, we have to use the + * kernel page tables. + */ + andis. r11, r10, 0x8000 + beq 3f + lis r11, swapper_pg_dir@h + ori r11, r11, swapper_pg_dir@l + li r9, 0 + mtspr SPRN_PID, r9 /* TLB will have 0 TID */ + b 4f + + /* Get the PGD for the current thread. + */ +3: + mfspr r11,SPRN_SPRG3 + lwz r11,PGDIR(r11) +4: + tophys(r11, r11) + rlwimi r11, r10, 12, 20, 29 /* Create L1 (pgdir/pmd) address */ + lwz r11, 0(r11) /* Get L1 entry */ + rlwinm. r12, r11, 0, 0, 19 /* Extract L2 (pte) base address */ + beq 2f /* Bail if no table */ + + rlwimi r12, r10, 22, 20, 29 /* Compute PTE address */ + lwz r11, 0(r12) /* Get Linux PTE */ + + andi. r9, r11, _PAGE_RW /* Is it writeable? */ + beq 2f /* Bail if not */ + + /* Update 'changed'. + */ + ori r11, r11, _PAGE_DIRTY|_PAGE_ACCESSED|_PAGE_HWWRITE + stw r11, 0(r12) /* Update Linux page table */ + + /* Most of the Linux PTE is ready to load into the TLB LO. + * We set ZSEL, where only the LS-bit determines user access. + * We set execute, because we don't have the granularity to + * properly set this at the page level (Linux problem). + * If shared is set, we cause a zero PID->TID load. + * Many of these bits are software only. Bits we don't set + * here we (properly should) assume have the appropriate value. + */ + li r12, 0x0ce2 + andc r11, r11, r12 /* Make sure 20, 21 are zero */ + + /* find the TLB index that caused the fault. It has to be here. + */ + tlbsx r9, 0, r10 + + tlbwe r11, r9, TLB_DATA /* Load TLB LO */ + + /* Done...restore registers and get out of here. + */ +#ifdef CONFIG_403GCX + lwz r12, 12(r0) + lwz r11, 8(r0) + mtspr SPRN_PID, r12 + mtcr r11 + lwz r9, 4(r0) + lwz r12, 0(r0) +#else + mfspr r12, SPRN_SPRG6 + mfspr r11, SPRN_SPRG7 + mtspr SPRN_PID, r12 + mtcr r11 + mfspr r9, SPRN_SPRG5 + mfspr r12, SPRN_SPRG4 +#endif + mfspr r11, SPRN_SPRG1 + mfspr r10, SPRN_SPRG0 + PPC405_ERR77_SYNC + rfi /* Should sync shadow TLBs */ + b . /* prevent prefetch past rfi */ + +2: + /* The bailout. Restore registers to pre-exception conditions + * and call the heavyweights to help us out. + */ +#ifdef CONFIG_403GCX + lwz r12, 12(r0) + lwz r11, 8(r0) + mtspr SPRN_PID, r12 + mtcr r11 + lwz r9, 4(r0) + lwz r12, 0(r0) +#else + mfspr r12, SPRN_SPRG6 + mfspr r11, SPRN_SPRG7 + mtspr SPRN_PID, r12 + mtcr r11 + mfspr r9, SPRN_SPRG5 + mfspr r12, SPRN_SPRG4 +#endif + mfspr r11, SPRN_SPRG1 + mfspr r10, SPRN_SPRG0 + b DataAccess + +/* + * 0x0400 - Instruction Storage Exception + * This is caused by a fetch from non-execute or guarded pages. + */ + START_EXCEPTION(0x0400, InstructionAccess) + NORMAL_EXCEPTION_PROLOG + mr r4,r12 /* Pass SRR0 as arg2 */ + li r5,0 /* Pass zero as arg3 */ + EXC_XFER_EE_LITE(0x400, handle_page_fault) + +/* 0x0500 - External Interrupt Exception */ + EXCEPTION(0x0500, HardwareInterrupt, do_IRQ, EXC_XFER_LITE) + +/* 0x0600 - Alignment Exception */ + START_EXCEPTION(0x0600, Alignment) + NORMAL_EXCEPTION_PROLOG + mfspr r4,SPRN_DEAR /* Grab the DEAR and save it */ + stw r4,_DEAR(r11) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_EE(0x600, AlignmentException) + +/* 0x0700 - Program Exception */ + START_EXCEPTION(0x0700, ProgramCheck) + NORMAL_EXCEPTION_PROLOG + mfspr r4,SPRN_ESR /* Grab the ESR and save it */ + stw r4,_ESR(r11) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_STD(0x700, ProgramCheckException) + + EXCEPTION(0x0800, Trap_08, UnknownException, EXC_XFER_EE) + EXCEPTION(0x0900, Trap_09, UnknownException, EXC_XFER_EE) + EXCEPTION(0x0A00, Trap_0A, UnknownException, EXC_XFER_EE) + EXCEPTION(0x0B00, Trap_0B, UnknownException, EXC_XFER_EE) + +/* 0x0C00 - System Call Exception */ + START_EXCEPTION(0x0C00, SystemCall) + NORMAL_EXCEPTION_PROLOG + EXC_XFER_EE_LITE(0xc00, DoSyscall) + + EXCEPTION(0x0D00, Trap_0D, UnknownException, EXC_XFER_EE) + EXCEPTION(0x0E00, Trap_0E, UnknownException, EXC_XFER_EE) + EXCEPTION(0x0F00, Trap_0F, UnknownException, EXC_XFER_EE) + +/* 0x1000 - Programmable Interval Timer (PIT) Exception */ + START_EXCEPTION(0x1000, Decrementer) + NORMAL_EXCEPTION_PROLOG + lis r0,TSR_PIS@h + mtspr SPRN_TSR,r0 /* Clear the PIT exception */ + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_LITE(0x1000, timer_interrupt) + +#if 0 +/* NOTE: + * FIT and WDT handlers are not implemented yet. + */ + +/* 0x1010 - Fixed Interval Timer (FIT) Exception +*/ + STND_EXCEPTION(0x1010, FITException, UnknownException) + +/* 0x1020 - Watchdog Timer (WDT) Exception +*/ + + CRITICAL_EXCEPTION(0x1020, WDTException, UnknownException) +#endif + +/* 0x1100 - Data TLB Miss Exception + * As the name implies, translation is not in the MMU, so search the + * page tables and fix it. The only purpose of this function is to + * load TLB entries from the page table if they exist. + */ + START_EXCEPTION(0x1100, DTLBMiss) + mtspr SPRN_SPRG0, r10 /* Save some working registers */ + mtspr SPRN_SPRG1, r11 +#ifdef CONFIG_403GCX + stw r12, 0(r0) + stw r9, 4(r0) + mfcr r11 + mfspr r12, SPRN_PID + stw r11, 8(r0) + stw r12, 12(r0) +#else + mtspr SPRN_SPRG4, r12 + mtspr SPRN_SPRG5, r9 + mfcr r11 + mfspr r12, SPRN_PID + mtspr SPRN_SPRG7, r11 + mtspr SPRN_SPRG6, r12 +#endif + mfspr r10, SPRN_DEAR /* Get faulting address */ + + /* If we are faulting a kernel address, we have to use the + * kernel page tables. + */ + andis. r11, r10, 0x8000 + beq 3f + lis r11, swapper_pg_dir@h + ori r11, r11, swapper_pg_dir@l + li r9, 0 + mtspr SPRN_PID, r9 /* TLB will have 0 TID */ + b 4f + + /* Get the PGD for the current thread. + */ +3: + mfspr r11,SPRN_SPRG3 + lwz r11,PGDIR(r11) +4: + tophys(r11, r11) + rlwimi r11, r10, 12, 20, 29 /* Create L1 (pgdir/pmd) address */ + lwz r12, 0(r11) /* Get L1 entry */ + andi. r9, r12, _PMD_PRESENT /* Check if it points to a PTE page */ + beq 2f /* Bail if no table */ + + rlwimi r12, r10, 22, 20, 29 /* Compute PTE address */ + lwz r11, 0(r12) /* Get Linux PTE */ + andi. r9, r11, _PAGE_PRESENT + beq 5f + + ori r11, r11, _PAGE_ACCESSED + stw r11, 0(r12) + + /* Create TLB tag. This is the faulting address plus a static + * set of bits. These are size, valid, E, U0. + */ + li r12, 0x00c0 + rlwimi r10, r12, 0, 20, 31 + + b finish_tlb_load + +2: /* Check for possible large-page pmd entry */ + rlwinm. r9, r12, 2, 22, 24 + beq 5f + + /* Create TLB tag. This is the faulting address, plus a static + * set of bits (valid, E, U0) plus the size from the PMD. + */ + ori r9, r9, 0x40 + rlwimi r10, r9, 0, 20, 31 + mr r11, r12 + + b finish_tlb_load + +5: + /* The bailout. Restore registers to pre-exception conditions + * and call the heavyweights to help us out. + */ +#ifdef CONFIG_403GCX + lwz r12, 12(r0) + lwz r11, 8(r0) + mtspr SPRN_PID, r12 + mtcr r11 + lwz r9, 4(r0) + lwz r12, 0(r0) +#else + mfspr r12, SPRN_SPRG6 + mfspr r11, SPRN_SPRG7 + mtspr SPRN_PID, r12 + mtcr r11 + mfspr r9, SPRN_SPRG5 + mfspr r12, SPRN_SPRG4 +#endif + mfspr r11, SPRN_SPRG1 + mfspr r10, SPRN_SPRG0 + b DataAccess + +/* 0x1200 - Instruction TLB Miss Exception + * Nearly the same as above, except we get our information from different + * registers and bailout to a different point. + */ + START_EXCEPTION(0x1200, ITLBMiss) + mtspr SPRN_SPRG0, r10 /* Save some working registers */ + mtspr SPRN_SPRG1, r11 +#ifdef CONFIG_403GCX + stw r12, 0(r0) + stw r9, 4(r0) + mfcr r11 + mfspr r12, SPRN_PID + stw r11, 8(r0) + stw r12, 12(r0) +#else + mtspr SPRN_SPRG4, r12 + mtspr SPRN_SPRG5, r9 + mfcr r11 + mfspr r12, SPRN_PID + mtspr SPRN_SPRG7, r11 + mtspr SPRN_SPRG6, r12 +#endif + mfspr r10, SPRN_SRR0 /* Get faulting address */ + + /* If we are faulting a kernel address, we have to use the + * kernel page tables. + */ + andis. r11, r10, 0x8000 + beq 3f + lis r11, swapper_pg_dir@h + ori r11, r11, swapper_pg_dir@l + li r9, 0 + mtspr SPRN_PID, r9 /* TLB will have 0 TID */ + b 4f + + /* Get the PGD for the current thread. + */ +3: + mfspr r11,SPRN_SPRG3 + lwz r11,PGDIR(r11) +4: + tophys(r11, r11) + rlwimi r11, r10, 12, 20, 29 /* Create L1 (pgdir/pmd) address */ + lwz r12, 0(r11) /* Get L1 entry */ + andi. r9, r12, _PMD_PRESENT /* Check if it points to a PTE page */ + beq 2f /* Bail if no table */ + + rlwimi r12, r10, 22, 20, 29 /* Compute PTE address */ + lwz r11, 0(r12) /* Get Linux PTE */ + andi. r9, r11, _PAGE_PRESENT + beq 5f + + ori r11, r11, _PAGE_ACCESSED + stw r11, 0(r12) + + /* Create TLB tag. This is the faulting address plus a static + * set of bits. These are size, valid, E, U0. + */ + li r12, 0x00c0 + rlwimi r10, r12, 0, 20, 31 + + b finish_tlb_load + +2: /* Check for possible large-page pmd entry */ + rlwinm. r9, r12, 2, 22, 24 + beq 5f + + /* Create TLB tag. This is the faulting address, plus a static + * set of bits (valid, E, U0) plus the size from the PMD. + */ + ori r9, r9, 0x40 + rlwimi r10, r9, 0, 20, 31 + mr r11, r12 + + b finish_tlb_load + +5: + /* The bailout. Restore registers to pre-exception conditions + * and call the heavyweights to help us out. + */ +#ifdef CONFIG_403GCX + lwz r12, 12(r0) + lwz r11, 8(r0) + mtspr SPRN_PID, r12 + mtcr r11 + lwz r9, 4(r0) + lwz r12, 0(r0) +#else + mfspr r12, SPRN_SPRG6 + mfspr r11, SPRN_SPRG7 + mtspr SPRN_PID, r12 + mtcr r11 + mfspr r9, SPRN_SPRG5 + mfspr r12, SPRN_SPRG4 +#endif + mfspr r11, SPRN_SPRG1 + mfspr r10, SPRN_SPRG0 + b InstructionAccess + + EXCEPTION(0x1300, Trap_13, UnknownException, EXC_XFER_EE) + EXCEPTION(0x1400, Trap_14, UnknownException, EXC_XFER_EE) + EXCEPTION(0x1500, Trap_15, UnknownException, EXC_XFER_EE) + EXCEPTION(0x1600, Trap_16, UnknownException, EXC_XFER_EE) +#ifdef CONFIG_IBM405_ERR51 + /* 405GP errata 51 */ + START_EXCEPTION(0x1700, Trap_17) + b DTLBMiss +#else + EXCEPTION(0x1700, Trap_17, UnknownException, EXC_XFER_EE) +#endif + EXCEPTION(0x1800, Trap_18, UnknownException, EXC_XFER_EE) + EXCEPTION(0x1900, Trap_19, UnknownException, EXC_XFER_EE) + EXCEPTION(0x1A00, Trap_1A, UnknownException, EXC_XFER_EE) + EXCEPTION(0x1B00, Trap_1B, UnknownException, EXC_XFER_EE) + EXCEPTION(0x1C00, Trap_1C, UnknownException, EXC_XFER_EE) + EXCEPTION(0x1D00, Trap_1D, UnknownException, EXC_XFER_EE) + EXCEPTION(0x1E00, Trap_1E, UnknownException, EXC_XFER_EE) + EXCEPTION(0x1F00, Trap_1F, UnknownException, EXC_XFER_EE) + +/* Check for a single step debug exception while in an exception + * handler before state has been saved. This is to catch the case + * where an instruction that we are trying to single step causes + * an exception (eg ITLB/DTLB miss) and thus the first instruction of + * the exception handler generates a single step debug exception. + * + * If we get a debug trap on the first instruction of an exception handler, + * we reset the MSR_DE in the _exception handler's_ MSR (the debug trap is + * a critical exception, so we are using SPRN_CSRR1 to manipulate the MSR). + * The exception handler was handling a non-critical interrupt, so it will + * save (and later restore) the MSR via SPRN_SRR1, which will still have + * the MSR_DE bit set. + */ + /* 0x2000 - Debug Exception */ + START_EXCEPTION(0x2000, DebugTrap) + CRITICAL_EXCEPTION_PROLOG + + /* + * If this is a single step or branch-taken exception in an + * exception entry sequence, it was probably meant to apply to + * the code where the exception occurred (since exception entry + * doesn't turn off DE automatically). We simulate the effect + * of turning off DE on entry to an exception handler by turning + * off DE in the SRR3 value and clearing the debug status. + */ + mfspr r10,SPRN_DBSR /* check single-step/branch taken */ + andis. r10,r10,DBSR_IC@h + beq+ 2f + + andi. r10,r9,MSR_IR|MSR_PR /* check supervisor + MMU off */ + beq 1f /* branch and fix it up */ + + mfspr r10,SPRN_SRR2 /* Faulting instruction address */ + cmplwi r10,0x2100 + bgt+ 2f /* address above exception vectors */ + + /* here it looks like we got an inappropriate debug exception. */ +1: rlwinm r9,r9,0,~MSR_DE /* clear DE in the SRR3 value */ + lis r10,DBSR_IC@h /* clear the IC event */ + mtspr SPRN_DBSR,r10 + /* restore state and get out */ + lwz r10,_CCR(r11) + lwz r0,GPR0(r11) + lwz r1,GPR1(r11) + mtcrf 0x80,r10 + mtspr SPRN_SRR2,r12 + mtspr SPRN_SRR3,r9 + lwz r9,GPR9(r11) + lwz r12,GPR12(r11) + lwz r10,crit_r10@l(0) + lwz r11,crit_r11@l(0) + PPC405_ERR77_SYNC + rfci + b . + + /* continue normal handling for a critical exception... */ +2: mfspr r4,SPRN_DBSR + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_TEMPLATE(DebugException, 0x2002, \ + (MSR_KERNEL & ~(MSR_ME|MSR_DE|MSR_CE)), \ + NOCOPY, crit_transfer_to_handler, ret_from_crit_exc) + +/* + * The other Data TLB exceptions bail out to this point + * if they can't resolve the lightweight TLB fault. + */ +DataAccess: + NORMAL_EXCEPTION_PROLOG + mfspr r5,SPRN_ESR /* Grab the ESR, save it, pass arg3 */ + stw r5,_ESR(r11) + mfspr r4,SPRN_DEAR /* Grab the DEAR, save it, pass arg2 */ + EXC_XFER_EE_LITE(0x300, handle_page_fault) + +/* Other PowerPC processors, namely those derived from the 6xx-series + * have vectors from 0x2100 through 0x2F00 defined, but marked as reserved. + * However, for the 4xx-series processors these are neither defined nor + * reserved. + */ + + /* Damn, I came up one instruction too many to fit into the + * exception space :-). Both the instruction and data TLB + * miss get to this point to load the TLB. + * r10 - TLB_TAG value + * r11 - Linux PTE + * r12, r9 - avilable to use + * PID - loaded with proper value when we get here + * Upon exit, we reload everything and RFI. + * Actually, it will fit now, but oh well.....a common place + * to load the TLB. + */ +tlb_4xx_index: + .long 0 +finish_tlb_load: + /* load the next available TLB index. + */ + lwz r9, tlb_4xx_index@l(0) + addi r9, r9, 1 + andi. r9, r9, (PPC4XX_TLB_SIZE-1) + stw r9, tlb_4xx_index@l(0) + +6: + /* + * Clear out the software-only bits in the PTE to generate the + * TLB_DATA value. These are the bottom 2 bits of the RPM, the + * top 3 bits of the zone field, and M. + */ + li r12, 0x0ce2 + andc r11, r11, r12 + + tlbwe r11, r9, TLB_DATA /* Load TLB LO */ + tlbwe r10, r9, TLB_TAG /* Load TLB HI */ + + /* Done...restore registers and get out of here. + */ +#ifdef CONFIG_403GCX + lwz r12, 12(r0) + lwz r11, 8(r0) + mtspr SPRN_PID, r12 + mtcr r11 + lwz r9, 4(r0) + lwz r12, 0(r0) +#else + mfspr r12, SPRN_SPRG6 + mfspr r11, SPRN_SPRG7 + mtspr SPRN_PID, r12 + mtcr r11 + mfspr r9, SPRN_SPRG5 + mfspr r12, SPRN_SPRG4 +#endif + mfspr r11, SPRN_SPRG1 + mfspr r10, SPRN_SPRG0 + PPC405_ERR77_SYNC + rfi /* Should sync shadow TLBs */ + b . /* prevent prefetch past rfi */ + +/* extern void giveup_fpu(struct task_struct *prev) + * + * The PowerPC 4xx family of processors do not have an FPU, so this just + * returns. + */ +_GLOBAL(giveup_fpu) + blr + +/* This is where the main kernel code starts. + */ +start_here: + + /* ptr to current */ + lis r2,init_task@h + ori r2,r2,init_task@l + + /* ptr to phys current thread */ + tophys(r4,r2) + addi r4,r4,THREAD /* init task's THREAD */ + mtspr SPRN_SPRG3,r4 + + /* stack */ + lis r1,init_thread_union@ha + addi r1,r1,init_thread_union@l + li r0,0 + stwu r0,THREAD_SIZE-STACK_FRAME_OVERHEAD(r1) + + bl early_init /* We have to do this with MMU on */ + +/* + * Decide what sort of machine this is and initialize the MMU. + */ + mr r3,r31 + mr r4,r30 + mr r5,r29 + mr r6,r28 + mr r7,r27 + bl machine_init + bl MMU_init + +/* Go back to running unmapped so we can load up new values + * and change to using our exception vectors. + * On the 4xx, all we have to do is invalidate the TLB to clear + * the old 16M byte TLB mappings. + */ + lis r4,2f@h + ori r4,r4,2f@l + tophys(r4,r4) + lis r3,(MSR_KERNEL & ~(MSR_IR|MSR_DR))@h + ori r3,r3,(MSR_KERNEL & ~(MSR_IR|MSR_DR))@l + mtspr SPRN_SRR0,r4 + mtspr SPRN_SRR1,r3 + rfi + b . /* prevent prefetch past rfi */ + +/* Load up the kernel context */ +2: + sync /* Flush to memory before changing TLB */ + tlbia + isync /* Flush shadow TLBs */ + + /* set up the PTE pointers for the Abatron bdiGDB. + */ + lis r6, swapper_pg_dir@h + ori r6, r6, swapper_pg_dir@l + lis r5, abatron_pteptrs@h + ori r5, r5, abatron_pteptrs@l + stw r5, 0xf0(r0) /* Must match your Abatron config file */ + tophys(r5,r5) + stw r6, 0(r5) + +/* Now turn on the MMU for real! */ + lis r4,MSR_KERNEL@h + ori r4,r4,MSR_KERNEL@l + lis r3,start_kernel@h + ori r3,r3,start_kernel@l + mtspr SPRN_SRR0,r3 + mtspr SPRN_SRR1,r4 + rfi /* enable MMU and jump to start_kernel */ + b . /* prevent prefetch past rfi */ + +/* Set up the initial MMU state so we can do the first level of + * kernel initialization. This maps the first 16 MBytes of memory 1:1 + * virtual to physical and more importantly sets the cache mode. + */ +initial_mmu: + tlbia /* Invalidate all TLB entries */ + isync + + /* We should still be executing code at physical address 0x0000xxxx + * at this point. However, start_here is at virtual address + * 0xC000xxxx. So, set up a TLB mapping to cover this once + * translation is enabled. + */ + + lis r3,KERNELBASE@h /* Load the kernel virtual address */ + ori r3,r3,KERNELBASE@l + tophys(r4,r3) /* Load the kernel physical address */ + + iccci r0,r3 /* Invalidate the i-cache before use */ + + /* Load the kernel PID. + */ + li r0,0 + mtspr SPRN_PID,r0 + sync + + /* Configure and load two entries into TLB slots 62 and 63. + * In case we are pinning TLBs, these are reserved in by the + * other TLB functions. If not reserving, then it doesn't + * matter where they are loaded. + */ + clrrwi r4,r4,10 /* Mask off the real page number */ + ori r4,r4,(TLB_WR | TLB_EX) /* Set the write and execute bits */ + + clrrwi r3,r3,10 /* Mask off the effective page number */ + ori r3,r3,(TLB_VALID | TLB_PAGESZ(PAGESZ_16M)) + + li r0,63 /* TLB slot 63 */ + + tlbwe r4,r0,TLB_DATA /* Load the data portion of the entry */ + tlbwe r3,r0,TLB_TAG /* Load the tag portion of the entry */ + +#if defined(CONFIG_SERIAL_TEXT_DEBUG) && defined(SERIAL_DEBUG_IO_BASE) + + /* Load a TLB entry for the UART, so that ppc4xx_progress() can use + * the UARTs nice and early. We use a 4k real==virtual mapping. */ + + lis r3,SERIAL_DEBUG_IO_BASE@h + ori r3,r3,SERIAL_DEBUG_IO_BASE@l + mr r4,r3 + clrrwi r4,r4,12 + ori r4,r4,(TLB_WR|TLB_I|TLB_M|TLB_G) + + clrrwi r3,r3,12 + ori r3,r3,(TLB_VALID | TLB_PAGESZ(PAGESZ_4K)) + + li r0,0 /* TLB slot 0 */ + tlbwe r4,r0,TLB_DATA + tlbwe r3,r0,TLB_TAG +#endif /* CONFIG_SERIAL_DEBUG_TEXT && SERIAL_DEBUG_IO_BASE */ + + isync + + /* Establish the exception vector base + */ + lis r4,KERNELBASE@h /* EVPR only uses the high 16-bits */ + tophys(r0,r4) /* Use the physical address */ + mtspr SPRN_EVPR,r0 + + blr + +_GLOBAL(abort) + mfspr r13,SPRN_DBCR0 + oris r13,r13,DBCR0_RST_SYSTEM@h + mtspr SPRN_DBCR0,r13 + +_GLOBAL(set_context) + +#ifdef CONFIG_BDI_SWITCH + /* Context switch the PTE pointer for the Abatron BDI2000. + * The PGDIR is the second parameter. + */ + lis r5, KERNELBASE@h + lwz r5, 0xf0(r5) + stw r4, 0x4(r5) +#endif + sync + mtspr SPRN_PID,r3 + isync /* Need an isync to flush shadow */ + /* TLBs after changing PID */ + blr + +/* We put a few things here that have to be page-aligned. This stuff + * goes at the beginning of the data segment, which is page-aligned. + */ + .data +_GLOBAL(sdata) +_GLOBAL(empty_zero_page) + .space 4096 +_GLOBAL(swapper_pg_dir) + .space 4096 + + +/* Stack for handling critical exceptions from kernel mode */ + .section .bss + .align 12 +exception_stack_bottom: + .space 4096 +critical_stack_top: +_GLOBAL(exception_stack_top) + +/* This space gets a copy of optional info passed to us by the bootstrap + * which is used to pass parameters into the kernel like root=/dev/sda1, etc. + */ +_GLOBAL(cmd_line) + .space 512 + +/* Room for two PTE pointers, usually the kernel and current user pointers + * to their respective root page table. + */ +abatron_pteptrs: + .space 8 diff --git a/arch/ppc/kernel/head_8xx.S b/arch/ppc/kernel/head_8xx.S new file mode 100644 index 00000000000..5a7a64e91fc --- /dev/null +++ b/arch/ppc/kernel/head_8xx.S @@ -0,0 +1,862 @@ +/* + * arch/ppc/kernel/except_8xx.S + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * Rewritten by Cort Dougan (cort@cs.nmt.edu) for PReP + * Copyright (C) 1996 Cort Dougan + * Low-level exception handlers and MMU support + * rewritten by Paul Mackerras. + * Copyright (C) 1996 Paul Mackerras. + * MPC8xx modifications by Dan Malek + * Copyright (C) 1997 Dan Malek (dmalek@jlc.net). + * + * This file contains low-level support and setup for PowerPC 8xx + * embedded processors, including trap and interrupt dispatch. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Macro to make the code more readable. */ +#ifdef CONFIG_8xx_CPU6 +#define DO_8xx_CPU6(val, reg) \ + li reg, val; \ + stw reg, 12(r0); \ + lwz reg, 12(r0); +#else +#define DO_8xx_CPU6(val, reg) +#endif + .text + .globl _stext +_stext: + .text + .globl _start +_start: + +/* MPC8xx + * This port was done on an MBX board with an 860. Right now I only + * support an ELF compressed (zImage) boot from EPPC-Bug because the + * code there loads up some registers before calling us: + * r3: ptr to board info data + * r4: initrd_start or if no initrd then 0 + * r5: initrd_end - unused if r4 is 0 + * r6: Start of command line string + * r7: End of command line string + * + * I decided to use conditional compilation instead of checking PVR and + * adding more processor specific branches around code I don't need. + * Since this is an embedded processor, I also appreciate any memory + * savings I can get. + * + * The MPC8xx does not have any BATs, but it supports large page sizes. + * We first initialize the MMU to support 8M byte pages, then load one + * entry into each of the instruction and data TLBs to map the first + * 8M 1:1. I also mapped an additional I/O space 1:1 so we can get to + * the "internal" processor registers before MMU_init is called. + * + * The TLB code currently contains a major hack. Since I use the condition + * code register, I have to save and restore it. I am out of registers, so + * I just store it in memory location 0 (the TLB handlers are not reentrant). + * To avoid making any decisions, I need to use the "segment" valid bit + * in the first level table, but that would require many changes to the + * Linux page directory/table functions that I don't want to do right now. + * + * I used to use SPRG2 for a temporary register in the TLB handler, but it + * has since been put to other uses. I now use a hack to save a register + * and the CCR at memory location 0.....Someday I'll fix this..... + * -- Dan + */ + .globl __start +__start: + mr r31,r3 /* save parameters */ + mr r30,r4 + mr r29,r5 + mr r28,r6 + mr r27,r7 + + /* We have to turn on the MMU right away so we get cache modes + * set correctly. + */ + bl initial_mmu + +/* We now have the lower 8 Meg mapped into TLB entries, and the caches + * ready to work. + */ + +turn_on_mmu: + mfmsr r0 + ori r0,r0,MSR_DR|MSR_IR + mtspr SPRN_SRR1,r0 + lis r0,start_here@h + ori r0,r0,start_here@l + mtspr SPRN_SRR0,r0 + SYNC + rfi /* enables MMU */ + +/* + * Exception entry code. This code runs with address translation + * turned off, i.e. using physical addresses. + * We assume sprg3 has the physical address of the current + * task's thread_struct. + */ +#define EXCEPTION_PROLOG \ + mtspr SPRN_SPRG0,r10; \ + mtspr SPRN_SPRG1,r11; \ + mfcr r10; \ + EXCEPTION_PROLOG_1; \ + EXCEPTION_PROLOG_2 + +#define EXCEPTION_PROLOG_1 \ + mfspr r11,SPRN_SRR1; /* check whether user or kernel */ \ + andi. r11,r11,MSR_PR; \ + tophys(r11,r1); /* use tophys(r1) if kernel */ \ + beq 1f; \ + mfspr r11,SPRN_SPRG3; \ + lwz r11,THREAD_INFO-THREAD(r11); \ + addi r11,r11,THREAD_SIZE; \ + tophys(r11,r11); \ +1: subi r11,r11,INT_FRAME_SIZE /* alloc exc. frame */ + + +#define EXCEPTION_PROLOG_2 \ + CLR_TOP32(r11); \ + stw r10,_CCR(r11); /* save registers */ \ + stw r12,GPR12(r11); \ + stw r9,GPR9(r11); \ + mfspr r10,SPRN_SPRG0; \ + stw r10,GPR10(r11); \ + mfspr r12,SPRN_SPRG1; \ + stw r12,GPR11(r11); \ + mflr r10; \ + stw r10,_LINK(r11); \ + mfspr r12,SPRN_SRR0; \ + mfspr r9,SPRN_SRR1; \ + stw r1,GPR1(r11); \ + stw r1,0(r11); \ + tovirt(r1,r11); /* set new kernel sp */ \ + li r10,MSR_KERNEL & ~(MSR_IR|MSR_DR); /* can take exceptions */ \ + MTMSRD(r10); /* (except for mach check in rtas) */ \ + stw r0,GPR0(r11); \ + SAVE_4GPRS(3, r11); \ + SAVE_2GPRS(7, r11) + +/* + * Note: code which follows this uses cr0.eq (set if from kernel), + * r11, r12 (SRR0), and r9 (SRR1). + * + * Note2: once we have set r1 we are in a position to take exceptions + * again, and we could thus set MSR:RI at that point. + */ + +/* + * Exception vectors. + */ +#define EXCEPTION(n, label, hdlr, xfer) \ + . = n; \ +label: \ + EXCEPTION_PROLOG; \ + addi r3,r1,STACK_FRAME_OVERHEAD; \ + xfer(n, hdlr) + +#define EXC_XFER_TEMPLATE(n, hdlr, trap, copyee, tfer, ret) \ + li r10,trap; \ + stw r10,TRAP(r11); \ + li r10,MSR_KERNEL; \ + copyee(r10, r9); \ + bl tfer; \ +i##n: \ + .long hdlr; \ + .long ret + +#define COPY_EE(d, s) rlwimi d,s,0,16,16 +#define NOCOPY(d, s) + +#define EXC_XFER_STD(n, hdlr) \ + EXC_XFER_TEMPLATE(n, hdlr, n, NOCOPY, transfer_to_handler_full, \ + ret_from_except_full) + +#define EXC_XFER_LITE(n, hdlr) \ + EXC_XFER_TEMPLATE(n, hdlr, n+1, NOCOPY, transfer_to_handler, \ + ret_from_except) + +#define EXC_XFER_EE(n, hdlr) \ + EXC_XFER_TEMPLATE(n, hdlr, n, COPY_EE, transfer_to_handler_full, \ + ret_from_except_full) + +#define EXC_XFER_EE_LITE(n, hdlr) \ + EXC_XFER_TEMPLATE(n, hdlr, n+1, COPY_EE, transfer_to_handler, \ + ret_from_except) + +/* System reset */ + EXCEPTION(0x100, Reset, UnknownException, EXC_XFER_STD) + +/* Machine check */ + . = 0x200 +MachineCheck: + EXCEPTION_PROLOG + mfspr r4,SPRN_DAR + stw r4,_DAR(r11) + mfspr r5,SPRN_DSISR + stw r5,_DSISR(r11) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_STD(0x200, MachineCheckException) + +/* Data access exception. + * This is "never generated" by the MPC8xx. We jump to it for other + * translation errors. + */ + . = 0x300 +DataAccess: + EXCEPTION_PROLOG + mfspr r10,SPRN_DSISR + stw r10,_DSISR(r11) + mr r5,r10 + mfspr r4,SPRN_DAR + EXC_XFER_EE_LITE(0x300, handle_page_fault) + +/* Instruction access exception. + * This is "never generated" by the MPC8xx. We jump to it for other + * translation errors. + */ + . = 0x400 +InstructionAccess: + EXCEPTION_PROLOG + mr r4,r12 + mr r5,r9 + EXC_XFER_EE_LITE(0x400, handle_page_fault) + +/* External interrupt */ + EXCEPTION(0x500, HardwareInterrupt, do_IRQ, EXC_XFER_LITE) + +/* Alignment exception */ + . = 0x600 +Alignment: + EXCEPTION_PROLOG + mfspr r4,SPRN_DAR + stw r4,_DAR(r11) + mfspr r5,SPRN_DSISR + stw r5,_DSISR(r11) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_EE(0x600, AlignmentException) + +/* Program check exception */ + EXCEPTION(0x700, ProgramCheck, ProgramCheckException, EXC_XFER_STD) + +/* No FPU on MPC8xx. This exception is not supposed to happen. +*/ + EXCEPTION(0x800, FPUnavailable, UnknownException, EXC_XFER_STD) + +/* Decrementer */ + EXCEPTION(0x900, Decrementer, timer_interrupt, EXC_XFER_LITE) + + EXCEPTION(0xa00, Trap_0a, UnknownException, EXC_XFER_EE) + EXCEPTION(0xb00, Trap_0b, UnknownException, EXC_XFER_EE) + +/* System call */ + . = 0xc00 +SystemCall: + EXCEPTION_PROLOG + EXC_XFER_EE_LITE(0xc00, DoSyscall) + +/* Single step - not used on 601 */ + EXCEPTION(0xd00, SingleStep, SingleStepException, EXC_XFER_STD) + EXCEPTION(0xe00, Trap_0e, UnknownException, EXC_XFER_EE) + EXCEPTION(0xf00, Trap_0f, UnknownException, EXC_XFER_EE) + +/* On the MPC8xx, this is a software emulation interrupt. It occurs + * for all unimplemented and illegal instructions. + */ + EXCEPTION(0x1000, SoftEmu, SoftwareEmulation, EXC_XFER_STD) + + . = 0x1100 +/* + * For the MPC8xx, this is a software tablewalk to load the instruction + * TLB. It is modelled after the example in the Motorola manual. The task + * switch loads the M_TWB register with the pointer to the first level table. + * If we discover there is no second level table (the value is zero), the + * plan was to load that into the TLB, which causes another fault into the + * TLB Error interrupt where we can handle such problems. However, that did + * not work, so if we discover there is no second level table, we restore + * registers and branch to the error exception. We have to use the MD_xxx + * registers for the tablewalk because the equivalent MI_xxx registers + * only perform the attribute functions. + */ +InstructionTLBMiss: +#ifdef CONFIG_8xx_CPU6 + stw r3, 8(r0) +#endif + DO_8xx_CPU6(0x3f80, r3) + mtspr SPRN_M_TW, r10 /* Save a couple of working registers */ + mfcr r10 + stw r10, 0(r0) + stw r11, 4(r0) + mfspr r10, SPRN_SRR0 /* Get effective address of fault */ + DO_8xx_CPU6(0x3780, r3) + mtspr SPRN_MD_EPN, r10 /* Have to use MD_EPN for walk, MI_EPN can't */ + mfspr r10, SPRN_M_TWB /* Get level 1 table entry address */ + + /* If we are faulting a kernel address, we have to use the + * kernel page tables. + */ + andi. r11, r10, 0x0800 /* Address >= 0x80000000 */ + beq 3f + lis r11, swapper_pg_dir@h + ori r11, r11, swapper_pg_dir@l + rlwimi r10, r11, 0, 2, 19 +3: + lwz r11, 0(r10) /* Get the level 1 entry */ + rlwinm. r10, r11,0,0,19 /* Extract page descriptor page address */ + beq 2f /* If zero, don't try to find a pte */ + + /* We have a pte table, so load the MI_TWC with the attributes + * for this "segment." + */ + ori r11,r11,1 /* Set valid bit */ + DO_8xx_CPU6(0x2b80, r3) + mtspr SPRN_MI_TWC, r11 /* Set segment attributes */ + DO_8xx_CPU6(0x3b80, r3) + mtspr SPRN_MD_TWC, r11 /* Load pte table base address */ + mfspr r11, SPRN_MD_TWC /* ....and get the pte address */ + lwz r10, 0(r11) /* Get the pte */ + + ori r10, r10, _PAGE_ACCESSED + stw r10, 0(r11) + + /* The Linux PTE won't go exactly into the MMU TLB. + * Software indicator bits 21, 22 and 28 must be clear. + * Software indicator bits 24, 25, 26, and 27 must be + * set. All other Linux PTE bits control the behavior + * of the MMU. + */ +2: li r11, 0x00f0 + rlwimi r10, r11, 0, 24, 28 /* Set 24-27, clear 28 */ + DO_8xx_CPU6(0x2d80, r3) + mtspr SPRN_MI_RPN, r10 /* Update TLB entry */ + + mfspr r10, SPRN_M_TW /* Restore registers */ + lwz r11, 0(r0) + mtcr r11 + lwz r11, 4(r0) +#ifdef CONFIG_8xx_CPU6 + lwz r3, 8(r0) +#endif + rfi + + . = 0x1200 +DataStoreTLBMiss: +#ifdef CONFIG_8xx_CPU6 + stw r3, 8(r0) +#endif + DO_8xx_CPU6(0x3f80, r3) + mtspr SPRN_M_TW, r10 /* Save a couple of working registers */ + mfcr r10 + stw r10, 0(r0) + stw r11, 4(r0) + mfspr r10, SPRN_M_TWB /* Get level 1 table entry address */ + + /* If we are faulting a kernel address, we have to use the + * kernel page tables. + */ + andi. r11, r10, 0x0800 + beq 3f + lis r11, swapper_pg_dir@h + ori r11, r11, swapper_pg_dir@l + rlwimi r10, r11, 0, 2, 19 +3: + lwz r11, 0(r10) /* Get the level 1 entry */ + rlwinm. r10, r11,0,0,19 /* Extract page descriptor page address */ + beq 2f /* If zero, don't try to find a pte */ + + /* We have a pte table, so load fetch the pte from the table. + */ + ori r11, r11, 1 /* Set valid bit in physical L2 page */ + DO_8xx_CPU6(0x3b80, r3) + mtspr SPRN_MD_TWC, r11 /* Load pte table base address */ + mfspr r10, SPRN_MD_TWC /* ....and get the pte address */ + lwz r10, 0(r10) /* Get the pte */ + + /* Insert the Guarded flag into the TWC from the Linux PTE. + * It is bit 27 of both the Linux PTE and the TWC (at least + * I got that right :-). It will be better when we can put + * this into the Linux pgd/pmd and load it in the operation + * above. + */ + rlwimi r11, r10, 0, 27, 27 + DO_8xx_CPU6(0x3b80, r3) + mtspr SPRN_MD_TWC, r11 + + mfspr r11, SPRN_MD_TWC /* get the pte address again */ + ori r10, r10, _PAGE_ACCESSED + stw r10, 0(r11) + + /* The Linux PTE won't go exactly into the MMU TLB. + * Software indicator bits 21, 22 and 28 must be clear. + * Software indicator bits 24, 25, 26, and 27 must be + * set. All other Linux PTE bits control the behavior + * of the MMU. + */ +2: li r11, 0x00f0 + rlwimi r10, r11, 0, 24, 28 /* Set 24-27, clear 28 */ + DO_8xx_CPU6(0x3d80, r3) + mtspr SPRN_MD_RPN, r10 /* Update TLB entry */ + + mfspr r10, SPRN_M_TW /* Restore registers */ + lwz r11, 0(r0) + mtcr r11 + lwz r11, 4(r0) +#ifdef CONFIG_8xx_CPU6 + lwz r3, 8(r0) +#endif + rfi + +/* This is an instruction TLB error on the MPC8xx. This could be due + * to many reasons, such as executing guarded memory or illegal instruction + * addresses. There is nothing to do but handle a big time error fault. + */ + . = 0x1300 +InstructionTLBError: + b InstructionAccess + +/* This is the data TLB error on the MPC8xx. This could be due to + * many reasons, including a dirty update to a pte. We can catch that + * one here, but anything else is an error. First, we track down the + * Linux pte. If it is valid, write access is allowed, but the + * page dirty bit is not set, we will set it and reload the TLB. For + * any other case, we bail out to a higher level function that can + * handle it. + */ + . = 0x1400 +DataTLBError: +#ifdef CONFIG_8xx_CPU6 + stw r3, 8(r0) +#endif + DO_8xx_CPU6(0x3f80, r3) + mtspr SPRN_M_TW, r10 /* Save a couple of working registers */ + mfcr r10 + stw r10, 0(r0) + stw r11, 4(r0) + + /* First, make sure this was a store operation. + */ + mfspr r10, SPRN_DSISR + andis. r11, r10, 0x0200 /* If set, indicates store op */ + beq 2f + + /* The EA of a data TLB miss is automatically stored in the MD_EPN + * register. The EA of a data TLB error is automatically stored in + * the DAR, but not the MD_EPN register. We must copy the 20 most + * significant bits of the EA from the DAR to MD_EPN before we + * start walking the page tables. We also need to copy the CASID + * value from the M_CASID register. + * Addendum: The EA of a data TLB error is _supposed_ to be stored + * in DAR, but it seems that this doesn't happen in some cases, such + * as when the error is due to a dcbi instruction to a page with a + * TLB that doesn't have the changed bit set. In such cases, there + * does not appear to be any way to recover the EA of the error + * since it is neither in DAR nor MD_EPN. As a workaround, the + * _PAGE_HWWRITE bit is set for all kernel data pages when the PTEs + * are initialized in mapin_ram(). This will avoid the problem, + * assuming we only use the dcbi instruction on kernel addresses. + */ + mfspr r10, SPRN_DAR + rlwinm r11, r10, 0, 0, 19 + ori r11, r11, MD_EVALID + mfspr r10, SPRN_M_CASID + rlwimi r11, r10, 0, 28, 31 + DO_8xx_CPU6(0x3780, r3) + mtspr SPRN_MD_EPN, r11 + + mfspr r10, SPRN_M_TWB /* Get level 1 table entry address */ + + /* If we are faulting a kernel address, we have to use the + * kernel page tables. + */ + andi. r11, r10, 0x0800 + beq 3f + lis r11, swapper_pg_dir@h + ori r11, r11, swapper_pg_dir@l + rlwimi r10, r11, 0, 2, 19 +3: + lwz r11, 0(r10) /* Get the level 1 entry */ + rlwinm. r10, r11,0,0,19 /* Extract page descriptor page address */ + beq 2f /* If zero, bail */ + + /* We have a pte table, so fetch the pte from the table. + */ + ori r11, r11, 1 /* Set valid bit in physical L2 page */ + DO_8xx_CPU6(0x3b80, r3) + mtspr SPRN_MD_TWC, r11 /* Load pte table base address */ + mfspr r11, SPRN_MD_TWC /* ....and get the pte address */ + lwz r10, 0(r11) /* Get the pte */ + + andi. r11, r10, _PAGE_RW /* Is it writeable? */ + beq 2f /* Bail out if not */ + + /* Update 'changed', among others. + */ + ori r10, r10, _PAGE_DIRTY|_PAGE_ACCESSED|_PAGE_HWWRITE + mfspr r11, SPRN_MD_TWC /* Get pte address again */ + stw r10, 0(r11) /* and update pte in table */ + + /* The Linux PTE won't go exactly into the MMU TLB. + * Software indicator bits 21, 22 and 28 must be clear. + * Software indicator bits 24, 25, 26, and 27 must be + * set. All other Linux PTE bits control the behavior + * of the MMU. + */ + li r11, 0x00f0 + rlwimi r10, r11, 0, 24, 28 /* Set 24-27, clear 28 */ + DO_8xx_CPU6(0x3d80, r3) + mtspr SPRN_MD_RPN, r10 /* Update TLB entry */ + + mfspr r10, SPRN_M_TW /* Restore registers */ + lwz r11, 0(r0) + mtcr r11 + lwz r11, 4(r0) +#ifdef CONFIG_8xx_CPU6 + lwz r3, 8(r0) +#endif + rfi +2: + mfspr r10, SPRN_M_TW /* Restore registers */ + lwz r11, 0(r0) + mtcr r11 + lwz r11, 4(r0) +#ifdef CONFIG_8xx_CPU6 + lwz r3, 8(r0) +#endif + b DataAccess + + EXCEPTION(0x1500, Trap_15, UnknownException, EXC_XFER_EE) + EXCEPTION(0x1600, Trap_16, UnknownException, EXC_XFER_EE) + EXCEPTION(0x1700, Trap_17, UnknownException, EXC_XFER_EE) + EXCEPTION(0x1800, Trap_18, UnknownException, EXC_XFER_EE) + EXCEPTION(0x1900, Trap_19, UnknownException, EXC_XFER_EE) + EXCEPTION(0x1a00, Trap_1a, UnknownException, EXC_XFER_EE) + EXCEPTION(0x1b00, Trap_1b, UnknownException, EXC_XFER_EE) + +/* On the MPC8xx, these next four traps are used for development + * support of breakpoints and such. Someday I will get around to + * using them. + */ + EXCEPTION(0x1c00, Trap_1c, UnknownException, EXC_XFER_EE) + EXCEPTION(0x1d00, Trap_1d, UnknownException, EXC_XFER_EE) + EXCEPTION(0x1e00, Trap_1e, UnknownException, EXC_XFER_EE) + EXCEPTION(0x1f00, Trap_1f, UnknownException, EXC_XFER_EE) + + . = 0x2000 + + .globl giveup_fpu +giveup_fpu: + blr + +/* + * This is where the main kernel code starts. + */ +start_here: + /* ptr to current */ + lis r2,init_task@h + ori r2,r2,init_task@l + + /* ptr to phys current thread */ + tophys(r4,r2) + addi r4,r4,THREAD /* init task's THREAD */ + mtspr SPRN_SPRG3,r4 + li r3,0 + mtspr SPRN_SPRG2,r3 /* 0 => r1 has kernel sp */ + + /* stack */ + lis r1,init_thread_union@ha + addi r1,r1,init_thread_union@l + li r0,0 + stwu r0,THREAD_SIZE-STACK_FRAME_OVERHEAD(r1) + + bl early_init /* We have to do this with MMU on */ + +/* + * Decide what sort of machine this is and initialize the MMU. + */ + mr r3,r31 + mr r4,r30 + mr r5,r29 + mr r6,r28 + mr r7,r27 + bl machine_init + bl MMU_init + +/* + * Go back to running unmapped so we can load up new values + * and change to using our exception vectors. + * On the 8xx, all we have to do is invalidate the TLB to clear + * the old 8M byte TLB mappings and load the page table base register. + */ + /* The right way to do this would be to track it down through + * init's THREAD like the context switch code does, but this is + * easier......until someone changes init's static structures. + */ + lis r6, swapper_pg_dir@h + ori r6, r6, swapper_pg_dir@l + tophys(r6,r6) +#ifdef CONFIG_8xx_CPU6 + lis r4, cpu6_errata_word@h + ori r4, r4, cpu6_errata_word@l + li r3, 0x3980 + stw r3, 12(r4) + lwz r3, 12(r4) +#endif + mtspr SPRN_M_TWB, r6 + lis r4,2f@h + ori r4,r4,2f@l + tophys(r4,r4) + li r3,MSR_KERNEL & ~(MSR_IR|MSR_DR) + mtspr SPRN_SRR0,r4 + mtspr SPRN_SRR1,r3 + rfi +/* Load up the kernel context */ +2: + SYNC /* Force all PTE updates to finish */ + tlbia /* Clear all TLB entries */ + sync /* wait for tlbia/tlbie to finish */ + TLBSYNC /* ... on all CPUs */ + + /* set up the PTE pointers for the Abatron bdiGDB. + */ + tovirt(r6,r6) + lis r5, abatron_pteptrs@h + ori r5, r5, abatron_pteptrs@l + stw r5, 0xf0(r0) /* Must match your Abatron config file */ + tophys(r5,r5) + stw r6, 0(r5) + +/* Now turn on the MMU for real! */ + li r4,MSR_KERNEL + lis r3,start_kernel@h + ori r3,r3,start_kernel@l + mtspr SPRN_SRR0,r3 + mtspr SPRN_SRR1,r4 + rfi /* enable MMU and jump to start_kernel */ + +/* Set up the initial MMU state so we can do the first level of + * kernel initialization. This maps the first 8 MBytes of memory 1:1 + * virtual to physical. Also, set the cache mode since that is defined + * by TLB entries and perform any additional mapping (like of the IMMR). + * If configured to pin some TLBs, we pin the first 8 Mbytes of kernel, + * 24 Mbytes of data, and the 8M IMMR space. Anything not covered by + * these mappings is mapped by page tables. + */ +initial_mmu: + tlbia /* Invalidate all TLB entries */ +#ifdef CONFIG_PIN_TLB + lis r8, MI_RSV4I@h + ori r8, r8, 0x1c00 +#else + li r8, 0 +#endif + mtspr SPRN_MI_CTR, r8 /* Set instruction MMU control */ + +#ifdef CONFIG_PIN_TLB + lis r10, (MD_RSV4I | MD_RESETVAL)@h + ori r10, r10, 0x1c00 + mr r8, r10 +#else + lis r10, MD_RESETVAL@h +#endif +#ifndef CONFIG_8xx_COPYBACK + oris r10, r10, MD_WTDEF@h +#endif + mtspr SPRN_MD_CTR, r10 /* Set data TLB control */ + + /* Now map the lower 8 Meg into the TLBs. For this quick hack, + * we can load the instruction and data TLB registers with the + * same values. + */ + lis r8, KERNELBASE@h /* Create vaddr for TLB */ + ori r8, r8, MI_EVALID /* Mark it valid */ + mtspr SPRN_MI_EPN, r8 + mtspr SPRN_MD_EPN, r8 + li r8, MI_PS8MEG /* Set 8M byte page */ + ori r8, r8, MI_SVALID /* Make it valid */ + mtspr SPRN_MI_TWC, r8 + mtspr SPRN_MD_TWC, r8 + li r8, MI_BOOTINIT /* Create RPN for address 0 */ + mtspr SPRN_MI_RPN, r8 /* Store TLB entry */ + mtspr SPRN_MD_RPN, r8 + lis r8, MI_Kp@h /* Set the protection mode */ + mtspr SPRN_MI_AP, r8 + mtspr SPRN_MD_AP, r8 + + /* Map another 8 MByte at the IMMR to get the processor + * internal registers (among other things). + */ +#ifdef CONFIG_PIN_TLB + addi r10, r10, 0x0100 + mtspr SPRN_MD_CTR, r10 +#endif + mfspr r9, 638 /* Get current IMMR */ + andis. r9, r9, 0xff80 /* Get 8Mbyte boundary */ + + mr r8, r9 /* Create vaddr for TLB */ + ori r8, r8, MD_EVALID /* Mark it valid */ + mtspr SPRN_MD_EPN, r8 + li r8, MD_PS8MEG /* Set 8M byte page */ + ori r8, r8, MD_SVALID /* Make it valid */ + mtspr SPRN_MD_TWC, r8 + mr r8, r9 /* Create paddr for TLB */ + ori r8, r8, MI_BOOTINIT|0x2 /* Inhibit cache -- Cort */ + mtspr SPRN_MD_RPN, r8 + +#ifdef CONFIG_PIN_TLB + /* Map two more 8M kernel data pages. + */ + addi r10, r10, 0x0100 + mtspr SPRN_MD_CTR, r10 + + lis r8, KERNELBASE@h /* Create vaddr for TLB */ + addis r8, r8, 0x0080 /* Add 8M */ + ori r8, r8, MI_EVALID /* Mark it valid */ + mtspr SPRN_MD_EPN, r8 + li r9, MI_PS8MEG /* Set 8M byte page */ + ori r9, r9, MI_SVALID /* Make it valid */ + mtspr SPRN_MD_TWC, r9 + li r11, MI_BOOTINIT /* Create RPN for address 0 */ + addis r11, r11, 0x0080 /* Add 8M */ + mtspr SPRN_MD_RPN, r8 + + addis r8, r8, 0x0080 /* Add 8M */ + mtspr SPRN_MD_EPN, r8 + mtspr SPRN_MD_TWC, r9 + addis r11, r11, 0x0080 /* Add 8M */ + mtspr SPRN_MD_RPN, r8 +#endif + + /* Since the cache is enabled according to the information we + * just loaded into the TLB, invalidate and enable the caches here. + * We should probably check/set other modes....later. + */ + lis r8, IDC_INVALL@h + mtspr SPRN_IC_CST, r8 + mtspr SPRN_DC_CST, r8 + lis r8, IDC_ENABLE@h + mtspr SPRN_IC_CST, r8 +#ifdef CONFIG_8xx_COPYBACK + mtspr SPRN_DC_CST, r8 +#else + /* For a debug option, I left this here to easily enable + * the write through cache mode + */ + lis r8, DC_SFWT@h + mtspr SPRN_DC_CST, r8 + lis r8, IDC_ENABLE@h + mtspr SPRN_DC_CST, r8 +#endif + blr + + +/* + * Set up to use a given MMU context. + * r3 is context number, r4 is PGD pointer. + * + * We place the physical address of the new task page directory loaded + * into the MMU base register, and set the ASID compare register with + * the new "context." + */ +_GLOBAL(set_context) + +#ifdef CONFIG_BDI_SWITCH + /* Context switch the PTE pointer for the Abatron BDI2000. + * The PGDIR is passed as second argument. + */ + lis r5, KERNELBASE@h + lwz r5, 0xf0(r5) + stw r4, 0x4(r5) +#endif + +#ifdef CONFIG_8xx_CPU6 + lis r6, cpu6_errata_word@h + ori r6, r6, cpu6_errata_word@l + tophys (r4, r4) + li r7, 0x3980 + stw r7, 12(r6) + lwz r7, 12(r6) + mtspr SPRN_M_TWB, r4 /* Update MMU base address */ + li r7, 0x3380 + stw r7, 12(r6) + lwz r7, 12(r6) + mtspr SPRN_M_CASID, r3 /* Update context */ +#else + mtspr SPRN_M_CASID,r3 /* Update context */ + tophys (r4, r4) + mtspr SPRN_M_TWB, r4 /* and pgd */ +#endif + SYNC + blr + +#ifdef CONFIG_8xx_CPU6 +/* It's here because it is unique to the 8xx. + * It is important we get called with interrupts disabled. I used to + * do that, but it appears that all code that calls this already had + * interrupt disabled. + */ + .globl set_dec_cpu6 +set_dec_cpu6: + lis r7, cpu6_errata_word@h + ori r7, r7, cpu6_errata_word@l + li r4, 0x2c00 + stw r4, 8(r7) + lwz r4, 8(r7) + mtspr 22, r3 /* Update Decrementer */ + SYNC + blr +#endif + +/* + * We put a few things here that have to be page-aligned. + * This stuff goes at the beginning of the data segment, + * which is page-aligned. + */ + .data + .globl sdata +sdata: + .globl empty_zero_page +empty_zero_page: + .space 4096 + + .globl swapper_pg_dir +swapper_pg_dir: + .space 4096 + +/* + * This space gets a copy of optional info passed to us by the bootstrap + * Used to pass parameters into the kernel like root=/dev/sda1, etc. + */ + .globl cmd_line +cmd_line: + .space 512 + +/* Room for two PTE table poiners, usually the kernel and current user + * pointer to their respective root page table (pgdir). + */ +abatron_pteptrs: + .space 8 + +#ifdef CONFIG_8xx_CPU6 + .globl cpu6_errata_word +cpu6_errata_word: + .space 16 +#endif + diff --git a/arch/ppc/kernel/head_booke.h b/arch/ppc/kernel/head_booke.h new file mode 100644 index 00000000000..884dac916bc --- /dev/null +++ b/arch/ppc/kernel/head_booke.h @@ -0,0 +1,340 @@ +#ifndef __HEAD_BOOKE_H__ +#define __HEAD_BOOKE_H__ + +/* + * Macros used for common Book-e exception handling + */ + +#define SET_IVOR(vector_number, vector_label) \ + li r26,vector_label@l; \ + mtspr SPRN_IVOR##vector_number,r26; \ + sync + +#define NORMAL_EXCEPTION_PROLOG \ + mtspr SPRN_SPRG0,r10; /* save two registers to work with */\ + mtspr SPRN_SPRG1,r11; \ + mtspr SPRN_SPRG4W,r1; \ + mfcr r10; /* save CR in r10 for now */\ + mfspr r11,SPRN_SRR1; /* check whether user or kernel */\ + andi. r11,r11,MSR_PR; \ + beq 1f; \ + mfspr r1,SPRN_SPRG3; /* if from user, start at top of */\ + lwz r1,THREAD_INFO-THREAD(r1); /* this thread's kernel stack */\ + addi r1,r1,THREAD_SIZE; \ +1: subi r1,r1,INT_FRAME_SIZE; /* Allocate an exception frame */\ + mr r11,r1; \ + stw r10,_CCR(r11); /* save various registers */\ + stw r12,GPR12(r11); \ + stw r9,GPR9(r11); \ + mfspr r10,SPRN_SPRG0; \ + stw r10,GPR10(r11); \ + mfspr r12,SPRN_SPRG1; \ + stw r12,GPR11(r11); \ + mflr r10; \ + stw r10,_LINK(r11); \ + mfspr r10,SPRN_SPRG4R; \ + mfspr r12,SPRN_SRR0; \ + stw r10,GPR1(r11); \ + mfspr r9,SPRN_SRR1; \ + stw r10,0(r11); \ + rlwinm r9,r9,0,14,12; /* clear MSR_WE (necessary?) */\ + stw r0,GPR0(r11); \ + SAVE_4GPRS(3, r11); \ + SAVE_2GPRS(7, r11) + +/* To handle the additional exception priority levels on 40x and Book-E + * processors we allocate a 4k stack per additional priority level. The various + * head_xxx.S files allocate space (exception_stack_top) for each priority's + * stack times the number of CPUs + * + * On 40x critical is the only additional level + * On 44x/e500 we have critical and machine check + * + * Additionally we reserve a SPRG for each priority level so we can free up a + * GPR to use as the base for indirect access to the exception stacks. This + * is necessary since the MMU is always on, for Book-E parts, and the stacks + * are offset from KERNELBASE. + * + */ +#define BOOKE_EXCEPTION_STACK_SIZE (8192) + +/* CRIT_SPRG only used in critical exception handling */ +#define CRIT_SPRG SPRN_SPRG2 +/* MCHECK_SPRG only used in critical exception handling */ +#define MCHECK_SPRG SPRN_SPRG6W + +#define MCHECK_STACK_TOP (exception_stack_top - 4096) +#define CRIT_STACK_TOP (exception_stack_top) + +#ifdef CONFIG_SMP +#define BOOKE_LOAD_CRIT_STACK \ + mfspr r8,SPRN_PIR; \ + mulli r8,r8,BOOKE_EXCEPTION_STACK_SIZE; \ + neg r8,r8; \ + addis r8,r8,CRIT_STACK_TOP@ha; \ + addi r8,r8,CRIT_STACK_TOP@l +#define BOOKE_LOAD_MCHECK_STACK \ + mfspr r8,SPRN_PIR; \ + mulli r8,r8,BOOKE_EXCEPTION_STACK_SIZE; \ + neg r8,r8; \ + addis r8,r8,MCHECK_STACK_TOP@ha; \ + addi r8,r8,MCHECK_STACK_TOP@l +#else +#define BOOKE_LOAD_CRIT_STACK \ + lis r8,CRIT_STACK_TOP@h; \ + ori r8,r8,CRIT_STACK_TOP@l +#define BOOKE_LOAD_MCHECK_STACK \ + lis r8,MCHECK_STACK_TOP@h; \ + ori r8,r8,MCHECK_STACK_TOP@l +#endif + +/* + * Exception prolog for critical exceptions. This is a little different + * from the normal exception prolog above since a critical exception + * can potentially occur at any point during normal exception processing. + * Thus we cannot use the same SPRG registers as the normal prolog above. + * Instead we use a portion of the critical exception stack at low physical + * addresses. + */ + +#define CRITICAL_EXCEPTION_PROLOG \ + mtspr CRIT_SPRG,r8; \ + BOOKE_LOAD_CRIT_STACK; /* r8 points to the crit stack */ \ + stw r10,GPR10-INT_FRAME_SIZE(r8); \ + stw r11,GPR11-INT_FRAME_SIZE(r8); \ + mfcr r10; /* save CR in r10 for now */\ + mfspr r11,SPRN_CSRR1; /* check whether user or kernel */\ + andi. r11,r11,MSR_PR; \ + mr r11,r8; \ + mfspr r8,CRIT_SPRG; \ + beq 1f; \ + /* COMING FROM USER MODE */ \ + mfspr r11,SPRN_SPRG3; /* if from user, start at top of */\ + lwz r11,THREAD_INFO-THREAD(r11); /* this thread's kernel stack */\ + addi r11,r11,THREAD_SIZE; \ +1: subi r11,r11,INT_FRAME_SIZE; /* Allocate an exception frame */\ + stw r10,_CCR(r11); /* save various registers */\ + stw r12,GPR12(r11); \ + stw r9,GPR9(r11); \ + mflr r10; \ + stw r10,_LINK(r11); \ + mfspr r12,SPRN_DEAR; /* save DEAR and ESR in the frame */\ + stw r12,_DEAR(r11); /* since they may have had stuff */\ + mfspr r9,SPRN_ESR; /* in them at the point where the */\ + stw r9,_ESR(r11); /* exception was taken */\ + mfspr r12,SPRN_CSRR0; \ + stw r1,GPR1(r11); \ + mfspr r9,SPRN_CSRR1; \ + stw r1,0(r11); \ + mr r1,r11; \ + rlwinm r9,r9,0,14,12; /* clear MSR_WE (necessary?) */\ + stw r0,GPR0(r11); \ + SAVE_4GPRS(3, r11); \ + SAVE_2GPRS(7, r11) + +/* + * Exception prolog for machine check exceptions. This is similar to + * the critical exception prolog, except that machine check exceptions + * have their stack. + */ +#define MCHECK_EXCEPTION_PROLOG \ + mtspr MCHECK_SPRG,r8; \ + BOOKE_LOAD_MCHECK_STACK; /* r8 points to the mcheck stack */\ + stw r10,GPR10-INT_FRAME_SIZE(r8); \ + stw r11,GPR11-INT_FRAME_SIZE(r8); \ + mfcr r10; /* save CR in r10 for now */\ + mfspr r11,SPRN_MCSRR1; /* check whether user or kernel */\ + andi. r11,r11,MSR_PR; \ + mr r11,r8; \ + mfspr r8,MCHECK_SPRG; \ + beq 1f; \ + /* COMING FROM USER MODE */ \ + mfspr r11,SPRN_SPRG3; /* if from user, start at top of */\ + lwz r11,THREAD_INFO-THREAD(r11); /* this thread's kernel stack */\ + addi r11,r11,THREAD_SIZE; \ +1: subi r11,r11,INT_FRAME_SIZE; /* Allocate an exception frame */\ + stw r10,_CCR(r11); /* save various registers */\ + stw r12,GPR12(r11); \ + stw r9,GPR9(r11); \ + mflr r10; \ + stw r10,_LINK(r11); \ + mfspr r12,SPRN_DEAR; /* save DEAR and ESR in the frame */\ + stw r12,_DEAR(r11); /* since they may have had stuff */\ + mfspr r9,SPRN_ESR; /* in them at the point where the */\ + stw r9,_ESR(r11); /* exception was taken */\ + mfspr r12,SPRN_MCSRR0; \ + stw r1,GPR1(r11); \ + mfspr r9,SPRN_MCSRR1; \ + stw r1,0(r11); \ + mr r1,r11; \ + rlwinm r9,r9,0,14,12; /* clear MSR_WE (necessary?) */\ + stw r0,GPR0(r11); \ + SAVE_4GPRS(3, r11); \ + SAVE_2GPRS(7, r11) + +/* + * Exception vectors. + */ +#define START_EXCEPTION(label) \ + .align 5; \ +label: + +#define FINISH_EXCEPTION(func) \ + bl transfer_to_handler_full; \ + .long func; \ + .long ret_from_except_full + +#define EXCEPTION(n, label, hdlr, xfer) \ + START_EXCEPTION(label); \ + NORMAL_EXCEPTION_PROLOG; \ + addi r3,r1,STACK_FRAME_OVERHEAD; \ + xfer(n, hdlr) + +#define CRITICAL_EXCEPTION(n, label, hdlr) \ + START_EXCEPTION(label); \ + CRITICAL_EXCEPTION_PROLOG; \ + addi r3,r1,STACK_FRAME_OVERHEAD; \ + EXC_XFER_TEMPLATE(hdlr, n+2, (MSR_KERNEL & ~(MSR_ME|MSR_DE|MSR_CE)), \ + NOCOPY, crit_transfer_to_handler, \ + ret_from_crit_exc) + +#define MCHECK_EXCEPTION(n, label, hdlr) \ + START_EXCEPTION(label); \ + MCHECK_EXCEPTION_PROLOG; \ + mfspr r5,SPRN_ESR; \ + stw r5,_ESR(r11); \ + addi r3,r1,STACK_FRAME_OVERHEAD; \ + EXC_XFER_TEMPLATE(hdlr, n+2, (MSR_KERNEL & ~(MSR_ME|MSR_DE|MSR_CE)), \ + NOCOPY, mcheck_transfer_to_handler, \ + ret_from_mcheck_exc) + +#define EXC_XFER_TEMPLATE(hdlr, trap, msr, copyee, tfer, ret) \ + li r10,trap; \ + stw r10,TRAP(r11); \ + lis r10,msr@h; \ + ori r10,r10,msr@l; \ + copyee(r10, r9); \ + bl tfer; \ + .long hdlr; \ + .long ret + +#define COPY_EE(d, s) rlwimi d,s,0,16,16 +#define NOCOPY(d, s) + +#define EXC_XFER_STD(n, hdlr) \ + EXC_XFER_TEMPLATE(hdlr, n, MSR_KERNEL, NOCOPY, transfer_to_handler_full, \ + ret_from_except_full) + +#define EXC_XFER_LITE(n, hdlr) \ + EXC_XFER_TEMPLATE(hdlr, n+1, MSR_KERNEL, NOCOPY, transfer_to_handler, \ + ret_from_except) + +#define EXC_XFER_EE(n, hdlr) \ + EXC_XFER_TEMPLATE(hdlr, n, MSR_KERNEL, COPY_EE, transfer_to_handler_full, \ + ret_from_except_full) + +#define EXC_XFER_EE_LITE(n, hdlr) \ + EXC_XFER_TEMPLATE(hdlr, n+1, MSR_KERNEL, COPY_EE, transfer_to_handler, \ + ret_from_except) + + +/* Check for a single step debug exception while in an exception + * handler before state has been saved. This is to catch the case + * where an instruction that we are trying to single step causes + * an exception (eg ITLB/DTLB miss) and thus the first instruction of + * the exception handler generates a single step debug exception. + * + * If we get a debug trap on the first instruction of an exception handler, + * we reset the MSR_DE in the _exception handler's_ MSR (the debug trap is + * a critical exception, so we are using SPRN_CSRR1 to manipulate the MSR). + * The exception handler was handling a non-critical interrupt, so it will + * save (and later restore) the MSR via SPRN_CSRR1, which will still have + * the MSR_DE bit set. + */ +#define DEBUG_EXCEPTION \ + START_EXCEPTION(Debug); \ + CRITICAL_EXCEPTION_PROLOG; \ + \ + /* \ + * If there is a single step or branch-taken exception in an \ + * exception entry sequence, it was probably meant to apply to \ + * the code where the exception occurred (since exception entry \ + * doesn't turn off DE automatically). We simulate the effect \ + * of turning off DE on entry to an exception handler by turning \ + * off DE in the CSRR1 value and clearing the debug status. \ + */ \ + mfspr r10,SPRN_DBSR; /* check single-step/branch taken */ \ + andis. r10,r10,DBSR_IC@h; \ + beq+ 2f; \ + \ + lis r10,KERNELBASE@h; /* check if exception in vectors */ \ + ori r10,r10,KERNELBASE@l; \ + cmplw r12,r10; \ + blt+ 2f; /* addr below exception vectors */ \ + \ + lis r10,Debug@h; \ + ori r10,r10,Debug@l; \ + cmplw r12,r10; \ + bgt+ 2f; /* addr above exception vectors */ \ + \ + /* here it looks like we got an inappropriate debug exception. */ \ +1: rlwinm r9,r9,0,~MSR_DE; /* clear DE in the CSRR1 value */ \ + lis r10,DBSR_IC@h; /* clear the IC event */ \ + mtspr SPRN_DBSR,r10; \ + /* restore state and get out */ \ + lwz r10,_CCR(r11); \ + lwz r0,GPR0(r11); \ + lwz r1,GPR1(r11); \ + mtcrf 0x80,r10; \ + mtspr SPRN_CSRR0,r12; \ + mtspr SPRN_CSRR1,r9; \ + lwz r9,GPR9(r11); \ + lwz r12,GPR12(r11); \ + mtspr CRIT_SPRG,r8; \ + BOOKE_LOAD_CRIT_STACK; /* r8 points to the crit stack */ \ + lwz r10,GPR10-INT_FRAME_SIZE(r8); \ + lwz r11,GPR11-INT_FRAME_SIZE(r8); \ + mfspr r8,CRIT_SPRG; \ + \ + rfci; \ + b .; \ + \ + /* continue normal handling for a critical exception... */ \ +2: mfspr r4,SPRN_DBSR; \ + addi r3,r1,STACK_FRAME_OVERHEAD; \ + EXC_XFER_TEMPLATE(DebugException, 0x2002, (MSR_KERNEL & ~(MSR_ME|MSR_DE|MSR_CE)), NOCOPY, crit_transfer_to_handler, ret_from_crit_exc) + +#define INSTRUCTION_STORAGE_EXCEPTION \ + START_EXCEPTION(InstructionStorage) \ + NORMAL_EXCEPTION_PROLOG; \ + mfspr r5,SPRN_ESR; /* Grab the ESR and save it */ \ + stw r5,_ESR(r11); \ + mr r4,r12; /* Pass SRR0 as arg2 */ \ + li r5,0; /* Pass zero as arg3 */ \ + EXC_XFER_EE_LITE(0x0400, handle_page_fault) + +#define ALIGNMENT_EXCEPTION \ + START_EXCEPTION(Alignment) \ + NORMAL_EXCEPTION_PROLOG; \ + mfspr r4,SPRN_DEAR; /* Grab the DEAR and save it */ \ + stw r4,_DEAR(r11); \ + addi r3,r1,STACK_FRAME_OVERHEAD; \ + EXC_XFER_EE(0x0600, AlignmentException) + +#define PROGRAM_EXCEPTION \ + START_EXCEPTION(Program) \ + NORMAL_EXCEPTION_PROLOG; \ + mfspr r4,SPRN_ESR; /* Grab the ESR and save it */ \ + stw r4,_ESR(r11); \ + addi r3,r1,STACK_FRAME_OVERHEAD; \ + EXC_XFER_STD(0x0700, ProgramCheckException) + +#define DECREMENTER_EXCEPTION \ + START_EXCEPTION(Decrementer) \ + NORMAL_EXCEPTION_PROLOG; \ + lis r0,TSR_DIS@h; /* Setup the DEC interrupt mask */ \ + mtspr SPRN_TSR,r0; /* Clear the DEC interrupt */ \ + addi r3,r1,STACK_FRAME_OVERHEAD; \ + EXC_XFER_LITE(0x0900, timer_interrupt) + +#endif /* __HEAD_BOOKE_H__ */ diff --git a/arch/ppc/kernel/head_fsl_booke.S b/arch/ppc/kernel/head_fsl_booke.S new file mode 100644 index 00000000000..dea19c216fc --- /dev/null +++ b/arch/ppc/kernel/head_fsl_booke.S @@ -0,0 +1,952 @@ +/* + * arch/ppc/kernel/head_fsl_booke.S + * + * Kernel execution entry point code. + * + * Copyright (c) 1995-1996 Gary Thomas + * Initial PowerPC version. + * Copyright (c) 1996 Cort Dougan + * Rewritten for PReP + * Copyright (c) 1996 Paul Mackerras + * Low-level exception handers, MMU support, and rewrite. + * Copyright (c) 1997 Dan Malek + * PowerPC 8xx modifications. + * Copyright (c) 1998-1999 TiVo, Inc. + * PowerPC 403GCX modifications. + * Copyright (c) 1999 Grant Erickson + * PowerPC 403GCX/405GP modifications. + * Copyright 2000 MontaVista Software Inc. + * PPC405 modifications + * PowerPC 403GCX/405GP modifications. + * Author: MontaVista Software, Inc. + * frank_rowand@mvista.com or source@mvista.com + * debbie_chu@mvista.com + * Copyright 2002-2004 MontaVista Software, Inc. + * PowerPC 44x support, Matt Porter + * Copyright 2004 Freescale Semiconductor, Inc + * PowerPC e500 modifications, Kumar Gala + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "head_booke.h" + +/* As with the other PowerPC ports, it is expected that when code + * execution begins here, the following registers contain valid, yet + * optional, information: + * + * r3 - Board info structure pointer (DRAM, frequency, MAC address, etc.) + * r4 - Starting address of the init RAM disk + * r5 - Ending address of the init RAM disk + * r6 - Start of kernel command line string (e.g. "mem=128") + * r7 - End of kernel command line string + * + */ + .text +_GLOBAL(_stext) +_GLOBAL(_start) + /* + * Reserve a word at a fixed location to store the address + * of abatron_pteptrs + */ + nop +/* + * Save parameters we are passed + */ + mr r31,r3 + mr r30,r4 + mr r29,r5 + mr r28,r6 + mr r27,r7 + li r24,0 /* CPU number */ + +/* We try to not make any assumptions about how the boot loader + * setup or used the TLBs. We invalidate all mappings from the + * boot loader and load a single entry in TLB1[0] to map the + * first 16M of kernel memory. Any boot info passed from the + * bootloader needs to live in this first 16M. + * + * Requirement on bootloader: + * - The page we're executing in needs to reside in TLB1 and + * have IPROT=1. If not an invalidate broadcast could + * evict the entry we're currently executing in. + * + * r3 = Index of TLB1 were executing in + * r4 = Current MSR[IS] + * r5 = Index of TLB1 temp mapping + * + * Later in mapin_ram we will correctly map lowmem, and resize TLB1[0] + * if needed + */ + +/* 1. Find the index of the entry we're executing in */ + bl invstr /* Find our address */ +invstr: mflr r6 /* Make it accessible */ + mfmsr r7 + rlwinm r4,r7,27,31,31 /* extract MSR[IS] */ + mfspr r7, SPRN_PID0 + slwi r7,r7,16 + or r7,r7,r4 + mtspr SPRN_MAS6,r7 + tlbsx 0,r6 /* search MSR[IS], SPID=PID0 */ + mfspr r7,SPRN_MAS1 + andis. r7,r7,MAS1_VALID@h + bne match_TLB + mfspr r7,SPRN_PID1 + slwi r7,r7,16 + or r7,r7,r4 + mtspr SPRN_MAS6,r7 + tlbsx 0,r6 /* search MSR[IS], SPID=PID1 */ + mfspr r7,SPRN_MAS1 + andis. r7,r7,MAS1_VALID@h + bne match_TLB + mfspr r7, SPRN_PID2 + slwi r7,r7,16 + or r7,r7,r4 + mtspr SPRN_MAS6,r7 + tlbsx 0,r6 /* Fall through, we had to match */ +match_TLB: + mfspr r7,SPRN_MAS0 + rlwinm r3,r7,16,20,31 /* Extract MAS0(Entry) */ + + mfspr r7,SPRN_MAS1 /* Insure IPROT set */ + oris r7,r7,MAS1_IPROT@h + mtspr SPRN_MAS1,r7 + tlbwe + +/* 2. Invalidate all entries except the entry we're executing in */ + mfspr r9,SPRN_TLB1CFG + andi. r9,r9,0xfff + li r6,0 /* Set Entry counter to 0 */ +1: lis r7,0x1000 /* Set MAS0(TLBSEL) = 1 */ + rlwimi r7,r6,16,4,15 /* Setup MAS0 = TLBSEL | ESEL(r6) */ + mtspr SPRN_MAS0,r7 + tlbre + mfspr r7,SPRN_MAS1 + rlwinm r7,r7,0,2,31 /* Clear MAS1 Valid and IPROT */ + cmpw r3,r6 + beq skpinv /* Dont update the current execution TLB */ + mtspr SPRN_MAS1,r7 + tlbwe + isync +skpinv: addi r6,r6,1 /* Increment */ + cmpw r6,r9 /* Are we done? */ + bne 1b /* If not, repeat */ + + /* Invalidate TLB0 */ + li r6,0x04 + tlbivax 0,r6 +#ifdef CONFIG_SMP + tlbsync +#endif + /* Invalidate TLB1 */ + li r6,0x0c + tlbivax 0,r6 +#ifdef CONFIG_SMP + tlbsync +#endif + msync + +/* 3. Setup a temp mapping and jump to it */ + andi. r5, r3, 0x1 /* Find an entry not used and is non-zero */ + addi r5, r5, 0x1 + lis r7,0x1000 /* Set MAS0(TLBSEL) = 1 */ + rlwimi r7,r3,16,4,15 /* Setup MAS0 = TLBSEL | ESEL(r3) */ + mtspr SPRN_MAS0,r7 + tlbre + + /* Just modify the entry ID and EPN for the temp mapping */ + lis r7,0x1000 /* Set MAS0(TLBSEL) = 1 */ + rlwimi r7,r5,16,4,15 /* Setup MAS0 = TLBSEL | ESEL(r5) */ + mtspr SPRN_MAS0,r7 + xori r6,r4,1 /* Setup TMP mapping in the other Address space */ + slwi r6,r6,12 + oris r6,r6,(MAS1_VALID|MAS1_IPROT)@h + ori r6,r6,(MAS1_TSIZE(BOOKE_PAGESZ_4K))@l + mtspr SPRN_MAS1,r6 + mfspr r6,SPRN_MAS2 + li r7,0 /* temp EPN = 0 */ + rlwimi r7,r6,0,20,31 + mtspr SPRN_MAS2,r7 + tlbwe + + xori r6,r4,1 + slwi r6,r6,5 /* setup new context with other address space */ + bl 1f /* Find our address */ +1: mflr r9 + rlwimi r7,r9,0,20,31 + addi r7,r7,24 + mtspr SPRN_SRR0,r7 + mtspr SPRN_SRR1,r6 + rfi + +/* 4. Clear out PIDs & Search info */ + li r6,0 + mtspr SPRN_PID0,r6 + mtspr SPRN_PID1,r6 + mtspr SPRN_PID2,r6 + mtspr SPRN_MAS6,r6 + +/* 5. Invalidate mapping we started in */ + lis r7,0x1000 /* Set MAS0(TLBSEL) = 1 */ + rlwimi r7,r3,16,4,15 /* Setup MAS0 = TLBSEL | ESEL(r3) */ + mtspr SPRN_MAS0,r7 + tlbre + li r6,0 + mtspr SPRN_MAS1,r6 + tlbwe + /* Invalidate TLB1 */ + li r9,0x0c + tlbivax 0,r9 +#ifdef CONFIG_SMP + tlbsync +#endif + msync + +/* 6. Setup KERNELBASE mapping in TLB1[0] */ + lis r6,0x1000 /* Set MAS0(TLBSEL) = TLB1(1), ESEL = 0 */ + mtspr SPRN_MAS0,r6 + lis r6,(MAS1_VALID|MAS1_IPROT)@h + ori r6,r6,(MAS1_TSIZE(BOOKE_PAGESZ_16M))@l + mtspr SPRN_MAS1,r6 + li r7,0 + lis r6,KERNELBASE@h + ori r6,r6,KERNELBASE@l + rlwimi r6,r7,0,20,31 + mtspr SPRN_MAS2,r6 + li r7,(MAS3_SX|MAS3_SW|MAS3_SR) + mtspr SPRN_MAS3,r7 + tlbwe + +/* 7. Jump to KERNELBASE mapping */ + li r7,0 + bl 1f /* Find our address */ +1: mflr r9 + rlwimi r6,r9,0,20,31 + addi r6,r6,24 + mtspr SPRN_SRR0,r6 + mtspr SPRN_SRR1,r7 + rfi /* start execution out of TLB1[0] entry */ + +/* 8. Clear out the temp mapping */ + lis r7,0x1000 /* Set MAS0(TLBSEL) = 1 */ + rlwimi r7,r5,16,4,15 /* Setup MAS0 = TLBSEL | ESEL(r5) */ + mtspr SPRN_MAS0,r7 + tlbre + mtspr SPRN_MAS1,r8 + tlbwe + /* Invalidate TLB1 */ + li r9,0x0c + tlbivax 0,r9 +#ifdef CONFIG_SMP + tlbsync +#endif + msync + + /* Establish the interrupt vector offsets */ + SET_IVOR(0, CriticalInput); + SET_IVOR(1, MachineCheck); + SET_IVOR(2, DataStorage); + SET_IVOR(3, InstructionStorage); + SET_IVOR(4, ExternalInput); + SET_IVOR(5, Alignment); + SET_IVOR(6, Program); + SET_IVOR(7, FloatingPointUnavailable); + SET_IVOR(8, SystemCall); + SET_IVOR(9, AuxillaryProcessorUnavailable); + SET_IVOR(10, Decrementer); + SET_IVOR(11, FixedIntervalTimer); + SET_IVOR(12, WatchdogTimer); + SET_IVOR(13, DataTLBError); + SET_IVOR(14, InstructionTLBError); + SET_IVOR(15, Debug); + SET_IVOR(32, SPEUnavailable); + SET_IVOR(33, SPEFloatingPointData); + SET_IVOR(34, SPEFloatingPointRound); + SET_IVOR(35, PerformanceMonitor); + + /* Establish the interrupt vector base */ + lis r4,interrupt_base@h /* IVPR only uses the high 16-bits */ + mtspr SPRN_IVPR,r4 + + /* Setup the defaults for TLB entries */ + li r2,(MAS4_TSIZED(BOOKE_PAGESZ_4K))@l + mtspr SPRN_MAS4, r2 + +#if 0 + /* Enable DOZE */ + mfspr r2,SPRN_HID0 + oris r2,r2,HID0_DOZE@h + mtspr SPRN_HID0, r2 +#endif + + /* + * This is where the main kernel code starts. + */ + + /* ptr to current */ + lis r2,init_task@h + ori r2,r2,init_task@l + + /* ptr to current thread */ + addi r4,r2,THREAD /* init task's THREAD */ + mtspr SPRN_SPRG3,r4 + + /* stack */ + lis r1,init_thread_union@h + ori r1,r1,init_thread_union@l + li r0,0 + stwu r0,THREAD_SIZE-STACK_FRAME_OVERHEAD(r1) + + bl early_init + + mfspr r3,SPRN_TLB1CFG + andi. r3,r3,0xfff + lis r4,num_tlbcam_entries@ha + stw r3,num_tlbcam_entries@l(r4) +/* + * Decide what sort of machine this is and initialize the MMU. + */ + mr r3,r31 + mr r4,r30 + mr r5,r29 + mr r6,r28 + mr r7,r27 + bl machine_init + bl MMU_init + + /* Setup PTE pointers for the Abatron bdiGDB */ + lis r6, swapper_pg_dir@h + ori r6, r6, swapper_pg_dir@l + lis r5, abatron_pteptrs@h + ori r5, r5, abatron_pteptrs@l + lis r4, KERNELBASE@h + ori r4, r4, KERNELBASE@l + stw r5, 0(r4) /* Save abatron_pteptrs at a fixed location */ + stw r6, 0(r5) + + /* Let's move on */ + lis r4,start_kernel@h + ori r4,r4,start_kernel@l + lis r3,MSR_KERNEL@h + ori r3,r3,MSR_KERNEL@l + mtspr SPRN_SRR0,r4 + mtspr SPRN_SRR1,r3 + rfi /* change context and jump to start_kernel */ + +/* + * Interrupt vector entry code + * + * The Book E MMUs are always on so we don't need to handle + * interrupts in real mode as with previous PPC processors. In + * this case we handle interrupts in the kernel virtual address + * space. + * + * Interrupt vectors are dynamically placed relative to the + * interrupt prefix as determined by the address of interrupt_base. + * The interrupt vectors offsets are programmed using the labels + * for each interrupt vector entry. + * + * Interrupt vectors must be aligned on a 16 byte boundary. + * We align on a 32 byte cache line boundary for good measure. + */ + +interrupt_base: + /* Critical Input Interrupt */ + CRITICAL_EXCEPTION(0x0100, CriticalInput, UnknownException) + + /* Machine Check Interrupt */ + MCHECK_EXCEPTION(0x0200, MachineCheck, MachineCheckException) + + /* Data Storage Interrupt */ + START_EXCEPTION(DataStorage) + mtspr SPRN_SPRG0, r10 /* Save some working registers */ + mtspr SPRN_SPRG1, r11 + mtspr SPRN_SPRG4W, r12 + mtspr SPRN_SPRG5W, r13 + mfcr r11 + mtspr SPRN_SPRG7W, r11 + + /* + * Check if it was a store fault, if not then bail + * because a user tried to access a kernel or + * read-protected page. Otherwise, get the + * offending address and handle it. + */ + mfspr r10, SPRN_ESR + andis. r10, r10, ESR_ST@h + beq 2f + + mfspr r10, SPRN_DEAR /* Get faulting address */ + + /* If we are faulting a kernel address, we have to use the + * kernel page tables. + */ + lis r11, TASK_SIZE@h + ori r11, r11, TASK_SIZE@l + cmplw 0, r10, r11 + bge 2f + + /* Get the PGD for the current thread */ +3: + mfspr r11,SPRN_SPRG3 + lwz r11,PGDIR(r11) +4: + rlwimi r11, r10, 12, 20, 29 /* Create L1 (pgdir/pmd) address */ + lwz r11, 0(r11) /* Get L1 entry */ + rlwinm. r12, r11, 0, 0, 19 /* Extract L2 (pte) base address */ + beq 2f /* Bail if no table */ + + rlwimi r12, r10, 22, 20, 29 /* Compute PTE address */ + lwz r11, 0(r12) /* Get Linux PTE */ + + /* Are _PAGE_USER & _PAGE_RW set & _PAGE_HWWRITE not? */ + andi. r13, r11, _PAGE_RW|_PAGE_USER|_PAGE_HWWRITE + cmpwi 0, r13, _PAGE_RW|_PAGE_USER + bne 2f /* Bail if not */ + + /* Update 'changed'. */ + ori r11, r11, _PAGE_DIRTY|_PAGE_ACCESSED|_PAGE_HWWRITE + stw r11, 0(r12) /* Update Linux page table */ + + /* MAS2 not updated as the entry does exist in the tlb, this + fault taken to detect state transition (eg: COW -> DIRTY) + */ + lis r12, MAS3_RPN@h + ori r12, r12, _PAGE_HWEXEC | MAS3_RPN@l + and r11, r11, r12 + rlwimi r11, r11, 31, 27, 27 /* SX <- _PAGE_HWEXEC */ + ori r11, r11, (MAS3_UW|MAS3_SW|MAS3_UR|MAS3_SR)@l /* set static perms */ + + /* update search PID in MAS6, AS = 0 */ + mfspr r12, SPRN_PID0 + slwi r12, r12, 16 + mtspr SPRN_MAS6, r12 + + /* find the TLB index that caused the fault. It has to be here. */ + tlbsx 0, r10 + + mtspr SPRN_MAS3,r11 + tlbwe + + /* Done...restore registers and get out of here. */ + mfspr r11, SPRN_SPRG7R + mtcr r11 + mfspr r13, SPRN_SPRG5R + mfspr r12, SPRN_SPRG4R + mfspr r11, SPRN_SPRG1 + mfspr r10, SPRN_SPRG0 + rfi /* Force context change */ + +2: + /* + * The bailout. Restore registers to pre-exception conditions + * and call the heavyweights to help us out. + */ + mfspr r11, SPRN_SPRG7R + mtcr r11 + mfspr r13, SPRN_SPRG5R + mfspr r12, SPRN_SPRG4R + mfspr r11, SPRN_SPRG1 + mfspr r10, SPRN_SPRG0 + b data_access + + /* Instruction Storage Interrupt */ + INSTRUCTION_STORAGE_EXCEPTION + + /* External Input Interrupt */ + EXCEPTION(0x0500, ExternalInput, do_IRQ, EXC_XFER_LITE) + + /* Alignment Interrupt */ + ALIGNMENT_EXCEPTION + + /* Program Interrupt */ + PROGRAM_EXCEPTION + + /* Floating Point Unavailable Interrupt */ + EXCEPTION(0x0800, FloatingPointUnavailable, UnknownException, EXC_XFER_EE) + + /* System Call Interrupt */ + START_EXCEPTION(SystemCall) + NORMAL_EXCEPTION_PROLOG + EXC_XFER_EE_LITE(0x0c00, DoSyscall) + + /* Auxillary Processor Unavailable Interrupt */ + EXCEPTION(0x2900, AuxillaryProcessorUnavailable, UnknownException, EXC_XFER_EE) + + /* Decrementer Interrupt */ + DECREMENTER_EXCEPTION + + /* Fixed Internal Timer Interrupt */ + /* TODO: Add FIT support */ + EXCEPTION(0x3100, FixedIntervalTimer, UnknownException, EXC_XFER_EE) + + /* Watchdog Timer Interrupt */ + /* TODO: Add watchdog support */ + CRITICAL_EXCEPTION(0x3200, WatchdogTimer, UnknownException) + + /* Data TLB Error Interrupt */ + START_EXCEPTION(DataTLBError) + mtspr SPRN_SPRG0, r10 /* Save some working registers */ + mtspr SPRN_SPRG1, r11 + mtspr SPRN_SPRG4W, r12 + mtspr SPRN_SPRG5W, r13 + mfcr r11 + mtspr SPRN_SPRG7W, r11 + mfspr r10, SPRN_DEAR /* Get faulting address */ + + /* If we are faulting a kernel address, we have to use the + * kernel page tables. + */ + lis r11, TASK_SIZE@h + ori r11, r11, TASK_SIZE@l + cmplw 5, r10, r11 + blt 5, 3f + lis r11, swapper_pg_dir@h + ori r11, r11, swapper_pg_dir@l + + mfspr r12,SPRN_MAS1 /* Set TID to 0 */ + rlwinm r12,r12,0,16,1 + mtspr SPRN_MAS1,r12 + + b 4f + + /* Get the PGD for the current thread */ +3: + mfspr r11,SPRN_SPRG3 + lwz r11,PGDIR(r11) + +4: + rlwimi r11, r10, 12, 20, 29 /* Create L1 (pgdir/pmd) address */ + lwz r11, 0(r11) /* Get L1 entry */ + rlwinm. r12, r11, 0, 0, 19 /* Extract L2 (pte) base address */ + beq 2f /* Bail if no table */ + + rlwimi r12, r10, 22, 20, 29 /* Compute PTE address */ + lwz r11, 0(r12) /* Get Linux PTE */ + andi. r13, r11, _PAGE_PRESENT + beq 2f + + ori r11, r11, _PAGE_ACCESSED + stw r11, 0(r12) + + /* Jump to common tlb load */ + b finish_tlb_load +2: + /* The bailout. Restore registers to pre-exception conditions + * and call the heavyweights to help us out. + */ + mfspr r11, SPRN_SPRG7R + mtcr r11 + mfspr r13, SPRN_SPRG5R + mfspr r12, SPRN_SPRG4R + mfspr r11, SPRN_SPRG1 + mfspr r10, SPRN_SPRG0 + b data_access + + /* Instruction TLB Error Interrupt */ + /* + * Nearly the same as above, except we get our + * information from different registers and bailout + * to a different point. + */ + START_EXCEPTION(InstructionTLBError) + mtspr SPRN_SPRG0, r10 /* Save some working registers */ + mtspr SPRN_SPRG1, r11 + mtspr SPRN_SPRG4W, r12 + mtspr SPRN_SPRG5W, r13 + mfcr r11 + mtspr SPRN_SPRG7W, r11 + mfspr r10, SPRN_SRR0 /* Get faulting address */ + + /* If we are faulting a kernel address, we have to use the + * kernel page tables. + */ + lis r11, TASK_SIZE@h + ori r11, r11, TASK_SIZE@l + cmplw 5, r10, r11 + blt 5, 3f + lis r11, swapper_pg_dir@h + ori r11, r11, swapper_pg_dir@l + + mfspr r12,SPRN_MAS1 /* Set TID to 0 */ + rlwinm r12,r12,0,16,1 + mtspr SPRN_MAS1,r12 + + b 4f + + /* Get the PGD for the current thread */ +3: + mfspr r11,SPRN_SPRG3 + lwz r11,PGDIR(r11) + +4: + rlwimi r11, r10, 12, 20, 29 /* Create L1 (pgdir/pmd) address */ + lwz r11, 0(r11) /* Get L1 entry */ + rlwinm. r12, r11, 0, 0, 19 /* Extract L2 (pte) base address */ + beq 2f /* Bail if no table */ + + rlwimi r12, r10, 22, 20, 29 /* Compute PTE address */ + lwz r11, 0(r12) /* Get Linux PTE */ + andi. r13, r11, _PAGE_PRESENT + beq 2f + + ori r11, r11, _PAGE_ACCESSED + stw r11, 0(r12) + + /* Jump to common TLB load point */ + b finish_tlb_load + +2: + /* The bailout. Restore registers to pre-exception conditions + * and call the heavyweights to help us out. + */ + mfspr r11, SPRN_SPRG7R + mtcr r11 + mfspr r13, SPRN_SPRG5R + mfspr r12, SPRN_SPRG4R + mfspr r11, SPRN_SPRG1 + mfspr r10, SPRN_SPRG0 + b InstructionStorage + +#ifdef CONFIG_SPE + /* SPE Unavailable */ + START_EXCEPTION(SPEUnavailable) + NORMAL_EXCEPTION_PROLOG + bne load_up_spe + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_EE_LITE(0x2010, KernelSPE) +#else + EXCEPTION(0x2020, SPEUnavailable, UnknownException, EXC_XFER_EE) +#endif /* CONFIG_SPE */ + + /* SPE Floating Point Data */ +#ifdef CONFIG_SPE + EXCEPTION(0x2030, SPEFloatingPointData, SPEFloatingPointException, EXC_XFER_EE); +#else + EXCEPTION(0x2040, SPEFloatingPointData, UnknownException, EXC_XFER_EE) +#endif /* CONFIG_SPE */ + + /* SPE Floating Point Round */ + EXCEPTION(0x2050, SPEFloatingPointRound, UnknownException, EXC_XFER_EE) + + /* Performance Monitor */ + EXCEPTION(0x2060, PerformanceMonitor, PerformanceMonitorException, EXC_XFER_STD) + + + /* Debug Interrupt */ + DEBUG_EXCEPTION + +/* + * Local functions + */ + /* + * Data TLB exceptions will bail out to this point + * if they can't resolve the lightweight TLB fault. + */ +data_access: + NORMAL_EXCEPTION_PROLOG + mfspr r5,SPRN_ESR /* Grab the ESR, save it, pass arg3 */ + stw r5,_ESR(r11) + mfspr r4,SPRN_DEAR /* Grab the DEAR, save it, pass arg2 */ + andis. r10,r5,(ESR_ILK|ESR_DLK)@h + bne 1f + EXC_XFER_EE_LITE(0x0300, handle_page_fault) +1: + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_EE_LITE(0x0300, CacheLockingException) + +/* + + * Both the instruction and data TLB miss get to this + * point to load the TLB. + * r10 - EA of fault + * r11 - TLB (info from Linux PTE) + * r12, r13 - available to use + * CR5 - results of addr < TASK_SIZE + * MAS0, MAS1 - loaded with proper value when we get here + * MAS2, MAS3 - will need additional info from Linux PTE + * Upon exit, we reload everything and RFI. + */ +finish_tlb_load: + /* + * We set execute, because we don't have the granularity to + * properly set this at the page level (Linux problem). + * Many of these bits are software only. Bits we don't set + * here we (properly should) assume have the appropriate value. + */ + + mfspr r12, SPRN_MAS2 + rlwimi r12, r11, 26, 27, 31 /* extract WIMGE from pte */ + mtspr SPRN_MAS2, r12 + + bge 5, 1f + + /* addr > TASK_SIZE */ + li r10, (MAS3_UX | MAS3_UW | MAS3_UR) + andi. r13, r11, (_PAGE_USER | _PAGE_HWWRITE | _PAGE_HWEXEC) + andi. r12, r11, _PAGE_USER /* Test for _PAGE_USER */ + iseleq r12, 0, r10 + and r10, r12, r13 + srwi r12, r10, 1 + or r12, r12, r10 /* Copy user perms into supervisor */ + b 2f + + /* addr <= TASK_SIZE */ +1: rlwinm r12, r11, 31, 29, 29 /* Extract _PAGE_HWWRITE into SW */ + ori r12, r12, (MAS3_SX | MAS3_SR) + +2: rlwimi r11, r12, 0, 20, 31 /* Extract RPN from PTE and merge with perms */ + mtspr SPRN_MAS3, r11 + tlbwe + + /* Done...restore registers and get out of here. */ + mfspr r11, SPRN_SPRG7R + mtcr r11 + mfspr r13, SPRN_SPRG5R + mfspr r12, SPRN_SPRG4R + mfspr r11, SPRN_SPRG1 + mfspr r10, SPRN_SPRG0 + rfi /* Force context change */ + +#ifdef CONFIG_SPE +/* Note that the SPE support is closely modeled after the AltiVec + * support. Changes to one are likely to be applicable to the + * other! */ +load_up_spe: +/* + * Disable SPE for the task which had SPE previously, + * and save its SPE registers in its thread_struct. + * Enables SPE for use in the kernel on return. + * On SMP we know the SPE units are free, since we give it up every + * switch. -- Kumar + */ + mfmsr r5 + oris r5,r5,MSR_SPE@h + mtmsr r5 /* enable use of SPE now */ + isync +/* + * For SMP, we don't do lazy SPE switching because it just gets too + * horrendously complex, especially when a task switches from one CPU + * to another. Instead we call giveup_spe in switch_to. + */ +#ifndef CONFIG_SMP + lis r3,last_task_used_spe@ha + lwz r4,last_task_used_spe@l(r3) + cmpi 0,r4,0 + beq 1f + addi r4,r4,THREAD /* want THREAD of last_task_used_spe */ + SAVE_32EVR(0,r10,r4) + evxor evr10, evr10, evr10 /* clear out evr10 */ + evmwumiaa evr10, evr10, evr10 /* evr10 <- ACC = 0 * 0 + ACC */ + li r5,THREAD_ACC + evstddx evr10, r4, r5 /* save off accumulator */ + lwz r5,PT_REGS(r4) + lwz r4,_MSR-STACK_FRAME_OVERHEAD(r5) + lis r10,MSR_SPE@h + andc r4,r4,r10 /* disable SPE for previous task */ + stw r4,_MSR-STACK_FRAME_OVERHEAD(r5) +1: +#endif /* CONFIG_SMP */ + /* enable use of SPE after return */ + oris r9,r9,MSR_SPE@h + mfspr r5,SPRN_SPRG3 /* current task's THREAD (phys) */ + li r4,1 + li r10,THREAD_ACC + stw r4,THREAD_USED_SPE(r5) + evlddx evr4,r10,r5 + evmra evr4,evr4 + REST_32EVR(0,r10,r5) +#ifndef CONFIG_SMP + subi r4,r5,THREAD + stw r4,last_task_used_spe@l(r3) +#endif /* CONFIG_SMP */ + /* restore registers and return */ +2: REST_4GPRS(3, r11) + lwz r10,_CCR(r11) + REST_GPR(1, r11) + mtcr r10 + lwz r10,_LINK(r11) + mtlr r10 + REST_GPR(10, r11) + mtspr SPRN_SRR1,r9 + mtspr SPRN_SRR0,r12 + REST_GPR(9, r11) + REST_GPR(12, r11) + lwz r11,GPR11(r11) + SYNC + rfi + +/* + * SPE unavailable trap from kernel - print a message, but let + * the task use SPE in the kernel until it returns to user mode. + */ +KernelSPE: + lwz r3,_MSR(r1) + oris r3,r3,MSR_SPE@h + stw r3,_MSR(r1) /* enable use of SPE after return */ + lis r3,87f@h + ori r3,r3,87f@l + mr r4,r2 /* current */ + lwz r5,_NIP(r1) + bl printk + b ret_from_except +87: .string "SPE used in kernel (task=%p, pc=%x) \n" + .align 4,0 + +#endif /* CONFIG_SPE */ + +/* + * Global functions + */ + +/* + * extern void loadcam_entry(unsigned int index) + * + * Load TLBCAM[index] entry in to the L2 CAM MMU + */ +_GLOBAL(loadcam_entry) + lis r4,TLBCAM@ha + addi r4,r4,TLBCAM@l + mulli r5,r3,20 + add r3,r5,r4 + lwz r4,0(r3) + mtspr SPRN_MAS0,r4 + lwz r4,4(r3) + mtspr SPRN_MAS1,r4 + lwz r4,8(r3) + mtspr SPRN_MAS2,r4 + lwz r4,12(r3) + mtspr SPRN_MAS3,r4 + tlbwe + isync + blr + +/* + * extern void giveup_altivec(struct task_struct *prev) + * + * The e500 core does not have an AltiVec unit. + */ +_GLOBAL(giveup_altivec) + blr + +#ifdef CONFIG_SPE +/* + * extern void giveup_spe(struct task_struct *prev) + * + */ +_GLOBAL(giveup_spe) + mfmsr r5 + oris r5,r5,MSR_SPE@h + SYNC + mtmsr r5 /* enable use of SPE now */ + isync + cmpi 0,r3,0 + beqlr- /* if no previous owner, done */ + addi r3,r3,THREAD /* want THREAD of task */ + lwz r5,PT_REGS(r3) + cmpi 0,r5,0 + SAVE_32EVR(0, r4, r3) + evxor evr6, evr6, evr6 /* clear out evr6 */ + evmwumiaa evr6, evr6, evr6 /* evr6 <- ACC = 0 * 0 + ACC */ + li r4,THREAD_ACC + evstddx evr6, r4, r3 /* save off accumulator */ + mfspr r6,SPRN_SPEFSCR + stw r6,THREAD_SPEFSCR(r3) /* save spefscr register value */ + beq 1f + lwz r4,_MSR-STACK_FRAME_OVERHEAD(r5) + lis r3,MSR_SPE@h + andc r4,r4,r3 /* disable SPE for previous task */ + stw r4,_MSR-STACK_FRAME_OVERHEAD(r5) +1: +#ifndef CONFIG_SMP + li r5,0 + lis r4,last_task_used_spe@ha + stw r5,last_task_used_spe@l(r4) +#endif /* CONFIG_SMP */ + blr +#endif /* CONFIG_SPE */ + +/* + * extern void giveup_fpu(struct task_struct *prev) + * + * The e500 core does not have an FPU. + */ +_GLOBAL(giveup_fpu) + blr + +/* + * extern void abort(void) + * + * At present, this routine just applies a system reset. + */ +_GLOBAL(abort) + li r13,0 + mtspr SPRN_DBCR0,r13 /* disable all debug events */ + mfmsr r13 + ori r13,r13,MSR_DE@l /* Enable Debug Events */ + mtmsr r13 + mfspr r13,SPRN_DBCR0 + lis r13,(DBCR0_IDM|DBCR0_RST_CHIP)@h + mtspr SPRN_DBCR0,r13 + +_GLOBAL(set_context) + +#ifdef CONFIG_BDI_SWITCH + /* Context switch the PTE pointer for the Abatron BDI2000. + * The PGDIR is the second parameter. + */ + lis r5, abatron_pteptrs@h + ori r5, r5, abatron_pteptrs@l + stw r4, 0x4(r5) +#endif + mtspr SPRN_PID,r3 + isync /* Force context change */ + blr + +/* + * We put a few things here that have to be page-aligned. This stuff + * goes at the beginning of the data segment, which is page-aligned. + */ + .data +_GLOBAL(sdata) +_GLOBAL(empty_zero_page) + .space 4096 +_GLOBAL(swapper_pg_dir) + .space 4096 + +/* Reserved 4k for the critical exception stack & 4k for the machine + * check stack per CPU for kernel mode exceptions */ + .section .bss + .align 12 +exception_stack_bottom: + .space BOOKE_EXCEPTION_STACK_SIZE * NR_CPUS +_GLOBAL(exception_stack_top) + +/* + * This space gets a copy of optional info passed to us by the bootstrap + * which is used to pass parameters into the kernel like root=/dev/sda1, etc. + */ +_GLOBAL(cmd_line) + .space 512 + +/* + * Room for two PTE pointers, usually the kernel and current user pointers + * to their respective root page table. + */ +abatron_pteptrs: + .space 8 + diff --git a/arch/ppc/kernel/idle.c b/arch/ppc/kernel/idle.c new file mode 100644 index 00000000000..53547b6de45 --- /dev/null +++ b/arch/ppc/kernel/idle.c @@ -0,0 +1,100 @@ +/* + * Idle daemon for PowerPC. Idle daemon will handle any action + * that needs to be taken when the system becomes idle. + * + * Written by Cort Dougan (cort@cs.nmt.edu). Subsequently hacked + * on by Tom Rini, Armin Kuster, Paul Mackerras and others. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +void default_idle(void) +{ + void (*powersave)(void); + + powersave = ppc_md.power_save; + + if (!need_resched()) { + if (powersave != NULL) + powersave(); +#ifdef CONFIG_SMP + else { + set_thread_flag(TIF_POLLING_NRFLAG); + while (!need_resched()) + barrier(); + clear_thread_flag(TIF_POLLING_NRFLAG); + } +#endif + } + if (need_resched()) + schedule(); +} + +/* + * The body of the idle task. + */ +void cpu_idle(void) +{ + for (;;) + if (ppc_md.idle != NULL) + ppc_md.idle(); + else + default_idle(); +} + +#if defined(CONFIG_SYSCTL) && defined(CONFIG_6xx) +/* + * Register the sysctl to set/clear powersave_nap. + */ +extern unsigned long powersave_nap; + +static ctl_table powersave_nap_ctl_table[]={ + { + .ctl_name = KERN_PPC_POWERSAVE_NAP, + .procname = "powersave-nap", + .data = &powersave_nap, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, + { 0, }, +}; +static ctl_table powersave_nap_sysctl_root[] = { + { 1, "kernel", NULL, 0, 0755, powersave_nap_ctl_table, }, + { 0,}, +}; + +static int __init +register_powersave_nap_sysctl(void) +{ + register_sysctl_table(powersave_nap_sysctl_root, 0); + + return 0; +} + +__initcall(register_powersave_nap_sysctl); +#endif diff --git a/arch/ppc/kernel/idle_6xx.S b/arch/ppc/kernel/idle_6xx.S new file mode 100644 index 00000000000..25d009c75f7 --- /dev/null +++ b/arch/ppc/kernel/idle_6xx.S @@ -0,0 +1,233 @@ +/* + * This file contains the power_save function for 6xx & 7xxx CPUs + * rewritten in assembler + * + * Warning ! This code assumes that if your machine has a 750fx + * it will have PLL 1 set to low speed mode (used during NAP/DOZE). + * if this is not the case some additional changes will have to + * be done to check a runtime var (a bit like powersave-nap) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG + + .text + +/* + * Init idle, called at early CPU setup time from head.S for each CPU + * Make sure no rest of NAP mode remains in HID0, save default + * values for some CPU specific registers. Called with r24 + * containing CPU number and r3 reloc offset + */ +_GLOBAL(init_idle_6xx) +BEGIN_FTR_SECTION + mfspr r4,SPRN_HID0 + rlwinm r4,r4,0,10,8 /* Clear NAP */ + mtspr SPRN_HID0, r4 + b 1f +END_FTR_SECTION_IFSET(CPU_FTR_CAN_NAP) + blr +1: + slwi r5,r24,2 + add r5,r5,r3 +BEGIN_FTR_SECTION + mfspr r4,SPRN_MSSCR0 + addis r6,r5, nap_save_msscr0@ha + stw r4,nap_save_msscr0@l(r6) +END_FTR_SECTION_IFSET(CPU_FTR_NAP_DISABLE_L2_PR) +BEGIN_FTR_SECTION + mfspr r4,SPRN_HID1 + addis r6,r5,nap_save_hid1@ha + stw r4,nap_save_hid1@l(r6) +END_FTR_SECTION_IFSET(CPU_FTR_DUAL_PLL_750FX) + blr + +/* + * Here is the power_save_6xx function. This could eventually be + * split into several functions & changing the function pointer + * depending on the various features. + */ +_GLOBAL(ppc6xx_idle) + /* Check if we can nap or doze, put HID0 mask in r3 + */ + lis r3, 0 +BEGIN_FTR_SECTION + lis r3,HID0_DOZE@h +END_FTR_SECTION_IFSET(CPU_FTR_CAN_DOZE) +BEGIN_FTR_SECTION + /* We must dynamically check for the NAP feature as it + * can be cleared by CPU init after the fixups are done + */ + lis r4,cur_cpu_spec@ha + lwz r4,cur_cpu_spec@l(r4) + lwz r4,CPU_SPEC_FEATURES(r4) + andi. r0,r4,CPU_FTR_CAN_NAP + beq 1f + /* Now check if user or arch enabled NAP mode */ + lis r4,powersave_nap@ha + lwz r4,powersave_nap@l(r4) + cmpwi 0,r4,0 + beq 1f + lis r3,HID0_NAP@h +1: +END_FTR_SECTION_IFSET(CPU_FTR_CAN_NAP) + cmpwi 0,r3,0 + beqlr + + /* Clear MSR:EE */ + mfmsr r7 + rlwinm r0,r7,0,17,15 + mtmsr r0 + + /* Check current_thread_info()->flags */ + rlwinm r4,r1,0,0,18 + lwz r4,TI_FLAGS(r4) + andi. r0,r4,_TIF_NEED_RESCHED + beq 1f + mtmsr r7 /* out of line this ? */ + blr +1: + /* Some pre-nap cleanups needed on some CPUs */ + andis. r0,r3,HID0_NAP@h + beq 2f +BEGIN_FTR_SECTION + /* Disable L2 prefetch on some 745x and try to ensure + * L2 prefetch engines are idle. As explained by errata + * text, we can't be sure they are, we just hope very hard + * that well be enough (sic !). At least I noticed Apple + * doesn't even bother doing the dcbf's here... + */ + mfspr r4,SPRN_MSSCR0 + rlwinm r4,r4,0,0,29 + sync + mtspr SPRN_MSSCR0,r4 + sync + isync + lis r4,KERNELBASE@h + dcbf 0,r4 + dcbf 0,r4 + dcbf 0,r4 + dcbf 0,r4 +END_FTR_SECTION_IFSET(CPU_FTR_NAP_DISABLE_L2_PR) +#ifdef DEBUG + lis r6,nap_enter_count@ha + lwz r4,nap_enter_count@l(r6) + addi r4,r4,1 + stw r4,nap_enter_count@l(r6) +#endif +2: +BEGIN_FTR_SECTION + /* Go to low speed mode on some 750FX */ + lis r4,powersave_lowspeed@ha + lwz r4,powersave_lowspeed@l(r4) + cmpwi 0,r4,0 + beq 1f + mfspr r4,SPRN_HID1 + oris r4,r4,0x0001 + mtspr SPRN_HID1,r4 +1: +END_FTR_SECTION_IFSET(CPU_FTR_DUAL_PLL_750FX) + + /* Go to NAP or DOZE now */ + mfspr r4,SPRN_HID0 + lis r5,(HID0_NAP|HID0_SLEEP)@h +BEGIN_FTR_SECTION + oris r5,r5,HID0_DOZE@h +END_FTR_SECTION_IFSET(CPU_FTR_CAN_DOZE) + andc r4,r4,r5 + or r4,r4,r3 +BEGIN_FTR_SECTION + oris r4,r4,HID0_DPM@h /* that should be done once for all */ +END_FTR_SECTION_IFCLR(CPU_FTR_NO_DPM) + mtspr SPRN_HID0,r4 +BEGIN_FTR_SECTION + DSSALL + sync +END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) + ori r7,r7,MSR_EE /* Could be ommited (already set) */ + oris r7,r7,MSR_POW@h + sync + isync + mtmsr r7 + isync + sync + blr + +/* + * Return from NAP/DOZE mode, restore some CPU specific registers, + * we are called with DR/IR still off and r2 containing physical + * address of current. + */ +_GLOBAL(power_save_6xx_restore) + mfspr r11,SPRN_HID0 + rlwinm. r11,r11,0,10,8 /* Clear NAP & copy NAP bit !state to cr1 EQ */ + cror 4*cr1+eq,4*cr0+eq,4*cr0+eq +BEGIN_FTR_SECTION + rlwinm r11,r11,0,9,7 /* Clear DOZE */ +END_FTR_SECTION_IFSET(CPU_FTR_CAN_DOZE) + mtspr SPRN_HID0, r11 + +#ifdef DEBUG + beq cr1,1f + lis r11,(nap_return_count-KERNELBASE)@ha + lwz r9,nap_return_count@l(r11) + addi r9,r9,1 + stw r9,nap_return_count@l(r11) +1: +#endif + + rlwinm r9,r1,0,0,18 + tophys(r9,r9) + lwz r11,TI_CPU(r9) + slwi r11,r11,2 + /* Todo make sure all these are in the same page + * and load r22 (@ha part + CPU offset) only once + */ +BEGIN_FTR_SECTION + beq cr1,1f + addis r9,r11,(nap_save_msscr0-KERNELBASE)@ha + lwz r9,nap_save_msscr0@l(r9) + mtspr SPRN_MSSCR0, r9 + sync + isync +1: +END_FTR_SECTION_IFSET(CPU_FTR_NAP_DISABLE_L2_PR) +BEGIN_FTR_SECTION + addis r9,r11,(nap_save_hid1-KERNELBASE)@ha + lwz r9,nap_save_hid1@l(r9) + mtspr SPRN_HID1, r9 +END_FTR_SECTION_IFSET(CPU_FTR_DUAL_PLL_750FX) + b transfer_to_handler_cont + + .data + +_GLOBAL(nap_save_msscr0) + .space 4*NR_CPUS + +_GLOBAL(nap_save_hid1) + .space 4*NR_CPUS + +_GLOBAL(powersave_nap) + .long 0 +_GLOBAL(powersave_lowspeed) + .long 0 + +#ifdef DEBUG +_GLOBAL(nap_enter_count) + .space 4 +_GLOBAL(nap_return_count) + .space 4 +#endif diff --git a/arch/ppc/kernel/idle_power4.S b/arch/ppc/kernel/idle_power4.S new file mode 100644 index 00000000000..73a58ff0390 --- /dev/null +++ b/arch/ppc/kernel/idle_power4.S @@ -0,0 +1,91 @@ +/* + * This file contains the power_save function for 6xx & 7xxx CPUs + * rewritten in assembler + * + * Warning ! This code assumes that if your machine has a 750fx + * it will have PLL 1 set to low speed mode (used during NAP/DOZE). + * if this is not the case some additional changes will have to + * be done to check a runtime var (a bit like powersave-nap) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG + + .text + +/* + * Init idle, called at early CPU setup time from head.S for each CPU + * So nothing for now. Called with r24 containing CPU number and r3 + * reloc offset + */ + .globl init_idle_power4 +init_idle_power4: + blr + +/* + * Here is the power_save_6xx function. This could eventually be + * split into several functions & changing the function pointer + * depending on the various features. + */ + .globl power4_idle +power4_idle: +BEGIN_FTR_SECTION + blr +END_FTR_SECTION_IFCLR(CPU_FTR_CAN_NAP) + /* We must dynamically check for the NAP feature as it + * can be cleared by CPU init after the fixups are done + */ + lis r4,cur_cpu_spec@ha + lwz r4,cur_cpu_spec@l(r4) + lwz r4,CPU_SPEC_FEATURES(r4) + andi. r0,r4,CPU_FTR_CAN_NAP + beqlr + /* Now check if user or arch enabled NAP mode */ + lis r4,powersave_nap@ha + lwz r4,powersave_nap@l(r4) + cmpwi 0,r4,0 + beqlr + + /* Clear MSR:EE */ + mfmsr r7 + rlwinm r0,r7,0,17,15 + mtmsr r0 + + /* Check current_thread_info()->flags */ + rlwinm r4,r1,0,0,18 + lwz r4,TI_FLAGS(r4) + andi. r0,r4,_TIF_NEED_RESCHED + beq 1f + mtmsr r7 /* out of line this ? */ + blr +1: + /* Go to NAP now */ +BEGIN_FTR_SECTION + DSSALL + sync +END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) + ori r7,r7,MSR_EE /* Could be ommited (already set) */ + oris r7,r7,MSR_POW@h + sync + isync + mtmsr r7 + isync + sync + blr + + .globl powersave_nap +powersave_nap: + .long 0 diff --git a/arch/ppc/kernel/irq.c b/arch/ppc/kernel/irq.c new file mode 100644 index 00000000000..8843f3af230 --- /dev/null +++ b/arch/ppc/kernel/irq.c @@ -0,0 +1,164 @@ +/* + * arch/ppc/kernel/irq.c + * + * Derived from arch/i386/kernel/irq.c + * Copyright (C) 1992 Linus Torvalds + * Adapted from arch/i386 by Gary Thomas + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * Updated and modified by Cort Dougan + * Copyright (C) 1996-2001 Cort Dougan + * Adapted for Power Macintosh by Paul Mackerras + * Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au) + * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). + * + * This file contains the code used by various IRQ handling routines: + * asking for different IRQ's should be done through these routines + * instead of just grabbing them. Thus setups with different IRQ numbers + * shouldn't result in any weird surprises, and installing new handlers + * should be easier. + * + * The MPC8xx has an interrupt mask in the SIU. If a bit is set, the + * interrupt is _enabled_. As expected, IRQ0 is bit 0 in the 32-bit + * mask register (of which only 16 are defined), hence the weird shifting + * and complement of the cached_irq_mask. I want to be able to stuff + * this right into the SIU SMASK register. + * Many of the prep/chrp functions are conditional compiled on CONFIG_8xx + * to reduce code space and undefined function references. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define NR_MASK_WORDS ((NR_IRQS + 31) / 32) + +extern atomic_t ipi_recv; +extern atomic_t ipi_sent; + +#define MAXCOUNT 10000000 + +int ppc_spurious_interrupts = 0; +struct irqaction *ppc_irq_action[NR_IRQS]; +unsigned long ppc_cached_irq_mask[NR_MASK_WORDS]; +unsigned long ppc_lost_interrupts[NR_MASK_WORDS]; +atomic_t ppc_n_lost_interrupts; + +#ifdef CONFIG_TAU_INT +extern int tau_initialized; +extern int tau_interrupts(int); +#endif + +int show_interrupts(struct seq_file *p, void *v) +{ + int i = *(loff_t *) v, j; + struct irqaction * action; + unsigned long flags; + + if (i == 0) { + seq_puts(p, " "); + for (j=0; jhandler ) + goto skip; + seq_printf(p, "%3d: ", i); +#ifdef CONFIG_SMP + for (j = 0; j < NR_CPUS; j++) + if (cpu_online(j)) + seq_printf(p, "%10u ", + kstat_cpu(j).irqs[i]); +#else + seq_printf(p, "%10u ", kstat_irqs(i)); +#endif /* CONFIG_SMP */ + if (irq_desc[i].handler) + seq_printf(p, " %s ", irq_desc[i].handler->typename); + else + seq_puts(p, " None "); + seq_printf(p, "%s", (irq_desc[i].status & IRQ_LEVEL) ? "Level " : "Edge "); + seq_printf(p, " %s", action->name); + for (action = action->next; action; action = action->next) + seq_printf(p, ", %s", action->name); + seq_putc(p, '\n'); +skip: + spin_unlock_irqrestore(&irq_desc[i].lock, flags); + } else if (i == NR_IRQS) { +#ifdef CONFIG_TAU_INT + if (tau_initialized){ + seq_puts(p, "TAU: "); + for (j = 0; j < NR_CPUS; j++) + if (cpu_online(j)) + seq_printf(p, "%10u ", tau_interrupts(j)); + seq_puts(p, " PowerPC Thermal Assist (cpu temp)\n"); + } +#endif +#ifdef CONFIG_SMP + /* should this be per processor send/receive? */ + seq_printf(p, "IPI (recv/sent): %10u/%u\n", + atomic_read(&ipi_recv), atomic_read(&ipi_sent)); +#endif + seq_printf(p, "BAD: %10u\n", ppc_spurious_interrupts); + } + return 0; +} + +void do_IRQ(struct pt_regs *regs) +{ + int irq, first = 1; + irq_enter(); + + /* + * Every platform is required to implement ppc_md.get_irq. + * This function will either return an irq number or -1 to + * indicate there are no more pending. But the first time + * through the loop this means there wasn't and IRQ pending. + * The value -2 is for buggy hardware and means that this IRQ + * has already been handled. -- Tom + */ + while ((irq = ppc_md.get_irq(regs)) >= 0) { + __do_IRQ(irq, regs); + first = 0; + } + if (irq != -2 && first) + /* That's not SMP safe ... but who cares ? */ + ppc_spurious_interrupts++; + irq_exit(); +} + +void __init init_IRQ(void) +{ + ppc_md.init_IRQ(); +} diff --git a/arch/ppc/kernel/l2cr.S b/arch/ppc/kernel/l2cr.S new file mode 100644 index 00000000000..c3944104826 --- /dev/null +++ b/arch/ppc/kernel/l2cr.S @@ -0,0 +1,442 @@ +/* + L2CR functions + Copyright © 1997-1998 by PowerLogix R & D, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +/* + Thur, Dec. 12, 1998. + - First public release, contributed by PowerLogix. + *********** + Sat, Aug. 7, 1999. + - Terry: Made sure code disabled interrupts before running. (Previously + it was assumed interrupts were already disabled). + - Terry: Updated for tentative G4 support. 4MB of memory is now flushed + instead of 2MB. (Prob. only 3 is necessary). + - Terry: Updated for workaround to HID0[DPM] processor bug + during global invalidates. + *********** + Thu, July 13, 2000. + - Terry: Added isync to correct for an errata. + + 22 August 2001. + - DanM: Finally added the 7450 patch I've had for the past + several months. The L2CR is similar, but I'm going + to assume the user of this functions knows what they + are doing. + + Author: Terry Greeniaus (tgree@phys.ualberta.ca) + Please e-mail updates to this file to me, thanks! +*/ +#include +#include +#include +#include +#include +#include + +/* Usage: + + When setting the L2CR register, you must do a few special + things. If you are enabling the cache, you must perform a + global invalidate. If you are disabling the cache, you must + flush the cache contents first. This routine takes care of + doing these things. When first enabling the cache, make sure + you pass in the L2CR you want, as well as passing in the + global invalidate bit set. A global invalidate will only be + performed if the L2I bit is set in applyThis. When enabling + the cache, you should also set the L2E bit in applyThis. If + you want to modify the L2CR contents after the cache has been + enabled, the recommended procedure is to first call + __setL2CR(0) to disable the cache and then call it again with + the new values for L2CR. Examples: + + _setL2CR(0) - disables the cache + _setL2CR(0xB3A04000) - enables my G3 upgrade card: + - L2E set to turn on the cache + - L2SIZ set to 1MB + - L2CLK set to 1:1 + - L2RAM set to pipelined synchronous late-write + - L2I set to perform a global invalidation + - L2OH set to 0.5 nS + - L2DF set because this upgrade card + requires it + + A similar call should work for your card. You need to know + the correct setting for your card and then place them in the + fields I have outlined above. Other fields support optional + features, such as L2DO which caches only data, or L2TS which + causes cache pushes from the L1 cache to go to the L2 cache + instead of to main memory. + +IMPORTANT: + Starting with the 7450, the bits in this register have moved + or behave differently. The Enable, Parity Enable, Size, + and L2 Invalidate are the only bits that have not moved. + The size is read-only for these processors with internal L2 + cache, and the invalidate is a control as well as status. + -- Dan + +*/ +/* + * Summary: this procedure ignores the L2I bit in the value passed in, + * flushes the cache if it was already enabled, always invalidates the + * cache, then enables the cache if the L2E bit is set in the value + * passed in. + * -- paulus. + */ +_GLOBAL(_set_L2CR) + /* Make sure this is a 750 or 7400 chip */ +BEGIN_FTR_SECTION + li r3,-1 + blr +END_FTR_SECTION_IFCLR(CPU_FTR_L2CR) + + mflr r9 + + /* Stop DST streams */ +BEGIN_FTR_SECTION + DSSALL + sync +END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) + + /* Turn off interrupts and data relocation. */ + mfmsr r7 /* Save MSR in r7 */ + rlwinm r4,r7,0,17,15 + rlwinm r4,r4,0,28,26 /* Turn off DR bit */ + sync + mtmsr r4 + isync + + /* Before we perform the global invalidation, we must disable dynamic + * power management via HID0[DPM] to work around a processor bug where + * DPM can possibly interfere with the state machine in the processor + * that invalidates the L2 cache tags. + */ + mfspr r8,SPRN_HID0 /* Save HID0 in r8 */ + rlwinm r4,r8,0,12,10 /* Turn off HID0[DPM] */ + sync + mtspr SPRN_HID0,r4 /* Disable DPM */ + sync + + /* Get the current enable bit of the L2CR into r4 */ + mfspr r4,SPRN_L2CR + + /* Tweak some bits */ + rlwinm r5,r3,0,0,0 /* r5 contains the new enable bit */ + rlwinm r3,r3,0,11,9 /* Turn off the invalidate bit */ + rlwinm r3,r3,0,1,31 /* Turn off the enable bit */ + + /* Check to see if we need to flush */ + rlwinm. r4,r4,0,0,0 + beq 2f + + /* Flush the cache. First, read the first 4MB of memory (physical) to + * put new data in the cache. (Actually we only need + * the size of the L2 cache plus the size of the L1 cache, but 4MB will + * cover everything just to be safe). + */ + + /**** Might be a good idea to set L2DO here - to prevent instructions + from getting into the cache. But since we invalidate + the next time we enable the cache it doesn't really matter. + Don't do this unless you accomodate all processor variations. + The bit moved on the 7450..... + ****/ + + /* TODO: use HW flush assist when available */ + + lis r4,0x0002 + mtctr r4 + li r4,0 +1: + lwzx r0,r0,r4 + addi r4,r4,32 /* Go to start of next cache line */ + bdnz 1b + isync + + /* Now, flush the first 4MB of memory */ + lis r4,0x0002 + mtctr r4 + li r4,0 + sync +1: + dcbf 0,r4 + addi r4,r4,32 /* Go to start of next cache line */ + bdnz 1b + +2: + /* Set up the L2CR configuration bits (and switch L2 off) */ + /* CPU errata: Make sure the mtspr below is already in the + * L1 icache + */ + b 20f + .balign L1_CACHE_LINE_SIZE +22: + sync + mtspr SPRN_L2CR,r3 + sync + b 23f +20: + b 21f +21: sync + isync + b 22b + +23: + /* Perform a global invalidation */ + oris r3,r3,0x0020 + sync + mtspr SPRN_L2CR,r3 + sync + isync /* For errata */ + +BEGIN_FTR_SECTION + /* On the 7450, we wait for the L2I bit to clear...... + */ +10: mfspr r3,SPRN_L2CR + andis. r4,r3,0x0020 + bne 10b + b 11f +END_FTR_SECTION_IFSET(CPU_FTR_SPEC7450) + + /* Wait for the invalidation to complete */ +3: mfspr r3,SPRN_L2CR + rlwinm. r4,r3,0,31,31 + bne 3b + +11: rlwinm r3,r3,0,11,9 /* Turn off the L2I bit */ + sync + mtspr SPRN_L2CR,r3 + sync + + /* See if we need to enable the cache */ + cmplwi r5,0 + beq 4f + + /* Enable the cache */ + oris r3,r3,0x8000 + mtspr SPRN_L2CR,r3 + sync + +4: + + /* Restore HID0[DPM] to whatever it was before */ + sync + mtspr 1008,r8 + sync + + /* Restore MSR (restores EE and DR bits to original state) */ + SYNC + mtmsr r7 + isync + + mtlr r9 + blr + +_GLOBAL(_get_L2CR) + /* Return the L2CR contents */ + li r3,0 +BEGIN_FTR_SECTION + mfspr r3,SPRN_L2CR +END_FTR_SECTION_IFSET(CPU_FTR_L2CR) + blr + + +/* + * Here is a similar routine for dealing with the L3 cache + * on the 745x family of chips + */ + +_GLOBAL(_set_L3CR) + /* Make sure this is a 745x chip */ +BEGIN_FTR_SECTION + li r3,-1 + blr +END_FTR_SECTION_IFCLR(CPU_FTR_L3CR) + + /* Turn off interrupts and data relocation. */ + mfmsr r7 /* Save MSR in r7 */ + rlwinm r4,r7,0,17,15 + rlwinm r4,r4,0,28,26 /* Turn off DR bit */ + sync + mtmsr r4 + isync + + /* Stop DST streams */ + DSSALL + sync + + /* Get the current enable bit of the L3CR into r4 */ + mfspr r4,SPRN_L3CR + + /* Tweak some bits */ + rlwinm r5,r3,0,0,0 /* r5 contains the new enable bit */ + rlwinm r3,r3,0,22,20 /* Turn off the invalidate bit */ + rlwinm r3,r3,0,2,31 /* Turn off the enable & PE bits */ + rlwinm r3,r3,0,5,3 /* Turn off the clken bit */ + /* Check to see if we need to flush */ + rlwinm. r4,r4,0,0,0 + beq 2f + + /* Flush the cache. + */ + + /* TODO: use HW flush assist */ + + lis r4,0x0008 + mtctr r4 + li r4,0 +1: + lwzx r0,r0,r4 + dcbf 0,r4 + addi r4,r4,32 /* Go to start of next cache line */ + bdnz 1b + +2: + /* Set up the L3CR configuration bits (and switch L3 off) */ + sync + mtspr SPRN_L3CR,r3 + sync + + oris r3,r3,L3CR_L3RES@h /* Set reserved bit 5 */ + mtspr SPRN_L3CR,r3 + sync + oris r3,r3,L3CR_L3CLKEN@h /* Set clken */ + mtspr SPRN_L3CR,r3 + sync + + /* Wait for stabilize */ + li r0,256 + mtctr r0 +1: bdnz 1b + + /* Perform a global invalidation */ + ori r3,r3,0x0400 + sync + mtspr SPRN_L3CR,r3 + sync + isync + + /* We wait for the L3I bit to clear...... */ +10: mfspr r3,SPRN_L3CR + andi. r4,r3,0x0400 + bne 10b + + /* Clear CLKEN */ + rlwinm r3,r3,0,5,3 /* Turn off the clken bit */ + mtspr SPRN_L3CR,r3 + sync + + /* Wait for stabilize */ + li r0,256 + mtctr r0 +1: bdnz 1b + + /* See if we need to enable the cache */ + cmplwi r5,0 + beq 4f + + /* Enable the cache */ + oris r3,r3,(L3CR_L3E | L3CR_L3CLKEN)@h + mtspr SPRN_L3CR,r3 + sync + + /* Wait for stabilize */ + li r0,256 + mtctr r0 +1: bdnz 1b + + /* Restore MSR (restores EE and DR bits to original state) */ +4: SYNC + mtmsr r7 + isync + blr + +_GLOBAL(_get_L3CR) + /* Return the L3CR contents */ + li r3,0 +BEGIN_FTR_SECTION + mfspr r3,SPRN_L3CR +END_FTR_SECTION_IFSET(CPU_FTR_L3CR) + blr + +/* --- End of PowerLogix code --- + */ + + +/* flush_disable_L1() - Flush and disable L1 cache + * + * clobbers r0, r3, ctr, cr0 + * Must be called with interrupts disabled and MMU enabled. + */ +_GLOBAL(__flush_disable_L1) + /* Stop pending alitvec streams and memory accesses */ +BEGIN_FTR_SECTION + DSSALL +END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) + sync + + /* Load counter to 0x4000 cache lines (512k) and + * load cache with datas + */ + li r3,0x4000 /* 512kB / 32B */ + mtctr r3 + lis r3,KERNELBASE@h +1: + lwz r0,0(r3) + addi r3,r3,0x0020 /* Go to start of next cache line */ + bdnz 1b + isync + sync + + /* Now flush those cache lines */ + li r3,0x4000 /* 512kB / 32B */ + mtctr r3 + lis r3,KERNELBASE@h +1: + dcbf 0,r3 + addi r3,r3,0x0020 /* Go to start of next cache line */ + bdnz 1b + sync + + /* We can now disable the L1 cache (HID0:DCE, HID0:ICE) */ + mfspr r3,SPRN_HID0 + rlwinm r3,r3,0,18,15 + mtspr SPRN_HID0,r3 + sync + isync + blr + +/* inval_enable_L1 - Invalidate and enable L1 cache + * + * Assumes L1 is already disabled and MSR:EE is off + * + * clobbers r3 + */ +_GLOBAL(__inval_enable_L1) + /* Enable and then Flash inval the instruction & data cache */ + mfspr r3,SPRN_HID0 + ori r3,r3, HID0_ICE|HID0_ICFI|HID0_DCE|HID0_DCI + sync + isync + mtspr SPRN_HID0,r3 + xori r3,r3, HID0_ICFI|HID0_DCI + mtspr SPRN_HID0,r3 + sync + + blr + + diff --git a/arch/ppc/kernel/misc.S b/arch/ppc/kernel/misc.S new file mode 100644 index 00000000000..73f7c23b0dd --- /dev/null +++ b/arch/ppc/kernel/misc.S @@ -0,0 +1,1453 @@ +/* + * This file contains miscellaneous low-level functions. + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Largely rewritten by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + .text + + .align 5 +_GLOBAL(__delay) + cmpwi 0,r3,0 + mtctr r3 + beqlr +1: bdnz 1b + blr + +/* + * Returns (address we're running at) - (address we were linked at) + * for use before the text and data are mapped to KERNELBASE. + */ +_GLOBAL(reloc_offset) + mflr r0 + bl 1f +1: mflr r3 + lis r4,1b@ha + addi r4,r4,1b@l + subf r3,r4,r3 + mtlr r0 + blr + +/* + * add_reloc_offset(x) returns x + reloc_offset(). + */ +_GLOBAL(add_reloc_offset) + mflr r0 + bl 1f +1: mflr r5 + lis r4,1b@ha + addi r4,r4,1b@l + subf r5,r4,r5 + add r3,r3,r5 + mtlr r0 + blr + +/* + * sub_reloc_offset(x) returns x - reloc_offset(). + */ +_GLOBAL(sub_reloc_offset) + mflr r0 + bl 1f +1: mflr r5 + lis r4,1b@ha + addi r4,r4,1b@l + subf r5,r4,r5 + subf r3,r5,r3 + mtlr r0 + blr + +/* + * reloc_got2 runs through the .got2 section adding an offset + * to each entry. + */ +_GLOBAL(reloc_got2) + mflr r11 + lis r7,__got2_start@ha + addi r7,r7,__got2_start@l + lis r8,__got2_end@ha + addi r8,r8,__got2_end@l + subf r8,r7,r8 + srwi. r8,r8,2 + beqlr + mtctr r8 + bl 1f +1: mflr r0 + lis r4,1b@ha + addi r4,r4,1b@l + subf r0,r4,r0 + add r7,r0,r7 +2: lwz r0,0(r7) + add r0,r0,r3 + stw r0,0(r7) + addi r7,r7,4 + bdnz 2b + mtlr r11 + blr + +/* + * identify_cpu, + * called with r3 = data offset and r4 = CPU number + * doesn't change r3 + */ +_GLOBAL(identify_cpu) + addis r8,r3,cpu_specs@ha + addi r8,r8,cpu_specs@l + mfpvr r7 +1: + lwz r5,CPU_SPEC_PVR_MASK(r8) + and r5,r5,r7 + lwz r6,CPU_SPEC_PVR_VALUE(r8) + cmplw 0,r6,r5 + beq 1f + addi r8,r8,CPU_SPEC_ENTRY_SIZE + b 1b +1: + addis r6,r3,cur_cpu_spec@ha + addi r6,r6,cur_cpu_spec@l + slwi r4,r4,2 + sub r8,r8,r3 + stwx r8,r4,r6 + blr + +/* + * do_cpu_ftr_fixups - goes through the list of CPU feature fixups + * and writes nop's over sections of code that don't apply for this cpu. + * r3 = data offset (not changed) + */ +_GLOBAL(do_cpu_ftr_fixups) + /* Get CPU 0 features */ + addis r6,r3,cur_cpu_spec@ha + addi r6,r6,cur_cpu_spec@l + lwz r4,0(r6) + add r4,r4,r3 + lwz r4,CPU_SPEC_FEATURES(r4) + + /* Get the fixup table */ + addis r6,r3,__start___ftr_fixup@ha + addi r6,r6,__start___ftr_fixup@l + addis r7,r3,__stop___ftr_fixup@ha + addi r7,r7,__stop___ftr_fixup@l + + /* Do the fixup */ +1: cmplw 0,r6,r7 + bgelr + addi r6,r6,16 + lwz r8,-16(r6) /* mask */ + and r8,r8,r4 + lwz r9,-12(r6) /* value */ + cmplw 0,r8,r9 + beq 1b + lwz r8,-8(r6) /* section begin */ + lwz r9,-4(r6) /* section end */ + subf. r9,r8,r9 + beq 1b + /* write nops over the section of code */ + /* todo: if large section, add a branch at the start of it */ + srwi r9,r9,2 + mtctr r9 + add r8,r8,r3 + lis r0,0x60000000@h /* nop */ +3: stw r0,0(r8) + andi. r10,r4,CPU_FTR_SPLIT_ID_CACHE@l + beq 2f + dcbst 0,r8 /* suboptimal, but simpler */ + sync + icbi 0,r8 +2: addi r8,r8,4 + bdnz 3b + sync /* additional sync needed on g4 */ + isync + b 1b + +/* + * call_setup_cpu - call the setup_cpu function for this cpu + * r3 = data offset, r24 = cpu number + * + * Setup function is called with: + * r3 = data offset + * r4 = CPU number + * r5 = ptr to CPU spec (relocated) + */ +_GLOBAL(call_setup_cpu) + addis r5,r3,cur_cpu_spec@ha + addi r5,r5,cur_cpu_spec@l + slwi r4,r24,2 + lwzx r5,r4,r5 + add r5,r5,r3 + lwz r6,CPU_SPEC_SETUP(r5) + add r6,r6,r3 + mtctr r6 + mr r4,r24 + bctr + +#if defined(CONFIG_CPU_FREQ_PMAC) && defined(CONFIG_6xx) + +/* This gets called by via-pmu.c to switch the PLL selection + * on 750fx CPU. This function should really be moved to some + * other place (as most of the cpufreq code in via-pmu + */ +_GLOBAL(low_choose_750fx_pll) + /* Clear MSR:EE */ + mfmsr r7 + rlwinm r0,r7,0,17,15 + mtmsr r0 + + /* If switching to PLL1, disable HID0:BTIC */ + cmplwi cr0,r3,0 + beq 1f + mfspr r5,SPRN_HID0 + rlwinm r5,r5,0,27,25 + sync + mtspr SPRN_HID0,r5 + isync + sync + +1: + /* Calc new HID1 value */ + mfspr r4,SPRN_HID1 /* Build a HID1:PS bit from parameter */ + rlwinm r5,r3,16,15,15 /* Clear out HID1:PS from value read */ + rlwinm r4,r4,0,16,14 /* Could have I used rlwimi here ? */ + or r4,r4,r5 + mtspr SPRN_HID1,r4 + + /* Store new HID1 image */ + rlwinm r6,r1,0,0,18 + lwz r6,TI_CPU(r6) + slwi r6,r6,2 + addis r6,r6,nap_save_hid1@ha + stw r4,nap_save_hid1@l(r6) + + /* If switching to PLL0, enable HID0:BTIC */ + cmplwi cr0,r3,0 + bne 1f + mfspr r5,SPRN_HID0 + ori r5,r5,HID0_BTIC + sync + mtspr SPRN_HID0,r5 + isync + sync + +1: + /* Return */ + mtmsr r7 + blr + +_GLOBAL(low_choose_7447a_dfs) + /* Clear MSR:EE */ + mfmsr r7 + rlwinm r0,r7,0,17,15 + mtmsr r0 + + /* Calc new HID1 value */ + mfspr r4,SPRN_HID1 + insrwi r4,r3,1,9 /* insert parameter into bit 9 */ + sync + mtspr SPRN_HID1,r4 + sync + isync + + /* Return */ + mtmsr r7 + blr + +#endif /* CONFIG_CPU_FREQ_PMAC && CONFIG_6xx */ + +/* void local_save_flags_ptr(unsigned long *flags) */ +_GLOBAL(local_save_flags_ptr) + mfmsr r4 + stw r4,0(r3) + blr + /* + * Need these nops here for taking over save/restore to + * handle lost intrs + * -- Cort + */ + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop +_GLOBAL(local_save_flags_ptr_end) + +/* void local_irq_restore(unsigned long flags) */ +_GLOBAL(local_irq_restore) +/* + * Just set/clear the MSR_EE bit through restore/flags but do not + * change anything else. This is needed by the RT system and makes + * sense anyway. + * -- Cort + */ + mfmsr r4 + /* Copy all except the MSR_EE bit from r4 (current MSR value) + to r3. This is the sort of thing the rlwimi instruction is + designed for. -- paulus. */ + rlwimi r3,r4,0,17,15 + /* Check if things are setup the way we want _already_. */ + cmpw 0,r3,r4 + beqlr +1: SYNC + mtmsr r3 + SYNC + blr + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop +_GLOBAL(local_irq_restore_end) + +_GLOBAL(local_irq_disable) + mfmsr r0 /* Get current interrupt state */ + rlwinm r3,r0,16+1,32-1,31 /* Extract old value of 'EE' */ + rlwinm r0,r0,0,17,15 /* clear MSR_EE in r0 */ + SYNC /* Some chip revs have problems here... */ + mtmsr r0 /* Update machine state */ + blr /* Done */ + /* + * Need these nops here for taking over save/restore to + * handle lost intrs + * -- Cort + */ + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop +_GLOBAL(local_irq_disable_end) + +_GLOBAL(local_irq_enable) + mfmsr r3 /* Get current state */ + ori r3,r3,MSR_EE /* Turn on 'EE' bit */ + SYNC /* Some chip revs have problems here... */ + mtmsr r3 /* Update machine state */ + blr + /* + * Need these nops here for taking over save/restore to + * handle lost intrs + * -- Cort + */ + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop +_GLOBAL(local_irq_enable_end) + +/* + * complement mask on the msr then "or" some values on. + * _nmask_and_or_msr(nmask, value_to_or) + */ +_GLOBAL(_nmask_and_or_msr) + mfmsr r0 /* Get current msr */ + andc r0,r0,r3 /* And off the bits set in r3 (first parm) */ + or r0,r0,r4 /* Or on the bits in r4 (second parm) */ + SYNC /* Some chip revs have problems here... */ + mtmsr r0 /* Update machine state */ + isync + blr /* Done */ + + +/* + * Flush MMU TLB + */ +_GLOBAL(_tlbia) +#if defined(CONFIG_40x) + sync /* Flush to memory before changing mapping */ + tlbia + isync /* Flush shadow TLB */ +#elif defined(CONFIG_44x) + li r3,0 + sync + + /* Load high watermark */ + lis r4,tlb_44x_hwater@ha + lwz r5,tlb_44x_hwater@l(r4) + +1: tlbwe r3,r3,PPC44x_TLB_PAGEID + addi r3,r3,1 + cmpw 0,r3,r5 + ble 1b + + isync +#elif defined(CONFIG_FSL_BOOKE) + /* Invalidate all entries in TLB0 */ + li r3, 0x04 + tlbivax 0,3 + /* Invalidate all entries in TLB1 */ + li r3, 0x0c + tlbivax 0,3 + /* Invalidate all entries in TLB2 */ + li r3, 0x14 + tlbivax 0,3 + /* Invalidate all entries in TLB3 */ + li r3, 0x1c + tlbivax 0,3 + msync +#ifdef CONFIG_SMP + tlbsync +#endif /* CONFIG_SMP */ +#else /* !(CONFIG_40x || CONFIG_44x || CONFIG_FSL_BOOKE) */ +#if defined(CONFIG_SMP) + rlwinm r8,r1,0,0,18 + lwz r8,TI_CPU(r8) + oris r8,r8,10 + mfmsr r10 + SYNC + rlwinm r0,r10,0,17,15 /* clear bit 16 (MSR_EE) */ + rlwinm r0,r0,0,28,26 /* clear DR */ + mtmsr r0 + SYNC_601 + isync + lis r9,mmu_hash_lock@h + ori r9,r9,mmu_hash_lock@l + tophys(r9,r9) +10: lwarx r7,0,r9 + cmpwi 0,r7,0 + bne- 10b + stwcx. r8,0,r9 + bne- 10b + sync + tlbia + sync + TLBSYNC + li r0,0 + stw r0,0(r9) /* clear mmu_hash_lock */ + mtmsr r10 + SYNC_601 + isync +#else /* CONFIG_SMP */ + sync + tlbia + sync +#endif /* CONFIG_SMP */ +#endif /* ! defined(CONFIG_40x) */ + blr + +/* + * Flush MMU TLB for a particular address + */ +_GLOBAL(_tlbie) +#if defined(CONFIG_40x) + tlbsx. r3, 0, r3 + bne 10f + sync + /* There are only 64 TLB entries, so r3 < 64, which means bit 25 is clear. + * Since 25 is the V bit in the TLB_TAG, loading this value will invalidate + * the TLB entry. */ + tlbwe r3, r3, TLB_TAG + isync +10: +#elif defined(CONFIG_44x) + mfspr r4,SPRN_MMUCR + mfspr r5,SPRN_PID /* Get PID */ + rlwimi r4,r5,0,24,31 /* Set TID */ + mtspr SPRN_MMUCR,r4 + + tlbsx. r3, 0, r3 + bne 10f + sync + /* There are only 64 TLB entries, so r3 < 64, + * which means bit 22, is clear. Since 22 is + * the V bit in the TLB_PAGEID, loading this + * value will invalidate the TLB entry. + */ + tlbwe r3, r3, PPC44x_TLB_PAGEID + isync +10: +#elif defined(CONFIG_FSL_BOOKE) + rlwinm r4, r3, 0, 0, 19 + ori r5, r4, 0x08 /* TLBSEL = 1 */ + ori r6, r4, 0x10 /* TLBSEL = 2 */ + ori r7, r4, 0x18 /* TLBSEL = 3 */ + tlbivax 0, r4 + tlbivax 0, r5 + tlbivax 0, r6 + tlbivax 0, r7 + msync +#if defined(CONFIG_SMP) + tlbsync +#endif /* CONFIG_SMP */ +#else /* !(CONFIG_40x || CONFIG_44x || CONFIG_FSL_BOOKE) */ +#if defined(CONFIG_SMP) + rlwinm r8,r1,0,0,18 + lwz r8,TI_CPU(r8) + oris r8,r8,11 + mfmsr r10 + SYNC + rlwinm r0,r10,0,17,15 /* clear bit 16 (MSR_EE) */ + rlwinm r0,r0,0,28,26 /* clear DR */ + mtmsr r0 + SYNC_601 + isync + lis r9,mmu_hash_lock@h + ori r9,r9,mmu_hash_lock@l + tophys(r9,r9) +10: lwarx r7,0,r9 + cmpwi 0,r7,0 + bne- 10b + stwcx. r8,0,r9 + bne- 10b + eieio + tlbie r3 + sync + TLBSYNC + li r0,0 + stw r0,0(r9) /* clear mmu_hash_lock */ + mtmsr r10 + SYNC_601 + isync +#else /* CONFIG_SMP */ + tlbie r3 + sync +#endif /* CONFIG_SMP */ +#endif /* ! CONFIG_40x */ + blr + +/* + * Flush instruction cache. + * This is a no-op on the 601. + */ +_GLOBAL(flush_instruction_cache) +#if defined(CONFIG_8xx) + isync + lis r5, IDC_INVALL@h + mtspr SPRN_IC_CST, r5 +#elif defined(CONFIG_4xx) +#ifdef CONFIG_403GCX + li r3, 512 + mtctr r3 + lis r4, KERNELBASE@h +1: iccci 0, r4 + addi r4, r4, 16 + bdnz 1b +#else + lis r3, KERNELBASE@h + iccci 0,r3 +#endif +#elif CONFIG_FSL_BOOKE + mfspr r3,SPRN_L1CSR1 + ori r3,r3,L1CSR1_ICFI|L1CSR1_ICLFR + mtspr SPRN_L1CSR1,r3 +#else + mfspr r3,SPRN_PVR + rlwinm r3,r3,16,16,31 + cmpwi 0,r3,1 + beqlr /* for 601, do nothing */ + /* 603/604 processor - use invalidate-all bit in HID0 */ + mfspr r3,SPRN_HID0 + ori r3,r3,HID0_ICFI + mtspr SPRN_HID0,r3 +#endif /* CONFIG_8xx/4xx */ + isync + blr + +/* + * Write any modified data cache blocks out to memory + * and invalidate the corresponding instruction cache blocks. + * This is a no-op on the 601. + * + * flush_icache_range(unsigned long start, unsigned long stop) + */ +_GLOBAL(flush_icache_range) +BEGIN_FTR_SECTION + blr /* for 601, do nothing */ +END_FTR_SECTION_IFSET(PPC_FEATURE_UNIFIED_CACHE) + li r5,L1_CACHE_LINE_SIZE-1 + andc r3,r3,r5 + subf r4,r3,r4 + add r4,r4,r5 + srwi. r4,r4,LG_L1_CACHE_LINE_SIZE + beqlr + mtctr r4 + mr r6,r3 +1: dcbst 0,r3 + addi r3,r3,L1_CACHE_LINE_SIZE + bdnz 1b + sync /* wait for dcbst's to get to ram */ + mtctr r4 +2: icbi 0,r6 + addi r6,r6,L1_CACHE_LINE_SIZE + bdnz 2b + sync /* additional sync needed on g4 */ + isync + blr +/* + * Write any modified data cache blocks out to memory. + * Does not invalidate the corresponding cache lines (especially for + * any corresponding instruction cache). + * + * clean_dcache_range(unsigned long start, unsigned long stop) + */ +_GLOBAL(clean_dcache_range) + li r5,L1_CACHE_LINE_SIZE-1 + andc r3,r3,r5 + subf r4,r3,r4 + add r4,r4,r5 + srwi. r4,r4,LG_L1_CACHE_LINE_SIZE + beqlr + mtctr r4 + +1: dcbst 0,r3 + addi r3,r3,L1_CACHE_LINE_SIZE + bdnz 1b + sync /* wait for dcbst's to get to ram */ + blr + +/* + * Write any modified data cache blocks out to memory and invalidate them. + * Does not invalidate the corresponding instruction cache blocks. + * + * flush_dcache_range(unsigned long start, unsigned long stop) + */ +_GLOBAL(flush_dcache_range) + li r5,L1_CACHE_LINE_SIZE-1 + andc r3,r3,r5 + subf r4,r3,r4 + add r4,r4,r5 + srwi. r4,r4,LG_L1_CACHE_LINE_SIZE + beqlr + mtctr r4 + +1: dcbf 0,r3 + addi r3,r3,L1_CACHE_LINE_SIZE + bdnz 1b + sync /* wait for dcbst's to get to ram */ + blr + +/* + * Like above, but invalidate the D-cache. This is used by the 8xx + * to invalidate the cache so the PPC core doesn't get stale data + * from the CPM (no cache snooping here :-). + * + * invalidate_dcache_range(unsigned long start, unsigned long stop) + */ +_GLOBAL(invalidate_dcache_range) + li r5,L1_CACHE_LINE_SIZE-1 + andc r3,r3,r5 + subf r4,r3,r4 + add r4,r4,r5 + srwi. r4,r4,LG_L1_CACHE_LINE_SIZE + beqlr + mtctr r4 + +1: dcbi 0,r3 + addi r3,r3,L1_CACHE_LINE_SIZE + bdnz 1b + sync /* wait for dcbi's to get to ram */ + blr + +#ifdef CONFIG_NOT_COHERENT_CACHE +/* + * 40x cores have 8K or 16K dcache and 32 byte line size. + * 44x has a 32K dcache and 32 byte line size. + * 8xx has 1, 2, 4, 8K variants. + * For now, cover the worst case of the 44x. + * Must be called with external interrupts disabled. + */ +#define CACHE_NWAYS 64 +#define CACHE_NLINES 16 + +_GLOBAL(flush_dcache_all) + li r4, (2 * CACHE_NWAYS * CACHE_NLINES) + mtctr r4 + lis r5, KERNELBASE@h +1: lwz r3, 0(r5) /* Load one word from every line */ + addi r5, r5, L1_CACHE_LINE_SIZE + bdnz 1b + blr +#endif /* CONFIG_NOT_COHERENT_CACHE */ + +/* + * Flush a particular page from the data cache to RAM. + * Note: this is necessary because the instruction cache does *not* + * snoop from the data cache. + * This is a no-op on the 601 which has a unified cache. + * + * void __flush_dcache_icache(void *page) + */ +_GLOBAL(__flush_dcache_icache) +BEGIN_FTR_SECTION + blr /* for 601, do nothing */ +END_FTR_SECTION_IFSET(PPC_FEATURE_UNIFIED_CACHE) + rlwinm r3,r3,0,0,19 /* Get page base address */ + li r4,4096/L1_CACHE_LINE_SIZE /* Number of lines in a page */ + mtctr r4 + mr r6,r3 +0: dcbst 0,r3 /* Write line to ram */ + addi r3,r3,L1_CACHE_LINE_SIZE + bdnz 0b + sync + mtctr r4 +1: icbi 0,r6 + addi r6,r6,L1_CACHE_LINE_SIZE + bdnz 1b + sync + isync + blr + +/* + * Flush a particular page from the data cache to RAM, identified + * by its physical address. We turn off the MMU so we can just use + * the physical address (this may be a highmem page without a kernel + * mapping). + * + * void __flush_dcache_icache_phys(unsigned long physaddr) + */ +_GLOBAL(__flush_dcache_icache_phys) +BEGIN_FTR_SECTION + blr /* for 601, do nothing */ +END_FTR_SECTION_IFSET(PPC_FEATURE_UNIFIED_CACHE) + mfmsr r10 + rlwinm r0,r10,0,28,26 /* clear DR */ + mtmsr r0 + isync + rlwinm r3,r3,0,0,19 /* Get page base address */ + li r4,4096/L1_CACHE_LINE_SIZE /* Number of lines in a page */ + mtctr r4 + mr r6,r3 +0: dcbst 0,r3 /* Write line to ram */ + addi r3,r3,L1_CACHE_LINE_SIZE + bdnz 0b + sync + mtctr r4 +1: icbi 0,r6 + addi r6,r6,L1_CACHE_LINE_SIZE + bdnz 1b + sync + mtmsr r10 /* restore DR */ + isync + blr + +/* + * Clear pages using the dcbz instruction, which doesn't cause any + * memory traffic (except to write out any cache lines which get + * displaced). This only works on cacheable memory. + * + * void clear_pages(void *page, int order) ; + */ +_GLOBAL(clear_pages) + li r0,4096/L1_CACHE_LINE_SIZE + slw r0,r0,r4 + mtctr r0 +#ifdef CONFIG_8xx + li r4, 0 +1: stw r4, 0(r3) + stw r4, 4(r3) + stw r4, 8(r3) + stw r4, 12(r3) +#else +1: dcbz 0,r3 +#endif + addi r3,r3,L1_CACHE_LINE_SIZE + bdnz 1b + blr + +/* + * Copy a whole page. We use the dcbz instruction on the destination + * to reduce memory traffic (it eliminates the unnecessary reads of + * the destination into cache). This requires that the destination + * is cacheable. + */ +#define COPY_16_BYTES \ + lwz r6,4(r4); \ + lwz r7,8(r4); \ + lwz r8,12(r4); \ + lwzu r9,16(r4); \ + stw r6,4(r3); \ + stw r7,8(r3); \ + stw r8,12(r3); \ + stwu r9,16(r3) + +_GLOBAL(copy_page) + addi r3,r3,-4 + addi r4,r4,-4 + +#ifdef CONFIG_8xx + /* don't use prefetch on 8xx */ + li r0,4096/L1_CACHE_LINE_SIZE + mtctr r0 +1: COPY_16_BYTES + bdnz 1b + blr + +#else /* not 8xx, we can prefetch */ + li r5,4 + +#if MAX_COPY_PREFETCH > 1 + li r0,MAX_COPY_PREFETCH + li r11,4 + mtctr r0 +11: dcbt r11,r4 + addi r11,r11,L1_CACHE_LINE_SIZE + bdnz 11b +#else /* MAX_COPY_PREFETCH == 1 */ + dcbt r5,r4 + li r11,L1_CACHE_LINE_SIZE+4 +#endif /* MAX_COPY_PREFETCH */ + li r0,4096/L1_CACHE_LINE_SIZE - MAX_COPY_PREFETCH + crclr 4*cr0+eq +2: + mtctr r0 +1: + dcbt r11,r4 + dcbz r5,r3 + COPY_16_BYTES +#if L1_CACHE_LINE_SIZE >= 32 + COPY_16_BYTES +#if L1_CACHE_LINE_SIZE >= 64 + COPY_16_BYTES + COPY_16_BYTES +#if L1_CACHE_LINE_SIZE >= 128 + COPY_16_BYTES + COPY_16_BYTES + COPY_16_BYTES + COPY_16_BYTES +#endif +#endif +#endif + bdnz 1b + beqlr + crnot 4*cr0+eq,4*cr0+eq + li r0,MAX_COPY_PREFETCH + li r11,4 + b 2b +#endif /* CONFIG_8xx */ + +/* + * void atomic_clear_mask(atomic_t mask, atomic_t *addr) + * void atomic_set_mask(atomic_t mask, atomic_t *addr); + */ +_GLOBAL(atomic_clear_mask) +10: lwarx r5,0,r4 + andc r5,r5,r3 + PPC405_ERR77(0,r4) + stwcx. r5,0,r4 + bne- 10b + blr +_GLOBAL(atomic_set_mask) +10: lwarx r5,0,r4 + or r5,r5,r3 + PPC405_ERR77(0,r4) + stwcx. r5,0,r4 + bne- 10b + blr + +/* + * I/O string operations + * + * insb(port, buf, len) + * outsb(port, buf, len) + * insw(port, buf, len) + * outsw(port, buf, len) + * insl(port, buf, len) + * outsl(port, buf, len) + * insw_ns(port, buf, len) + * outsw_ns(port, buf, len) + * insl_ns(port, buf, len) + * outsl_ns(port, buf, len) + * + * The *_ns versions don't do byte-swapping. + */ +_GLOBAL(_insb) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,1 + blelr- +00: lbz r5,0(r3) + eieio + stbu r5,1(r4) + bdnz 00b + blr + +_GLOBAL(_outsb) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,1 + blelr- +00: lbzu r5,1(r4) + stb r5,0(r3) + eieio + bdnz 00b + blr + +_GLOBAL(_insw) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,2 + blelr- +00: lhbrx r5,0,r3 + eieio + sthu r5,2(r4) + bdnz 00b + blr + +_GLOBAL(_outsw) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,2 + blelr- +00: lhzu r5,2(r4) + eieio + sthbrx r5,0,r3 + bdnz 00b + blr + +_GLOBAL(_insl) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,4 + blelr- +00: lwbrx r5,0,r3 + eieio + stwu r5,4(r4) + bdnz 00b + blr + +_GLOBAL(_outsl) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,4 + blelr- +00: lwzu r5,4(r4) + stwbrx r5,0,r3 + eieio + bdnz 00b + blr + +_GLOBAL(__ide_mm_insw) +_GLOBAL(_insw_ns) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,2 + blelr- +00: lhz r5,0(r3) + eieio + sthu r5,2(r4) + bdnz 00b + blr + +_GLOBAL(__ide_mm_outsw) +_GLOBAL(_outsw_ns) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,2 + blelr- +00: lhzu r5,2(r4) + sth r5,0(r3) + eieio + bdnz 00b + blr + +_GLOBAL(__ide_mm_insl) +_GLOBAL(_insl_ns) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,4 + blelr- +00: lwz r5,0(r3) + eieio + stwu r5,4(r4) + bdnz 00b + blr + +_GLOBAL(__ide_mm_outsl) +_GLOBAL(_outsl_ns) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,4 + blelr- +00: lwzu r5,4(r4) + stw r5,0(r3) + eieio + bdnz 00b + blr + +/* + * Extended precision shifts. + * + * Updated to be valid for shift counts from 0 to 63 inclusive. + * -- Gabriel + * + * R3/R4 has 64 bit value + * R5 has shift count + * result in R3/R4 + * + * ashrdi3: arithmetic right shift (sign propagation) + * lshrdi3: logical right shift + * ashldi3: left shift + */ +_GLOBAL(__ashrdi3) + subfic r6,r5,32 + srw r4,r4,r5 # LSW = count > 31 ? 0 : LSW >> count + addi r7,r5,32 # could be xori, or addi with -32 + slw r6,r3,r6 # t1 = count > 31 ? 0 : MSW << (32-count) + rlwinm r8,r7,0,32 # t3 = (count < 32) ? 32 : 0 + sraw r7,r3,r7 # t2 = MSW >> (count-32) + or r4,r4,r6 # LSW |= t1 + slw r7,r7,r8 # t2 = (count < 32) ? 0 : t2 + sraw r3,r3,r5 # MSW = MSW >> count + or r4,r4,r7 # LSW |= t2 + blr + +_GLOBAL(__ashldi3) + subfic r6,r5,32 + slw r3,r3,r5 # MSW = count > 31 ? 0 : MSW << count + addi r7,r5,32 # could be xori, or addi with -32 + srw r6,r4,r6 # t1 = count > 31 ? 0 : LSW >> (32-count) + slw r7,r4,r7 # t2 = count < 32 ? 0 : LSW << (count-32) + or r3,r3,r6 # MSW |= t1 + slw r4,r4,r5 # LSW = LSW << count + or r3,r3,r7 # MSW |= t2 + blr + +_GLOBAL(__lshrdi3) + subfic r6,r5,32 + srw r4,r4,r5 # LSW = count > 31 ? 0 : LSW >> count + addi r7,r5,32 # could be xori, or addi with -32 + slw r6,r3,r6 # t1 = count > 31 ? 0 : MSW << (32-count) + srw r7,r3,r7 # t2 = count < 32 ? 0 : MSW >> (count-32) + or r4,r4,r6 # LSW |= t1 + srw r3,r3,r5 # MSW = MSW >> count + or r4,r4,r7 # LSW |= t2 + blr + +_GLOBAL(abs) + srawi r4,r3,31 + xor r3,r3,r4 + sub r3,r3,r4 + blr + +_GLOBAL(_get_SP) + mr r3,r1 /* Close enough */ + blr + +/* + * These are used in the alignment trap handler when emulating + * single-precision loads and stores. + * We restore and save the fpscr so the task gets the same result + * and exceptions as if the cpu had performed the load or store. + */ + +#if defined(CONFIG_4xx) || defined(CONFIG_E500) +_GLOBAL(cvt_fd) + lfs 0,0(r3) + stfd 0,0(r4) + blr + +_GLOBAL(cvt_df) + lfd 0,0(r3) + stfs 0,0(r4) + blr +#else +_GLOBAL(cvt_fd) + lfd 0,-4(r5) /* load up fpscr value */ + mtfsf 0xff,0 + lfs 0,0(r3) + stfd 0,0(r4) + mffs 0 /* save new fpscr value */ + stfd 0,-4(r5) + blr + +_GLOBAL(cvt_df) + lfd 0,-4(r5) /* load up fpscr value */ + mtfsf 0xff,0 + lfd 0,0(r3) + stfs 0,0(r4) + mffs 0 /* save new fpscr value */ + stfd 0,-4(r5) + blr +#endif + +/* + * Create a kernel thread + * kernel_thread(fn, arg, flags) + */ +_GLOBAL(kernel_thread) + stwu r1,-16(r1) + stw r30,8(r1) + stw r31,12(r1) + mr r30,r3 /* function */ + mr r31,r4 /* argument */ + ori r3,r5,CLONE_VM /* flags */ + oris r3,r3,CLONE_UNTRACED>>16 + li r4,0 /* new sp (unused) */ + li r0,__NR_clone + sc + cmpwi 0,r3,0 /* parent or child? */ + bne 1f /* return if parent */ + li r0,0 /* make top-level stack frame */ + stwu r0,-16(r1) + mtlr r30 /* fn addr in lr */ + mr r3,r31 /* load arg and call fn */ + blrl + li r0,__NR_exit /* exit if function returns */ + li r3,0 + sc +1: lwz r30,8(r1) + lwz r31,12(r1) + addi r1,r1,16 + blr + +/* + * This routine is just here to keep GCC happy - sigh... + */ +_GLOBAL(__main) + blr + +#define SYSCALL(name) \ +_GLOBAL(name) \ + li r0,__NR_##name; \ + sc; \ + bnslr; \ + lis r4,errno@ha; \ + stw r3,errno@l(r4); \ + li r3,-1; \ + blr + +SYSCALL(execve) + +/* Why isn't this a) automatic, b) written in 'C'? */ + .data + .align 4 +_GLOBAL(sys_call_table) + .long sys_restart_syscall /* 0 */ + .long sys_exit + .long ppc_fork + .long sys_read + .long sys_write + .long sys_open /* 5 */ + .long sys_close + .long sys_waitpid + .long sys_creat + .long sys_link + .long sys_unlink /* 10 */ + .long sys_execve + .long sys_chdir + .long sys_time + .long sys_mknod + .long sys_chmod /* 15 */ + .long sys_lchown + .long sys_ni_syscall /* old break syscall holder */ + .long sys_stat + .long sys_lseek + .long sys_getpid /* 20 */ + .long sys_mount + .long sys_oldumount + .long sys_setuid + .long sys_getuid + .long sys_stime /* 25 */ + .long sys_ptrace + .long sys_alarm + .long sys_fstat + .long sys_pause + .long sys_utime /* 30 */ + .long sys_ni_syscall /* old stty syscall holder */ + .long sys_ni_syscall /* old gtty syscall holder */ + .long sys_access + .long sys_nice + .long sys_ni_syscall /* 35 */ /* old ftime syscall holder */ + .long sys_sync + .long sys_kill + .long sys_rename + .long sys_mkdir + .long sys_rmdir /* 40 */ + .long sys_dup + .long sys_pipe + .long sys_times + .long sys_ni_syscall /* old prof syscall holder */ + .long sys_brk /* 45 */ + .long sys_setgid + .long sys_getgid + .long sys_signal + .long sys_geteuid + .long sys_getegid /* 50 */ + .long sys_acct + .long sys_umount /* recycled never used phys() */ + .long sys_ni_syscall /* old lock syscall holder */ + .long sys_ioctl + .long sys_fcntl /* 55 */ + .long sys_ni_syscall /* old mpx syscall holder */ + .long sys_setpgid + .long sys_ni_syscall /* old ulimit syscall holder */ + .long sys_olduname + .long sys_umask /* 60 */ + .long sys_chroot + .long sys_ustat + .long sys_dup2 + .long sys_getppid + .long sys_getpgrp /* 65 */ + .long sys_setsid + .long sys_sigaction + .long sys_sgetmask + .long sys_ssetmask + .long sys_setreuid /* 70 */ + .long sys_setregid + .long ppc_sigsuspend + .long sys_sigpending + .long sys_sethostname + .long sys_setrlimit /* 75 */ + .long sys_old_getrlimit + .long sys_getrusage + .long sys_gettimeofday + .long sys_settimeofday + .long sys_getgroups /* 80 */ + .long sys_setgroups + .long ppc_select + .long sys_symlink + .long sys_lstat + .long sys_readlink /* 85 */ + .long sys_uselib + .long sys_swapon + .long sys_reboot + .long old_readdir + .long sys_mmap /* 90 */ + .long sys_munmap + .long sys_truncate + .long sys_ftruncate + .long sys_fchmod + .long sys_fchown /* 95 */ + .long sys_getpriority + .long sys_setpriority + .long sys_ni_syscall /* old profil syscall holder */ + .long sys_statfs + .long sys_fstatfs /* 100 */ + .long sys_ni_syscall + .long sys_socketcall + .long sys_syslog + .long sys_setitimer + .long sys_getitimer /* 105 */ + .long sys_newstat + .long sys_newlstat + .long sys_newfstat + .long sys_uname + .long sys_ni_syscall /* 110 */ + .long sys_vhangup + .long sys_ni_syscall /* old 'idle' syscall */ + .long sys_ni_syscall + .long sys_wait4 + .long sys_swapoff /* 115 */ + .long sys_sysinfo + .long sys_ipc + .long sys_fsync + .long sys_sigreturn + .long ppc_clone /* 120 */ + .long sys_setdomainname + .long sys_newuname + .long sys_ni_syscall + .long sys_adjtimex + .long sys_mprotect /* 125 */ + .long sys_sigprocmask + .long sys_ni_syscall /* old sys_create_module */ + .long sys_init_module + .long sys_delete_module + .long sys_ni_syscall /* old sys_get_kernel_syms */ /* 130 */ + .long sys_quotactl + .long sys_getpgid + .long sys_fchdir + .long sys_bdflush + .long sys_sysfs /* 135 */ + .long sys_personality + .long sys_ni_syscall /* for afs_syscall */ + .long sys_setfsuid + .long sys_setfsgid + .long sys_llseek /* 140 */ + .long sys_getdents + .long ppc_select + .long sys_flock + .long sys_msync + .long sys_readv /* 145 */ + .long sys_writev + .long sys_getsid + .long sys_fdatasync + .long sys_sysctl + .long sys_mlock /* 150 */ + .long sys_munlock + .long sys_mlockall + .long sys_munlockall + .long sys_sched_setparam + .long sys_sched_getparam /* 155 */ + .long sys_sched_setscheduler + .long sys_sched_getscheduler + .long sys_sched_yield + .long sys_sched_get_priority_max + .long sys_sched_get_priority_min /* 160 */ + .long sys_sched_rr_get_interval + .long sys_nanosleep + .long sys_mremap + .long sys_setresuid + .long sys_getresuid /* 165 */ + .long sys_ni_syscall /* old sys_query_module */ + .long sys_poll + .long sys_nfsservctl + .long sys_setresgid + .long sys_getresgid /* 170 */ + .long sys_prctl + .long sys_rt_sigreturn + .long sys_rt_sigaction + .long sys_rt_sigprocmask + .long sys_rt_sigpending /* 175 */ + .long sys_rt_sigtimedwait + .long sys_rt_sigqueueinfo + .long ppc_rt_sigsuspend + .long sys_pread64 + .long sys_pwrite64 /* 180 */ + .long sys_chown + .long sys_getcwd + .long sys_capget + .long sys_capset + .long sys_sigaltstack /* 185 */ + .long sys_sendfile + .long sys_ni_syscall /* streams1 */ + .long sys_ni_syscall /* streams2 */ + .long ppc_vfork + .long sys_getrlimit /* 190 */ + .long sys_readahead + .long sys_mmap2 + .long sys_truncate64 + .long sys_ftruncate64 + .long sys_stat64 /* 195 */ + .long sys_lstat64 + .long sys_fstat64 + .long sys_pciconfig_read + .long sys_pciconfig_write + .long sys_pciconfig_iobase /* 200 */ + .long sys_ni_syscall /* 201 - reserved - MacOnLinux - new */ + .long sys_getdents64 + .long sys_pivot_root + .long sys_fcntl64 + .long sys_madvise /* 205 */ + .long sys_mincore + .long sys_gettid + .long sys_tkill + .long sys_setxattr + .long sys_lsetxattr /* 210 */ + .long sys_fsetxattr + .long sys_getxattr + .long sys_lgetxattr + .long sys_fgetxattr + .long sys_listxattr /* 215 */ + .long sys_llistxattr + .long sys_flistxattr + .long sys_removexattr + .long sys_lremovexattr + .long sys_fremovexattr /* 220 */ + .long sys_futex + .long sys_sched_setaffinity + .long sys_sched_getaffinity + .long sys_ni_syscall + .long sys_ni_syscall /* 225 - reserved for Tux */ + .long sys_sendfile64 + .long sys_io_setup + .long sys_io_destroy + .long sys_io_getevents + .long sys_io_submit /* 230 */ + .long sys_io_cancel + .long sys_set_tid_address + .long sys_fadvise64 + .long sys_exit_group + .long sys_lookup_dcookie /* 235 */ + .long sys_epoll_create + .long sys_epoll_ctl + .long sys_epoll_wait + .long sys_remap_file_pages + .long sys_timer_create /* 240 */ + .long sys_timer_settime + .long sys_timer_gettime + .long sys_timer_getoverrun + .long sys_timer_delete + .long sys_clock_settime /* 245 */ + .long sys_clock_gettime + .long sys_clock_getres + .long sys_clock_nanosleep + .long ppc_swapcontext + .long sys_tgkill /* 250 */ + .long sys_utimes + .long sys_statfs64 + .long sys_fstatfs64 + .long ppc_fadvise64_64 + .long sys_ni_syscall /* 255 - rtas (used on ppc64) */ + .long sys_debug_setcontext + .long sys_ni_syscall /* 257 reserved for vserver */ + .long sys_ni_syscall /* 258 reserved for new sys_remap_file_pages */ + .long sys_ni_syscall /* 259 reserved for new sys_mbind */ + .long sys_ni_syscall /* 260 reserved for new sys_get_mempolicy */ + .long sys_ni_syscall /* 261 reserved for new sys_set_mempolicy */ + .long sys_mq_open + .long sys_mq_unlink + .long sys_mq_timedsend + .long sys_mq_timedreceive /* 265 */ + .long sys_mq_notify + .long sys_mq_getsetattr + .long sys_ni_syscall /* 268 reserved for sys_kexec_load */ + .long sys_add_key + .long sys_request_key /* 270 */ + .long sys_keyctl + .long sys_waitid diff --git a/arch/ppc/kernel/module.c b/arch/ppc/kernel/module.c new file mode 100644 index 00000000000..92f4e5f64f0 --- /dev/null +++ b/arch/ppc/kernel/module.c @@ -0,0 +1,320 @@ +/* Kernel module help for PPC. + Copyright (C) 2001 Rusty Russell. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(fmt , ...) +#endif + +LIST_HEAD(module_bug_list); + +void *module_alloc(unsigned long size) +{ + if (size == 0) + return NULL; + return vmalloc(size); +} + +/* Free memory returned from module_alloc */ +void module_free(struct module *mod, void *module_region) +{ + vfree(module_region); + /* FIXME: If module_region == mod->init_region, trim exception + table entries. */ +} + +/* Count how many different relocations (different symbol, different + addend) */ +static unsigned int count_relocs(const Elf32_Rela *rela, unsigned int num) +{ + unsigned int i, j, ret = 0; + + /* Sure, this is order(n^2), but it's usually short, and not + time critical */ + for (i = 0; i < num; i++) { + for (j = 0; j < i; j++) { + /* If this addend appeared before, it's + already been counted */ + if (ELF32_R_SYM(rela[i].r_info) + == ELF32_R_SYM(rela[j].r_info) + && rela[i].r_addend == rela[j].r_addend) + break; + } + if (j == i) ret++; + } + return ret; +} + +/* Get the potential trampolines size required of the init and + non-init sections */ +static unsigned long get_plt_size(const Elf32_Ehdr *hdr, + const Elf32_Shdr *sechdrs, + const char *secstrings, + int is_init) +{ + unsigned long ret = 0; + unsigned i; + + /* Everything marked ALLOC (this includes the exported + symbols) */ + for (i = 1; i < hdr->e_shnum; i++) { + /* If it's called *.init*, and we're not init, we're + not interested */ + if ((strstr(secstrings + sechdrs[i].sh_name, ".init") != 0) + != is_init) + continue; + + /* We don't want to look at debug sections. */ + if (strstr(secstrings + sechdrs[i].sh_name, ".debug") != 0) + continue; + + if (sechdrs[i].sh_type == SHT_RELA) { + DEBUGP("Found relocations in section %u\n", i); + DEBUGP("Ptr: %p. Number: %u\n", + (void *)hdr + sechdrs[i].sh_offset, + sechdrs[i].sh_size / sizeof(Elf32_Rela)); + ret += count_relocs((void *)hdr + + sechdrs[i].sh_offset, + sechdrs[i].sh_size + / sizeof(Elf32_Rela)) + * sizeof(struct ppc_plt_entry); + } + } + + return ret; +} + +int module_frob_arch_sections(Elf32_Ehdr *hdr, + Elf32_Shdr *sechdrs, + char *secstrings, + struct module *me) +{ + unsigned int i; + + /* Find .plt and .init.plt sections */ + for (i = 0; i < hdr->e_shnum; i++) { + if (strcmp(secstrings + sechdrs[i].sh_name, ".init.plt") == 0) + me->arch.init_plt_section = i; + else if (strcmp(secstrings + sechdrs[i].sh_name, ".plt") == 0) + me->arch.core_plt_section = i; + } + if (!me->arch.core_plt_section || !me->arch.init_plt_section) { + printk("Module doesn't contain .plt or .init.plt sections.\n"); + return -ENOEXEC; + } + + /* Override their sizes */ + sechdrs[me->arch.core_plt_section].sh_size + = get_plt_size(hdr, sechdrs, secstrings, 0); + sechdrs[me->arch.init_plt_section].sh_size + = get_plt_size(hdr, sechdrs, secstrings, 1); + return 0; +} + +int apply_relocate(Elf32_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *module) +{ + printk(KERN_ERR "%s: Non-ADD RELOCATION unsupported\n", + module->name); + return -ENOEXEC; +} + +static inline int entry_matches(struct ppc_plt_entry *entry, Elf32_Addr val) +{ + if (entry->jump[0] == 0x3d600000 + ((val + 0x8000) >> 16) + && entry->jump[1] == 0x396b0000 + (val & 0xffff)) + return 1; + return 0; +} + +/* Set up a trampoline in the PLT to bounce us to the distant function */ +static uint32_t do_plt_call(void *location, + Elf32_Addr val, + Elf32_Shdr *sechdrs, + struct module *mod) +{ + struct ppc_plt_entry *entry; + + DEBUGP("Doing plt for call to 0x%x at 0x%x\n", val, (unsigned int)location); + /* Init, or core PLT? */ + if (location >= mod->module_core + && location < mod->module_core + mod->core_size) + entry = (void *)sechdrs[mod->arch.core_plt_section].sh_addr; + else + entry = (void *)sechdrs[mod->arch.init_plt_section].sh_addr; + + /* Find this entry, or if that fails, the next avail. entry */ + while (entry->jump[0]) { + if (entry_matches(entry, val)) return (uint32_t)entry; + entry++; + } + + /* Stolen from Paul Mackerras as well... */ + entry->jump[0] = 0x3d600000+((val+0x8000)>>16); /* lis r11,sym@ha */ + entry->jump[1] = 0x396b0000 + (val&0xffff); /* addi r11,r11,sym@l*/ + entry->jump[2] = 0x7d6903a6; /* mtctr r11 */ + entry->jump[3] = 0x4e800420; /* bctr */ + + DEBUGP("Initialized plt for 0x%x at %p\n", val, entry); + return (uint32_t)entry; +} + +int apply_relocate_add(Elf32_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *module) +{ + unsigned int i; + Elf32_Rela *rela = (void *)sechdrs[relsec].sh_addr; + Elf32_Sym *sym; + uint32_t *location; + uint32_t value; + + DEBUGP("Applying ADD relocate section %u to %u\n", relsec, + sechdrs[relsec].sh_info); + for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) { + /* This is where to make the change */ + location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr + + rela[i].r_offset; + /* This is the symbol it is referring to. Note that all + undefined symbols have been resolved. */ + sym = (Elf32_Sym *)sechdrs[symindex].sh_addr + + ELF32_R_SYM(rela[i].r_info); + /* `Everything is relative'. */ + value = sym->st_value + rela[i].r_addend; + + switch (ELF32_R_TYPE(rela[i].r_info)) { + case R_PPC_ADDR32: + /* Simply set it */ + *(uint32_t *)location = value; + break; + + case R_PPC_ADDR16_LO: + /* Low half of the symbol */ + *(uint16_t *)location = value; + break; + + case R_PPC_ADDR16_HA: + /* Sign-adjusted lower 16 bits: PPC ELF ABI says: + (((x >> 16) + ((x & 0x8000) ? 1 : 0))) & 0xFFFF. + This is the same, only sane. + */ + *(uint16_t *)location = (value + 0x8000) >> 16; + break; + + case R_PPC_REL24: + if ((int)(value - (uint32_t)location) < -0x02000000 + || (int)(value - (uint32_t)location) >= 0x02000000) + value = do_plt_call(location, value, + sechdrs, module); + + /* Only replace bits 2 through 26 */ + DEBUGP("REL24 value = %08X. location = %08X\n", + value, (uint32_t)location); + DEBUGP("Location before: %08X.\n", + *(uint32_t *)location); + *(uint32_t *)location + = (*(uint32_t *)location & ~0x03fffffc) + | ((value - (uint32_t)location) + & 0x03fffffc); + DEBUGP("Location after: %08X.\n", + *(uint32_t *)location); + DEBUGP("ie. jump to %08X+%08X = %08X\n", + *(uint32_t *)location & 0x03fffffc, + (uint32_t)location, + (*(uint32_t *)location & 0x03fffffc) + + (uint32_t)location); + break; + + case R_PPC_REL32: + /* 32-bit relative jump. */ + *(uint32_t *)location = value - (uint32_t)location; + break; + + default: + printk("%s: unknown ADD relocation: %u\n", + module->name, + ELF32_R_TYPE(rela[i].r_info)); + return -ENOEXEC; + } + } + return 0; +} + +int module_finalize(const Elf_Ehdr *hdr, + const Elf_Shdr *sechdrs, + struct module *me) +{ + char *secstrings; + unsigned int i; + + me->arch.bug_table = NULL; + me->arch.num_bugs = 0; + + /* Find the __bug_table section, if present */ + secstrings = (char *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; + for (i = 1; i < hdr->e_shnum; i++) { + if (strcmp(secstrings+sechdrs[i].sh_name, "__bug_table")) + continue; + me->arch.bug_table = (void *) sechdrs[i].sh_addr; + me->arch.num_bugs = sechdrs[i].sh_size / sizeof(struct bug_entry); + break; + } + + /* + * Strictly speaking this should have a spinlock to protect against + * traversals, but since we only traverse on BUG()s, a spinlock + * could potentially lead to deadlock and thus be counter-productive. + */ + list_add(&me->arch.bug_list, &module_bug_list); + + return 0; +} + +void module_arch_cleanup(struct module *mod) +{ + list_del(&mod->arch.bug_list); +} + +struct bug_entry *module_find_bug(unsigned long bugaddr) +{ + struct mod_arch_specific *mod; + unsigned int i; + struct bug_entry *bug; + + list_for_each_entry(mod, &module_bug_list, bug_list) { + bug = mod->bug_table; + for (i = 0; i < mod->num_bugs; ++i, ++bug) + if (bugaddr == bug->bug_addr) + return bug; + } + return NULL; +} diff --git a/arch/ppc/kernel/pci.c b/arch/ppc/kernel/pci.c new file mode 100644 index 00000000000..98f94b60204 --- /dev/null +++ b/arch/ppc/kernel/pci.c @@ -0,0 +1,1849 @@ +/* + * Common pmac/prep/chrp pci routines. -- Cort + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +unsigned long isa_io_base = 0; +unsigned long isa_mem_base = 0; +unsigned long pci_dram_offset = 0; +int pcibios_assign_bus_offset = 1; + +void pcibios_make_OF_bus_map(void); + +static int pci_relocate_bridge_resource(struct pci_bus *bus, int i); +static int probe_resource(struct pci_bus *parent, struct resource *pr, + struct resource *res, struct resource **conflict); +static void update_bridge_base(struct pci_bus *bus, int i); +static void pcibios_fixup_resources(struct pci_dev* dev); +static void fixup_broken_pcnet32(struct pci_dev* dev); +static int reparent_resources(struct resource *parent, struct resource *res); +static void fixup_rev1_53c810(struct pci_dev* dev); +static void fixup_cpc710_pci64(struct pci_dev* dev); +#ifdef CONFIG_PPC_OF +static u8* pci_to_OF_bus_map; +#endif + +/* By default, we don't re-assign bus numbers. We do this only on + * some pmacs + */ +int pci_assign_all_busses; + +struct pci_controller* hose_head; +struct pci_controller** hose_tail = &hose_head; + +static int pci_bus_count; + +static void +fixup_rev1_53c810(struct pci_dev* dev) +{ + /* rev 1 ncr53c810 chips don't set the class at all which means + * they don't get their resources remapped. Fix that here. + */ + + if ((dev->class == PCI_CLASS_NOT_DEFINED)) { + printk("NCR 53c810 rev 1 detected, setting PCI class.\n"); + dev->class = PCI_CLASS_STORAGE_SCSI; + } +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NCR, PCI_DEVICE_ID_NCR_53C810, fixup_rev1_53c810); + +static void +fixup_broken_pcnet32(struct pci_dev* dev) +{ + if ((dev->class>>8 == PCI_CLASS_NETWORK_ETHERNET)) { + dev->vendor = PCI_VENDOR_ID_AMD; + pci_write_config_word(dev, PCI_VENDOR_ID, PCI_VENDOR_ID_AMD); + pci_name_device(dev); + } +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TRIDENT, PCI_ANY_ID, fixup_broken_pcnet32); + +static void +fixup_cpc710_pci64(struct pci_dev* dev) +{ + /* Hide the PCI64 BARs from the kernel as their content doesn't + * fit well in the resource management + */ + dev->resource[0].start = dev->resource[0].end = 0; + dev->resource[0].flags = 0; + dev->resource[1].start = dev->resource[1].end = 0; + dev->resource[1].flags = 0; +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CPC710_PCI64, fixup_cpc710_pci64); + +static void +pcibios_fixup_resources(struct pci_dev *dev) +{ + struct pci_controller* hose = (struct pci_controller *)dev->sysdata; + int i; + unsigned long offset; + + if (!hose) { + printk(KERN_ERR "No hose for PCI dev %s!\n", pci_name(dev)); + return; + } + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { + struct resource *res = dev->resource + i; + if (!res->flags) + continue; + if (res->end == 0xffffffff) { + DBG("PCI:%s Resource %d [%08lx-%08lx] is unassigned\n", + pci_name(dev), i, res->start, res->end); + res->end -= res->start; + res->start = 0; + res->flags |= IORESOURCE_UNSET; + continue; + } + offset = 0; + if (res->flags & IORESOURCE_MEM) { + offset = hose->pci_mem_offset; + } else if (res->flags & IORESOURCE_IO) { + offset = (unsigned long) hose->io_base_virt + - isa_io_base; + } + if (offset != 0) { + res->start += offset; + res->end += offset; +#ifdef DEBUG + printk("Fixup res %d (%lx) of dev %s: %lx -> %lx\n", + i, res->flags, pci_name(dev), + res->start - offset, res->start); +#endif + } + } + + /* Call machine specific resource fixup */ + if (ppc_md.pcibios_fixup_resources) + ppc_md.pcibios_fixup_resources(dev); +} +DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pcibios_fixup_resources); + +void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, + struct resource *res) +{ + unsigned long offset = 0; + struct pci_controller *hose = dev->sysdata; + + if (hose && res->flags & IORESOURCE_IO) + offset = (unsigned long)hose->io_base_virt - isa_io_base; + else if (hose && res->flags & IORESOURCE_MEM) + offset = hose->pci_mem_offset; + region->start = res->start - offset; + region->end = res->end - offset; +} +EXPORT_SYMBOL(pcibios_resource_to_bus); + +/* + * We need to avoid collisions with `mirrored' VGA ports + * and other strange ISA hardware, so we always want the + * addresses to be allocated in the 0x000-0x0ff region + * modulo 0x400. + * + * Why? Because some silly external IO cards only decode + * the low 10 bits of the IO address. The 0x00-0xff region + * is reserved for motherboard devices that decode all 16 + * bits, so it's ok to allocate at, say, 0x2800-0x28ff, + * but we want to try to avoid allocating at 0x2900-0x2bff + * which might have be mirrored at 0x0100-0x03ff.. + */ +void pcibios_align_resource(void *data, struct resource *res, unsigned long size, + unsigned long align) +{ + struct pci_dev *dev = data; + + if (res->flags & IORESOURCE_IO) { + unsigned long start = res->start; + + if (size > 0x100) { + printk(KERN_ERR "PCI: I/O Region %s/%d too large" + " (%ld bytes)\n", pci_name(dev), + dev->resource - res, size); + } + + if (start & 0x300) { + start = (start + 0x3ff) & ~0x3ff; + res->start = start; + } + } +} +EXPORT_SYMBOL(pcibios_align_resource); + +/* + * Handle resources of PCI devices. If the world were perfect, we could + * just allocate all the resource regions and do nothing more. It isn't. + * On the other hand, we cannot just re-allocate all devices, as it would + * require us to know lots of host bridge internals. So we attempt to + * keep as much of the original configuration as possible, but tweak it + * when it's found to be wrong. + * + * Known BIOS problems we have to work around: + * - I/O or memory regions not configured + * - regions configured, but not enabled in the command register + * - bogus I/O addresses above 64K used + * - expansion ROMs left enabled (this may sound harmless, but given + * the fact the PCI specs explicitly allow address decoders to be + * shared between expansion ROMs and other resource regions, it's + * at least dangerous) + * + * Our solution: + * (1) Allocate resources for all buses behind PCI-to-PCI bridges. + * This gives us fixed barriers on where we can allocate. + * (2) Allocate resources for all enabled devices. If there is + * a collision, just mark the resource as unallocated. Also + * disable expansion ROMs during this step. + * (3) Try to allocate resources for disabled devices. If the + * resources were assigned correctly, everything goes well, + * if they weren't, they won't disturb allocation of other + * resources. + * (4) Assign new addresses to resources which were either + * not configured at all or misconfigured. If explicitly + * requested by the user, configure expansion ROM address + * as well. + */ + +static void __init +pcibios_allocate_bus_resources(struct list_head *bus_list) +{ + struct pci_bus *bus; + int i; + struct resource *res, *pr; + + /* Depth-First Search on bus tree */ + list_for_each_entry(bus, bus_list, node) { + for (i = 0; i < 4; ++i) { + if ((res = bus->resource[i]) == NULL || !res->flags + || res->start > res->end) + continue; + if (bus->parent == NULL) + pr = (res->flags & IORESOURCE_IO)? + &ioport_resource: &iomem_resource; + else { + pr = pci_find_parent_resource(bus->self, res); + if (pr == res) { + /* this happens when the generic PCI + * code (wrongly) decides that this + * bridge is transparent -- paulus + */ + continue; + } + } + + DBG("PCI: bridge rsrc %lx..%lx (%lx), parent %p\n", + res->start, res->end, res->flags, pr); + if (pr) { + if (request_resource(pr, res) == 0) + continue; + /* + * Must be a conflict with an existing entry. + * Move that entry (or entries) under the + * bridge resource and try again. + */ + if (reparent_resources(pr, res) == 0) + continue; + } + printk(KERN_ERR "PCI: Cannot allocate resource region " + "%d of PCI bridge %d\n", i, bus->number); + if (pci_relocate_bridge_resource(bus, i)) + bus->resource[i] = NULL; + } + pcibios_allocate_bus_resources(&bus->children); + } +} + +/* + * Reparent resource children of pr that conflict with res + * under res, and make res replace those children. + */ +static int __init +reparent_resources(struct resource *parent, struct resource *res) +{ + struct resource *p, **pp; + struct resource **firstpp = NULL; + + for (pp = &parent->child; (p = *pp) != NULL; pp = &p->sibling) { + if (p->end < res->start) + continue; + if (res->end < p->start) + break; + if (p->start < res->start || p->end > res->end) + return -1; /* not completely contained */ + if (firstpp == NULL) + firstpp = pp; + } + if (firstpp == NULL) + return -1; /* didn't find any conflicting entries? */ + res->parent = parent; + res->child = *firstpp; + res->sibling = *pp; + *firstpp = res; + *pp = NULL; + for (p = res->child; p != NULL; p = p->sibling) { + p->parent = res; + DBG(KERN_INFO "PCI: reparented %s [%lx..%lx] under %s\n", + p->name, p->start, p->end, res->name); + } + return 0; +} + +/* + * A bridge has been allocated a range which is outside the range + * of its parent bridge, so it needs to be moved. + */ +static int __init +pci_relocate_bridge_resource(struct pci_bus *bus, int i) +{ + struct resource *res, *pr, *conflict; + unsigned long try, size; + int j; + struct pci_bus *parent = bus->parent; + + if (parent == NULL) { + /* shouldn't ever happen */ + printk(KERN_ERR "PCI: can't move host bridge resource\n"); + return -1; + } + res = bus->resource[i]; + if (res == NULL) + return -1; + pr = NULL; + for (j = 0; j < 4; j++) { + struct resource *r = parent->resource[j]; + if (!r) + continue; + if ((res->flags ^ r->flags) & (IORESOURCE_IO | IORESOURCE_MEM)) + continue; + if (!((res->flags ^ r->flags) & IORESOURCE_PREFETCH)) { + pr = r; + break; + } + if (res->flags & IORESOURCE_PREFETCH) + pr = r; + } + if (pr == NULL) + return -1; + size = res->end - res->start; + if (pr->start > pr->end || size > pr->end - pr->start) + return -1; + try = pr->end; + for (;;) { + res->start = try - size; + res->end = try; + if (probe_resource(bus->parent, pr, res, &conflict) == 0) + break; + if (conflict->start <= pr->start + size) + return -1; + try = conflict->start - 1; + } + if (request_resource(pr, res)) { + DBG(KERN_ERR "PCI: huh? couldn't move to %lx..%lx\n", + res->start, res->end); + return -1; /* "can't happen" */ + } + update_bridge_base(bus, i); + printk(KERN_INFO "PCI: bridge %d resource %d moved to %lx..%lx\n", + bus->number, i, res->start, res->end); + return 0; +} + +static int __init +probe_resource(struct pci_bus *parent, struct resource *pr, + struct resource *res, struct resource **conflict) +{ + struct pci_bus *bus; + struct pci_dev *dev; + struct resource *r; + int i; + + for (r = pr->child; r != NULL; r = r->sibling) { + if (r->end >= res->start && res->end >= r->start) { + *conflict = r; + return 1; + } + } + list_for_each_entry(bus, &parent->children, node) { + for (i = 0; i < 4; ++i) { + if ((r = bus->resource[i]) == NULL) + continue; + if (!r->flags || r->start > r->end || r == res) + continue; + if (pci_find_parent_resource(bus->self, r) != pr) + continue; + if (r->end >= res->start && res->end >= r->start) { + *conflict = r; + return 1; + } + } + } + list_for_each_entry(dev, &parent->devices, bus_list) { + for (i = 0; i < 6; ++i) { + r = &dev->resource[i]; + if (!r->flags || (r->flags & IORESOURCE_UNSET)) + continue; + if (pci_find_parent_resource(dev, r) != pr) + continue; + if (r->end >= res->start && res->end >= r->start) { + *conflict = r; + return 1; + } + } + } + return 0; +} + +static void __init +update_bridge_base(struct pci_bus *bus, int i) +{ + struct resource *res = bus->resource[i]; + u8 io_base_lo, io_limit_lo; + u16 mem_base, mem_limit; + u16 cmd; + unsigned long start, end, off; + struct pci_dev *dev = bus->self; + struct pci_controller *hose = dev->sysdata; + + if (!hose) { + printk("update_bridge_base: no hose?\n"); + return; + } + pci_read_config_word(dev, PCI_COMMAND, &cmd); + pci_write_config_word(dev, PCI_COMMAND, + cmd & ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY)); + if (res->flags & IORESOURCE_IO) { + off = (unsigned long) hose->io_base_virt - isa_io_base; + start = res->start - off; + end = res->end - off; + io_base_lo = (start >> 8) & PCI_IO_RANGE_MASK; + io_limit_lo = (end >> 8) & PCI_IO_RANGE_MASK; + if (end > 0xffff) { + pci_write_config_word(dev, PCI_IO_BASE_UPPER16, + start >> 16); + pci_write_config_word(dev, PCI_IO_LIMIT_UPPER16, + end >> 16); + io_base_lo |= PCI_IO_RANGE_TYPE_32; + } else + io_base_lo |= PCI_IO_RANGE_TYPE_16; + pci_write_config_byte(dev, PCI_IO_BASE, io_base_lo); + pci_write_config_byte(dev, PCI_IO_LIMIT, io_limit_lo); + + } else if ((res->flags & (IORESOURCE_MEM | IORESOURCE_PREFETCH)) + == IORESOURCE_MEM) { + off = hose->pci_mem_offset; + mem_base = ((res->start - off) >> 16) & PCI_MEMORY_RANGE_MASK; + mem_limit = ((res->end - off) >> 16) & PCI_MEMORY_RANGE_MASK; + pci_write_config_word(dev, PCI_MEMORY_BASE, mem_base); + pci_write_config_word(dev, PCI_MEMORY_LIMIT, mem_limit); + + } else if ((res->flags & (IORESOURCE_MEM | IORESOURCE_PREFETCH)) + == (IORESOURCE_MEM | IORESOURCE_PREFETCH)) { + off = hose->pci_mem_offset; + mem_base = ((res->start - off) >> 16) & PCI_PREF_RANGE_MASK; + mem_limit = ((res->end - off) >> 16) & PCI_PREF_RANGE_MASK; + pci_write_config_word(dev, PCI_PREF_MEMORY_BASE, mem_base); + pci_write_config_word(dev, PCI_PREF_MEMORY_LIMIT, mem_limit); + + } else { + DBG(KERN_ERR "PCI: ugh, bridge %s res %d has flags=%lx\n", + pci_name(dev), i, res->flags); + } + pci_write_config_word(dev, PCI_COMMAND, cmd); +} + +static inline void alloc_resource(struct pci_dev *dev, int idx) +{ + struct resource *pr, *r = &dev->resource[idx]; + + DBG("PCI:%s: Resource %d: %08lx-%08lx (f=%lx)\n", + pci_name(dev), idx, r->start, r->end, r->flags); + pr = pci_find_parent_resource(dev, r); + if (!pr || request_resource(pr, r) < 0) { + printk(KERN_ERR "PCI: Cannot allocate resource region %d" + " of device %s\n", idx, pci_name(dev)); + if (pr) + DBG("PCI: parent is %p: %08lx-%08lx (f=%lx)\n", + pr, pr->start, pr->end, pr->flags); + /* We'll assign a new address later */ + r->flags |= IORESOURCE_UNSET; + r->end -= r->start; + r->start = 0; + } +} + +static void __init +pcibios_allocate_resources(int pass) +{ + struct pci_dev *dev = NULL; + int idx, disabled; + u16 command; + struct resource *r; + + while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { + pci_read_config_word(dev, PCI_COMMAND, &command); + for (idx = 0; idx < 6; idx++) { + r = &dev->resource[idx]; + if (r->parent) /* Already allocated */ + continue; + if (!r->flags || (r->flags & IORESOURCE_UNSET)) + continue; /* Not assigned at all */ + if (r->flags & IORESOURCE_IO) + disabled = !(command & PCI_COMMAND_IO); + else + disabled = !(command & PCI_COMMAND_MEMORY); + if (pass == disabled) + alloc_resource(dev, idx); + } + if (pass) + continue; + r = &dev->resource[PCI_ROM_RESOURCE]; + if (r->flags & IORESOURCE_ROM_ENABLE) { + /* Turn the ROM off, leave the resource region, but keep it unregistered. */ + u32 reg; + DBG("PCI: Switching off ROM of %s\n", pci_name(dev)); + r->flags &= ~IORESOURCE_ROM_ENABLE; + pci_read_config_dword(dev, dev->rom_base_reg, ®); + pci_write_config_dword(dev, dev->rom_base_reg, + reg & ~PCI_ROM_ADDRESS_ENABLE); + } + } +} + +static void __init +pcibios_assign_resources(void) +{ + struct pci_dev *dev = NULL; + int idx; + struct resource *r; + + while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { + int class = dev->class >> 8; + + /* Don't touch classless devices and host bridges */ + if (!class || class == PCI_CLASS_BRIDGE_HOST) + continue; + + for (idx = 0; idx < 6; idx++) { + r = &dev->resource[idx]; + + /* + * We shall assign a new address to this resource, + * either because the BIOS (sic) forgot to do so + * or because we have decided the old address was + * unusable for some reason. + */ + if ((r->flags & IORESOURCE_UNSET) && r->end && + (!ppc_md.pcibios_enable_device_hook || + !ppc_md.pcibios_enable_device_hook(dev, 1))) { + r->flags &= ~IORESOURCE_UNSET; + pci_assign_resource(dev, idx); + } + } + +#if 0 /* don't assign ROMs */ + r = &dev->resource[PCI_ROM_RESOURCE]; + r->end -= r->start; + r->start = 0; + if (r->end) + pci_assign_resource(dev, PCI_ROM_RESOURCE); +#endif + } +} + + +int +pcibios_enable_resources(struct pci_dev *dev, int mask) +{ + u16 cmd, old_cmd; + int idx; + struct resource *r; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + old_cmd = cmd; + for (idx=0; idx<6; idx++) { + /* Only set up the requested stuff */ + if (!(mask & (1<resource[idx]; + if (r->flags & IORESOURCE_UNSET) { + printk(KERN_ERR "PCI: Device %s not available because of resource collisions\n", pci_name(dev)); + return -EINVAL; + } + if (r->flags & IORESOURCE_IO) + cmd |= PCI_COMMAND_IO; + if (r->flags & IORESOURCE_MEM) + cmd |= PCI_COMMAND_MEMORY; + } + if (dev->resource[PCI_ROM_RESOURCE].start) + cmd |= PCI_COMMAND_MEMORY; + if (cmd != old_cmd) { + printk("PCI: Enabling device %s (%04x -> %04x)\n", pci_name(dev), old_cmd, cmd); + pci_write_config_word(dev, PCI_COMMAND, cmd); + } + return 0; +} + +static int next_controller_index; + +struct pci_controller * __init +pcibios_alloc_controller(void) +{ + struct pci_controller *hose; + + hose = (struct pci_controller *)alloc_bootmem(sizeof(*hose)); + memset(hose, 0, sizeof(struct pci_controller)); + + *hose_tail = hose; + hose_tail = &hose->next; + + hose->index = next_controller_index++; + + return hose; +} + +#ifdef CONFIG_PPC_OF +/* + * Functions below are used on OpenFirmware machines. + */ +static void __openfirmware +make_one_node_map(struct device_node* node, u8 pci_bus) +{ + int *bus_range; + int len; + + if (pci_bus >= pci_bus_count) + return; + bus_range = (int *) get_property(node, "bus-range", &len); + if (bus_range == NULL || len < 2 * sizeof(int)) { + printk(KERN_WARNING "Can't get bus-range for %s, " + "assuming it starts at 0\n", node->full_name); + pci_to_OF_bus_map[pci_bus] = 0; + } else + pci_to_OF_bus_map[pci_bus] = bus_range[0]; + + for (node=node->child; node != 0;node = node->sibling) { + struct pci_dev* dev; + unsigned int *class_code, *reg; + + class_code = (unsigned int *) get_property(node, "class-code", NULL); + if (!class_code || ((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI && + (*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS)) + continue; + reg = (unsigned int *)get_property(node, "reg", NULL); + if (!reg) + continue; + dev = pci_find_slot(pci_bus, ((reg[0] >> 8) & 0xff)); + if (!dev || !dev->subordinate) + continue; + make_one_node_map(node, dev->subordinate->number); + } +} + +void __openfirmware +pcibios_make_OF_bus_map(void) +{ + int i; + struct pci_controller* hose; + u8* of_prop_map; + + pci_to_OF_bus_map = (u8*)kmalloc(pci_bus_count, GFP_KERNEL); + if (!pci_to_OF_bus_map) { + printk(KERN_ERR "Can't allocate OF bus map !\n"); + return; + } + + /* We fill the bus map with invalid values, that helps + * debugging. + */ + for (i=0; inext) { + struct device_node* node; + node = (struct device_node *)hose->arch_data; + if (!node) + continue; + make_one_node_map(node, hose->first_busno); + } + of_prop_map = get_property(find_path_device("/"), "pci-OF-bus-map", NULL); + if (of_prop_map) + memcpy(of_prop_map, pci_to_OF_bus_map, pci_bus_count); +#ifdef DEBUG + printk("PCI->OF bus map:\n"); + for (i=0; i %d\n", i, pci_to_OF_bus_map[i]); + } +#endif +} + +typedef int (*pci_OF_scan_iterator)(struct device_node* node, void* data); + +static struct device_node* __openfirmware +scan_OF_pci_childs(struct device_node* node, pci_OF_scan_iterator filter, void* data) +{ + struct device_node* sub_node; + + for (; node != 0;node = node->sibling) { + unsigned int *class_code; + + if (filter(node, data)) + return node; + + /* For PCI<->PCI bridges or CardBus bridges, we go down + * Note: some OFs create a parent node "multifunc-device" as + * a fake root for all functions of a multi-function device, + * we go down them as well. + */ + class_code = (unsigned int *) get_property(node, "class-code", NULL); + if ((!class_code || ((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI && + (*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS)) && + strcmp(node->name, "multifunc-device")) + continue; + sub_node = scan_OF_pci_childs(node->child, filter, data); + if (sub_node) + return sub_node; + } + return NULL; +} + +static int +scan_OF_pci_childs_iterator(struct device_node* node, void* data) +{ + unsigned int *reg; + u8* fdata = (u8*)data; + + reg = (unsigned int *) get_property(node, "reg", NULL); + if (reg && ((reg[0] >> 8) & 0xff) == fdata[1] + && ((reg[0] >> 16) & 0xff) == fdata[0]) + return 1; + return 0; +} + +static struct device_node* __openfirmware +scan_OF_childs_for_device(struct device_node* node, u8 bus, u8 dev_fn) +{ + u8 filter_data[2] = {bus, dev_fn}; + + return scan_OF_pci_childs(node, scan_OF_pci_childs_iterator, filter_data); +} + +/* + * Scans the OF tree for a device node matching a PCI device + */ +struct device_node * +pci_busdev_to_OF_node(struct pci_bus *bus, int devfn) +{ + struct pci_controller *hose; + struct device_node *node; + int busnr; + + if (!have_of) + return NULL; + + /* Lookup the hose */ + busnr = bus->number; + hose = pci_bus_to_hose(busnr); + if (!hose) + return NULL; + + /* Check it has an OF node associated */ + node = (struct device_node *) hose->arch_data; + if (!node) + return NULL; + + /* Fixup bus number according to what OF think it is. */ +#ifdef CONFIG_PPC_PMAC + /* The G5 need a special case here. Basically, we don't remap all + * busses on it so we don't create the pci-OF-map. However, we do + * remap the AGP bus and so have to deal with it. A future better + * fix has to be done by making the remapping per-host and always + * filling the pci_to_OF map. --BenH + */ + if (_machine == _MACH_Pmac && busnr >= 0xf0) + busnr -= 0xf0; + else +#endif + if (pci_to_OF_bus_map) + busnr = pci_to_OF_bus_map[busnr]; + if (busnr == 0xff) + return NULL; + + /* Now, lookup childs of the hose */ + return scan_OF_childs_for_device(node->child, busnr, devfn); +} + +struct device_node* +pci_device_to_OF_node(struct pci_dev *dev) +{ + return pci_busdev_to_OF_node(dev->bus, dev->devfn); +} + +/* This routine is meant to be used early during boot, when the + * PCI bus numbers have not yet been assigned, and you need to + * issue PCI config cycles to an OF device. + * It could also be used to "fix" RTAS config cycles if you want + * to set pci_assign_all_busses to 1 and still use RTAS for PCI + * config cycles. + */ +struct pci_controller* +pci_find_hose_for_OF_device(struct device_node* node) +{ + if (!have_of) + return NULL; + while(node) { + struct pci_controller* hose; + for (hose=hose_head;hose;hose=hose->next) + if (hose->arch_data == node) + return hose; + node=node->parent; + } + return NULL; +} + +static int __openfirmware +find_OF_pci_device_filter(struct device_node* node, void* data) +{ + return ((void *)node == data); +} + +/* + * Returns the PCI device matching a given OF node + */ +int +pci_device_from_OF_node(struct device_node* node, u8* bus, u8* devfn) +{ + unsigned int *reg; + struct pci_controller* hose; + struct pci_dev* dev = NULL; + + if (!have_of) + return -ENODEV; + /* Make sure it's really a PCI device */ + hose = pci_find_hose_for_OF_device(node); + if (!hose || !hose->arch_data) + return -ENODEV; + if (!scan_OF_pci_childs(((struct device_node*)hose->arch_data)->child, + find_OF_pci_device_filter, (void *)node)) + return -ENODEV; + reg = (unsigned int *) get_property(node, "reg", NULL); + if (!reg) + return -ENODEV; + *bus = (reg[0] >> 16) & 0xff; + *devfn = ((reg[0] >> 8) & 0xff); + + /* Ok, here we need some tweak. If we have already renumbered + * all busses, we can't rely on the OF bus number any more. + * the pci_to_OF_bus_map is not enough as several PCI busses + * may match the same OF bus number. + */ + if (!pci_to_OF_bus_map) + return 0; + while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { + if (pci_to_OF_bus_map[dev->bus->number] != *bus) + continue; + if (dev->devfn != *devfn) + continue; + *bus = dev->bus->number; + return 0; + } + return -ENODEV; +} + +void __init +pci_process_bridge_OF_ranges(struct pci_controller *hose, + struct device_node *dev, int primary) +{ + static unsigned int static_lc_ranges[256] __initdata; + unsigned int *dt_ranges, *lc_ranges, *ranges, *prev; + unsigned int size; + int rlen = 0, orig_rlen; + int memno = 0; + struct resource *res; + int np, na = prom_n_addr_cells(dev); + np = na + 5; + + /* First we try to merge ranges to fix a problem with some pmacs + * that can have more than 3 ranges, fortunately using contiguous + * addresses -- BenH + */ + dt_ranges = (unsigned int *) get_property(dev, "ranges", &rlen); + if (!dt_ranges) + return; + /* Sanity check, though hopefully that never happens */ + if (rlen > sizeof(static_lc_ranges)) { + printk(KERN_WARNING "OF ranges property too large !\n"); + rlen = sizeof(static_lc_ranges); + } + lc_ranges = static_lc_ranges; + memcpy(lc_ranges, dt_ranges, rlen); + orig_rlen = rlen; + + /* Let's work on a copy of the "ranges" property instead of damaging + * the device-tree image in memory + */ + ranges = lc_ranges; + prev = NULL; + while ((rlen -= np * sizeof(unsigned int)) >= 0) { + if (prev) { + if (prev[0] == ranges[0] && prev[1] == ranges[1] && + (prev[2] + prev[na+4]) == ranges[2] && + (prev[na+2] + prev[na+4]) == ranges[na+2]) { + prev[na+4] += ranges[na+4]; + ranges[0] = 0; + ranges += np; + continue; + } + } + prev = ranges; + ranges += np; + } + + /* + * The ranges property is laid out as an array of elements, + * each of which comprises: + * cells 0 - 2: a PCI address + * cells 3 or 3+4: a CPU physical address + * (size depending on dev->n_addr_cells) + * cells 4+5 or 5+6: the size of the range + */ + ranges = lc_ranges; + rlen = orig_rlen; + while (ranges && (rlen -= np * sizeof(unsigned int)) >= 0) { + res = NULL; + size = ranges[na+4]; + switch (ranges[0] >> 24) { + case 1: /* I/O space */ + if (ranges[2] != 0) + break; + hose->io_base_phys = ranges[na+2]; + /* limit I/O space to 16MB */ + if (size > 0x01000000) + size = 0x01000000; + hose->io_base_virt = ioremap(ranges[na+2], size); + if (primary) + isa_io_base = (unsigned long) hose->io_base_virt; + res = &hose->io_resource; + res->flags = IORESOURCE_IO; + res->start = ranges[2]; + break; + case 2: /* memory space */ + memno = 0; + if (ranges[1] == 0 && ranges[2] == 0 + && ranges[na+4] <= (16 << 20)) { + /* 1st 16MB, i.e. ISA memory area */ + if (primary) + isa_mem_base = ranges[na+2]; + memno = 1; + } + while (memno < 3 && hose->mem_resources[memno].flags) + ++memno; + if (memno == 0) + hose->pci_mem_offset = ranges[na+2] - ranges[2]; + if (memno < 3) { + res = &hose->mem_resources[memno]; + res->flags = IORESOURCE_MEM; + res->start = ranges[na+2]; + } + break; + } + if (res != NULL) { + res->name = dev->full_name; + res->end = res->start + size - 1; + res->parent = NULL; + res->sibling = NULL; + res->child = NULL; + } + ranges += np; + } +} + +/* We create the "pci-OF-bus-map" property now so it appears in the + * /proc device tree + */ +void __init +pci_create_OF_bus_map(void) +{ + struct property* of_prop; + + of_prop = (struct property*) alloc_bootmem(sizeof(struct property) + 256); + if (of_prop && find_path_device("/")) { + memset(of_prop, -1, sizeof(struct property) + 256); + of_prop->name = "pci-OF-bus-map"; + of_prop->length = 256; + of_prop->value = (unsigned char *)&of_prop[1]; + prom_add_property(find_path_device("/"), of_prop); + } +} + +static ssize_t pci_show_devspec(struct device *dev, char *buf) +{ + struct pci_dev *pdev; + struct device_node *np; + + pdev = to_pci_dev (dev); + np = pci_device_to_OF_node(pdev); + if (np == NULL || np->full_name == NULL) + return 0; + return sprintf(buf, "%s", np->full_name); +} +static DEVICE_ATTR(devspec, S_IRUGO, pci_show_devspec, NULL); + +#endif /* CONFIG_PPC_OF */ + +/* Add sysfs properties */ +void pcibios_add_platform_entries(struct pci_dev *pdev) +{ +#ifdef CONFIG_PPC_OF + device_create_file(&pdev->dev, &dev_attr_devspec); +#endif /* CONFIG_PPC_OF */ +} + + +#ifdef CONFIG_PPC_PMAC +/* + * This set of routines checks for PCI<->PCI bridges that have closed + * IO resources and have child devices. It tries to re-open an IO + * window on them. + * + * This is a _temporary_ fix to workaround a problem with Apple's OF + * closing IO windows on P2P bridges when the OF drivers of cards + * below this bridge don't claim any IO range (typically ATI or + * Adaptec). + * + * A more complete fix would be to use drivers/pci/setup-bus.c, which + * involves a working pcibios_fixup_pbus_ranges(), some more care about + * ordering when creating the host bus resources, and maybe a few more + * minor tweaks + */ + +/* Initialize bridges with base/limit values we have collected */ +static void __init +do_update_p2p_io_resource(struct pci_bus *bus, int enable_vga) +{ + struct pci_dev *bridge = bus->self; + struct pci_controller* hose = (struct pci_controller *)bridge->sysdata; + u32 l; + u16 w; + struct resource res; + + if (bus->resource[0] == NULL) + return; + res = *(bus->resource[0]); + + DBG("Remapping Bus %d, bridge: %s\n", bus->number, pci_name(bridge)); + res.start -= ((unsigned long) hose->io_base_virt - isa_io_base); + res.end -= ((unsigned long) hose->io_base_virt - isa_io_base); + DBG(" IO window: %08lx-%08lx\n", res.start, res.end); + + /* Set up the top and bottom of the PCI I/O segment for this bus. */ + pci_read_config_dword(bridge, PCI_IO_BASE, &l); + l &= 0xffff000f; + l |= (res.start >> 8) & 0x00f0; + l |= res.end & 0xf000; + pci_write_config_dword(bridge, PCI_IO_BASE, l); + + if ((l & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) { + l = (res.start >> 16) | (res.end & 0xffff0000); + pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, l); + } + + pci_read_config_word(bridge, PCI_COMMAND, &w); + w |= PCI_COMMAND_IO; + pci_write_config_word(bridge, PCI_COMMAND, w); + +#if 0 /* Enabling this causes XFree 4.2.0 to hang during PCI probe */ + if (enable_vga) { + pci_read_config_word(bridge, PCI_BRIDGE_CONTROL, &w); + w |= PCI_BRIDGE_CTL_VGA; + pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, w); + } +#endif +} + +/* This function is pretty basic and actually quite broken for the + * general case, it's enough for us right now though. It's supposed + * to tell us if we need to open an IO range at all or not and what + * size. + */ +static int __init +check_for_io_childs(struct pci_bus *bus, struct resource* res, int *found_vga) +{ + struct pci_dev *dev; + int i; + int rc = 0; + +#define push_end(res, size) do { unsigned long __sz = (size) ; \ + res->end = ((res->end + __sz) / (__sz + 1)) * (__sz + 1) + __sz; \ + } while (0) + + list_for_each_entry(dev, &bus->devices, bus_list) { + u16 class = dev->class >> 8; + + if (class == PCI_CLASS_DISPLAY_VGA || + class == PCI_CLASS_NOT_DEFINED_VGA) + *found_vga = 1; + if (class >> 8 == PCI_BASE_CLASS_BRIDGE && dev->subordinate) + rc |= check_for_io_childs(dev->subordinate, res, found_vga); + if (class == PCI_CLASS_BRIDGE_CARDBUS) + push_end(res, 0xfff); + + for (i=0; iclass >> 8 == PCI_CLASS_BRIDGE_PCI + && i >= PCI_BRIDGE_RESOURCES) + continue; + r = &dev->resource[i]; + r_size = r->end - r->start; + if (r_size < 0xfff) + r_size = 0xfff; + if (r->flags & IORESOURCE_IO && (r_size) != 0) { + rc = 1; + push_end(res, r_size); + } + } + } + + return rc; +} + +/* Here we scan all P2P bridges of a given level that have a closed + * IO window. Note that the test for the presence of a VGA card should + * be improved to take into account already configured P2P bridges, + * currently, we don't see them and might end up configuring 2 bridges + * with VGA pass through enabled + */ +static void __init +do_fixup_p2p_level(struct pci_bus *bus) +{ + struct pci_bus *b; + int i, parent_io; + int has_vga = 0; + + for (parent_io=0; parent_io<4; parent_io++) + if (bus->resource[parent_io] + && bus->resource[parent_io]->flags & IORESOURCE_IO) + break; + if (parent_io >= 4) + return; + + list_for_each_entry(b, &bus->children, node) { + struct pci_dev *d = b->self; + struct pci_controller* hose = (struct pci_controller *)d->sysdata; + struct resource *res = b->resource[0]; + struct resource tmp_res; + unsigned long max; + int found_vga = 0; + + memset(&tmp_res, 0, sizeof(tmp_res)); + tmp_res.start = bus->resource[parent_io]->start; + + /* We don't let low addresses go through that closed P2P bridge, well, + * that may not be necessary but I feel safer that way + */ + if (tmp_res.start == 0) + tmp_res.start = 0x1000; + + if (!list_empty(&b->devices) && res && res->flags == 0 && + res != bus->resource[parent_io] && + (d->class >> 8) == PCI_CLASS_BRIDGE_PCI && + check_for_io_childs(b, &tmp_res, &found_vga)) { + u8 io_base_lo; + + printk(KERN_INFO "Fixing up IO bus %s\n", b->name); + + if (found_vga) { + if (has_vga) { + printk(KERN_WARNING "Skipping VGA, already active" + " on bus segment\n"); + found_vga = 0; + } else + has_vga = 1; + } + pci_read_config_byte(d, PCI_IO_BASE, &io_base_lo); + + if ((io_base_lo & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) + max = ((unsigned long) hose->io_base_virt + - isa_io_base) + 0xffffffff; + else + max = ((unsigned long) hose->io_base_virt + - isa_io_base) + 0xffff; + + *res = tmp_res; + res->flags = IORESOURCE_IO; + res->name = b->name; + + /* Find a resource in the parent where we can allocate */ + for (i = 0 ; i < 4; i++) { + struct resource *r = bus->resource[i]; + if (!r) + continue; + if ((r->flags & IORESOURCE_IO) == 0) + continue; + DBG("Trying to allocate from %08lx, size %08lx from parent" + " res %d: %08lx -> %08lx\n", + res->start, res->end, i, r->start, r->end); + + if (allocate_resource(r, res, res->end + 1, res->start, max, + res->end + 1, NULL, NULL) < 0) { + DBG("Failed !\n"); + continue; + } + do_update_p2p_io_resource(b, found_vga); + break; + } + } + do_fixup_p2p_level(b); + } +} + +static void +pcibios_fixup_p2p_bridges(void) +{ + struct pci_bus *b; + + list_for_each_entry(b, &pci_root_buses, node) + do_fixup_p2p_level(b); +} + +#endif /* CONFIG_PPC_PMAC */ + +static int __init +pcibios_init(void) +{ + struct pci_controller *hose; + struct pci_bus *bus; + int next_busno; + + printk(KERN_INFO "PCI: Probing PCI hardware\n"); + + /* Scan all of the recorded PCI controllers. */ + for (next_busno = 0, hose = hose_head; hose; hose = hose->next) { + if (pci_assign_all_busses) + hose->first_busno = next_busno; + hose->last_busno = 0xff; + bus = pci_scan_bus(hose->first_busno, hose->ops, hose); + hose->last_busno = bus->subordinate; + if (pci_assign_all_busses || next_busno <= hose->last_busno) + next_busno = hose->last_busno + pcibios_assign_bus_offset; + } + pci_bus_count = next_busno; + + /* OpenFirmware based machines need a map of OF bus + * numbers vs. kernel bus numbers since we may have to + * remap them. + */ + if (pci_assign_all_busses && have_of) + pcibios_make_OF_bus_map(); + + /* Do machine dependent PCI interrupt routing */ + if (ppc_md.pci_swizzle && ppc_md.pci_map_irq) + pci_fixup_irqs(ppc_md.pci_swizzle, ppc_md.pci_map_irq); + + /* Call machine dependent fixup */ + if (ppc_md.pcibios_fixup) + ppc_md.pcibios_fixup(); + + /* Allocate and assign resources */ + pcibios_allocate_bus_resources(&pci_root_buses); + pcibios_allocate_resources(0); + pcibios_allocate_resources(1); +#ifdef CONFIG_PPC_PMAC + pcibios_fixup_p2p_bridges(); +#endif /* CONFIG_PPC_PMAC */ + pcibios_assign_resources(); + + /* Call machine dependent post-init code */ + if (ppc_md.pcibios_after_init) + ppc_md.pcibios_after_init(); + + return 0; +} + +subsys_initcall(pcibios_init); + +unsigned char __init +common_swizzle(struct pci_dev *dev, unsigned char *pinp) +{ + struct pci_controller *hose = dev->sysdata; + + if (dev->bus->number != hose->first_busno) { + u8 pin = *pinp; + do { + pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn)); + /* Move up the chain of bridges. */ + dev = dev->bus->self; + } while (dev->bus->self); + *pinp = pin; + + /* The slot is the idsel of the last bridge. */ + } + return PCI_SLOT(dev->devfn); +} + +unsigned long resource_fixup(struct pci_dev * dev, struct resource * res, + unsigned long start, unsigned long size) +{ + return start; +} + +void __init pcibios_fixup_bus(struct pci_bus *bus) +{ + struct pci_controller *hose = (struct pci_controller *) bus->sysdata; + unsigned long io_offset; + struct resource *res; + int i; + + io_offset = (unsigned long)hose->io_base_virt - isa_io_base; + if (bus->parent == NULL) { + /* This is a host bridge - fill in its resources */ + hose->bus = bus; + + bus->resource[0] = res = &hose->io_resource; + if (!res->flags) { + if (io_offset) + printk(KERN_ERR "I/O resource not set for host" + " bridge %d\n", hose->index); + res->start = 0; + res->end = IO_SPACE_LIMIT; + res->flags = IORESOURCE_IO; + } + res->start += io_offset; + res->end += io_offset; + + for (i = 0; i < 3; ++i) { + res = &hose->mem_resources[i]; + if (!res->flags) { + if (i > 0) + continue; + printk(KERN_ERR "Memory resource not set for " + "host bridge %d\n", hose->index); + res->start = hose->pci_mem_offset; + res->end = ~0U; + res->flags = IORESOURCE_MEM; + } + bus->resource[i+1] = res; + } + } else { + /* This is a subordinate bridge */ + pci_read_bridge_bases(bus); + + for (i = 0; i < 4; ++i) { + if ((res = bus->resource[i]) == NULL) + continue; + if (!res->flags) + continue; + if (io_offset && (res->flags & IORESOURCE_IO)) { + res->start += io_offset; + res->end += io_offset; + } else if (hose->pci_mem_offset + && (res->flags & IORESOURCE_MEM)) { + res->start += hose->pci_mem_offset; + res->end += hose->pci_mem_offset; + } + } + } + + if (ppc_md.pcibios_fixup_bus) + ppc_md.pcibios_fixup_bus(bus); +} + +char __init *pcibios_setup(char *str) +{ + return str; +} + +/* the next one is stolen from the alpha port... */ +void __init +pcibios_update_irq(struct pci_dev *dev, int irq) +{ + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); + /* XXX FIXME - update OF device tree node interrupt property */ +} + +int pcibios_enable_device(struct pci_dev *dev, int mask) +{ + u16 cmd, old_cmd; + int idx; + struct resource *r; + + if (ppc_md.pcibios_enable_device_hook) + if (ppc_md.pcibios_enable_device_hook(dev, 0)) + return -EINVAL; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + old_cmd = cmd; + for (idx=0; idx<6; idx++) { + r = &dev->resource[idx]; + if (r->flags & IORESOURCE_UNSET) { + printk(KERN_ERR "PCI: Device %s not available because of resource collisions\n", pci_name(dev)); + return -EINVAL; + } + if (r->flags & IORESOURCE_IO) + cmd |= PCI_COMMAND_IO; + if (r->flags & IORESOURCE_MEM) + cmd |= PCI_COMMAND_MEMORY; + } + if (cmd != old_cmd) { + printk("PCI: Enabling device %s (%04x -> %04x)\n", + pci_name(dev), old_cmd, cmd); + pci_write_config_word(dev, PCI_COMMAND, cmd); + } + return 0; +} + +struct pci_controller* +pci_bus_to_hose(int bus) +{ + struct pci_controller* hose = hose_head; + + for (; hose; hose = hose->next) + if (bus >= hose->first_busno && bus <= hose->last_busno) + return hose; + return NULL; +} + +void* +pci_bus_io_base(unsigned int bus) +{ + struct pci_controller *hose; + + hose = pci_bus_to_hose(bus); + if (!hose) + return NULL; + return hose->io_base_virt; +} + +unsigned long +pci_bus_io_base_phys(unsigned int bus) +{ + struct pci_controller *hose; + + hose = pci_bus_to_hose(bus); + if (!hose) + return 0; + return hose->io_base_phys; +} + +unsigned long +pci_bus_mem_base_phys(unsigned int bus) +{ + struct pci_controller *hose; + + hose = pci_bus_to_hose(bus); + if (!hose) + return 0; + return hose->pci_mem_offset; +} + +unsigned long +pci_resource_to_bus(struct pci_dev *pdev, struct resource *res) +{ + /* Hack alert again ! See comments in chrp_pci.c + */ + struct pci_controller* hose = + (struct pci_controller *)pdev->sysdata; + if (hose && res->flags & IORESOURCE_MEM) + return res->start - hose->pci_mem_offset; + /* We may want to do something with IOs here... */ + return res->start; +} + + +static struct resource *__pci_mmap_make_offset(struct pci_dev *dev, + unsigned long *offset, + enum pci_mmap_state mmap_state) +{ + struct pci_controller *hose = pci_bus_to_hose(dev->bus->number); + unsigned long io_offset = 0; + int i, res_bit; + + if (hose == 0) + return NULL; /* should never happen */ + + /* If memory, add on the PCI bridge address offset */ + if (mmap_state == pci_mmap_mem) { + *offset += hose->pci_mem_offset; + res_bit = IORESOURCE_MEM; + } else { + io_offset = (unsigned long)hose->io_base_virt; + *offset += io_offset; + res_bit = IORESOURCE_IO; + } + + /* + * Check that the offset requested corresponds to one of the + * resources of the device. + */ + for (i = 0; i <= PCI_ROM_RESOURCE; i++) { + struct resource *rp = &dev->resource[i]; + int flags = rp->flags; + + /* treat ROM as memory (should be already) */ + if (i == PCI_ROM_RESOURCE) + flags |= IORESOURCE_MEM; + + /* Active and same type? */ + if ((flags & res_bit) == 0) + continue; + + /* In the range of this resource? */ + if (*offset < (rp->start & PAGE_MASK) || *offset > rp->end) + continue; + + /* found it! construct the final physical address */ + if (mmap_state == pci_mmap_io) + *offset += hose->io_base_phys - _IO_BASE; + return rp; + } + + return NULL; +} + +/* + * Set vm_page_prot of VMA, as appropriate for this architecture, for a pci + * device mapping. + */ +static pgprot_t __pci_mmap_set_pgprot(struct pci_dev *dev, struct resource *rp, + pgprot_t protection, + enum pci_mmap_state mmap_state, + int write_combine) +{ + unsigned long prot = pgprot_val(protection); + + /* Write combine is always 0 on non-memory space mappings. On + * memory space, if the user didn't pass 1, we check for a + * "prefetchable" resource. This is a bit hackish, but we use + * this to workaround the inability of /sysfs to provide a write + * combine bit + */ + if (mmap_state != pci_mmap_mem) + write_combine = 0; + else if (write_combine == 0) { + if (rp->flags & IORESOURCE_PREFETCH) + write_combine = 1; + } + + /* XXX would be nice to have a way to ask for write-through */ + prot |= _PAGE_NO_CACHE; + if (write_combine) + prot &= ~_PAGE_GUARDED; + else + prot |= _PAGE_GUARDED; + + printk("PCI map for %s:%lx, prot: %lx\n", pci_name(dev), rp->start, + prot); + + return __pgprot(prot); +} + +/* + * This one is used by /dev/mem and fbdev who have no clue about the + * PCI device, it tries to find the PCI device first and calls the + * above routine + */ +pgprot_t pci_phys_mem_access_prot(struct file *file, + unsigned long offset, + unsigned long size, + pgprot_t protection) +{ + struct pci_dev *pdev = NULL; + struct resource *found = NULL; + unsigned long prot = pgprot_val(protection); + int i; + + if (page_is_ram(offset >> PAGE_SHIFT)) + return prot; + + prot |= _PAGE_NO_CACHE | _PAGE_GUARDED; + + for_each_pci_dev(pdev) { + for (i = 0; i <= PCI_ROM_RESOURCE; i++) { + struct resource *rp = &pdev->resource[i]; + int flags = rp->flags; + + /* Active and same type? */ + if ((flags & IORESOURCE_MEM) == 0) + continue; + /* In the range of this resource? */ + if (offset < (rp->start & PAGE_MASK) || + offset > rp->end) + continue; + found = rp; + break; + } + if (found) + break; + } + if (found) { + if (found->flags & IORESOURCE_PREFETCH) + prot &= ~_PAGE_GUARDED; + pci_dev_put(pdev); + } + + DBG("non-PCI map for %lx, prot: %lx\n", offset, prot); + + return __pgprot(prot); +} + + +/* + * Perform the actual remap of the pages for a PCI device mapping, as + * appropriate for this architecture. The region in the process to map + * is described by vm_start and vm_end members of VMA, the base physical + * address is found in vm_pgoff. + * The pci device structure is provided so that architectures may make mapping + * decisions on a per-device or per-bus basis. + * + * Returns a negative error code on failure, zero on success. + */ +int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, + enum pci_mmap_state mmap_state, + int write_combine) +{ + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + struct resource *rp; + int ret; + + rp = __pci_mmap_make_offset(dev, &offset, mmap_state); + if (rp == NULL) + return -EINVAL; + + vma->vm_pgoff = offset >> PAGE_SHIFT; + vma->vm_flags |= VM_SHM | VM_LOCKED | VM_IO; + vma->vm_page_prot = __pci_mmap_set_pgprot(dev, rp, + vma->vm_page_prot, + mmap_state, write_combine); + + ret = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + vma->vm_end - vma->vm_start, vma->vm_page_prot); + + return ret; +} + +/* Obsolete functions. Should be removed once the symbios driver + * is fixed + */ +unsigned long +phys_to_bus(unsigned long pa) +{ + struct pci_controller *hose; + int i; + + for (hose = hose_head; hose; hose = hose->next) { + for (i = 0; i < 3; ++i) { + if (pa >= hose->mem_resources[i].start + && pa <= hose->mem_resources[i].end) { + /* + * XXX the hose->pci_mem_offset really + * only applies to mem_resources[0]. + * We need a way to store an offset for + * the others. -- paulus + */ + if (i == 0) + pa -= hose->pci_mem_offset; + return pa; + } + } + } + /* hmmm, didn't find it */ + return 0; +} + +unsigned long +pci_phys_to_bus(unsigned long pa, int busnr) +{ + struct pci_controller* hose = pci_bus_to_hose(busnr); + if (!hose) + return pa; + return pa - hose->pci_mem_offset; +} + +unsigned long +pci_bus_to_phys(unsigned int ba, int busnr) +{ + struct pci_controller* hose = pci_bus_to_hose(busnr); + if (!hose) + return ba; + return ba + hose->pci_mem_offset; +} + +/* Provide information on locations of various I/O regions in physical + * memory. Do this on a per-card basis so that we choose the right + * root bridge. + * Note that the returned IO or memory base is a physical address + */ + +long sys_pciconfig_iobase(long which, unsigned long bus, unsigned long devfn) +{ + struct pci_controller* hose; + long result = -EOPNOTSUPP; + + /* Argh ! Please forgive me for that hack, but that's the + * simplest way to get existing XFree to not lockup on some + * G5 machines... So when something asks for bus 0 io base + * (bus 0 is HT root), we return the AGP one instead. + */ +#ifdef CONFIG_PPC_PMAC + if (_machine == _MACH_Pmac && machine_is_compatible("MacRISC4")) + if (bus == 0) + bus = 0xf0; +#endif /* CONFIG_PPC_PMAC */ + + hose = pci_bus_to_hose(bus); + if (!hose) + return -ENODEV; + + switch (which) { + case IOBASE_BRIDGE_NUMBER: + return (long)hose->first_busno; + case IOBASE_MEMORY: + return (long)hose->pci_mem_offset; + case IOBASE_IO: + return (long)hose->io_base_phys; + case IOBASE_ISA_IO: + return (long)isa_io_base; + case IOBASE_ISA_MEM: + return (long)isa_mem_base; + } + + return result; +} + +void __init +pci_init_resource(struct resource *res, unsigned long start, unsigned long end, + int flags, char *name) +{ + res->start = start; + res->end = end; + res->flags = flags; + res->name = name; + res->parent = NULL; + res->sibling = NULL; + res->child = NULL; +} + +void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max) +{ + unsigned long start = pci_resource_start(dev, bar); + unsigned long len = pci_resource_len(dev, bar); + unsigned long flags = pci_resource_flags(dev, bar); + + if (!len) + return NULL; + if (max && len > max) + len = max; + if (flags & IORESOURCE_IO) + return ioport_map(start, len); + if (flags & IORESOURCE_MEM) + /* Not checking IORESOURCE_CACHEABLE because PPC does + * not currently distinguish between ioremap and + * ioremap_nocache. + */ + return ioremap(start, len); + /* What? */ + return NULL; +} + +void pci_iounmap(struct pci_dev *dev, void __iomem *addr) +{ + /* Nothing to do */ +} +EXPORT_SYMBOL(pci_iomap); +EXPORT_SYMBOL(pci_iounmap); + + +/* + * Null PCI config access functions, for the case when we can't + * find a hose. + */ +#define NULL_PCI_OP(rw, size, type) \ +static int \ +null_##rw##_config_##size(struct pci_dev *dev, int offset, type val) \ +{ \ + return PCIBIOS_DEVICE_NOT_FOUND; \ +} + +static int +null_read_config(struct pci_bus *bus, unsigned int devfn, int offset, + int len, u32 *val) +{ + return PCIBIOS_DEVICE_NOT_FOUND; +} + +static int +null_write_config(struct pci_bus *bus, unsigned int devfn, int offset, + int len, u32 val) +{ + return PCIBIOS_DEVICE_NOT_FOUND; +} + +static struct pci_ops null_pci_ops = +{ + null_read_config, + null_write_config +}; + +/* + * These functions are used early on before PCI scanning is done + * and all of the pci_dev and pci_bus structures have been created. + */ +static struct pci_bus * +fake_pci_bus(struct pci_controller *hose, int busnr) +{ + static struct pci_bus bus; + + if (hose == 0) { + hose = pci_bus_to_hose(busnr); + if (hose == 0) + printk(KERN_ERR "Can't find hose for PCI bus %d!\n", busnr); + } + bus.number = busnr; + bus.sysdata = hose; + bus.ops = hose? hose->ops: &null_pci_ops; + return &bus; +} + +#define EARLY_PCI_OP(rw, size, type) \ +int early_##rw##_config_##size(struct pci_controller *hose, int bus, \ + int devfn, int offset, type value) \ +{ \ + return pci_bus_##rw##_config_##size(fake_pci_bus(hose, bus), \ + devfn, offset, value); \ +} + +EARLY_PCI_OP(read, byte, u8 *) +EARLY_PCI_OP(read, word, u16 *) +EARLY_PCI_OP(read, dword, u32 *) +EARLY_PCI_OP(write, byte, u8) +EARLY_PCI_OP(write, word, u16) +EARLY_PCI_OP(write, dword, u32) diff --git a/arch/ppc/kernel/perfmon.c b/arch/ppc/kernel/perfmon.c new file mode 100644 index 00000000000..918f6b252e4 --- /dev/null +++ b/arch/ppc/kernel/perfmon.c @@ -0,0 +1,93 @@ +/* kernel/perfmon.c + * PPC 32 Performance Monitor Infrastructure + * + * Author: Andy Fleming + * Copyright (c) 2004 Freescale Semiconductor, Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* A lock to regulate grabbing the interrupt */ +DEFINE_SPINLOCK(perfmon_lock); + +#ifdef CONFIG_FSL_BOOKE +static void dummy_perf(struct pt_regs *regs) +{ + unsigned int pmgc0 = mfpmr(PMRN_PMGC0); + + pmgc0 &= ~PMGC0_PMIE; + mtpmr(PMRN_PMGC0, pmgc0); +} + +#else +/* Ensure exceptions are disabled */ + +static void dummy_perf(struct pt_regs *regs) +{ + unsigned int mmcr0 = mfspr(SPRN_MMCR0); + + mmcr0 &= ~MMCR0_PMXE; + mtspr(SPRN_MMCR0, mmcr0); +} +#endif + +void (*perf_irq)(struct pt_regs *) = dummy_perf; + +/* Grab the interrupt, if it's free. + * Returns 0 on success, -1 if the interrupt is taken already */ +int request_perfmon_irq(void (*handler)(struct pt_regs *)) +{ + int err = 0; + + spin_lock(&perfmon_lock); + + if (perf_irq == dummy_perf) + perf_irq = handler; + else { + pr_info("perfmon irq already handled by %p\n", perf_irq); + err = -1; + } + + spin_unlock(&perfmon_lock); + + return err; +} + +void free_perfmon_irq(void) +{ + spin_lock(&perfmon_lock); + + perf_irq = dummy_perf; + + spin_unlock(&perfmon_lock); +} + +EXPORT_SYMBOL(perf_irq); +EXPORT_SYMBOL(request_perfmon_irq); +EXPORT_SYMBOL(free_perfmon_irq); diff --git a/arch/ppc/kernel/perfmon_fsl_booke.c b/arch/ppc/kernel/perfmon_fsl_booke.c new file mode 100644 index 00000000000..03526bfb084 --- /dev/null +++ b/arch/ppc/kernel/perfmon_fsl_booke.c @@ -0,0 +1,222 @@ +/* kernel/perfmon_fsl_booke.c + * Freescale Book-E Performance Monitor code + * + * Author: Andy Fleming + * Copyright (c) 2004 Freescale Semiconductor, Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +static inline u32 get_pmlca(int ctr); +static inline void set_pmlca(int ctr, u32 pmlca); + +static inline u32 get_pmlca(int ctr) +{ + u32 pmlca; + + switch (ctr) { + case 0: + pmlca = mfpmr(PMRN_PMLCA0); + break; + case 1: + pmlca = mfpmr(PMRN_PMLCA1); + break; + case 2: + pmlca = mfpmr(PMRN_PMLCA2); + break; + case 3: + pmlca = mfpmr(PMRN_PMLCA3); + break; + default: + panic("Bad ctr number\n"); + } + + return pmlca; +} + +static inline void set_pmlca(int ctr, u32 pmlca) +{ + switch (ctr) { + case 0: + mtpmr(PMRN_PMLCA0, pmlca); + break; + case 1: + mtpmr(PMRN_PMLCA1, pmlca); + break; + case 2: + mtpmr(PMRN_PMLCA2, pmlca); + break; + case 3: + mtpmr(PMRN_PMLCA3, pmlca); + break; + default: + panic("Bad ctr number\n"); + } +} + +void init_pmc_stop(int ctr) +{ + u32 pmlca = (PMLCA_FC | PMLCA_FCS | PMLCA_FCU | + PMLCA_FCM1 | PMLCA_FCM0); + u32 pmlcb = 0; + + switch (ctr) { + case 0: + mtpmr(PMRN_PMLCA0, pmlca); + mtpmr(PMRN_PMLCB0, pmlcb); + break; + case 1: + mtpmr(PMRN_PMLCA1, pmlca); + mtpmr(PMRN_PMLCB1, pmlcb); + break; + case 2: + mtpmr(PMRN_PMLCA2, pmlca); + mtpmr(PMRN_PMLCB2, pmlcb); + break; + case 3: + mtpmr(PMRN_PMLCA3, pmlca); + mtpmr(PMRN_PMLCB3, pmlcb); + break; + default: + panic("Bad ctr number!\n"); + } +} + +void set_pmc_event(int ctr, int event) +{ + u32 pmlca; + + pmlca = get_pmlca(ctr); + + pmlca = (pmlca & ~PMLCA_EVENT_MASK) | + ((event << PMLCA_EVENT_SHIFT) & + PMLCA_EVENT_MASK); + + set_pmlca(ctr, pmlca); +} + +void set_pmc_user_kernel(int ctr, int user, int kernel) +{ + u32 pmlca; + + pmlca = get_pmlca(ctr); + + if(user) + pmlca &= ~PMLCA_FCU; + else + pmlca |= PMLCA_FCU; + + if(kernel) + pmlca &= ~PMLCA_FCS; + else + pmlca |= PMLCA_FCS; + + set_pmlca(ctr, pmlca); +} + +void set_pmc_marked(int ctr, int mark0, int mark1) +{ + u32 pmlca = get_pmlca(ctr); + + if(mark0) + pmlca &= ~PMLCA_FCM0; + else + pmlca |= PMLCA_FCM0; + + if(mark1) + pmlca &= ~PMLCA_FCM1; + else + pmlca |= PMLCA_FCM1; + + set_pmlca(ctr, pmlca); +} + +void pmc_start_ctr(int ctr, int enable) +{ + u32 pmlca = get_pmlca(ctr); + + pmlca &= ~PMLCA_FC; + + if (enable) + pmlca |= PMLCA_CE; + else + pmlca &= ~PMLCA_CE; + + set_pmlca(ctr, pmlca); +} + +void pmc_start_ctrs(int enable) +{ + u32 pmgc0 = mfpmr(PMRN_PMGC0); + + pmgc0 &= ~PMGC0_FAC; + pmgc0 |= PMGC0_FCECE; + + if (enable) + pmgc0 |= PMGC0_PMIE; + else + pmgc0 &= ~PMGC0_PMIE; + + mtpmr(PMRN_PMGC0, pmgc0); +} + +void pmc_stop_ctrs(void) +{ + u32 pmgc0 = mfpmr(PMRN_PMGC0); + + pmgc0 |= PMGC0_FAC; + + pmgc0 &= ~(PMGC0_PMIE | PMGC0_FCECE); + + mtpmr(PMRN_PMGC0, pmgc0); +} + +void dump_pmcs(void) +{ + printk("pmgc0: %x\n", mfpmr(PMRN_PMGC0)); + printk("pmc\t\tpmlca\t\tpmlcb\n"); + printk("%8x\t%8x\t%8x\n", mfpmr(PMRN_PMC0), + mfpmr(PMRN_PMLCA0), mfpmr(PMRN_PMLCB0)); + printk("%8x\t%8x\t%8x\n", mfpmr(PMRN_PMC1), + mfpmr(PMRN_PMLCA1), mfpmr(PMRN_PMLCB1)); + printk("%8x\t%8x\t%8x\n", mfpmr(PMRN_PMC2), + mfpmr(PMRN_PMLCA2), mfpmr(PMRN_PMLCB2)); + printk("%8x\t%8x\t%8x\n", mfpmr(PMRN_PMC3), + mfpmr(PMRN_PMLCA3), mfpmr(PMRN_PMLCB3)); +} + +EXPORT_SYMBOL(init_pmc_stop); +EXPORT_SYMBOL(set_pmc_event); +EXPORT_SYMBOL(set_pmc_user_kernel); +EXPORT_SYMBOL(set_pmc_marked); +EXPORT_SYMBOL(pmc_start_ctr); +EXPORT_SYMBOL(pmc_start_ctrs); +EXPORT_SYMBOL(pmc_stop_ctrs); +EXPORT_SYMBOL(dump_pmcs); diff --git a/arch/ppc/kernel/ppc-stub.c b/arch/ppc/kernel/ppc-stub.c new file mode 100644 index 00000000000..d61889c2404 --- /dev/null +++ b/arch/ppc/kernel/ppc-stub.c @@ -0,0 +1,867 @@ +/* + * ppc-stub.c: KGDB support for the Linux kernel. + * + * adapted from arch/sparc/kernel/sparc-stub.c for the PowerPC + * some stuff borrowed from Paul Mackerras' xmon + * Copyright (C) 1998 Michael AK Tesch (tesch@cs.wisc.edu) + * + * Modifications to run under Linux + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + * + * This file originally came from the gdb sources, and the + * copyright notices have been retained below. + */ + +/**************************************************************************** + + THIS SOFTWARE IS NOT COPYRIGHTED + + HP offers the following for use in the public domain. HP makes no + warranty with regard to the software or its performance and the + user accepts the software "AS IS" with all faults. + + HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD + TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +****************************************************************************/ + +/**************************************************************************** + * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $ + * + * Module name: remcom.c $ + * Revision: 1.34 $ + * Date: 91/03/09 12:29:49 $ + * Contributor: Lake Stevens Instrument Division$ + * + * Description: low level support for gdb debugger. $ + * + * Considerations: only works on target hardware $ + * + * Written by: Glenn Engel $ + * ModuleState: Experimental $ + * + * NOTES: See Below $ + * + * Modified for SPARC by Stu Grossman, Cygnus Support. + * + * This code has been extensively tested on the Fujitsu SPARClite demo board. + * + * To enable debugger support, two things need to happen. One, a + * call to set_debug_traps() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). Breakpoint() + * simulates a breakpoint by executing a trap #1. + * + ************* + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * qOffsets Get section offsets. Reply is Text=xxx;Data=yyy;Bss=zzz + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * bBB..BB Set baud rate to BB..BB OK or BNN, then sets + * baud rate + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $#. + * + * where + * :: + * :: > + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +void breakinst(void); + +/* + * BUFMAX defines the maximum number of characters in inbound/outbound buffers + * at least NUMREGBYTES*2 are needed for register packets + */ +#define BUFMAX 2048 +static char remcomInBuffer[BUFMAX]; +static char remcomOutBuffer[BUFMAX]; + +static int initialized; +static int kgdb_active; +static int kgdb_started; +static u_int fault_jmp_buf[100]; +static int kdebug; + + +static const char hexchars[]="0123456789abcdef"; + +/* Place where we save old trap entries for restoration - sparc*/ +/* struct tt_entry kgdb_savettable[256]; */ +/* typedef void (*trapfunc_t)(void); */ + +static void kgdb_fault_handler(struct pt_regs *regs); +static int handle_exception (struct pt_regs *regs); + +#if 0 +/* Install an exception handler for kgdb */ +static void exceptionHandler(int tnum, unsigned int *tfunc) +{ + /* We are dorking with a live trap table, all irqs off */ +} +#endif + +int +kgdb_setjmp(long *buf) +{ + asm ("mflr 0; stw 0,0(%0);" + "stw 1,4(%0); stw 2,8(%0);" + "mfcr 0; stw 0,12(%0);" + "stmw 13,16(%0)" + : : "r" (buf)); + /* XXX should save fp regs as well */ + return 0; +} +void +kgdb_longjmp(long *buf, int val) +{ + if (val == 0) + val = 1; + asm ("lmw 13,16(%0);" + "lwz 0,12(%0); mtcrf 0x38,0;" + "lwz 0,0(%0); lwz 1,4(%0); lwz 2,8(%0);" + "mtlr 0; mr 3,%1" + : : "r" (buf), "r" (val)); +} +/* Convert ch from a hex digit to an int */ +static int +hex(unsigned char ch) +{ + if (ch >= 'a' && ch <= 'f') + return ch-'a'+10; + if (ch >= '0' && ch <= '9') + return ch-'0'; + if (ch >= 'A' && ch <= 'F') + return ch-'A'+10; + return -1; +} + +/* Convert the memory pointed to by mem into hex, placing result in buf. + * Return a pointer to the last char put in buf (null), in case of mem fault, + * return 0. + */ +static unsigned char * +mem2hex(const char *mem, char *buf, int count) +{ + unsigned char ch; + unsigned short tmp_s; + unsigned long tmp_l; + + if (kgdb_setjmp((long*)fault_jmp_buf) == 0) { + debugger_fault_handler = kgdb_fault_handler; + + /* Accessing 16 bit and 32 bit objects in a single + ** load instruction is required to avoid bad side + ** effects for some IO registers. + */ + + if ((count == 2) && (((long)mem & 1) == 0)) { + tmp_s = *(unsigned short *)mem; + mem += 2; + *buf++ = hexchars[(tmp_s >> 12) & 0xf]; + *buf++ = hexchars[(tmp_s >> 8) & 0xf]; + *buf++ = hexchars[(tmp_s >> 4) & 0xf]; + *buf++ = hexchars[tmp_s & 0xf]; + + } else if ((count == 4) && (((long)mem & 3) == 0)) { + tmp_l = *(unsigned int *)mem; + mem += 4; + *buf++ = hexchars[(tmp_l >> 28) & 0xf]; + *buf++ = hexchars[(tmp_l >> 24) & 0xf]; + *buf++ = hexchars[(tmp_l >> 20) & 0xf]; + *buf++ = hexchars[(tmp_l >> 16) & 0xf]; + *buf++ = hexchars[(tmp_l >> 12) & 0xf]; + *buf++ = hexchars[(tmp_l >> 8) & 0xf]; + *buf++ = hexchars[(tmp_l >> 4) & 0xf]; + *buf++ = hexchars[tmp_l & 0xf]; + + } else { + while (count-- > 0) { + ch = *mem++; + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch & 0xf]; + } + } + + } else { + /* error condition */ + } + debugger_fault_handler = NULL; + *buf = 0; + return buf; +} + +/* convert the hex array pointed to by buf into binary to be placed in mem + * return a pointer to the character AFTER the last byte written. +*/ +static char * +hex2mem(char *buf, char *mem, int count) +{ + unsigned char ch; + int i; + char *orig_mem; + unsigned short tmp_s; + unsigned long tmp_l; + + orig_mem = mem; + + if (kgdb_setjmp((long*)fault_jmp_buf) == 0) { + debugger_fault_handler = kgdb_fault_handler; + + /* Accessing 16 bit and 32 bit objects in a single + ** store instruction is required to avoid bad side + ** effects for some IO registers. + */ + + if ((count == 2) && (((long)mem & 1) == 0)) { + tmp_s = hex(*buf++) << 12; + tmp_s |= hex(*buf++) << 8; + tmp_s |= hex(*buf++) << 4; + tmp_s |= hex(*buf++); + + *(unsigned short *)mem = tmp_s; + mem += 2; + + } else if ((count == 4) && (((long)mem & 3) == 0)) { + tmp_l = hex(*buf++) << 28; + tmp_l |= hex(*buf++) << 24; + tmp_l |= hex(*buf++) << 20; + tmp_l |= hex(*buf++) << 16; + tmp_l |= hex(*buf++) << 12; + tmp_l |= hex(*buf++) << 8; + tmp_l |= hex(*buf++) << 4; + tmp_l |= hex(*buf++); + + *(unsigned long *)mem = tmp_l; + mem += 4; + + } else { + for (i=0; i# */ +static void +getpacket(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + unsigned char ch; + + do { + /* wait around for the start character, ignore all other + * characters */ + while ((ch = (getDebugChar() & 0x7f)) != '$') ; + + checksum = 0; + xmitcsum = -1; + + count = 0; + + /* now, read until a # or end of buffer is found */ + while (count < BUFMAX) { + ch = getDebugChar() & 0x7f; + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + + if (count >= BUFMAX) + continue; + + buffer[count] = 0; + + if (ch == '#') { + xmitcsum = hex(getDebugChar() & 0x7f) << 4; + xmitcsum |= hex(getDebugChar() & 0x7f); + if (checksum != xmitcsum) + putDebugChar('-'); /* failed checksum */ + else { + putDebugChar('+'); /* successful transfer */ + /* if a sequence char is present, reply the ID */ + if (buffer[2] == ':') { + putDebugChar(buffer[0]); + putDebugChar(buffer[1]); + /* remove sequence chars from buffer */ + count = strlen(buffer); + for (i=3; i <= count; i++) + buffer[i-3] = buffer[i]; + } + } + } + } while (checksum != xmitcsum); +} + +/* send the packet in buffer. */ +static void putpacket(unsigned char *buffer) +{ + unsigned char checksum; + int count; + unsigned char ch, recv; + + /* $#. */ + do { + putDebugChar('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count])) { + putDebugChar(ch); + checksum += ch; + count += 1; + } + + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum & 0xf]); + recv = getDebugChar(); + } while ((recv & 0x7f) != '+'); +} + +static void kgdb_flush_cache_all(void) +{ + flush_instruction_cache(); +} + +/* Set up exception handlers for tracing and breakpoints + * [could be called kgdb_init()] + */ +void set_debug_traps(void) +{ +#if 0 + unsigned char c; + + save_and_cli(flags); + + /* In case GDB is started before us, ack any packets (presumably + * "$?#xx") sitting there. + * + * I've found this code causes more problems than it solves, + * so that's why it's commented out. GDB seems to work fine + * now starting either before or after the kernel -bwb + */ + + while((c = getDebugChar()) != '$'); + while((c = getDebugChar()) != '#'); + c = getDebugChar(); /* eat first csum byte */ + c = getDebugChar(); /* eat second csum byte */ + putDebugChar('+'); /* ack it */ +#endif + debugger = kgdb; + debugger_bpt = kgdb_bpt; + debugger_sstep = kgdb_sstep; + debugger_iabr_match = kgdb_iabr_match; + debugger_dabr_match = kgdb_dabr_match; + + initialized = 1; +} + +static void kgdb_fault_handler(struct pt_regs *regs) +{ + kgdb_longjmp((long*)fault_jmp_buf, 1); +} + +int kgdb_bpt(struct pt_regs *regs) +{ + return handle_exception(regs); +} + +int kgdb_sstep(struct pt_regs *regs) +{ + return handle_exception(regs); +} + +void kgdb(struct pt_regs *regs) +{ + handle_exception(regs); +} + +int kgdb_iabr_match(struct pt_regs *regs) +{ + printk(KERN_ERR "kgdb doesn't support iabr, what?!?\n"); + return handle_exception(regs); +} + +int kgdb_dabr_match(struct pt_regs *regs) +{ + printk(KERN_ERR "kgdb doesn't support dabr, what?!?\n"); + return handle_exception(regs); +} + +/* Convert the hardware trap type code to a unix signal number. */ +/* + * This table contains the mapping between PowerPC hardware trap types, and + * signals, which are primarily what GDB understands. + */ +static struct hard_trap_info +{ + unsigned int tt; /* Trap type code for powerpc */ + unsigned char signo; /* Signal that we map this trap into */ +} hard_trap_info[] = { +#if defined(CONFIG_40x) || defined(CONFIG_BOOKE) + { 0x100, SIGINT }, /* critical input interrupt */ + { 0x200, SIGSEGV }, /* machine check */ + { 0x300, SIGSEGV }, /* data storage */ + { 0x400, SIGBUS }, /* instruction storage */ + { 0x500, SIGINT }, /* interrupt */ + { 0x600, SIGBUS }, /* alignment */ + { 0x700, SIGILL }, /* program */ + { 0x800, SIGILL }, /* reserved */ + { 0x900, SIGILL }, /* reserved */ + { 0xa00, SIGILL }, /* reserved */ + { 0xb00, SIGILL }, /* reserved */ + { 0xc00, SIGCHLD }, /* syscall */ + { 0xd00, SIGILL }, /* reserved */ + { 0xe00, SIGILL }, /* reserved */ + { 0xf00, SIGILL }, /* reserved */ + /* + ** 0x1000 PIT + ** 0x1010 FIT + ** 0x1020 watchdog + ** 0x1100 data TLB miss + ** 0x1200 instruction TLB miss + */ + { 0x2002, SIGTRAP}, /* debug */ +#else + { 0x200, SIGSEGV }, /* machine check */ + { 0x300, SIGSEGV }, /* address error (store) */ + { 0x400, SIGBUS }, /* instruction bus error */ + { 0x500, SIGINT }, /* interrupt */ + { 0x600, SIGBUS }, /* alingment */ + { 0x700, SIGTRAP }, /* breakpoint trap */ + { 0x800, SIGFPE }, /* fpu unavail */ + { 0x900, SIGALRM }, /* decrementer */ + { 0xa00, SIGILL }, /* reserved */ + { 0xb00, SIGILL }, /* reserved */ + { 0xc00, SIGCHLD }, /* syscall */ + { 0xd00, SIGTRAP }, /* single-step/watch */ + { 0xe00, SIGFPE }, /* fp assist */ +#endif + { 0, 0} /* Must be last */ + +}; + +static int computeSignal(unsigned int tt) +{ + struct hard_trap_info *ht; + + for (ht = hard_trap_info; ht->tt && ht->signo; ht++) + if (ht->tt == tt) + return ht->signo; + + return SIGHUP; /* default for things we don't know about */ +} + +#define PC_REGNUM 64 +#define SP_REGNUM 1 + +/* + * This function does all command processing for interfacing to gdb. + */ +static int +handle_exception (struct pt_regs *regs) +{ + int sigval; + int addr; + int length; + char *ptr; + unsigned int msr; + + /* We don't handle user-mode breakpoints. */ + if (user_mode(regs)) + return 0; + + if (debugger_fault_handler) { + debugger_fault_handler(regs); + panic("kgdb longjump failed!\n"); + } + if (kgdb_active) { + printk(KERN_ERR "interrupt while in kgdb, returning\n"); + return 0; + } + + kgdb_active = 1; + kgdb_started = 1; + +#ifdef KGDB_DEBUG + printk("kgdb: entering handle_exception; trap [0x%x]\n", + (unsigned int)regs->trap); +#endif + + kgdb_interruptible(0); + lock_kernel(); + msr = mfmsr(); + mtmsr(msr & ~MSR_EE); /* disable interrupts */ + + if (regs->nip == (unsigned long)breakinst) { + /* Skip over breakpoint trap insn */ + regs->nip += 4; + } + + /* reply to host that an exception has occurred */ + sigval = computeSignal(regs->trap); + ptr = remcomOutBuffer; + + *ptr++ = 'T'; + *ptr++ = hexchars[sigval >> 4]; + *ptr++ = hexchars[sigval & 0xf]; + *ptr++ = hexchars[PC_REGNUM >> 4]; + *ptr++ = hexchars[PC_REGNUM & 0xf]; + *ptr++ = ':'; + ptr = mem2hex((char *)®s->nip, ptr, 4); + *ptr++ = ';'; + *ptr++ = hexchars[SP_REGNUM >> 4]; + *ptr++ = hexchars[SP_REGNUM & 0xf]; + *ptr++ = ':'; + ptr = mem2hex(((char *)regs) + SP_REGNUM*4, ptr, 4); + *ptr++ = ';'; + *ptr++ = 0; + + putpacket(remcomOutBuffer); + if (kdebug) + printk("remcomOutBuffer: %s\n", remcomOutBuffer); + + /* XXX We may want to add some features dealing with poking the + * XXX page tables, ... (look at sparc-stub.c for more info) + * XXX also required hacking to the gdb sources directly... + */ + + while (1) { + remcomOutBuffer[0] = 0; + + getpacket(remcomInBuffer); + switch (remcomInBuffer[0]) { + case '?': /* report most recent signal */ + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = hexchars[sigval >> 4]; + remcomOutBuffer[2] = hexchars[sigval & 0xf]; + remcomOutBuffer[3] = 0; + break; +#if 0 + case 'q': /* this screws up gdb for some reason...*/ + { + extern long _start, sdata, __bss_start; + + ptr = &remcomInBuffer[1]; + if (strncmp(ptr, "Offsets", 7) != 0) + break; + + ptr = remcomOutBuffer; + sprintf(ptr, "Text=%8.8x;Data=%8.8x;Bss=%8.8x", + &_start, &sdata, &__bss_start); + break; + } +#endif + case 'd': + /* toggle debug flag */ + kdebug ^= 1; + break; + + case 'g': /* return the value of the CPU registers. + * some of them are non-PowerPC names :( + * they are stored in gdb like: + * struct { + * u32 gpr[32]; + * f64 fpr[32]; + * u32 pc, ps, cnd, lr; (ps=msr) + * u32 cnt, xer, mq; + * } + */ + { + int i; + ptr = remcomOutBuffer; + /* General Purpose Regs */ + ptr = mem2hex((char *)regs, ptr, 32 * 4); + /* Floating Point Regs - FIXME */ + /*ptr = mem2hex((char *), ptr, 32 * 8);*/ + for(i=0; i<(32*8*2); i++) { /* 2chars/byte */ + ptr[i] = '0'; + } + ptr += 32*8*2; + /* pc, msr, cr, lr, ctr, xer, (mq is unused) */ + ptr = mem2hex((char *)®s->nip, ptr, 4); + ptr = mem2hex((char *)®s->msr, ptr, 4); + ptr = mem2hex((char *)®s->ccr, ptr, 4); + ptr = mem2hex((char *)®s->link, ptr, 4); + ptr = mem2hex((char *)®s->ctr, ptr, 4); + ptr = mem2hex((char *)®s->xer, ptr, 4); + } + break; + + case 'G': /* set the value of the CPU registers */ + { + ptr = &remcomInBuffer[1]; + + /* + * If the stack pointer has moved, you should pray. + * (cause only god can help you). + */ + + /* General Purpose Regs */ + hex2mem(ptr, (char *)regs, 32 * 4); + + /* Floating Point Regs - FIXME?? */ + /*ptr = hex2mem(ptr, ??, 32 * 8);*/ + ptr += 32*8*2; + + /* pc, msr, cr, lr, ctr, xer, (mq is unused) */ + ptr = hex2mem(ptr, (char *)®s->nip, 4); + ptr = hex2mem(ptr, (char *)®s->msr, 4); + ptr = hex2mem(ptr, (char *)®s->ccr, 4); + ptr = hex2mem(ptr, (char *)®s->link, 4); + ptr = hex2mem(ptr, (char *)®s->ctr, 4); + ptr = hex2mem(ptr, (char *)®s->xer, 4); + + strcpy(remcomOutBuffer,"OK"); + } + break; + case 'H': + /* don't do anything, yet, just acknowledge */ + hexToInt(&ptr, &addr); + strcpy(remcomOutBuffer,"OK"); + break; + + case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ + /* Try to read %x,%x. */ + + ptr = &remcomInBuffer[1]; + + if (hexToInt(&ptr, &addr) && *ptr++ == ',' + && hexToInt(&ptr, &length)) { + if (mem2hex((char *)addr, remcomOutBuffer, + length)) + break; + strcpy(remcomOutBuffer, "E03"); + } else + strcpy(remcomOutBuffer, "E01"); + break; + + case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */ + /* Try to read '%x,%x:'. */ + + ptr = &remcomInBuffer[1]; + + if (hexToInt(&ptr, &addr) && *ptr++ == ',' + && hexToInt(&ptr, &length) + && *ptr++ == ':') { + if (hex2mem(ptr, (char *)addr, length)) + strcpy(remcomOutBuffer, "OK"); + else + strcpy(remcomOutBuffer, "E03"); + flush_icache_range(addr, addr+length); + } else + strcpy(remcomOutBuffer, "E02"); + break; + + + case 'k': /* kill the program, actually just continue */ + case 'c': /* cAA..AA Continue; address AA..AA optional */ + /* try to read optional parameter, pc unchanged if no parm */ + + ptr = &remcomInBuffer[1]; + if (hexToInt(&ptr, &addr)) + regs->nip = addr; + +/* Need to flush the instruction cache here, as we may have deposited a + * breakpoint, and the icache probably has no way of knowing that a data ref to + * some location may have changed something that is in the instruction cache. + */ + kgdb_flush_cache_all(); + mtmsr(msr); + + kgdb_interruptible(1); + unlock_kernel(); + kgdb_active = 0; + if (kdebug) { + printk("remcomInBuffer: %s\n", remcomInBuffer); + printk("remcomOutBuffer: %s\n", remcomOutBuffer); + } + return 1; + + case 's': + kgdb_flush_cache_all(); +#if defined(CONFIG_40x) || defined(CONFIG_BOOKE) + mtspr(SPRN_DBCR0, mfspr(SPRN_DBCR0) | DBCR0_IC); + regs->msr |= MSR_DE; +#else + regs->msr |= MSR_SE; +#endif + unlock_kernel(); + kgdb_active = 0; + if (kdebug) { + printk("remcomInBuffer: %s\n", remcomInBuffer); + printk("remcomOutBuffer: %s\n", remcomOutBuffer); + } + return 1; + + case 'r': /* Reset (if user process..exit ???)*/ + panic("kgdb reset."); + break; + } /* switch */ + if (remcomOutBuffer[0] && kdebug) { + printk("remcomInBuffer: %s\n", remcomInBuffer); + printk("remcomOutBuffer: %s\n", remcomOutBuffer); + } + /* reply to the request */ + putpacket(remcomOutBuffer); + } /* while(1) */ +} + +/* This function will generate a breakpoint exception. It is used at the + beginning of a program to sync up with a debugger and can be used + otherwise as a quick means to stop program execution and "break" into + the debugger. */ + +void +breakpoint(void) +{ + if (!initialized) { + printk("breakpoint() called b4 kgdb init\n"); + return; + } + + asm(" .globl breakinst \n\ + breakinst: .long 0x7d821008"); +} + +#ifdef CONFIG_KGDB_CONSOLE +/* Output string in GDB O-packet format if GDB has connected. If nothing + output, returns 0 (caller must then handle output). */ +int +kgdb_output_string (const char* s, unsigned int count) +{ + char buffer[512]; + + if (!kgdb_started) + return 0; + + count = (count <= (sizeof(buffer) / 2 - 2)) + ? count : (sizeof(buffer) / 2 - 2); + + buffer[0] = 'O'; + mem2hex (s, &buffer[1], count); + putpacket(buffer); + + return 1; +} +#endif + +static void sysrq_handle_gdb(int key, struct pt_regs *pt_regs, + struct tty_struct *tty) +{ + printk("Entering GDB stub\n"); + breakpoint(); +} +static struct sysrq_key_op sysrq_gdb_op = { + .handler = sysrq_handle_gdb, + .help_msg = "Gdb", + .action_msg = "GDB", +}; + +static int gdb_register_sysrq(void) +{ + printk("Registering GDB sysrq handler\n"); + register_sysrq_key('g', &sysrq_gdb_op); + return 0; +} +module_init(gdb_register_sysrq); diff --git a/arch/ppc/kernel/ppc_htab.c b/arch/ppc/kernel/ppc_htab.c new file mode 100644 index 00000000000..ca810025993 --- /dev/null +++ b/arch/ppc/kernel/ppc_htab.c @@ -0,0 +1,467 @@ +/* + * PowerPC hash table management proc entry. Will show information + * about the current hash table and will allow changes to it. + * + * Written by Cort Dougan (cort@cs.nmt.edu) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static int ppc_htab_show(struct seq_file *m, void *v); +static ssize_t ppc_htab_write(struct file * file, const char __user * buffer, + size_t count, loff_t *ppos); +extern PTE *Hash, *Hash_end; +extern unsigned long Hash_size, Hash_mask; +extern unsigned long _SDR1; +extern unsigned long htab_reloads; +extern unsigned long htab_preloads; +extern unsigned long htab_evicts; +extern unsigned long pte_misses; +extern unsigned long pte_errors; +extern unsigned int primary_pteg_full; +extern unsigned int htab_hash_searches; + +static int ppc_htab_open(struct inode *inode, struct file *file) +{ + return single_open(file, ppc_htab_show, NULL); +} + +struct file_operations ppc_htab_operations = { + .open = ppc_htab_open, + .read = seq_read, + .llseek = seq_lseek, + .write = ppc_htab_write, + .release = single_release, +}; + +static char *pmc1_lookup(unsigned long mmcr0) +{ + switch ( mmcr0 & (0x7f<<7) ) + { + case 0x0: + return "none"; + case MMCR0_PMC1_CYCLES: + return "cycles"; + case MMCR0_PMC1_ICACHEMISS: + return "ic miss"; + case MMCR0_PMC1_DTLB: + return "dtlb miss"; + default: + return "unknown"; + } +} + +static char *pmc2_lookup(unsigned long mmcr0) +{ + switch ( mmcr0 & 0x3f ) + { + case 0x0: + return "none"; + case MMCR0_PMC2_CYCLES: + return "cycles"; + case MMCR0_PMC2_DCACHEMISS: + return "dc miss"; + case MMCR0_PMC2_ITLB: + return "itlb miss"; + case MMCR0_PMC2_LOADMISSTIME: + return "load miss time"; + default: + return "unknown"; + } +} + +/* + * print some useful info about the hash table. This function + * is _REALLY_ slow (see the nested for loops below) but nothing + * in here should be really timing critical. -- Cort + */ +static int ppc_htab_show(struct seq_file *m, void *v) +{ + unsigned long mmcr0 = 0, pmc1 = 0, pmc2 = 0; +#if defined(CONFIG_PPC_STD_MMU) && !defined(CONFIG_PPC64BRIDGE) + unsigned int kptes = 0, uptes = 0; + PTE *ptr; +#endif /* CONFIG_PPC_STD_MMU */ + + if (cpu_has_feature(CPU_FTR_604_PERF_MON)) { + mmcr0 = mfspr(SPRN_MMCR0); + pmc1 = mfspr(SPRN_PMC1); + pmc2 = mfspr(SPRN_PMC2); + seq_printf(m, + "604 Performance Monitoring\n" + "MMCR0\t\t: %08lx %s%s ", + mmcr0, + ( mmcr0>>28 & 0x2 ) ? "(user mode counted)" : "", + ( mmcr0>>28 & 0x4 ) ? "(kernel mode counted)" : ""); + seq_printf(m, + "\nPMC1\t\t: %08lx (%s)\n" + "PMC2\t\t: %08lx (%s)\n", + pmc1, pmc1_lookup(mmcr0), + pmc2, pmc2_lookup(mmcr0)); + } + +#ifdef CONFIG_PPC_STD_MMU + /* if we don't have a htab */ + if ( Hash_size == 0 ) { + seq_printf(m, "No Hash Table used\n"); + return 0; + } + +#ifndef CONFIG_PPC64BRIDGE + for (ptr = Hash; ptr < Hash_end; ptr++) { + unsigned int mctx, vsid; + + if (!ptr->v) + continue; + /* undo the esid skew */ + vsid = ptr->vsid; + mctx = ((vsid - (vsid & 0xf) * 0x111) >> 4) & 0xfffff; + if (mctx == 0) + kptes++; + else + uptes++; + } +#endif + + seq_printf(m, + "PTE Hash Table Information\n" + "Size\t\t: %luKb\n" + "Buckets\t\t: %lu\n" + "Address\t\t: %08lx\n" + "Entries\t\t: %lu\n" +#ifndef CONFIG_PPC64BRIDGE + "User ptes\t: %u\n" + "Kernel ptes\t: %u\n" + "Percent full\t: %lu%%\n" +#endif + , (unsigned long)(Hash_size>>10), + (Hash_size/(sizeof(PTE)*8)), + (unsigned long)Hash, + Hash_size/sizeof(PTE) +#ifndef CONFIG_PPC64BRIDGE + , uptes, + kptes, + ((kptes+uptes)*100) / (Hash_size/sizeof(PTE)) +#endif + ); + + seq_printf(m, + "Reloads\t\t: %lu\n" + "Preloads\t: %lu\n" + "Searches\t: %u\n" + "Overflows\t: %u\n" + "Evicts\t\t: %lu\n", + htab_reloads, htab_preloads, htab_hash_searches, + primary_pteg_full, htab_evicts); +#endif /* CONFIG_PPC_STD_MMU */ + + seq_printf(m, + "Non-error misses: %lu\n" + "Error misses\t: %lu\n", + pte_misses, pte_errors); + return 0; +} + +/* + * Allow user to define performance counters and resize the hash table + */ +static ssize_t ppc_htab_write(struct file * file, const char __user * ubuffer, + size_t count, loff_t *ppos) +{ +#ifdef CONFIG_PPC_STD_MMU + unsigned long tmp; + char buffer[16]; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + if (strncpy_from_user(buffer, ubuffer, 15)) + return -EFAULT; + buffer[15] = 0; + + /* don't set the htab size for now */ + if ( !strncmp( buffer, "size ", 5) ) + return -EBUSY; + + if ( !strncmp( buffer, "reset", 5) ) + { + if (cpu_has_feature(CPU_FTR_604_PERF_MON)) { + /* reset PMC1 and PMC2 */ + mtspr(SPRN_PMC1, 0); + mtspr(SPRN_PMC2, 0); + } + htab_reloads = 0; + htab_evicts = 0; + pte_misses = 0; + pte_errors = 0; + } + + /* Everything below here requires the performance monitor feature. */ + if (!cpu_has_feature(CPU_FTR_604_PERF_MON)) + return count; + + /* turn off performance monitoring */ + if ( !strncmp( buffer, "off", 3) ) + { + mtspr(SPRN_MMCR0, 0); + mtspr(SPRN_PMC1, 0); + mtspr(SPRN_PMC2, 0); + } + + if ( !strncmp( buffer, "user", 4) ) + { + /* setup mmcr0 and clear the correct pmc */ + tmp = (mfspr(SPRN_MMCR0) & ~(0x60000000)) | 0x20000000; + mtspr(SPRN_MMCR0, tmp); + mtspr(SPRN_PMC1, 0); + mtspr(SPRN_PMC2, 0); + } + + if ( !strncmp( buffer, "kernel", 6) ) + { + /* setup mmcr0 and clear the correct pmc */ + tmp = (mfspr(SPRN_MMCR0) & ~(0x60000000)) | 0x40000000; + mtspr(SPRN_MMCR0, tmp); + mtspr(SPRN_PMC1, 0); + mtspr(SPRN_PMC2, 0); + } + + /* PMC1 values */ + if ( !strncmp( buffer, "dtlb", 4) ) + { + /* setup mmcr0 and clear the correct pmc */ + tmp = (mfspr(SPRN_MMCR0) & ~(0x7F << 7)) | MMCR0_PMC1_DTLB; + mtspr(SPRN_MMCR0, tmp); + mtspr(SPRN_PMC1, 0); + } + + if ( !strncmp( buffer, "ic miss", 7) ) + { + /* setup mmcr0 and clear the correct pmc */ + tmp = (mfspr(SPRN_MMCR0) & ~(0x7F<<7)) | MMCR0_PMC1_ICACHEMISS; + mtspr(SPRN_MMCR0, tmp); + mtspr(SPRN_PMC1, 0); + } + + /* PMC2 values */ + if ( !strncmp( buffer, "load miss time", 14) ) + { + /* setup mmcr0 and clear the correct pmc */ + asm volatile( + "mfspr %0,%1\n\t" /* get current mccr0 */ + "rlwinm %0,%0,0,0,31-6\n\t" /* clear bits [26-31] */ + "ori %0,%0,%2 \n\t" /* or in mmcr0 settings */ + "mtspr %1,%0 \n\t" /* set new mccr0 */ + "mtspr %3,%4 \n\t" /* reset the pmc */ + : "=r" (tmp) + : "i" (SPRN_MMCR0), + "i" (MMCR0_PMC2_LOADMISSTIME), + "i" (SPRN_PMC2), "r" (0) ); + } + + if ( !strncmp( buffer, "itlb", 4) ) + { + /* setup mmcr0 and clear the correct pmc */ + asm volatile( + "mfspr %0,%1\n\t" /* get current mccr0 */ + "rlwinm %0,%0,0,0,31-6\n\t" /* clear bits [26-31] */ + "ori %0,%0,%2 \n\t" /* or in mmcr0 settings */ + "mtspr %1,%0 \n\t" /* set new mccr0 */ + "mtspr %3,%4 \n\t" /* reset the pmc */ + : "=r" (tmp) + : "i" (SPRN_MMCR0), "i" (MMCR0_PMC2_ITLB), + "i" (SPRN_PMC2), "r" (0) ); + } + + if ( !strncmp( buffer, "dc miss", 7) ) + { + /* setup mmcr0 and clear the correct pmc */ + asm volatile( + "mfspr %0,%1\n\t" /* get current mccr0 */ + "rlwinm %0,%0,0,0,31-6\n\t" /* clear bits [26-31] */ + "ori %0,%0,%2 \n\t" /* or in mmcr0 settings */ + "mtspr %1,%0 \n\t" /* set new mccr0 */ + "mtspr %3,%4 \n\t" /* reset the pmc */ + : "=r" (tmp) + : "i" (SPRN_MMCR0), "i" (MMCR0_PMC2_DCACHEMISS), + "i" (SPRN_PMC2), "r" (0) ); + } + + return count; +#else /* CONFIG_PPC_STD_MMU */ + return 0; +#endif /* CONFIG_PPC_STD_MMU */ +} + +int proc_dol2crvec(ctl_table *table, int write, struct file *filp, + void __user *buffer_arg, size_t *lenp, loff_t *ppos) +{ + int vleft, first=1, len, left, val; + char __user *buffer = (char __user *) buffer_arg; + #define TMPBUFLEN 256 + char buf[TMPBUFLEN], *p; + static const char *sizestrings[4] = { + "2MB", "256KB", "512KB", "1MB" + }; + static const char *clockstrings[8] = { + "clock disabled", "+1 clock", "+1.5 clock", "reserved(3)", + "+2 clock", "+2.5 clock", "+3 clock", "reserved(7)" + }; + static const char *typestrings[4] = { + "flow-through burst SRAM", "reserved SRAM", + "pipelined burst SRAM", "pipelined late-write SRAM" + }; + static const char *holdstrings[4] = { + "0.5", "1.0", "(reserved2)", "(reserved3)" + }; + + if (!cpu_has_feature(CPU_FTR_L2CR)) + return -EFAULT; + + if ( /*!table->maxlen ||*/ (*ppos && !write)) { + *lenp = 0; + return 0; + } + + vleft = table->maxlen / sizeof(int); + left = *lenp; + + for (; left /*&& vleft--*/; first=0) { + if (write) { + while (left) { + char c; + if(get_user(c, buffer)) + return -EFAULT; + if (!isspace(c)) + break; + left--; + buffer++; + } + if (!left) + break; + len = left; + if (len > TMPBUFLEN-1) + len = TMPBUFLEN-1; + if(copy_from_user(buf, buffer, len)) + return -EFAULT; + buf[len] = 0; + p = buf; + if (*p < '0' || *p > '9') + break; + val = simple_strtoul(p, &p, 0); + len = p-buf; + if ((len < left) && *p && !isspace(*p)) + break; + buffer += len; + left -= len; + _set_L2CR(val); + } else { + p = buf; + if (!first) + *p++ = '\t'; + val = _get_L2CR(); + p += sprintf(p, "0x%08x: ", val); + p += sprintf(p, " %s", (val >> 31) & 1 ? "enabled" : + "disabled"); + p += sprintf(p, ", %sparity", (val>>30)&1 ? "" : "no "); + p += sprintf(p, ", %s", sizestrings[(val >> 28) & 3]); + p += sprintf(p, ", %s", clockstrings[(val >> 25) & 7]); + p += sprintf(p, ", %s", typestrings[(val >> 23) & 2]); + p += sprintf(p, "%s", (val>>22)&1 ? ", data only" : ""); + p += sprintf(p, "%s", (val>>20)&1 ? ", ZZ enabled": ""); + p += sprintf(p, ", %s", (val>>19)&1 ? "write-through" : + "copy-back"); + p += sprintf(p, "%s", (val>>18)&1 ? ", testing" : ""); + p += sprintf(p, ", %sns hold",holdstrings[(val>>16)&3]); + p += sprintf(p, "%s", (val>>15)&1 ? ", DLL slow" : ""); + p += sprintf(p, "%s", (val>>14)&1 ? ", diff clock" :""); + p += sprintf(p, "%s", (val>>13)&1 ? ", DLL bypass" :""); + + p += sprintf(p,"\n"); + + len = strlen(buf); + if (len > left) + len = left; + if (copy_to_user(buffer, buf, len)) + return -EFAULT; + left -= len; + buffer += len; + break; + } + } + + if (!write && !first && left) { + if(put_user('\n', (char __user *) buffer)) + return -EFAULT; + left--, buffer++; + } + if (write) { + char __user *s = (char __user *) buffer; + while (left) { + char c; + if(get_user(c, s++)) + return -EFAULT; + if (!isspace(c)) + break; + left--; + } + } + if (write && first) + return -EINVAL; + *lenp -= left; + *ppos += *lenp; + return 0; +} + +#ifdef CONFIG_SYSCTL +/* + * Register our sysctl. + */ +static ctl_table htab_ctl_table[]={ + { + .ctl_name = KERN_PPC_L2CR, + .procname = "l2cr", + .mode = 0644, + .proc_handler = &proc_dol2crvec, + }, + { 0, }, +}; +static ctl_table htab_sysctl_root[] = { + { 1, "kernel", NULL, 0, 0755, htab_ctl_table, }, + { 0,}, +}; + +static int __init +register_ppc_htab_sysctl(void) +{ + register_sysctl_table(htab_sysctl_root, 0); + + return 0; +} + +__initcall(register_ppc_htab_sysctl); +#endif diff --git a/arch/ppc/kernel/ppc_ksyms.c b/arch/ppc/kernel/ppc_ksyms.c new file mode 100644 index 00000000000..2ccb58fe4fc --- /dev/null +++ b/arch/ppc/kernel/ppc_ksyms.c @@ -0,0 +1,350 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_8xx +#include +#endif + +/* Tell string.h we don't want memcpy etc. as cpp defines */ +#define EXPORT_SYMTAB_STROPS + +extern void transfer_to_handler(void); +extern void do_syscall_trace(void); +extern void do_IRQ(struct pt_regs *regs); +extern void MachineCheckException(struct pt_regs *regs); +extern void AlignmentException(struct pt_regs *regs); +extern void ProgramCheckException(struct pt_regs *regs); +extern void SingleStepException(struct pt_regs *regs); +extern int do_signal(sigset_t *, struct pt_regs *); +extern int pmac_newworld; +extern int sys_sigreturn(struct pt_regs *regs); + +long long __ashrdi3(long long, int); +long long __ashldi3(long long, int); +long long __lshrdi3(long long, int); + +extern unsigned long mm_ptov (unsigned long paddr); + +EXPORT_SYMBOL(clear_pages); +EXPORT_SYMBOL(clear_user_page); +EXPORT_SYMBOL(do_signal); +EXPORT_SYMBOL(do_syscall_trace); +EXPORT_SYMBOL(transfer_to_handler); +EXPORT_SYMBOL(do_IRQ); +EXPORT_SYMBOL(MachineCheckException); +EXPORT_SYMBOL(AlignmentException); +EXPORT_SYMBOL(ProgramCheckException); +EXPORT_SYMBOL(SingleStepException); +EXPORT_SYMBOL(sys_sigreturn); +EXPORT_SYMBOL(ppc_n_lost_interrupts); +EXPORT_SYMBOL(ppc_lost_interrupts); + +EXPORT_SYMBOL(ISA_DMA_THRESHOLD); +EXPORT_SYMBOL(DMA_MODE_READ); +EXPORT_SYMBOL(DMA_MODE_WRITE); +#if defined(CONFIG_PPC_PREP) +EXPORT_SYMBOL(_prep_type); +EXPORT_SYMBOL(ucSystemType); +#endif + +#if !defined(__INLINE_BITOPS) +EXPORT_SYMBOL(set_bit); +EXPORT_SYMBOL(clear_bit); +EXPORT_SYMBOL(change_bit); +EXPORT_SYMBOL(test_and_set_bit); +EXPORT_SYMBOL(test_and_clear_bit); +EXPORT_SYMBOL(test_and_change_bit); +#endif /* __INLINE_BITOPS */ + +EXPORT_SYMBOL(strcpy); +EXPORT_SYMBOL(strncpy); +EXPORT_SYMBOL(strcat); +EXPORT_SYMBOL(strncat); +EXPORT_SYMBOL(strchr); +EXPORT_SYMBOL(strrchr); +EXPORT_SYMBOL(strpbrk); +EXPORT_SYMBOL(strstr); +EXPORT_SYMBOL(strlen); +EXPORT_SYMBOL(strnlen); +EXPORT_SYMBOL(strcmp); +EXPORT_SYMBOL(strncmp); +EXPORT_SYMBOL(strcasecmp); +EXPORT_SYMBOL(__div64_32); + +EXPORT_SYMBOL(csum_partial); +EXPORT_SYMBOL(csum_partial_copy_generic); +EXPORT_SYMBOL(ip_fast_csum); +EXPORT_SYMBOL(csum_tcpudp_magic); + +EXPORT_SYMBOL(__copy_tofrom_user); +EXPORT_SYMBOL(__clear_user); +EXPORT_SYMBOL(__strncpy_from_user); +EXPORT_SYMBOL(__strnlen_user); + +/* +EXPORT_SYMBOL(inb); +EXPORT_SYMBOL(inw); +EXPORT_SYMBOL(inl); +EXPORT_SYMBOL(outb); +EXPORT_SYMBOL(outw); +EXPORT_SYMBOL(outl); +EXPORT_SYMBOL(outsl);*/ + +EXPORT_SYMBOL(_insb); +EXPORT_SYMBOL(_outsb); +EXPORT_SYMBOL(_insw); +EXPORT_SYMBOL(_outsw); +EXPORT_SYMBOL(_insl); +EXPORT_SYMBOL(_outsl); +EXPORT_SYMBOL(_insw_ns); +EXPORT_SYMBOL(_outsw_ns); +EXPORT_SYMBOL(_insl_ns); +EXPORT_SYMBOL(_outsl_ns); +EXPORT_SYMBOL(iopa); +EXPORT_SYMBOL(mm_ptov); +EXPORT_SYMBOL(ioremap); +#ifdef CONFIG_44x +EXPORT_SYMBOL(ioremap64); +#endif +EXPORT_SYMBOL(__ioremap); +EXPORT_SYMBOL(iounmap); +EXPORT_SYMBOL(ioremap_bot); /* aka VMALLOC_END */ + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +EXPORT_SYMBOL(ppc_ide_md); +#endif + +#ifdef CONFIG_PCI +EXPORT_SYMBOL(isa_io_base); +EXPORT_SYMBOL(isa_mem_base); +EXPORT_SYMBOL(pci_dram_offset); +EXPORT_SYMBOL(pci_alloc_consistent); +EXPORT_SYMBOL(pci_free_consistent); +EXPORT_SYMBOL(pci_bus_io_base); +EXPORT_SYMBOL(pci_bus_io_base_phys); +EXPORT_SYMBOL(pci_bus_mem_base_phys); +EXPORT_SYMBOL(pci_bus_to_hose); +EXPORT_SYMBOL(pci_resource_to_bus); +EXPORT_SYMBOL(pci_phys_to_bus); +EXPORT_SYMBOL(pci_bus_to_phys); +#endif /* CONFIG_PCI */ + +#ifdef CONFIG_NOT_COHERENT_CACHE +EXPORT_SYMBOL(flush_dcache_all); +#endif + +EXPORT_SYMBOL(start_thread); +EXPORT_SYMBOL(kernel_thread); + +EXPORT_SYMBOL(flush_instruction_cache); +EXPORT_SYMBOL(giveup_fpu); +EXPORT_SYMBOL(flush_icache_range); +EXPORT_SYMBOL(flush_dcache_range); +EXPORT_SYMBOL(flush_icache_user_range); +EXPORT_SYMBOL(flush_dcache_page); +EXPORT_SYMBOL(flush_tlb_kernel_range); +EXPORT_SYMBOL(flush_tlb_page); +EXPORT_SYMBOL(_tlbie); +#ifdef CONFIG_ALTIVEC +EXPORT_SYMBOL(last_task_used_altivec); +EXPORT_SYMBOL(giveup_altivec); +#endif /* CONFIG_ALTIVEC */ +#ifdef CONFIG_SPE +EXPORT_SYMBOL(last_task_used_spe); +EXPORT_SYMBOL(giveup_spe); +#endif /* CONFIG_SPE */ +#ifdef CONFIG_SMP +EXPORT_SYMBOL(smp_call_function); +EXPORT_SYMBOL(smp_hw_index); +#endif + +EXPORT_SYMBOL(ppc_md); + +#ifdef CONFIG_ADB +EXPORT_SYMBOL(adb_request); +EXPORT_SYMBOL(adb_register); +EXPORT_SYMBOL(adb_unregister); +EXPORT_SYMBOL(adb_poll); +EXPORT_SYMBOL(adb_try_handler_change); +#endif /* CONFIG_ADB */ +#ifdef CONFIG_ADB_CUDA +EXPORT_SYMBOL(cuda_request); +EXPORT_SYMBOL(cuda_poll); +#endif /* CONFIG_ADB_CUDA */ +#ifdef CONFIG_PPC_MULTIPLATFORM +EXPORT_SYMBOL(_machine); +#endif +#ifdef CONFIG_PPC_PMAC +EXPORT_SYMBOL(sys_ctrler); +EXPORT_SYMBOL(pmac_newworld); +#endif +#ifdef CONFIG_PPC_OF +EXPORT_SYMBOL(find_devices); +EXPORT_SYMBOL(find_type_devices); +EXPORT_SYMBOL(find_compatible_devices); +EXPORT_SYMBOL(find_path_device); +EXPORT_SYMBOL(device_is_compatible); +EXPORT_SYMBOL(machine_is_compatible); +EXPORT_SYMBOL(find_all_nodes); +EXPORT_SYMBOL(get_property); +EXPORT_SYMBOL(request_OF_resource); +EXPORT_SYMBOL(release_OF_resource); +EXPORT_SYMBOL(pci_busdev_to_OF_node); +EXPORT_SYMBOL(pci_device_to_OF_node); +EXPORT_SYMBOL(pci_device_from_OF_node); +EXPORT_SYMBOL(of_find_node_by_name); +EXPORT_SYMBOL(of_find_node_by_type); +EXPORT_SYMBOL(of_find_compatible_node); +EXPORT_SYMBOL(of_find_node_by_path); +EXPORT_SYMBOL(of_find_all_nodes); +EXPORT_SYMBOL(of_get_parent); +EXPORT_SYMBOL(of_get_next_child); +EXPORT_SYMBOL(of_node_get); +EXPORT_SYMBOL(of_node_put); +#endif /* CONFIG_PPC_OF */ +#if defined(CONFIG_BOOTX_TEXT) +EXPORT_SYMBOL(btext_update_display); +#endif +#if defined(CONFIG_SCSI) && defined(CONFIG_PPC_PMAC) +EXPORT_SYMBOL(note_scsi_host); +#endif +#ifdef CONFIG_VT +EXPORT_SYMBOL(kd_mksound); +#endif +EXPORT_SYMBOL(to_tm); + +EXPORT_SYMBOL(pm_power_off); + +EXPORT_SYMBOL(__ashrdi3); +EXPORT_SYMBOL(__ashldi3); +EXPORT_SYMBOL(__lshrdi3); +EXPORT_SYMBOL(memcpy); +EXPORT_SYMBOL(memset); +EXPORT_SYMBOL(memmove); +EXPORT_SYMBOL(memscan); +EXPORT_SYMBOL(memcmp); +EXPORT_SYMBOL(memchr); + +#if defined(CONFIG_FB_VGA16_MODULE) +EXPORT_SYMBOL(screen_info); +#endif + +EXPORT_SYMBOL(__delay); +#ifndef INLINE_IRQS +EXPORT_SYMBOL(local_irq_enable); +EXPORT_SYMBOL(local_irq_enable_end); +EXPORT_SYMBOL(local_irq_disable); +EXPORT_SYMBOL(local_irq_disable_end); +EXPORT_SYMBOL(local_save_flags_ptr); +EXPORT_SYMBOL(local_save_flags_ptr_end); +EXPORT_SYMBOL(local_irq_restore); +EXPORT_SYMBOL(local_irq_restore_end); +#endif +EXPORT_SYMBOL(timer_interrupt); +EXPORT_SYMBOL(irq_desc); +EXPORT_SYMBOL(tb_ticks_per_jiffy); +EXPORT_SYMBOL(get_wchan); +EXPORT_SYMBOL(console_drivers); +#ifdef CONFIG_XMON +EXPORT_SYMBOL(xmon); +EXPORT_SYMBOL(xmon_printf); +#endif +EXPORT_SYMBOL(__up); +EXPORT_SYMBOL(__down); +EXPORT_SYMBOL(__down_interruptible); + +#if defined(CONFIG_KGDB) || defined(CONFIG_XMON) +extern void (*debugger)(struct pt_regs *regs); +extern int (*debugger_bpt)(struct pt_regs *regs); +extern int (*debugger_sstep)(struct pt_regs *regs); +extern int (*debugger_iabr_match)(struct pt_regs *regs); +extern int (*debugger_dabr_match)(struct pt_regs *regs); +extern void (*debugger_fault_handler)(struct pt_regs *regs); + +EXPORT_SYMBOL(debugger); +EXPORT_SYMBOL(debugger_bpt); +EXPORT_SYMBOL(debugger_sstep); +EXPORT_SYMBOL(debugger_iabr_match); +EXPORT_SYMBOL(debugger_dabr_match); +EXPORT_SYMBOL(debugger_fault_handler); +#endif + +#ifdef CONFIG_8xx +EXPORT_SYMBOL(cpm_install_handler); +EXPORT_SYMBOL(cpm_free_handler); +#endif /* CONFIG_8xx */ +#if defined(CONFIG_8xx) || defined(CONFIG_40x) || defined(CONFIG_85xx) ||\ + defined(CONFIG_83xx) +EXPORT_SYMBOL(__res); +#endif + +EXPORT_SYMBOL(next_mmu_context); +EXPORT_SYMBOL(set_context); +EXPORT_SYMBOL(handle_mm_fault); /* For MOL */ +EXPORT_SYMBOL(disarm_decr); +#ifdef CONFIG_PPC_STD_MMU +extern long mol_trampoline; +EXPORT_SYMBOL(mol_trampoline); /* For MOL */ +EXPORT_SYMBOL(flush_hash_pages); /* For MOL */ +#ifdef CONFIG_SMP +extern int mmu_hash_lock; +EXPORT_SYMBOL(mmu_hash_lock); /* For MOL */ +#endif /* CONFIG_SMP */ +extern long *intercept_table; +EXPORT_SYMBOL(intercept_table); +#endif /* CONFIG_PPC_STD_MMU */ +EXPORT_SYMBOL(cur_cpu_spec); +#ifdef CONFIG_PPC_PMAC +extern unsigned long agp_special_page; +EXPORT_SYMBOL(agp_special_page); +#endif +#if defined(CONFIG_40x) || defined(CONFIG_BOOKE) +EXPORT_SYMBOL(__mtdcr); +EXPORT_SYMBOL(__mfdcr); +#endif diff --git a/arch/ppc/kernel/process.c b/arch/ppc/kernel/process.c new file mode 100644 index 00000000000..82de66e4db6 --- /dev/null +++ b/arch/ppc/kernel/process.c @@ -0,0 +1,781 @@ +/* + * arch/ppc/kernel/process.c + * + * Derived from "arch/i386/kernel/process.c" + * Copyright (C) 1995 Linus Torvalds + * + * Updated and modified by Cort Dougan (cort@cs.nmt.edu) and + * Paul Mackerras (paulus@cs.anu.edu.au) + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +extern unsigned long _get_SP(void); + +struct task_struct *last_task_used_math = NULL; +struct task_struct *last_task_used_altivec = NULL; +struct task_struct *last_task_used_spe = NULL; + +static struct fs_struct init_fs = INIT_FS; +static struct files_struct init_files = INIT_FILES; +static struct signal_struct init_signals = INIT_SIGNALS(init_signals); +static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); +struct mm_struct init_mm = INIT_MM(init_mm); +EXPORT_SYMBOL(init_mm); + +/* this is 8kB-aligned so we can get to the thread_info struct + at the base of it from the stack pointer with 1 integer instruction. */ +union thread_union init_thread_union + __attribute__((__section__(".data.init_task"))) = +{ INIT_THREAD_INFO(init_task) }; + +/* initial task structure */ +struct task_struct init_task = INIT_TASK(init_task); +EXPORT_SYMBOL(init_task); + +/* only used to get secondary processor up */ +struct task_struct *current_set[NR_CPUS] = {&init_task, }; + +#undef SHOW_TASK_SWITCHES +#undef CHECK_STACK + +#if defined(CHECK_STACK) +unsigned long +kernel_stack_top(struct task_struct *tsk) +{ + return ((unsigned long)tsk) + sizeof(union task_union); +} + +unsigned long +task_top(struct task_struct *tsk) +{ + return ((unsigned long)tsk) + sizeof(struct thread_info); +} + +/* check to make sure the kernel stack is healthy */ +int check_stack(struct task_struct *tsk) +{ + unsigned long stack_top = kernel_stack_top(tsk); + unsigned long tsk_top = task_top(tsk); + int ret = 0; + +#if 0 + /* check thread magic */ + if ( tsk->thread.magic != THREAD_MAGIC ) + { + ret |= 1; + printk("thread.magic bad: %08x\n", tsk->thread.magic); + } +#endif + + if ( !tsk ) + printk("check_stack(): tsk bad tsk %p\n",tsk); + + /* check if stored ksp is bad */ + if ( (tsk->thread.ksp > stack_top) || (tsk->thread.ksp < tsk_top) ) + { + printk("stack out of bounds: %s/%d\n" + " tsk_top %08lx ksp %08lx stack_top %08lx\n", + tsk->comm,tsk->pid, + tsk_top, tsk->thread.ksp, stack_top); + ret |= 2; + } + + /* check if stack ptr RIGHT NOW is bad */ + if ( (tsk == current) && ((_get_SP() > stack_top ) || (_get_SP() < tsk_top)) ) + { + printk("current stack ptr out of bounds: %s/%d\n" + " tsk_top %08lx sp %08lx stack_top %08lx\n", + current->comm,current->pid, + tsk_top, _get_SP(), stack_top); + ret |= 4; + } + +#if 0 + /* check amount of free stack */ + for ( i = (unsigned long *)task_top(tsk) ; i < kernel_stack_top(tsk) ; i++ ) + { + if ( !i ) + printk("check_stack(): i = %p\n", i); + if ( *i != 0 ) + { + /* only notify if it's less than 900 bytes */ + if ( (i - (unsigned long *)task_top(tsk)) < 900 ) + printk("%d bytes free on stack\n", + i - task_top(tsk)); + break; + } + } +#endif + + if (ret) + { + panic("bad kernel stack"); + } + return(ret); +} +#endif /* defined(CHECK_STACK) */ + +#ifdef CONFIG_ALTIVEC +int +dump_altivec(struct pt_regs *regs, elf_vrregset_t *vrregs) +{ + if (regs->msr & MSR_VEC) + giveup_altivec(current); + memcpy(vrregs, ¤t->thread.vr[0], sizeof(*vrregs)); + return 1; +} + +void +enable_kernel_altivec(void) +{ + WARN_ON(preemptible()); + +#ifdef CONFIG_SMP + if (current->thread.regs && (current->thread.regs->msr & MSR_VEC)) + giveup_altivec(current); + else + giveup_altivec(NULL); /* just enable AltiVec for kernel - force */ +#else + giveup_altivec(last_task_used_altivec); +#endif /* __SMP __ */ +} +EXPORT_SYMBOL(enable_kernel_altivec); +#endif /* CONFIG_ALTIVEC */ + +#ifdef CONFIG_SPE +int +dump_spe(struct pt_regs *regs, elf_vrregset_t *evrregs) +{ + if (regs->msr & MSR_SPE) + giveup_spe(current); + /* We copy u32 evr[32] + u64 acc + u32 spefscr -> 35 */ + memcpy(evrregs, ¤t->thread.evr[0], sizeof(u32) * 35); + return 1; +} + +void +enable_kernel_spe(void) +{ + WARN_ON(preemptible()); + +#ifdef CONFIG_SMP + if (current->thread.regs && (current->thread.regs->msr & MSR_SPE)) + giveup_spe(current); + else + giveup_spe(NULL); /* just enable SPE for kernel - force */ +#else + giveup_spe(last_task_used_spe); +#endif /* __SMP __ */ +} +EXPORT_SYMBOL(enable_kernel_spe); +#endif /* CONFIG_SPE */ + +void +enable_kernel_fp(void) +{ + WARN_ON(preemptible()); + +#ifdef CONFIG_SMP + if (current->thread.regs && (current->thread.regs->msr & MSR_FP)) + giveup_fpu(current); + else + giveup_fpu(NULL); /* just enables FP for kernel */ +#else + giveup_fpu(last_task_used_math); +#endif /* CONFIG_SMP */ +} +EXPORT_SYMBOL(enable_kernel_fp); + +int +dump_task_fpu(struct task_struct *tsk, elf_fpregset_t *fpregs) +{ + preempt_disable(); + if (tsk->thread.regs && (tsk->thread.regs->msr & MSR_FP)) + giveup_fpu(tsk); + preempt_enable(); + memcpy(fpregs, &tsk->thread.fpr[0], sizeof(*fpregs)); + return 1; +} + +struct task_struct *__switch_to(struct task_struct *prev, + struct task_struct *new) +{ + struct thread_struct *new_thread, *old_thread; + unsigned long s; + struct task_struct *last; + + local_irq_save(s); +#ifdef CHECK_STACK + check_stack(prev); + check_stack(new); +#endif + +#ifdef CONFIG_SMP + /* avoid complexity of lazy save/restore of fpu + * by just saving it every time we switch out if + * this task used the fpu during the last quantum. + * + * If it tries to use the fpu again, it'll trap and + * reload its fp regs. So we don't have to do a restore + * every switch, just a save. + * -- Cort + */ + if (prev->thread.regs && (prev->thread.regs->msr & MSR_FP)) + giveup_fpu(prev); +#ifdef CONFIG_ALTIVEC + /* + * If the previous thread used altivec in the last quantum + * (thus changing altivec regs) then save them. + * We used to check the VRSAVE register but not all apps + * set it, so we don't rely on it now (and in fact we need + * to save & restore VSCR even if VRSAVE == 0). -- paulus + * + * On SMP we always save/restore altivec regs just to avoid the + * complexity of changing processors. + * -- Cort + */ + if ((prev->thread.regs && (prev->thread.regs->msr & MSR_VEC))) + giveup_altivec(prev); +#endif /* CONFIG_ALTIVEC */ +#ifdef CONFIG_SPE + /* + * If the previous thread used spe in the last quantum + * (thus changing spe regs) then save them. + * + * On SMP we always save/restore spe regs just to avoid the + * complexity of changing processors. + */ + if ((prev->thread.regs && (prev->thread.regs->msr & MSR_SPE))) + giveup_spe(prev); +#endif /* CONFIG_SPE */ +#endif /* CONFIG_SMP */ + + /* Avoid the trap. On smp this this never happens since + * we don't set last_task_used_altivec -- Cort + */ + if (new->thread.regs && last_task_used_altivec == new) + new->thread.regs->msr |= MSR_VEC; +#ifdef CONFIG_SPE + /* Avoid the trap. On smp this this never happens since + * we don't set last_task_used_spe + */ + if (new->thread.regs && last_task_used_spe == new) + new->thread.regs->msr |= MSR_SPE; +#endif /* CONFIG_SPE */ + new_thread = &new->thread; + old_thread = ¤t->thread; + last = _switch(old_thread, new_thread); + local_irq_restore(s); + return last; +} + +void show_regs(struct pt_regs * regs) +{ + int i, trap; + + printk("NIP: %08lX LR: %08lX SP: %08lX REGS: %p TRAP: %04lx %s\n", + regs->nip, regs->link, regs->gpr[1], regs, regs->trap, + print_tainted()); + printk("MSR: %08lx EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", + regs->msr, regs->msr&MSR_EE ? 1 : 0, regs->msr&MSR_PR ? 1 : 0, + regs->msr & MSR_FP ? 1 : 0,regs->msr&MSR_ME ? 1 : 0, + regs->msr&MSR_IR ? 1 : 0, + regs->msr&MSR_DR ? 1 : 0); + trap = TRAP(regs); + if (trap == 0x300 || trap == 0x600) + printk("DAR: %08lX, DSISR: %08lX\n", regs->dar, regs->dsisr); + printk("TASK = %p[%d] '%s' THREAD: %p\n", + current, current->pid, current->comm, current->thread_info); + printk("Last syscall: %ld ", current->thread.last_syscall); + +#ifdef CONFIG_SMP + printk(" CPU: %d", smp_processor_id()); +#endif /* CONFIG_SMP */ + + for (i = 0; i < 32; i++) { + long r; + if ((i % 8) == 0) + printk("\n" KERN_INFO "GPR%02d: ", i); + if (__get_user(r, ®s->gpr[i])) + break; + printk("%08lX ", r); + if (i == 12 && !FULL_REGS(regs)) + break; + } + printk("\n"); +#ifdef CONFIG_KALLSYMS + /* + * Lookup NIP late so we have the best change of getting the + * above info out without failing + */ + printk("NIP [%08lx] ", regs->nip); + print_symbol("%s\n", regs->nip); + printk("LR [%08lx] ", regs->link); + print_symbol("%s\n", regs->link); +#endif + show_stack(current, (unsigned long *) regs->gpr[1]); +} + +void exit_thread(void) +{ + if (last_task_used_math == current) + last_task_used_math = NULL; + if (last_task_used_altivec == current) + last_task_used_altivec = NULL; +#ifdef CONFIG_SPE + if (last_task_used_spe == current) + last_task_used_spe = NULL; +#endif +} + +void flush_thread(void) +{ + if (last_task_used_math == current) + last_task_used_math = NULL; + if (last_task_used_altivec == current) + last_task_used_altivec = NULL; +#ifdef CONFIG_SPE + if (last_task_used_spe == current) + last_task_used_spe = NULL; +#endif +} + +void +release_thread(struct task_struct *t) +{ +} + +/* + * This gets called before we allocate a new thread and copy + * the current task into it. + */ +void prepare_to_copy(struct task_struct *tsk) +{ + struct pt_regs *regs = tsk->thread.regs; + + if (regs == NULL) + return; + preempt_disable(); + if (regs->msr & MSR_FP) + giveup_fpu(current); +#ifdef CONFIG_ALTIVEC + if (regs->msr & MSR_VEC) + giveup_altivec(current); +#endif /* CONFIG_ALTIVEC */ +#ifdef CONFIG_SPE + if (regs->msr & MSR_SPE) + giveup_spe(current); +#endif /* CONFIG_SPE */ + preempt_enable(); +} + +/* + * Copy a thread.. + */ +int +copy_thread(int nr, unsigned long clone_flags, unsigned long usp, + unsigned long unused, + struct task_struct *p, struct pt_regs *regs) +{ + struct pt_regs *childregs, *kregs; + extern void ret_from_fork(void); + unsigned long sp = (unsigned long)p->thread_info + THREAD_SIZE; + unsigned long childframe; + + CHECK_FULL_REGS(regs); + /* Copy registers */ + sp -= sizeof(struct pt_regs); + childregs = (struct pt_regs *) sp; + *childregs = *regs; + if ((childregs->msr & MSR_PR) == 0) { + /* for kernel thread, set `current' and stackptr in new task */ + childregs->gpr[1] = sp + sizeof(struct pt_regs); + childregs->gpr[2] = (unsigned long) p; + p->thread.regs = NULL; /* no user register state */ + } else { + childregs->gpr[1] = usp; + p->thread.regs = childregs; + if (clone_flags & CLONE_SETTLS) + childregs->gpr[2] = childregs->gpr[6]; + } + childregs->gpr[3] = 0; /* Result from fork() */ + sp -= STACK_FRAME_OVERHEAD; + childframe = sp; + + /* + * The way this works is that at some point in the future + * some task will call _switch to switch to the new task. + * That will pop off the stack frame created below and start + * the new task running at ret_from_fork. The new task will + * do some house keeping and then return from the fork or clone + * system call, using the stack frame created above. + */ + sp -= sizeof(struct pt_regs); + kregs = (struct pt_regs *) sp; + sp -= STACK_FRAME_OVERHEAD; + p->thread.ksp = sp; + kregs->nip = (unsigned long)ret_from_fork; + + p->thread.last_syscall = -1; + + return 0; +} + +/* + * Set up a thread for executing a new program + */ +void start_thread(struct pt_regs *regs, unsigned long nip, unsigned long sp) +{ + set_fs(USER_DS); + memset(regs->gpr, 0, sizeof(regs->gpr)); + regs->ctr = 0; + regs->link = 0; + regs->xer = 0; + regs->ccr = 0; + regs->mq = 0; + regs->nip = nip; + regs->gpr[1] = sp; + regs->msr = MSR_USER; + if (last_task_used_math == current) + last_task_used_math = NULL; + if (last_task_used_altivec == current) + last_task_used_altivec = NULL; +#ifdef CONFIG_SPE + if (last_task_used_spe == current) + last_task_used_spe = NULL; +#endif + memset(current->thread.fpr, 0, sizeof(current->thread.fpr)); + current->thread.fpscr = 0; +#ifdef CONFIG_ALTIVEC + memset(current->thread.vr, 0, sizeof(current->thread.vr)); + memset(¤t->thread.vscr, 0, sizeof(current->thread.vscr)); + current->thread.vrsave = 0; + current->thread.used_vr = 0; +#endif /* CONFIG_ALTIVEC */ +#ifdef CONFIG_SPE + memset(current->thread.evr, 0, sizeof(current->thread.evr)); + current->thread.acc = 0; + current->thread.spefscr = 0; + current->thread.used_spe = 0; +#endif /* CONFIG_SPE */ +} + +#define PR_FP_ALL_EXCEPT (PR_FP_EXC_DIV | PR_FP_EXC_OVF | PR_FP_EXC_UND \ + | PR_FP_EXC_RES | PR_FP_EXC_INV) + +int set_fpexc_mode(struct task_struct *tsk, unsigned int val) +{ + struct pt_regs *regs = tsk->thread.regs; + + /* This is a bit hairy. If we are an SPE enabled processor + * (have embedded fp) we store the IEEE exception enable flags in + * fpexc_mode. fpexc_mode is also used for setting FP exception + * mode (asyn, precise, disabled) for 'Classic' FP. */ + if (val & PR_FP_EXC_SW_ENABLE) { +#ifdef CONFIG_SPE + tsk->thread.fpexc_mode = val & + (PR_FP_EXC_SW_ENABLE | PR_FP_ALL_EXCEPT); +#else + return -EINVAL; +#endif + } else { + /* on a CONFIG_SPE this does not hurt us. The bits that + * __pack_fe01 use do not overlap with bits used for + * PR_FP_EXC_SW_ENABLE. Additionally, the MSR[FE0,FE1] bits + * on CONFIG_SPE implementations are reserved so writing to + * them does not change anything */ + if (val > PR_FP_EXC_PRECISE) + return -EINVAL; + tsk->thread.fpexc_mode = __pack_fe01(val); + if (regs != NULL && (regs->msr & MSR_FP) != 0) + regs->msr = (regs->msr & ~(MSR_FE0|MSR_FE1)) + | tsk->thread.fpexc_mode; + } + return 0; +} + +int get_fpexc_mode(struct task_struct *tsk, unsigned long adr) +{ + unsigned int val; + + if (tsk->thread.fpexc_mode & PR_FP_EXC_SW_ENABLE) +#ifdef CONFIG_SPE + val = tsk->thread.fpexc_mode; +#else + return -EINVAL; +#endif + else + val = __unpack_fe01(tsk->thread.fpexc_mode); + return put_user(val, (unsigned int __user *) adr); +} + +int sys_clone(unsigned long clone_flags, unsigned long usp, + int __user *parent_tidp, void __user *child_threadptr, + int __user *child_tidp, int p6, + struct pt_regs *regs) +{ + CHECK_FULL_REGS(regs); + if (usp == 0) + usp = regs->gpr[1]; /* stack pointer for child */ + return do_fork(clone_flags, usp, regs, 0, parent_tidp, child_tidp); +} + +int sys_fork(int p1, int p2, int p3, int p4, int p5, int p6, + struct pt_regs *regs) +{ + CHECK_FULL_REGS(regs); + return do_fork(SIGCHLD, regs->gpr[1], regs, 0, NULL, NULL); +} + +int sys_vfork(int p1, int p2, int p3, int p4, int p5, int p6, + struct pt_regs *regs) +{ + CHECK_FULL_REGS(regs); + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->gpr[1], + regs, 0, NULL, NULL); +} + +int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2, + unsigned long a3, unsigned long a4, unsigned long a5, + struct pt_regs *regs) +{ + int error; + char * filename; + + filename = getname((char __user *) a0); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + goto out; + preempt_disable(); + if (regs->msr & MSR_FP) + giveup_fpu(current); +#ifdef CONFIG_ALTIVEC + if (regs->msr & MSR_VEC) + giveup_altivec(current); +#endif /* CONFIG_ALTIVEC */ +#ifdef CONFIG_SPE + if (regs->msr & MSR_SPE) + giveup_spe(current); +#endif /* CONFIG_SPE */ + preempt_enable(); + error = do_execve(filename, (char __user *__user *) a1, + (char __user *__user *) a2, regs); + if (error == 0) { + task_lock(current); + current->ptrace &= ~PT_DTRACE; + task_unlock(current); + } + putname(filename); +out: + return error; +} + +void dump_stack(void) +{ + show_stack(current, NULL); +} + +EXPORT_SYMBOL(dump_stack); + +void show_stack(struct task_struct *tsk, unsigned long *stack) +{ + unsigned long sp, stack_top, prev_sp, ret; + int count = 0; + unsigned long next_exc = 0; + struct pt_regs *regs; + extern char ret_from_except, ret_from_except_full, ret_from_syscall; + + sp = (unsigned long) stack; + if (tsk == NULL) + tsk = current; + if (sp == 0) { + if (tsk == current) + asm("mr %0,1" : "=r" (sp)); + else + sp = tsk->thread.ksp; + } + + prev_sp = (unsigned long) (tsk->thread_info + 1); + stack_top = (unsigned long) tsk->thread_info + THREAD_SIZE; + while (count < 16 && sp > prev_sp && sp < stack_top && (sp & 3) == 0) { + if (count == 0) { + printk("Call trace:"); +#ifdef CONFIG_KALLSYMS + printk("\n"); +#endif + } else { + if (next_exc) { + ret = next_exc; + next_exc = 0; + } else + ret = *(unsigned long *)(sp + 4); + printk(" [%08lx] ", ret); +#ifdef CONFIG_KALLSYMS + print_symbol("%s", ret); + printk("\n"); +#endif + if (ret == (unsigned long) &ret_from_except + || ret == (unsigned long) &ret_from_except_full + || ret == (unsigned long) &ret_from_syscall) { + /* sp + 16 points to an exception frame */ + regs = (struct pt_regs *) (sp + 16); + if (sp + 16 + sizeof(*regs) <= stack_top) + next_exc = regs->nip; + } + } + ++count; + sp = *(unsigned long *)sp; + } +#ifndef CONFIG_KALLSYMS + if (count > 0) + printk("\n"); +#endif +} + +#if 0 +/* + * Low level print for debugging - Cort + */ +int __init ll_printk(const char *fmt, ...) +{ + va_list args; + char buf[256]; + int i; + + va_start(args, fmt); + i=vsprintf(buf,fmt,args); + ll_puts(buf); + va_end(args); + return i; +} + +int lines = 24, cols = 80; +int orig_x = 0, orig_y = 0; + +void puthex(unsigned long val) +{ + unsigned char buf[10]; + int i; + for (i = 7; i >= 0; i--) + { + buf[i] = "0123456789ABCDEF"[val & 0x0F]; + val >>= 4; + } + buf[8] = '\0'; + prom_print(buf); +} + +void __init ll_puts(const char *s) +{ + int x,y; + char *vidmem = (char *)/*(_ISA_MEM_BASE + 0xB8000) */0xD00B8000; + char c; + extern int mem_init_done; + + if ( mem_init_done ) /* assume this means we can printk */ + { + printk(s); + return; + } + +#if 0 + if ( have_of ) + { + prom_print(s); + return; + } +#endif + + /* + * can't ll_puts on chrp without openfirmware yet. + * vidmem just needs to be setup for it. + * -- Cort + */ + if ( _machine != _MACH_prep ) + return; + x = orig_x; + y = orig_y; + + while ( ( c = *s++ ) != '\0' ) { + if ( c == '\n' ) { + x = 0; + if ( ++y >= lines ) { + /*scroll();*/ + /*y--;*/ + y = 0; + } + } else { + vidmem [ ( x + cols * y ) * 2 ] = c; + if ( ++x >= cols ) { + x = 0; + if ( ++y >= lines ) { + /*scroll();*/ + /*y--;*/ + y = 0; + } + } + } + } + + orig_x = x; + orig_y = y; +} +#endif + +unsigned long get_wchan(struct task_struct *p) +{ + unsigned long ip, sp; + unsigned long stack_page = (unsigned long) p->thread_info; + int count = 0; + if (!p || p == current || p->state == TASK_RUNNING) + return 0; + sp = p->thread.ksp; + do { + sp = *(unsigned long *)sp; + if (sp < stack_page || sp >= stack_page + 8188) + return 0; + if (count > 0) { + ip = *(unsigned long *)(sp + 4); + if (!in_sched_functions(ip)) + return ip; + } + } while (count++ < 16); + return 0; +} diff --git a/arch/ppc/kernel/ptrace.c b/arch/ppc/kernel/ptrace.c new file mode 100644 index 00000000000..426b6f7d9de --- /dev/null +++ b/arch/ppc/kernel/ptrace.c @@ -0,0 +1,474 @@ +/* + * arch/ppc/kernel/ptrace.c + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Derived from "arch/m68k/kernel/ptrace.c" + * Copyright (C) 1994 by Hamish Macdonald + * Taken from linux/kernel/ptrace.c and modified for M680x0. + * linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds + * + * Modified by Cort Dougan (cort@hq.fsmlabs.com) + * and Paul Mackerras (paulus@linuxcare.com.au). + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file README.legal in the main directory of + * this archive for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * Set of msr bits that gdb can change on behalf of a process. + */ +#if defined(CONFIG_40x) || defined(CONFIG_BOOKE) +#define MSR_DEBUGCHANGE 0 +#else +#define MSR_DEBUGCHANGE (MSR_SE | MSR_BE) +#endif + +/* + * does not yet catch signals sent when the child dies. + * in exit.c or in signal.c. + */ + +/* + * Get contents of register REGNO in task TASK. + */ +static inline unsigned long get_reg(struct task_struct *task, int regno) +{ + if (regno < sizeof(struct pt_regs) / sizeof(unsigned long) + && task->thread.regs != NULL) + return ((unsigned long *)task->thread.regs)[regno]; + return (0); +} + +/* + * Write contents of register REGNO in task TASK. + */ +static inline int put_reg(struct task_struct *task, int regno, + unsigned long data) +{ + if (regno <= PT_MQ && task->thread.regs != NULL) { + if (regno == PT_MSR) + data = (data & MSR_DEBUGCHANGE) + | (task->thread.regs->msr & ~MSR_DEBUGCHANGE); + ((unsigned long *)task->thread.regs)[regno] = data; + return 0; + } + return -EIO; +} + +#ifdef CONFIG_ALTIVEC +/* + * Get contents of AltiVec register state in task TASK + */ +static inline int get_vrregs(unsigned long __user *data, struct task_struct *task) +{ + int i, j; + + if (!access_ok(VERIFY_WRITE, data, 133 * sizeof(unsigned long))) + return -EFAULT; + + /* copy AltiVec registers VR[0] .. VR[31] */ + for (i = 0; i < 32; i++) + for (j = 0; j < 4; j++, data++) + if (__put_user(task->thread.vr[i].u[j], data)) + return -EFAULT; + + /* copy VSCR */ + for (i = 0; i < 4; i++, data++) + if (__put_user(task->thread.vscr.u[i], data)) + return -EFAULT; + + /* copy VRSAVE */ + if (__put_user(task->thread.vrsave, data)) + return -EFAULT; + + return 0; +} + +/* + * Write contents of AltiVec register state into task TASK. + */ +static inline int set_vrregs(struct task_struct *task, unsigned long __user *data) +{ + int i, j; + + if (!access_ok(VERIFY_READ, data, 133 * sizeof(unsigned long))) + return -EFAULT; + + /* copy AltiVec registers VR[0] .. VR[31] */ + for (i = 0; i < 32; i++) + for (j = 0; j < 4; j++, data++) + if (__get_user(task->thread.vr[i].u[j], data)) + return -EFAULT; + + /* copy VSCR */ + for (i = 0; i < 4; i++, data++) + if (__get_user(task->thread.vscr.u[i], data)) + return -EFAULT; + + /* copy VRSAVE */ + if (__get_user(task->thread.vrsave, data)) + return -EFAULT; + + return 0; +} +#endif + +#ifdef CONFIG_SPE + +/* + * For get_evrregs/set_evrregs functions 'data' has the following layout: + * + * struct { + * u32 evr[32]; + * u64 acc; + * u32 spefscr; + * } + */ + +/* + * Get contents of SPE register state in task TASK. + */ +static inline int get_evrregs(unsigned long *data, struct task_struct *task) +{ + int i; + + if (!access_ok(VERIFY_WRITE, data, 35 * sizeof(unsigned long))) + return -EFAULT; + + /* copy SPEFSCR */ + if (__put_user(task->thread.spefscr, &data[34])) + return -EFAULT; + + /* copy SPE registers EVR[0] .. EVR[31] */ + for (i = 0; i < 32; i++, data++) + if (__put_user(task->thread.evr[i], data)) + return -EFAULT; + + /* copy ACC */ + if (__put_user64(task->thread.acc, (unsigned long long *)data)) + return -EFAULT; + + return 0; +} + +/* + * Write contents of SPE register state into task TASK. + */ +static inline int set_evrregs(struct task_struct *task, unsigned long *data) +{ + int i; + + if (!access_ok(VERIFY_READ, data, 35 * sizeof(unsigned long))) + return -EFAULT; + + /* copy SPEFSCR */ + if (__get_user(task->thread.spefscr, &data[34])) + return -EFAULT; + + /* copy SPE registers EVR[0] .. EVR[31] */ + for (i = 0; i < 32; i++, data++) + if (__get_user(task->thread.evr[i], data)) + return -EFAULT; + /* copy ACC */ + if (__get_user64(task->thread.acc, (unsigned long long*)data)) + return -EFAULT; + + return 0; +} +#endif /* CONFIG_SPE */ + +static inline void +set_single_step(struct task_struct *task) +{ + struct pt_regs *regs = task->thread.regs; + + if (regs != NULL) { +#if defined(CONFIG_40x) || defined(CONFIG_BOOKE) + task->thread.dbcr0 = DBCR0_IDM | DBCR0_IC; + regs->msr |= MSR_DE; +#else + regs->msr |= MSR_SE; +#endif + } +} + +static inline void +clear_single_step(struct task_struct *task) +{ + struct pt_regs *regs = task->thread.regs; + + if (regs != NULL) { +#if defined(CONFIG_40x) || defined(CONFIG_BOOKE) + task->thread.dbcr0 = 0; + regs->msr &= ~MSR_DE; +#else + regs->msr &= ~MSR_SE; +#endif + } +} + +/* + * Called by kernel/ptrace.c when detaching.. + * + * Make sure single step bits etc are not set. + */ +void ptrace_disable(struct task_struct *child) +{ + /* make sure the single step bit is not set. */ + clear_single_step(child); +} + +int sys_ptrace(long request, long pid, long addr, long data) +{ + struct task_struct *child; + int ret = -EPERM; + + lock_kernel(); + if (request == PTRACE_TRACEME) { + /* are we already being traced? */ + if (current->ptrace & PT_PTRACED) + goto out; + ret = security_ptrace(current->parent, current); + if (ret) + goto out; + /* set the ptrace bit in the process flags. */ + current->ptrace |= PT_PTRACED; + ret = 0; + goto out; + } + ret = -ESRCH; + read_lock(&tasklist_lock); + child = find_task_by_pid(pid); + if (child) + get_task_struct(child); + read_unlock(&tasklist_lock); + if (!child) + goto out; + + ret = -EPERM; + if (pid == 1) /* you may not mess with init */ + goto out_tsk; + + if (request == PTRACE_ATTACH) { + ret = ptrace_attach(child); + goto out_tsk; + } + + ret = ptrace_check_attach(child, request == PTRACE_KILL); + if (ret < 0) + goto out_tsk; + + switch (request) { + /* when I and D space are separate, these will need to be fixed. */ + case PTRACE_PEEKTEXT: /* read word at location addr. */ + case PTRACE_PEEKDATA: { + unsigned long tmp; + int copied; + + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); + ret = -EIO; + if (copied != sizeof(tmp)) + break; + ret = put_user(tmp,(unsigned long __user *) data); + break; + } + + /* read the word at location addr in the USER area. */ + /* XXX this will need fixing for 64-bit */ + case PTRACE_PEEKUSR: { + unsigned long index, tmp; + + ret = -EIO; + /* convert to index and check */ + index = (unsigned long) addr >> 2; + if ((addr & 3) || index > PT_FPSCR + || child->thread.regs == NULL) + break; + + CHECK_FULL_REGS(child->thread.regs); + if (index < PT_FPR0) { + tmp = get_reg(child, (int) index); + } else { + preempt_disable(); + if (child->thread.regs->msr & MSR_FP) + giveup_fpu(child); + preempt_enable(); + tmp = ((unsigned long *)child->thread.fpr)[index - PT_FPR0]; + } + ret = put_user(tmp,(unsigned long __user *) data); + break; + } + + /* If I and D space are separate, this will have to be fixed. */ + case PTRACE_POKETEXT: /* write the word at location addr. */ + case PTRACE_POKEDATA: + ret = 0; + if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) + break; + ret = -EIO; + break; + + /* write the word at location addr in the USER area */ + case PTRACE_POKEUSR: { + unsigned long index; + + ret = -EIO; + /* convert to index and check */ + index = (unsigned long) addr >> 2; + if ((addr & 3) || index > PT_FPSCR + || child->thread.regs == NULL) + break; + + CHECK_FULL_REGS(child->thread.regs); + if (index == PT_ORIG_R3) + break; + if (index < PT_FPR0) { + ret = put_reg(child, index, data); + } else { + preempt_disable(); + if (child->thread.regs->msr & MSR_FP) + giveup_fpu(child); + preempt_enable(); + ((unsigned long *)child->thread.fpr)[index - PT_FPR0] = data; + ret = 0; + } + break; + } + + case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ + case PTRACE_CONT: { /* restart after signal. */ + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + if (request == PTRACE_SYSCALL) { + set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + } else { + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + } + child->exit_code = data; + /* make sure the single step bit is not set. */ + clear_single_step(child); + wake_up_process(child); + ret = 0; + break; + } + +/* + * make the child exit. Best I can do is send it a sigkill. + * perhaps it should be put in the status that it wants to + * exit. + */ + case PTRACE_KILL: { + ret = 0; + if (child->exit_state == EXIT_ZOMBIE) /* already dead */ + break; + child->exit_code = SIGKILL; + /* make sure the single step bit is not set. */ + clear_single_step(child); + wake_up_process(child); + break; + } + + case PTRACE_SINGLESTEP: { /* set the trap flag. */ + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + set_single_step(child); + child->exit_code = data; + /* give it a chance to run. */ + wake_up_process(child); + ret = 0; + break; + } + + case PTRACE_DETACH: + ret = ptrace_detach(child, data); + break; + +#ifdef CONFIG_ALTIVEC + case PTRACE_GETVRREGS: + /* Get the child altivec register state. */ + preempt_disable(); + if (child->thread.regs->msr & MSR_VEC) + giveup_altivec(child); + preempt_enable(); + ret = get_vrregs((unsigned long __user *)data, child); + break; + + case PTRACE_SETVRREGS: + /* Set the child altivec register state. */ + /* this is to clear the MSR_VEC bit to force a reload + * of register state from memory */ + preempt_disable(); + if (child->thread.regs->msr & MSR_VEC) + giveup_altivec(child); + preempt_enable(); + ret = set_vrregs(child, (unsigned long __user *)data); + break; +#endif +#ifdef CONFIG_SPE + case PTRACE_GETEVRREGS: + /* Get the child spe register state. */ + if (child->thread.regs->msr & MSR_SPE) + giveup_spe(child); + ret = get_evrregs((unsigned long __user *)data, child); + break; + + case PTRACE_SETEVRREGS: + /* Set the child spe register state. */ + /* this is to clear the MSR_SPE bit to force a reload + * of register state from memory */ + if (child->thread.regs->msr & MSR_SPE) + giveup_spe(child); + ret = set_evrregs(child, (unsigned long __user *)data); + break; +#endif + + default: + ret = ptrace_request(child, request, addr, data); + break; + } +out_tsk: + put_task_struct(child); +out: + unlock_kernel(); + return ret; +} + +void do_syscall_trace(void) +{ + if (!test_thread_flag(TIF_SYSCALL_TRACE) + || !(current->ptrace & PT_PTRACED)) + return; + ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 0x80 : 0)); + + /* + * this isn't the same as continuing with a signal, but it will do + * for normal use. strace only continues with a signal if the + * stopping signal is not SIGTRAP. -brl + */ + if (current->exit_code) { + send_sig(current->exit_code, current, 1); + current->exit_code = 0; + } +} diff --git a/arch/ppc/kernel/semaphore.c b/arch/ppc/kernel/semaphore.c new file mode 100644 index 00000000000..2fe429b27c1 --- /dev/null +++ b/arch/ppc/kernel/semaphore.c @@ -0,0 +1,131 @@ +/* + * PowerPC-specific semaphore code. + * + * Copyright (C) 1999 Cort Dougan + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * April 2001 - Reworked by Paul Mackerras + * to eliminate the SMP races in the old version between the updates + * of `count' and `waking'. Now we use negative `count' values to + * indicate that some process(es) are waiting for the semaphore. + */ + +#include +#include +#include +#include +#include + +/* + * Atomically update sem->count. + * This does the equivalent of the following: + * + * old_count = sem->count; + * tmp = MAX(old_count, 0) + incr; + * sem->count = tmp; + * return old_count; + */ +static inline int __sem_update_count(struct semaphore *sem, int incr) +{ + int old_count, tmp; + + __asm__ __volatile__("\n" +"1: lwarx %0,0,%3\n" +" srawi %1,%0,31\n" +" andc %1,%0,%1\n" +" add %1,%1,%4\n" + PPC405_ERR77(0,%3) +" stwcx. %1,0,%3\n" +" bne 1b" + : "=&r" (old_count), "=&r" (tmp), "=m" (sem->count) + : "r" (&sem->count), "r" (incr), "m" (sem->count) + : "cc"); + + return old_count; +} + +void __up(struct semaphore *sem) +{ + /* + * Note that we incremented count in up() before we came here, + * but that was ineffective since the result was <= 0, and + * any negative value of count is equivalent to 0. + * This ends up setting count to 1, unless count is now > 0 + * (i.e. because some other cpu has called up() in the meantime), + * in which case we just increment count. + */ + __sem_update_count(sem, 1); + wake_up(&sem->wait); +} + +/* + * Note that when we come in to __down or __down_interruptible, + * we have already decremented count, but that decrement was + * ineffective since the result was < 0, and any negative value + * of count is equivalent to 0. + * Thus it is only when we decrement count from some value > 0 + * that we have actually got the semaphore. + */ +void __sched __down(struct semaphore *sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + tsk->state = TASK_UNINTERRUPTIBLE; + add_wait_queue_exclusive(&sem->wait, &wait); + smp_wmb(); + + /* + * Try to get the semaphore. If the count is > 0, then we've + * got the semaphore; we decrement count and exit the loop. + * If the count is 0 or negative, we set it to -1, indicating + * that we are asleep, and then sleep. + */ + while (__sem_update_count(sem, -1) <= 0) { + schedule(); + tsk->state = TASK_UNINTERRUPTIBLE; + } + remove_wait_queue(&sem->wait, &wait); + tsk->state = TASK_RUNNING; + + /* + * If there are any more sleepers, wake one of them up so + * that it can either get the semaphore, or set count to -1 + * indicating that there are still processes sleeping. + */ + wake_up(&sem->wait); +} + +int __sched __down_interruptible(struct semaphore * sem) +{ + int retval = 0; + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + tsk->state = TASK_INTERRUPTIBLE; + add_wait_queue_exclusive(&sem->wait, &wait); + smp_wmb(); + + while (__sem_update_count(sem, -1) <= 0) { + if (signal_pending(current)) { + /* + * A signal is pending - give up trying. + * Set sem->count to 0 if it is negative, + * since we are no longer sleeping. + */ + __sem_update_count(sem, 0); + retval = -EINTR; + break; + } + schedule(); + tsk->state = TASK_INTERRUPTIBLE; + } + tsk->state = TASK_RUNNING; + remove_wait_queue(&sem->wait, &wait); + wake_up(&sem->wait); + return retval; +} diff --git a/arch/ppc/kernel/setup.c b/arch/ppc/kernel/setup.c new file mode 100644 index 00000000000..e97ce635b99 --- /dev/null +++ b/arch/ppc/kernel/setup.c @@ -0,0 +1,778 @@ +/* + * Common prep/pmac/chrp boot and setup code. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_85xx) || defined(CONFIG_83xx) +#include +#endif + +#if defined CONFIG_KGDB +#include +#endif + +extern void platform_init(unsigned long r3, unsigned long r4, + unsigned long r5, unsigned long r6, unsigned long r7); +extern void bootx_init(unsigned long r4, unsigned long phys); +extern void identify_cpu(unsigned long offset, unsigned long cpu); +extern void do_cpu_ftr_fixups(unsigned long offset); +extern void reloc_got2(unsigned long offset); + +extern void ppc6xx_idle(void); +extern void power4_idle(void); + +extern boot_infos_t *boot_infos; +struct ide_machdep_calls ppc_ide_md; +char *sysmap; +unsigned long sysmap_size; + +/* Used with the BI_MEMSIZE bootinfo parameter to store the memory + size value reported by the boot loader. */ +unsigned long boot_mem_size; + +unsigned long ISA_DMA_THRESHOLD; +unsigned long DMA_MODE_READ, DMA_MODE_WRITE; + +#ifdef CONFIG_PPC_MULTIPLATFORM +int _machine = 0; + +extern void prep_init(unsigned long r3, unsigned long r4, + unsigned long r5, unsigned long r6, unsigned long r7); +extern void pmac_init(unsigned long r3, unsigned long r4, + unsigned long r5, unsigned long r6, unsigned long r7); +extern void chrp_init(unsigned long r3, unsigned long r4, + unsigned long r5, unsigned long r6, unsigned long r7); +#endif /* CONFIG_PPC_MULTIPLATFORM */ + +#ifdef CONFIG_MAGIC_SYSRQ +unsigned long SYSRQ_KEY = 0x54; +#endif /* CONFIG_MAGIC_SYSRQ */ + +#ifdef CONFIG_VGA_CONSOLE +unsigned long vgacon_remap_base; +#endif + +struct machdep_calls ppc_md; + +/* + * These are used in binfmt_elf.c to put aux entries on the stack + * for each elf executable being started. + */ +int dcache_bsize; +int icache_bsize; +int ucache_bsize; + +#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_FB_VGA16) || \ + defined(CONFIG_FB_VGA16_MODULE) || defined(CONFIG_FB_VESA) +struct screen_info screen_info = { + 0, 25, /* orig-x, orig-y */ + 0, /* unused */ + 0, /* orig-video-page */ + 0, /* orig-video-mode */ + 80, /* orig-video-cols */ + 0,0,0, /* ega_ax, ega_bx, ega_cx */ + 25, /* orig-video-lines */ + 1, /* orig-video-isVGA */ + 16 /* orig-video-points */ +}; +#endif /* CONFIG_VGA_CONSOLE || CONFIG_FB_VGA16 || CONFIG_FB_VESA */ + +void machine_restart(char *cmd) +{ +#ifdef CONFIG_NVRAM + nvram_sync(); +#endif + ppc_md.restart(cmd); +} + +EXPORT_SYMBOL(machine_restart); + +void machine_power_off(void) +{ +#ifdef CONFIG_NVRAM + nvram_sync(); +#endif + ppc_md.power_off(); +} + +EXPORT_SYMBOL(machine_power_off); + +void machine_halt(void) +{ +#ifdef CONFIG_NVRAM + nvram_sync(); +#endif + ppc_md.halt(); +} + +EXPORT_SYMBOL(machine_halt); + +void (*pm_power_off)(void) = machine_power_off; + +#ifdef CONFIG_TAU +extern u32 cpu_temp(unsigned long cpu); +extern u32 cpu_temp_both(unsigned long cpu); +#endif /* CONFIG_TAU */ + +int show_cpuinfo(struct seq_file *m, void *v) +{ + int i = (int) v - 1; + int err = 0; + unsigned int pvr; + unsigned short maj, min; + unsigned long lpj; + + if (i >= NR_CPUS) { + /* Show summary information */ +#ifdef CONFIG_SMP + unsigned long bogosum = 0; + for (i = 0; i < NR_CPUS; ++i) + if (cpu_online(i)) + bogosum += cpu_data[i].loops_per_jiffy; + seq_printf(m, "total bogomips\t: %lu.%02lu\n", + bogosum/(500000/HZ), bogosum/(5000/HZ) % 100); +#endif /* CONFIG_SMP */ + + if (ppc_md.show_cpuinfo != NULL) + err = ppc_md.show_cpuinfo(m); + return err; + } + +#ifdef CONFIG_SMP + if (!cpu_online(i)) + return 0; + pvr = cpu_data[i].pvr; + lpj = cpu_data[i].loops_per_jiffy; +#else + pvr = mfspr(SPRN_PVR); + lpj = loops_per_jiffy; +#endif + + seq_printf(m, "processor\t: %d\n", i); + seq_printf(m, "cpu\t\t: "); + + if (cur_cpu_spec[i]->pvr_mask) + seq_printf(m, "%s", cur_cpu_spec[i]->cpu_name); + else + seq_printf(m, "unknown (%08x)", pvr); +#ifdef CONFIG_ALTIVEC + if (cur_cpu_spec[i]->cpu_features & CPU_FTR_ALTIVEC) + seq_printf(m, ", altivec supported"); +#endif + seq_printf(m, "\n"); + +#ifdef CONFIG_TAU + if (cur_cpu_spec[i]->cpu_features & CPU_FTR_TAU) { +#ifdef CONFIG_TAU_AVERAGE + /* more straightforward, but potentially misleading */ + seq_printf(m, "temperature \t: %u C (uncalibrated)\n", + cpu_temp(i)); +#else + /* show the actual temp sensor range */ + u32 temp; + temp = cpu_temp_both(i); + seq_printf(m, "temperature \t: %u-%u C (uncalibrated)\n", + temp & 0xff, temp >> 16); +#endif + } +#endif /* CONFIG_TAU */ + + if (ppc_md.show_percpuinfo != NULL) { + err = ppc_md.show_percpuinfo(m, i); + if (err) + return err; + } + + switch (PVR_VER(pvr)) { + case 0x0020: /* 403 family */ + maj = PVR_MAJ(pvr) + 1; + min = PVR_MIN(pvr); + break; + case 0x1008: /* 740P/750P ?? */ + maj = ((pvr >> 8) & 0xFF) - 1; + min = pvr & 0xFF; + break; + case 0x8083: /* e300 */ + maj = PVR_MAJ(pvr); + min = PVR_MIN(pvr); + break; + case 0x8020: /* e500 */ + maj = PVR_MAJ(pvr); + min = PVR_MIN(pvr); + break; + default: + maj = (pvr >> 8) & 0xFF; + min = pvr & 0xFF; + break; + } + + seq_printf(m, "revision\t: %hd.%hd (pvr %04x %04x)\n", + maj, min, PVR_VER(pvr), PVR_REV(pvr)); + + seq_printf(m, "bogomips\t: %lu.%02lu\n", + lpj / (500000/HZ), (lpj / (5000/HZ)) % 100); + +#if defined(CONFIG_85xx) || defined(CONFIG_83xx) + if (cur_ppc_sys_spec->ppc_sys_name) + seq_printf(m, "chipset\t\t: %s\n", + cur_ppc_sys_spec->ppc_sys_name); +#endif + +#ifdef CONFIG_SMP + seq_printf(m, "\n"); +#endif + + return 0; +} + +static void *c_start(struct seq_file *m, loff_t *pos) +{ + int i = *pos; + + return i <= NR_CPUS? (void *) (i + 1): NULL; +} + +static void *c_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return c_start(m, pos); +} + +static void c_stop(struct seq_file *m, void *v) +{ +} + +struct seq_operations cpuinfo_op = { + .start =c_start, + .next = c_next, + .stop = c_stop, + .show = show_cpuinfo, +}; + +/* + * We're called here very early in the boot. We determine the machine + * type and call the appropriate low-level setup functions. + * -- Cort + * + * Note that the kernel may be running at an address which is different + * from the address that it was linked at, so we must use RELOC/PTRRELOC + * to access static data (including strings). -- paulus + */ +__init +unsigned long +early_init(int r3, int r4, int r5) +{ + unsigned long phys; + unsigned long offset = reloc_offset(); + + /* Default */ + phys = offset + KERNELBASE; + + /* First zero the BSS -- use memset, some arches don't have + * caches on yet */ + memset_io(PTRRELOC(&__bss_start), 0, _end - __bss_start); + + /* + * Identify the CPU type and fix up code sections + * that depend on which cpu we have. + */ + identify_cpu(offset, 0); + do_cpu_ftr_fixups(offset); + +#if defined(CONFIG_PPC_MULTIPLATFORM) + reloc_got2(offset); + + /* If we came here from BootX, clear the screen, + * set up some pointers and return. */ + if ((r3 == 0x426f6f58) && (r5 == 0)) + bootx_init(r4, phys); + + /* + * don't do anything on prep + * for now, don't use bootinfo because it breaks yaboot 0.5 + * and assume that if we didn't find a magic number, we have OF + */ + else if (*(unsigned long *)(0) != 0xdeadc0de) + phys = prom_init(r3, r4, (prom_entry)r5); + + reloc_got2(-offset); +#endif + + return phys; +} + +#ifdef CONFIG_PPC_OF +/* + * Assume here that all clock rates are the same in a + * smp system. -- Cort + */ +int __openfirmware +of_show_percpuinfo(struct seq_file *m, int i) +{ + struct device_node *cpu_node; + u32 *fp; + int s; + + cpu_node = find_type_devices("cpu"); + if (!cpu_node) + return 0; + for (s = 0; s < i && cpu_node->next; s++) + cpu_node = cpu_node->next; + fp = (u32 *)get_property(cpu_node, "clock-frequency", NULL); + if (fp) + seq_printf(m, "clock\t\t: %dMHz\n", *fp / 1000000); + return 0; +} + +void __init +intuit_machine_type(void) +{ + char *model; + struct device_node *root; + + /* ask the OF info if we're a chrp or pmac */ + root = find_path_device("/"); + if (root != 0) { + /* assume pmac unless proven to be chrp -- Cort */ + _machine = _MACH_Pmac; + model = get_property(root, "device_type", NULL); + if (model && !strncmp("chrp", model, 4)) + _machine = _MACH_chrp; + else { + model = get_property(root, "model", NULL); + if (model && !strncmp(model, "IBM", 3)) + _machine = _MACH_chrp; + } + } +} +#endif + +#ifdef CONFIG_PPC_MULTIPLATFORM +/* + * The PPC_MULTIPLATFORM version of platform_init... + */ +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ +#ifdef CONFIG_BOOTX_TEXT + if (boot_text_mapped) { + btext_clearscreen(); + btext_welcome(); + } +#endif + + parse_bootinfo(find_bootinfo()); + + /* if we didn't get any bootinfo telling us what we are... */ + if (_machine == 0) { + /* prep boot loader tells us if we're prep or not */ + if ( *(unsigned long *)(KERNELBASE) == (0xdeadc0de) ) + _machine = _MACH_prep; + } + + /* not much more to do here, if prep */ + if (_machine == _MACH_prep) { + prep_init(r3, r4, r5, r6, r7); + return; + } + + /* prom_init has already been called from __start */ + if (boot_infos) + relocate_nodes(); + + /* If we aren't PReP, we can find out if we're Pmac + * or CHRP with this. */ + if (_machine == 0) + intuit_machine_type(); + + /* finish_device_tree may need _machine defined. */ + finish_device_tree(); + + /* + * If we were booted via quik, r3 points to the physical + * address of the command-line parameters. + * If we were booted from an xcoff image (i.e. netbooted or + * booted from floppy), we get the command line from the + * bootargs property of the /chosen node. + * If an initial ramdisk is present, r3 and r4 + * are used for initrd_start and initrd_size, + * otherwise they contain 0xdeadbeef. + */ + if (r3 >= 0x4000 && r3 < 0x800000 && r4 == 0) { + strlcpy(cmd_line, (char *)r3 + KERNELBASE, + sizeof(cmd_line)); + } else if (boot_infos != 0) { + /* booted by BootX - check for ramdisk */ + if (boot_infos->kernelParamsOffset != 0) + strlcpy(cmd_line, (char *) boot_infos + + boot_infos->kernelParamsOffset, + sizeof(cmd_line)); +#ifdef CONFIG_BLK_DEV_INITRD + if (boot_infos->ramDisk) { + initrd_start = (unsigned long) boot_infos + + boot_infos->ramDisk; + initrd_end = initrd_start + boot_infos->ramDiskSize; + initrd_below_start_ok = 1; + } +#endif + } else { + struct device_node *chosen; + char *p; + +#ifdef CONFIG_BLK_DEV_INITRD + if (r3 && r4 && r4 != 0xdeadbeef) { + if (r3 < KERNELBASE) + r3 += KERNELBASE; + initrd_start = r3; + initrd_end = r3 + r4; + ROOT_DEV = Root_RAM0; + initrd_below_start_ok = 1; + } +#endif + chosen = find_devices("chosen"); + if (chosen != NULL) { + p = get_property(chosen, "bootargs", NULL); + if (p && *p) { + strlcpy(cmd_line, p, sizeof(cmd_line)); + } + } + } +#ifdef CONFIG_ADB + if (strstr(cmd_line, "adb_sync")) { + extern int __adb_probe_sync; + __adb_probe_sync = 1; + } +#endif /* CONFIG_ADB */ + + switch (_machine) { + case _MACH_Pmac: + pmac_init(r3, r4, r5, r6, r7); + break; + case _MACH_chrp: + chrp_init(r3, r4, r5, r6, r7); + break; + } +} + +#ifdef CONFIG_SERIAL_CORE_CONSOLE +extern char *of_stdout_device; + +static int __init set_preferred_console(void) +{ + struct device_node *prom_stdout; + char *name; + int offset; + + if (of_stdout_device == NULL) + return -ENODEV; + + /* The user has requested a console so this is already set up. */ + if (strstr(saved_command_line, "console=")) + return -EBUSY; + + prom_stdout = find_path_device(of_stdout_device); + if (!prom_stdout) + return -ENODEV; + + name = (char *)get_property(prom_stdout, "name", NULL); + if (!name) + return -ENODEV; + + if (strcmp(name, "serial") == 0) { + int i; + u32 *reg = (u32 *)get_property(prom_stdout, "reg", &i); + if (i > 8) { + switch (reg[1]) { + case 0x3f8: + offset = 0; + break; + case 0x2f8: + offset = 1; + break; + case 0x898: + offset = 2; + break; + case 0x890: + offset = 3; + break; + default: + /* We dont recognise the serial port */ + return -ENODEV; + } + } + } else if (strcmp(name, "ch-a") == 0) + offset = 0; + else if (strcmp(name, "ch-b") == 0) + offset = 1; + else + return -ENODEV; + return add_preferred_console("ttyS", offset, NULL); +} +console_initcall(set_preferred_console); +#endif /* CONFIG_SERIAL_CORE_CONSOLE */ +#endif /* CONFIG_PPC_MULTIPLATFORM */ + +struct bi_record *find_bootinfo(void) +{ + struct bi_record *rec; + + rec = (struct bi_record *)_ALIGN((ulong)__bss_start+(1<<20)-1,(1<<20)); + if ( rec->tag != BI_FIRST ) { + /* + * This 0x10000 offset is a terrible hack but it will go away when + * we have the bootloader handle all the relocation and + * prom calls -- Cort + */ + rec = (struct bi_record *)_ALIGN((ulong)__bss_start+0x10000+(1<<20)-1,(1<<20)); + if ( rec->tag != BI_FIRST ) + return NULL; + } + return rec; +} + +void parse_bootinfo(struct bi_record *rec) +{ + if (rec == NULL || rec->tag != BI_FIRST) + return; + while (rec->tag != BI_LAST) { + ulong *data = rec->data; + switch (rec->tag) { + case BI_CMD_LINE: + strlcpy(cmd_line, (void *)data, sizeof(cmd_line)); + break; + case BI_SYSMAP: + sysmap = (char *)((data[0] >= (KERNELBASE)) ? data[0] : + (data[0]+KERNELBASE)); + sysmap_size = data[1]; + break; +#ifdef CONFIG_BLK_DEV_INITRD + case BI_INITRD: + initrd_start = data[0] + KERNELBASE; + initrd_end = data[0] + data[1] + KERNELBASE; + break; +#endif /* CONFIG_BLK_DEV_INITRD */ +#ifdef CONFIG_PPC_MULTIPLATFORM + case BI_MACHTYPE: + _machine = data[0]; + break; +#endif + case BI_MEMSIZE: + boot_mem_size = data[0]; + break; + } + rec = (struct bi_record *)((ulong)rec + rec->size); + } +} + +/* + * Find out what kind of machine we're on and save any data we need + * from the early boot process (devtree is copied on pmac by prom_init()). + * This is called very early on the boot process, after a minimal + * MMU environment has been set up but before MMU_init is called. + */ +void __init +machine_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ +#ifdef CONFIG_CMDLINE + strlcpy(cmd_line, CONFIG_CMDLINE, sizeof(cmd_line)); +#endif /* CONFIG_CMDLINE */ + +#ifdef CONFIG_6xx + ppc_md.power_save = ppc6xx_idle; +#endif +#ifdef CONFIG_POWER4 + ppc_md.power_save = power4_idle; +#endif + + platform_init(r3, r4, r5, r6, r7); + + if (ppc_md.progress) + ppc_md.progress("id mach(): done", 0x200); +} + +/* Checks "l2cr=xxxx" command-line option */ +int __init ppc_setup_l2cr(char *str) +{ + if (cpu_has_feature(CPU_FTR_L2CR)) { + unsigned long val = simple_strtoul(str, NULL, 0); + printk(KERN_INFO "l2cr set to %lx\n", val); + _set_L2CR(0); /* force invalidate by disable cache */ + _set_L2CR(val); /* and enable it */ + } + return 1; +} +__setup("l2cr=", ppc_setup_l2cr); + +#ifdef CONFIG_GENERIC_NVRAM + +/* Generic nvram hooks used by drivers/char/gen_nvram.c */ +unsigned char nvram_read_byte(int addr) +{ + if (ppc_md.nvram_read_val) + return ppc_md.nvram_read_val(addr); + return 0xff; +} +EXPORT_SYMBOL(nvram_read_byte); + +void nvram_write_byte(unsigned char val, int addr) +{ + if (ppc_md.nvram_write_val) + ppc_md.nvram_write_val(addr, val); +} +EXPORT_SYMBOL(nvram_write_byte); + +void nvram_sync(void) +{ + if (ppc_md.nvram_sync) + ppc_md.nvram_sync(); +} +EXPORT_SYMBOL(nvram_sync); + +#endif /* CONFIG_NVRAM */ + +static struct cpu cpu_devices[NR_CPUS]; + +int __init ppc_init(void) +{ + int i; + + /* clear the progress line */ + if ( ppc_md.progress ) ppc_md.progress(" ", 0xffff); + + /* register CPU devices */ + for (i = 0; i < NR_CPUS; i++) + if (cpu_possible(i)) + register_cpu(&cpu_devices[i], i, NULL); + + /* call platform init */ + if (ppc_md.init != NULL) { + ppc_md.init(); + } + return 0; +} + +arch_initcall(ppc_init); + +/* Warning, IO base is not yet inited */ +void __init setup_arch(char **cmdline_p) +{ + extern char *klimit; + extern void do_init_bootmem(void); + + /* so udelay does something sensible, assume <= 1000 bogomips */ + loops_per_jiffy = 500000000 / HZ; + +#ifdef CONFIG_PPC_MULTIPLATFORM + /* This could be called "early setup arch", it must be done + * now because xmon need it + */ + if (_machine == _MACH_Pmac) + pmac_feature_init(); /* New cool way */ +#endif + +#ifdef CONFIG_XMON + xmon_map_scc(); + if (strstr(cmd_line, "xmon")) + xmon(NULL); +#endif /* CONFIG_XMON */ + if ( ppc_md.progress ) ppc_md.progress("setup_arch: enter", 0x3eab); + +#if defined(CONFIG_KGDB) + if (ppc_md.kgdb_map_scc) + ppc_md.kgdb_map_scc(); + set_debug_traps(); + if (strstr(cmd_line, "gdb")) { + if (ppc_md.progress) + ppc_md.progress("setup_arch: kgdb breakpoint", 0x4000); + printk("kgdb breakpoint activated\n"); + breakpoint(); + } +#endif + + /* + * Set cache line size based on type of cpu as a default. + * Systems with OF can look in the properties on the cpu node(s) + * for a possibly more accurate value. + */ + if (cpu_has_feature(CPU_FTR_SPLIT_ID_CACHE)) { + dcache_bsize = cur_cpu_spec[0]->dcache_bsize; + icache_bsize = cur_cpu_spec[0]->icache_bsize; + ucache_bsize = 0; + } else + ucache_bsize = dcache_bsize = icache_bsize + = cur_cpu_spec[0]->dcache_bsize; + + /* reboot on panic */ + panic_timeout = 180; + + init_mm.start_code = PAGE_OFFSET; + init_mm.end_code = (unsigned long) _etext; + init_mm.end_data = (unsigned long) _edata; + init_mm.brk = (unsigned long) klimit; + + /* Save unparsed command line copy for /proc/cmdline */ + strlcpy(saved_command_line, cmd_line, COMMAND_LINE_SIZE); + *cmdline_p = cmd_line; + + /* set up the bootmem stuff with available memory */ + do_init_bootmem(); + if ( ppc_md.progress ) ppc_md.progress("setup_arch: bootmem", 0x3eab); + +#ifdef CONFIG_PPC_OCP + /* Initialize OCP device list */ + ocp_early_init(); + if ( ppc_md.progress ) ppc_md.progress("ocp: exit", 0x3eab); +#endif + +#ifdef CONFIG_DUMMY_CONSOLE + conswitchp = &dummy_con; +#endif + + ppc_md.setup_arch(); + if ( ppc_md.progress ) ppc_md.progress("arch: exit", 0x3eab); + + paging_init(); + + /* this is for modules since _machine can be a define -- Cort */ + ppc_md.ppc_machine = _machine; +} diff --git a/arch/ppc/kernel/signal.c b/arch/ppc/kernel/signal.c new file mode 100644 index 00000000000..9567d3041ea --- /dev/null +++ b/arch/ppc/kernel/signal.c @@ -0,0 +1,775 @@ +/* + * arch/ppc/kernel/signal.c + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Derived from "arch/i386/kernel/signal.c" + * Copyright (C) 1991, 1992 Linus Torvalds + * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG_SIG + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +extern void sigreturn_exit(struct pt_regs *); + +#define GP_REGS_SIZE min(sizeof(elf_gregset_t), sizeof(struct pt_regs)) + +int do_signal(sigset_t *oldset, struct pt_regs *regs); + +/* + * Atomically swap in the new signal mask, and wait for a signal. + */ +int +sys_sigsuspend(old_sigset_t mask, int p2, int p3, int p4, int p6, int p7, + struct pt_regs *regs) +{ + sigset_t saveset; + + mask &= _BLOCKABLE; + spin_lock_irq(¤t->sighand->siglock); + saveset = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + regs->result = -EINTR; + regs->gpr[3] = EINTR; + regs->ccr |= 0x10000000; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(&saveset, regs)) + sigreturn_exit(regs); + } +} + +int +sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize, int p3, int p4, + int p6, int p7, struct pt_regs *regs) +{ + sigset_t saveset, newset; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user(&newset, unewset, sizeof(newset))) + return -EFAULT; + sigdelsetmask(&newset, ~_BLOCKABLE); + + spin_lock_irq(¤t->sighand->siglock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + regs->result = -EINTR; + regs->gpr[3] = EINTR; + regs->ccr |= 0x10000000; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(&saveset, regs)) + sigreturn_exit(regs); + } +} + + +int +sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss, int r5, + int r6, int r7, int r8, struct pt_regs *regs) +{ + return do_sigaltstack(uss, uoss, regs->gpr[1]); +} + +int +sys_sigaction(int sig, const struct old_sigaction __user *act, + struct old_sigaction __user *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + if (act) { + old_sigset_t mask; + if (!access_ok(VERIFY_READ, act, sizeof(*act)) || + __get_user(new_ka.sa.sa_handler, &act->sa_handler) || + __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) + return -EFAULT; + __get_user(new_ka.sa.sa_flags, &act->sa_flags); + __get_user(mask, &act->sa_mask); + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, (act? &new_ka: NULL), (oact? &old_ka: NULL)); + + if (!ret && oact) { + if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) + return -EFAULT; + __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); + } + + return ret; +} + +/* + * When we have signals to deliver, we set up on the + * user stack, going down from the original stack pointer: + * a sigregs struct + * a sigcontext struct + * a gap of __SIGNAL_FRAMESIZE bytes + * + * Each of these things must be a multiple of 16 bytes in size. + * + */ +struct sigregs { + struct mcontext mctx; /* all the register values */ + /* Programs using the rs6000/xcoff abi can save up to 19 gp regs + and 18 fp regs below sp before decrementing it. */ + int abigap[56]; +}; + +/* We use the mc_pad field for the signal return trampoline. */ +#define tramp mc_pad + +/* + * When we have rt signals to deliver, we set up on the + * user stack, going down from the original stack pointer: + * one rt_sigframe struct (siginfo + ucontext + ABI gap) + * a gap of __SIGNAL_FRAMESIZE+16 bytes + * (the +16 is to get the siginfo and ucontext in the same + * positions as in older kernels). + * + * Each of these things must be a multiple of 16 bytes in size. + * + */ +struct rt_sigframe +{ + struct siginfo info; + struct ucontext uc; + /* Programs using the rs6000/xcoff abi can save up to 19 gp regs + and 18 fp regs below sp before decrementing it. */ + int abigap[56]; +}; + +/* + * Save the current user registers on the user stack. + * We only save the altivec/spe registers if the process has used + * altivec/spe instructions at some point. + */ +static int +save_user_regs(struct pt_regs *regs, struct mcontext __user *frame, int sigret) +{ + /* save general and floating-point registers */ + CHECK_FULL_REGS(regs); + preempt_disable(); + if (regs->msr & MSR_FP) + giveup_fpu(current); +#ifdef CONFIG_ALTIVEC + if (current->thread.used_vr && (regs->msr & MSR_VEC)) + giveup_altivec(current); +#endif /* CONFIG_ALTIVEC */ +#ifdef CONFIG_SPE + if (current->thread.used_spe && (regs->msr & MSR_SPE)) + giveup_spe(current); +#endif /* CONFIG_ALTIVEC */ + preempt_enable(); + + if (__copy_to_user(&frame->mc_gregs, regs, GP_REGS_SIZE) + || __copy_to_user(&frame->mc_fregs, current->thread.fpr, + ELF_NFPREG * sizeof(double))) + return 1; + + current->thread.fpscr = 0; /* turn off all fp exceptions */ + +#ifdef CONFIG_ALTIVEC + /* save altivec registers */ + if (current->thread.used_vr) { + if (__copy_to_user(&frame->mc_vregs, current->thread.vr, + ELF_NVRREG * sizeof(vector128))) + return 1; + /* set MSR_VEC in the saved MSR value to indicate that + frame->mc_vregs contains valid data */ + if (__put_user(regs->msr | MSR_VEC, &frame->mc_gregs[PT_MSR])) + return 1; + } + /* else assert((regs->msr & MSR_VEC) == 0) */ + + /* We always copy to/from vrsave, it's 0 if we don't have or don't + * use altivec. Since VSCR only contains 32 bits saved in the least + * significant bits of a vector, we "cheat" and stuff VRSAVE in the + * most significant bits of that same vector. --BenH + */ + if (__put_user(current->thread.vrsave, (u32 __user *)&frame->mc_vregs[32])) + return 1; +#endif /* CONFIG_ALTIVEC */ + +#ifdef CONFIG_SPE + /* save spe registers */ + if (current->thread.used_spe) { + if (__copy_to_user(&frame->mc_vregs, current->thread.evr, + ELF_NEVRREG * sizeof(u32))) + return 1; + /* set MSR_SPE in the saved MSR value to indicate that + frame->mc_vregs contains valid data */ + if (__put_user(regs->msr | MSR_SPE, &frame->mc_gregs[PT_MSR])) + return 1; + } + /* else assert((regs->msr & MSR_SPE) == 0) */ + + /* We always copy to/from spefscr */ + if (__put_user(current->thread.spefscr, (u32 *)&frame->mc_vregs + ELF_NEVRREG)) + return 1; +#endif /* CONFIG_SPE */ + + if (sigret) { + /* Set up the sigreturn trampoline: li r0,sigret; sc */ + if (__put_user(0x38000000UL + sigret, &frame->tramp[0]) + || __put_user(0x44000002UL, &frame->tramp[1])) + return 1; + flush_icache_range((unsigned long) &frame->tramp[0], + (unsigned long) &frame->tramp[2]); + } + + return 0; +} + +/* + * Restore the current user register values from the user stack, + * (except for MSR). + */ +static int +restore_user_regs(struct pt_regs *regs, struct mcontext __user *sr, int sig) +{ + unsigned long save_r2 = 0; +#if defined(CONFIG_ALTIVEC) || defined(CONFIG_SPE) + unsigned long msr; +#endif + + /* backup/restore the TLS as we don't want it to be modified */ + if (!sig) + save_r2 = regs->gpr[2]; + /* copy up to but not including MSR */ + if (__copy_from_user(regs, &sr->mc_gregs, PT_MSR * sizeof(elf_greg_t))) + return 1; + /* copy from orig_r3 (the word after the MSR) up to the end */ + if (__copy_from_user(®s->orig_gpr3, &sr->mc_gregs[PT_ORIG_R3], + GP_REGS_SIZE - PT_ORIG_R3 * sizeof(elf_greg_t))) + return 1; + if (!sig) + regs->gpr[2] = save_r2; + + /* force the process to reload the FP registers from + current->thread when it next does FP instructions */ + regs->msr &= ~(MSR_FP | MSR_FE0 | MSR_FE1); + if (__copy_from_user(current->thread.fpr, &sr->mc_fregs, + sizeof(sr->mc_fregs))) + return 1; + +#ifdef CONFIG_ALTIVEC + /* force the process to reload the altivec registers from + current->thread when it next does altivec instructions */ + regs->msr &= ~MSR_VEC; + if (!__get_user(msr, &sr->mc_gregs[PT_MSR]) && (msr & MSR_VEC) != 0) { + /* restore altivec registers from the stack */ + if (__copy_from_user(current->thread.vr, &sr->mc_vregs, + sizeof(sr->mc_vregs))) + return 1; + } else if (current->thread.used_vr) + memset(¤t->thread.vr, 0, ELF_NVRREG * sizeof(vector128)); + + /* Always get VRSAVE back */ + if (__get_user(current->thread.vrsave, (u32 __user *)&sr->mc_vregs[32])) + return 1; +#endif /* CONFIG_ALTIVEC */ + +#ifdef CONFIG_SPE + /* force the process to reload the spe registers from + current->thread when it next does spe instructions */ + regs->msr &= ~MSR_SPE; + if (!__get_user(msr, &sr->mc_gregs[PT_MSR]) && (msr & MSR_SPE) != 0) { + /* restore spe registers from the stack */ + if (__copy_from_user(current->thread.evr, &sr->mc_vregs, + ELF_NEVRREG * sizeof(u32))) + return 1; + } else if (current->thread.used_spe) + memset(¤t->thread.evr, 0, ELF_NEVRREG * sizeof(u32)); + + /* Always get SPEFSCR back */ + if (__get_user(current->thread.spefscr, (u32 *)&sr->mc_vregs + ELF_NEVRREG)) + return 1; +#endif /* CONFIG_SPE */ + +#ifndef CONFIG_SMP + preempt_disable(); + if (last_task_used_math == current) + last_task_used_math = NULL; + if (last_task_used_altivec == current) + last_task_used_altivec = NULL; + if (last_task_used_spe == current) + last_task_used_spe = NULL; + preempt_enable(); +#endif + return 0; +} + +/* + * Restore the user process's signal mask + */ +static void +restore_sigmask(sigset_t *set) +{ + sigdelsetmask(set, ~_BLOCKABLE); + spin_lock_irq(¤t->sighand->siglock); + current->blocked = *set; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); +} + +/* + * Set up a signal frame for a "real-time" signal handler + * (one which gets siginfo). + */ +static void +handle_rt_signal(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, struct pt_regs * regs, + unsigned long newsp) +{ + struct rt_sigframe __user *rt_sf; + struct mcontext __user *frame; + unsigned long origsp = newsp; + + /* Set up Signal Frame */ + /* Put a Real Time Context onto stack */ + newsp -= sizeof(*rt_sf); + rt_sf = (struct rt_sigframe __user *) newsp; + + /* create a stack frame for the caller of the handler */ + newsp -= __SIGNAL_FRAMESIZE + 16; + + if (!access_ok(VERIFY_WRITE, (void __user *) newsp, origsp - newsp)) + goto badframe; + + /* Put the siginfo & fill in most of the ucontext */ + if (copy_siginfo_to_user(&rt_sf->info, info) + || __put_user(0, &rt_sf->uc.uc_flags) + || __put_user(0, &rt_sf->uc.uc_link) + || __put_user(current->sas_ss_sp, &rt_sf->uc.uc_stack.ss_sp) + || __put_user(sas_ss_flags(regs->gpr[1]), + &rt_sf->uc.uc_stack.ss_flags) + || __put_user(current->sas_ss_size, &rt_sf->uc.uc_stack.ss_size) + || __put_user(&rt_sf->uc.uc_mcontext, &rt_sf->uc.uc_regs) + || __copy_to_user(&rt_sf->uc.uc_sigmask, oldset, sizeof(*oldset))) + goto badframe; + + /* Save user registers on the stack */ + frame = &rt_sf->uc.uc_mcontext; + if (save_user_regs(regs, frame, __NR_rt_sigreturn)) + goto badframe; + + if (put_user(regs->gpr[1], (unsigned long __user *)newsp)) + goto badframe; + regs->gpr[1] = newsp; + regs->gpr[3] = sig; + regs->gpr[4] = (unsigned long) &rt_sf->info; + regs->gpr[5] = (unsigned long) &rt_sf->uc; + regs->gpr[6] = (unsigned long) rt_sf; + regs->nip = (unsigned long) ka->sa.sa_handler; + regs->link = (unsigned long) frame->tramp; + regs->trap = 0; + + return; + +badframe: +#ifdef DEBUG_SIG + printk("badframe in handle_rt_signal, regs=%p frame=%p newsp=%lx\n", + regs, frame, newsp); +#endif + force_sigsegv(sig, current); +} + +static int do_setcontext(struct ucontext __user *ucp, struct pt_regs *regs, int sig) +{ + sigset_t set; + struct mcontext __user *mcp; + + if (__copy_from_user(&set, &ucp->uc_sigmask, sizeof(set)) + || __get_user(mcp, &ucp->uc_regs)) + return -EFAULT; + restore_sigmask(&set); + if (restore_user_regs(regs, mcp, sig)) + return -EFAULT; + + return 0; +} + +int sys_swapcontext(struct ucontext __user *old_ctx, + struct ucontext __user *new_ctx, + int ctx_size, int r6, int r7, int r8, struct pt_regs *regs) +{ + unsigned char tmp; + + /* Context size is for future use. Right now, we only make sure + * we are passed something we understand + */ + if (ctx_size < sizeof(struct ucontext)) + return -EINVAL; + + if (old_ctx != NULL) { + if (!access_ok(VERIFY_WRITE, old_ctx, sizeof(*old_ctx)) + || save_user_regs(regs, &old_ctx->uc_mcontext, 0) + || __copy_to_user(&old_ctx->uc_sigmask, + ¤t->blocked, sizeof(sigset_t)) + || __put_user(&old_ctx->uc_mcontext, &old_ctx->uc_regs)) + return -EFAULT; + } + if (new_ctx == NULL) + return 0; + if (!access_ok(VERIFY_READ, new_ctx, sizeof(*new_ctx)) + || __get_user(tmp, (u8 __user *) new_ctx) + || __get_user(tmp, (u8 __user *) (new_ctx + 1) - 1)) + return -EFAULT; + + /* + * If we get a fault copying the context into the kernel's + * image of the user's registers, we can't just return -EFAULT + * because the user's registers will be corrupted. For instance + * the NIP value may have been updated but not some of the + * other registers. Given that we have done the access_ok + * and successfully read the first and last bytes of the region + * above, this should only happen in an out-of-memory situation + * or if another thread unmaps the region containing the context. + * We kill the task with a SIGSEGV in this situation. + */ + if (do_setcontext(new_ctx, regs, 0)) + do_exit(SIGSEGV); + sigreturn_exit(regs); + /* doesn't actually return back to here */ + return 0; +} + +int sys_rt_sigreturn(int r3, int r4, int r5, int r6, int r7, int r8, + struct pt_regs *regs) +{ + struct rt_sigframe __user *rt_sf; + + /* Always make any pending restarted system calls return -EINTR */ + current_thread_info()->restart_block.fn = do_no_restart_syscall; + + rt_sf = (struct rt_sigframe __user *) + (regs->gpr[1] + __SIGNAL_FRAMESIZE + 16); + if (!access_ok(VERIFY_READ, rt_sf, sizeof(struct rt_sigframe))) + goto bad; + if (do_setcontext(&rt_sf->uc, regs, 1)) + goto bad; + + /* + * It's not clear whether or why it is desirable to save the + * sigaltstack setting on signal delivery and restore it on + * signal return. But other architectures do this and we have + * always done it up until now so it is probably better not to + * change it. -- paulus + */ + do_sigaltstack(&rt_sf->uc.uc_stack, NULL, regs->gpr[1]); + + sigreturn_exit(regs); /* doesn't return here */ + return 0; + + bad: + force_sig(SIGSEGV, current); + return 0; +} + +int sys_debug_setcontext(struct ucontext __user *ctx, + int ndbg, struct sig_dbg_op *dbg, + int r6, int r7, int r8, + struct pt_regs *regs) +{ + struct sig_dbg_op op; + int i; + unsigned long new_msr = regs->msr; +#if defined(CONFIG_4xx) || defined(CONFIG_BOOKE) + unsigned long new_dbcr0 = current->thread.dbcr0; +#endif + + for (i=0; imsr = new_msr; +#if defined(CONFIG_4xx) || defined(CONFIG_BOOKE) + current->thread.dbcr0 = new_dbcr0; +#endif + + /* + * If we get a fault copying the context into the kernel's + * image of the user's registers, we can't just return -EFAULT + * because the user's registers will be corrupted. For instance + * the NIP value may have been updated but not some of the + * other registers. Given that we have done the access_ok + * and successfully read the first and last bytes of the region + * above, this should only happen in an out-of-memory situation + * or if another thread unmaps the region containing the context. + * We kill the task with a SIGSEGV in this situation. + */ + if (do_setcontext(ctx, regs, 1)) { + force_sig(SIGSEGV, current); + goto out; + } + + /* + * It's not clear whether or why it is desirable to save the + * sigaltstack setting on signal delivery and restore it on + * signal return. But other architectures do this and we have + * always done it up until now so it is probably better not to + * change it. -- paulus + */ + do_sigaltstack(&ctx->uc_stack, NULL, regs->gpr[1]); + + sigreturn_exit(regs); + /* doesn't actually return back to here */ + + out: + return 0; +} + +/* + * OK, we're invoking a handler + */ +static void +handle_signal(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, struct pt_regs * regs, + unsigned long newsp) +{ + struct sigcontext __user *sc; + struct sigregs __user *frame; + unsigned long origsp = newsp; + + /* Set up Signal Frame */ + newsp -= sizeof(struct sigregs); + frame = (struct sigregs __user *) newsp; + + /* Put a sigcontext on the stack */ + newsp -= sizeof(*sc); + sc = (struct sigcontext __user *) newsp; + + /* create a stack frame for the caller of the handler */ + newsp -= __SIGNAL_FRAMESIZE; + + if (!access_ok(VERIFY_WRITE, (void __user *) newsp, origsp - newsp)) + goto badframe; + +#if _NSIG != 64 +#error "Please adjust handle_signal()" +#endif + if (__put_user((unsigned long) ka->sa.sa_handler, &sc->handler) + || __put_user(oldset->sig[0], &sc->oldmask) + || __put_user(oldset->sig[1], &sc->_unused[3]) + || __put_user((struct pt_regs *)frame, &sc->regs) + || __put_user(sig, &sc->signal)) + goto badframe; + + if (save_user_regs(regs, &frame->mctx, __NR_sigreturn)) + goto badframe; + + if (put_user(regs->gpr[1], (unsigned long __user *)newsp)) + goto badframe; + regs->gpr[1] = newsp; + regs->gpr[3] = sig; + regs->gpr[4] = (unsigned long) sc; + regs->nip = (unsigned long) ka->sa.sa_handler; + regs->link = (unsigned long) frame->mctx.tramp; + regs->trap = 0; + + return; + +badframe: +#ifdef DEBUG_SIG + printk("badframe in handle_signal, regs=%p frame=%p newsp=%lx\n", + regs, frame, newsp); +#endif + force_sigsegv(sig, current); +} + +/* + * Do a signal return; undo the signal stack. + */ +int sys_sigreturn(int r3, int r4, int r5, int r6, int r7, int r8, + struct pt_regs *regs) +{ + struct sigcontext __user *sc; + struct sigcontext sigctx; + struct mcontext __user *sr; + sigset_t set; + + /* Always make any pending restarted system calls return -EINTR */ + current_thread_info()->restart_block.fn = do_no_restart_syscall; + + sc = (struct sigcontext __user *)(regs->gpr[1] + __SIGNAL_FRAMESIZE); + if (copy_from_user(&sigctx, sc, sizeof(sigctx))) + goto badframe; + + set.sig[0] = sigctx.oldmask; + set.sig[1] = sigctx._unused[3]; + restore_sigmask(&set); + + sr = (struct mcontext __user *) sigctx.regs; + if (!access_ok(VERIFY_READ, sr, sizeof(*sr)) + || restore_user_regs(regs, sr, 1)) + goto badframe; + + sigreturn_exit(regs); /* doesn't return */ + return 0; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + */ +int do_signal(sigset_t *oldset, struct pt_regs *regs) +{ + siginfo_t info; + struct k_sigaction ka; + unsigned long frame, newsp; + int signr, ret; + + if (current->flags & PF_FREEZE) { + refrigerator(PF_FREEZE); + signr = 0; + ret = regs->gpr[3]; + if (!signal_pending(current)) + goto no_signal; + } + + if (!oldset) + oldset = ¤t->blocked; + + newsp = frame = 0; + + signr = get_signal_to_deliver(&info, &ka, regs, NULL); + + if (TRAP(regs) == 0x0C00 /* System Call! */ + && regs->ccr & 0x10000000 /* error signalled */ + && ((ret = regs->gpr[3]) == ERESTARTSYS + || ret == ERESTARTNOHAND || ret == ERESTARTNOINTR + || ret == ERESTART_RESTARTBLOCK)) { + + if (signr > 0 + && (ret == ERESTARTNOHAND || ret == ERESTART_RESTARTBLOCK + || (ret == ERESTARTSYS + && !(ka.sa.sa_flags & SA_RESTART)))) { + /* make the system call return an EINTR error */ + regs->result = -EINTR; + regs->gpr[3] = EINTR; + /* note that the cr0.SO bit is already set */ + } else { +no_signal: + regs->nip -= 4; /* Back up & retry system call */ + regs->result = 0; + regs->trap = 0; + if (ret == ERESTART_RESTARTBLOCK) + regs->gpr[0] = __NR_restart_syscall; + else + regs->gpr[3] = regs->orig_gpr3; + } + } + + if (signr == 0) + return 0; /* no signals delivered */ + + if ((ka.sa.sa_flags & SA_ONSTACK) && current->sas_ss_size + && !on_sig_stack(regs->gpr[1])) + newsp = current->sas_ss_sp + current->sas_ss_size; + else + newsp = regs->gpr[1]; + newsp &= ~0xfUL; + + /* Whee! Actually deliver the signal. */ + if (ka.sa.sa_flags & SA_SIGINFO) + handle_rt_signal(signr, &ka, &info, oldset, regs, newsp); + else + handle_signal(signr, &ka, &info, oldset, regs, newsp); + + if (!(ka.sa.sa_flags & SA_NODEFER)) { + spin_lock_irq(¤t->sighand->siglock); + sigorsets(¤t->blocked,¤t->blocked,&ka.sa.sa_mask); + sigaddset(¤t->blocked, signr); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + } + + return 1; +} + diff --git a/arch/ppc/kernel/smp-tbsync.c b/arch/ppc/kernel/smp-tbsync.c new file mode 100644 index 00000000000..2c9cd95bcea --- /dev/null +++ b/arch/ppc/kernel/smp-tbsync.c @@ -0,0 +1,181 @@ +/* + * Smp timebase synchronization for ppc. + * + * Copyright (C) 2003 Samuel Rydh (samuel@ibrium.se) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NUM_ITER 300 + +enum { + kExit=0, kSetAndTest, kTest +}; + +static struct { + volatile int tbu; + volatile int tbl; + volatile int mark; + volatile int cmd; + volatile int handshake; + int filler[3]; + + volatile int ack; + int filler2[7]; + + volatile int race_result; +} *tbsync; + +static volatile int running; + +static void __devinit +enter_contest( int mark, int add ) +{ + while( (int)(get_tbl() - mark) < 0 ) + tbsync->race_result = add; +} + +void __devinit +smp_generic_take_timebase( void ) +{ + int cmd, tbl, tbu; + + local_irq_disable(); + while( !running ) + ; + rmb(); + + for( ;; ) { + tbsync->ack = 1; + while( !tbsync->handshake ) + ; + rmb(); + + cmd = tbsync->cmd; + tbl = tbsync->tbl; + tbu = tbsync->tbu; + tbsync->ack = 0; + if( cmd == kExit ) + return; + + if( cmd == kSetAndTest ) { + while( tbsync->handshake ) + ; + asm volatile ("mttbl %0" :: "r" (tbl) ); + asm volatile ("mttbu %0" :: "r" (tbu) ); + } else { + while( tbsync->handshake ) + ; + } + enter_contest( tbsync->mark, -1 ); + } + local_irq_enable(); +} + +static int __devinit +start_contest( int cmd, int offset, int num ) +{ + int i, tbu, tbl, mark, score=0; + + tbsync->cmd = cmd; + + local_irq_disable(); + for( i=-3; itbu = tbu = get_tbu(); + tbsync->tbl = tbl + offset; + tbsync->mark = mark = tbl + 400; + + wmb(); + + tbsync->handshake = 1; + while( tbsync->ack ) + ; + + while( (int)(get_tbl() - tbl) <= 0 ) + ; + tbsync->handshake = 0; + enter_contest( mark, 1 ); + + while( !tbsync->ack ) + ; + + if( tbsync->tbu != get_tbu() || ((tbsync->tbl ^ get_tbl()) & 0x80000000) ) + continue; + if( i++ > 0 ) + score += tbsync->race_result; + } + local_irq_enable(); + return score; +} + +void __devinit +smp_generic_give_timebase( void ) +{ + int i, score, score2, old, min=0, max=5000, offset=1000; + + printk("Synchronizing timebase\n"); + + /* if this fails then this kernel won't work anyway... */ + tbsync = kmalloc( sizeof(*tbsync), GFP_KERNEL ); + memset( tbsync, 0, sizeof(*tbsync) ); + mb(); + running = 1; + + while( !tbsync->ack ) + ; + + /* binary search */ + for( old=-1 ; old != offset ; offset=(min+max)/2 ) { + score = start_contest( kSetAndTest, offset, NUM_ITER ); + + printk("score %d, offset %d\n", score, offset ); + + if( score > 0 ) + max = offset; + else + min = offset; + old = offset; + } + score = start_contest( kSetAndTest, min, NUM_ITER ); + score2 = start_contest( kSetAndTest, max, NUM_ITER ); + + printk( "Min %d (score %d), Max %d (score %d)\n", min, score, max, score2 ); + score = abs( score ); + score2 = abs( score2 ); + offset = (score < score2) ? min : max; + + /* guard against inaccurate mttb */ + for( i=0; i<10; i++ ) { + start_contest( kSetAndTest, offset, NUM_ITER/10 ); + + if( (score2=start_contest(kTest, offset, NUM_ITER)) < 0 ) + score2 = -score2; + if( score2 <= score || score2 < 20 ) + break; + } + printk("Final offset: %d (%d/%d)\n", offset, score2, NUM_ITER ); + + /* exiting */ + tbsync->cmd = kExit; + wmb(); + tbsync->handshake = 1; + while( tbsync->ack ) + ; + tbsync->handshake = 0; + kfree( tbsync ); + tbsync = NULL; + running = 0; + + /* all done */ + smp_tb_synchronized = 1; +} diff --git a/arch/ppc/kernel/smp.c b/arch/ppc/kernel/smp.c new file mode 100644 index 00000000000..e70b587b9e5 --- /dev/null +++ b/arch/ppc/kernel/smp.c @@ -0,0 +1,399 @@ +/* + * Smp support for ppc. + * + * Written by Cort Dougan (cort@cs.nmt.edu) borrowing a great + * deal of code from the sparc and intel versions. + * + * Copyright (C) 1999 Cort Dougan + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +volatile int smp_commenced; +int smp_tb_synchronized; +struct cpuinfo_PPC cpu_data[NR_CPUS]; +struct klock_info_struct klock_info = { KLOCK_CLEAR, 0 }; +atomic_t ipi_recv; +atomic_t ipi_sent; +cpumask_t cpu_online_map; +cpumask_t cpu_possible_map; +int smp_hw_index[NR_CPUS]; +struct thread_info *secondary_ti; + +EXPORT_SYMBOL(cpu_online_map); +EXPORT_SYMBOL(cpu_possible_map); + +/* SMP operations for this machine */ +static struct smp_ops_t *smp_ops; + +/* all cpu mappings are 1-1 -- Cort */ +volatile unsigned long cpu_callin_map[NR_CPUS]; + +int start_secondary(void *); +void smp_call_function_interrupt(void); +static int __smp_call_function(void (*func) (void *info), void *info, + int wait, int target); + +/* Low level assembly function used to backup CPU 0 state */ +extern void __save_cpu_setup(void); + +/* Since OpenPIC has only 4 IPIs, we use slightly different message numbers. + * + * Make sure this matches openpic_request_IPIs in open_pic.c, or what shows up + * in /proc/interrupts will be wrong!!! --Troy */ +#define PPC_MSG_CALL_FUNCTION 0 +#define PPC_MSG_RESCHEDULE 1 +#define PPC_MSG_INVALIDATE_TLB 2 +#define PPC_MSG_XMON_BREAK 3 + +static inline void +smp_message_pass(int target, int msg, unsigned long data, int wait) +{ + if (smp_ops){ + atomic_inc(&ipi_sent); + smp_ops->message_pass(target,msg,data,wait); + } +} + +/* + * Common functions + */ +void smp_message_recv(int msg, struct pt_regs *regs) +{ + atomic_inc(&ipi_recv); + + switch( msg ) { + case PPC_MSG_CALL_FUNCTION: + smp_call_function_interrupt(); + break; + case PPC_MSG_RESCHEDULE: + set_need_resched(); + break; + case PPC_MSG_INVALIDATE_TLB: + _tlbia(); + break; +#ifdef CONFIG_XMON + case PPC_MSG_XMON_BREAK: + xmon(regs); + break; +#endif /* CONFIG_XMON */ + default: + printk("SMP %d: smp_message_recv(): unknown msg %d\n", + smp_processor_id(), msg); + break; + } +} + +/* + * 750's don't broadcast tlb invalidates so + * we have to emulate that behavior. + * -- Cort + */ +void smp_send_tlb_invalidate(int cpu) +{ + if ( PVR_VER(mfspr(SPRN_PVR)) == 8 ) + smp_message_pass(MSG_ALL_BUT_SELF, PPC_MSG_INVALIDATE_TLB, 0, 0); +} + +void smp_send_reschedule(int cpu) +{ + /* + * This is only used if `cpu' is running an idle task, + * so it will reschedule itself anyway... + * + * This isn't the case anymore since the other CPU could be + * sleeping and won't reschedule until the next interrupt (such + * as the timer). + * -- Cort + */ + /* This is only used if `cpu' is running an idle task, + so it will reschedule itself anyway... */ + smp_message_pass(cpu, PPC_MSG_RESCHEDULE, 0, 0); +} + +#ifdef CONFIG_XMON +void smp_send_xmon_break(int cpu) +{ + smp_message_pass(cpu, PPC_MSG_XMON_BREAK, 0, 0); +} +#endif /* CONFIG_XMON */ + +static void stop_this_cpu(void *dummy) +{ + local_irq_disable(); + while (1) + ; +} + +void smp_send_stop(void) +{ + smp_call_function(stop_this_cpu, NULL, 1, 0); +} + +/* + * Structure and data for smp_call_function(). This is designed to minimise + * static memory requirements. It also looks cleaner. + * Stolen from the i386 version. + */ +static DEFINE_SPINLOCK(call_lock); + +static struct call_data_struct { + void (*func) (void *info); + void *info; + atomic_t started; + atomic_t finished; + int wait; +} *call_data; + +/* + * this function sends a 'generic call function' IPI to all other CPUs + * in the system. + */ + +int smp_call_function(void (*func) (void *info), void *info, int nonatomic, + int wait) +/* + * [SUMMARY] Run a function on all other CPUs. + * The function to run. This must be fast and non-blocking. + * An arbitrary pointer to pass to the function. + * currently unused. + * If true, wait (atomically) until function has completed on other CPUs. + * [RETURNS] 0 on success, else a negative status code. Does not return until + * remote CPUs are nearly ready to execute <> or are or have executed. + * + * You must not call this function with disabled interrupts or from a + * hardware interrupt handler or from a bottom half handler. + */ +{ + /* FIXME: get cpu lock with hotplug cpus, or change this to + bitmask. --RR */ + if (num_online_cpus() <= 1) + return 0; + /* Can deadlock when called with interrupts disabled */ + WARN_ON(irqs_disabled()); + return __smp_call_function(func, info, wait, MSG_ALL_BUT_SELF); +} + +static int __smp_call_function(void (*func) (void *info), void *info, + int wait, int target) +{ + struct call_data_struct data; + int ret = -1; + int timeout; + int ncpus = 1; + + if (target == MSG_ALL_BUT_SELF) + ncpus = num_online_cpus() - 1; + else if (target == MSG_ALL) + ncpus = num_online_cpus(); + + data.func = func; + data.info = info; + atomic_set(&data.started, 0); + data.wait = wait; + if (wait) + atomic_set(&data.finished, 0); + + spin_lock(&call_lock); + call_data = &data; + /* Send a message to all other CPUs and wait for them to respond */ + smp_message_pass(target, PPC_MSG_CALL_FUNCTION, 0, 0); + + /* Wait for response */ + timeout = 1000000; + while (atomic_read(&data.started) != ncpus) { + if (--timeout == 0) { + printk("smp_call_function on cpu %d: other cpus not responding (%d)\n", + smp_processor_id(), atomic_read(&data.started)); + goto out; + } + barrier(); + udelay(1); + } + + if (wait) { + timeout = 1000000; + while (atomic_read(&data.finished) != ncpus) { + if (--timeout == 0) { + printk("smp_call_function on cpu %d: other cpus not finishing (%d/%d)\n", + smp_processor_id(), atomic_read(&data.finished), atomic_read(&data.started)); + goto out; + } + barrier(); + udelay(1); + } + } + ret = 0; + + out: + spin_unlock(&call_lock); + return ret; +} + +void smp_call_function_interrupt(void) +{ + void (*func) (void *info) = call_data->func; + void *info = call_data->info; + int wait = call_data->wait; + + /* + * Notify initiating CPU that I've grabbed the data and am + * about to execute the function + */ + atomic_inc(&call_data->started); + /* + * At this point the info structure may be out of scope unless wait==1 + */ + (*func)(info); + if (wait) + atomic_inc(&call_data->finished); +} + +static void __devinit smp_store_cpu_info(int id) +{ + struct cpuinfo_PPC *c = &cpu_data[id]; + + /* assume bogomips are same for everything */ + c->loops_per_jiffy = loops_per_jiffy; + c->pvr = mfspr(SPRN_PVR); +} + +void __init smp_prepare_cpus(unsigned int max_cpus) +{ + int num_cpus, i; + + /* Fixup boot cpu */ + smp_store_cpu_info(smp_processor_id()); + cpu_callin_map[smp_processor_id()] = 1; + + smp_ops = ppc_md.smp_ops; + if (smp_ops == NULL) { + printk("SMP not supported on this machine.\n"); + return; + } + + /* Probe platform for CPUs: always linear. */ + num_cpus = smp_ops->probe(); + for (i = 0; i < num_cpus; ++i) + cpu_set(i, cpu_possible_map); + + /* Backup CPU 0 state */ + __save_cpu_setup(); + + if (smp_ops->space_timers) + smp_ops->space_timers(num_cpus); +} + +void __devinit smp_prepare_boot_cpu(void) +{ + cpu_set(smp_processor_id(), cpu_online_map); + cpu_set(smp_processor_id(), cpu_possible_map); +} + +int __init setup_profiling_timer(unsigned int multiplier) +{ + return 0; +} + +/* Processor coming up starts here */ +int __devinit start_secondary(void *unused) +{ + int cpu; + + atomic_inc(&init_mm.mm_count); + current->active_mm = &init_mm; + + cpu = smp_processor_id(); + smp_store_cpu_info(cpu); + set_dec(tb_ticks_per_jiffy); + cpu_callin_map[cpu] = 1; + + printk("CPU %i done callin...\n", cpu); + smp_ops->setup_cpu(cpu); + printk("CPU %i done setup...\n", cpu); + local_irq_enable(); + smp_ops->take_timebase(); + printk("CPU %i done timebase take...\n", cpu); + + cpu_idle(); + return 0; +} + +int __cpu_up(unsigned int cpu) +{ + struct task_struct *p; + char buf[32]; + int c; + + /* create a process for the processor */ + /* only regs.msr is actually used, and 0 is OK for it */ + p = fork_idle(cpu); + if (IS_ERR(p)) + panic("failed fork for CPU %u: %li", cpu, PTR_ERR(p)); + secondary_ti = p->thread_info; + p->thread_info->cpu = cpu; + + /* + * There was a cache flush loop here to flush the cache + * to memory for the first 8MB of RAM. The cache flush + * has been pushed into the kick_cpu function for those + * platforms that need it. + */ + + /* wake up cpu */ + smp_ops->kick_cpu(cpu); + + /* + * wait to see if the cpu made a callin (is actually up). + * use this value that I found through experimentation. + * -- Cort + */ + for (c = 1000; c && !cpu_callin_map[cpu]; c--) + udelay(100); + + if (!cpu_callin_map[cpu]) { + sprintf(buf, "didn't find cpu %u", cpu); + if (ppc_md.progress) ppc_md.progress(buf, 0x360+cpu); + printk("Processor %u is stuck.\n", cpu); + return -ENOENT; + } + + sprintf(buf, "found cpu %u", cpu); + if (ppc_md.progress) ppc_md.progress(buf, 0x350+cpu); + printk("Processor %d found.\n", cpu); + + smp_ops->give_timebase(); + cpu_set(cpu, cpu_online_map); + return 0; +} + +void smp_cpus_done(unsigned int max_cpus) +{ + smp_ops->setup_cpu(0); +} diff --git a/arch/ppc/kernel/softemu8xx.c b/arch/ppc/kernel/softemu8xx.c new file mode 100644 index 00000000000..9bbb6bf7b64 --- /dev/null +++ b/arch/ppc/kernel/softemu8xx.c @@ -0,0 +1,147 @@ +/* + * Software emulation of some PPC instructions for the 8xx core. + * + * Copyright (C) 1998 Dan Malek (dmalek@jlc.net) + * + * Software floating emuation for the MPC8xx processor. I did this mostly + * because it was easier than trying to get the libraries compiled for + * software floating point. The goal is still to get the libraries done, + * but I lost patience and needed some hacks to at least get init and + * shells running. The first problem is the setjmp/longjmp that save + * and restore the floating point registers. + * + * For this emulation, our working registers are found on the register + * save area. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +extern void +print_8xx_pte(struct mm_struct *mm, unsigned long addr); +extern int +get_8xx_pte(struct mm_struct *mm, unsigned long addr); + +/* Eventually we may need a look-up table, but this works for now. +*/ +#define LFS 48 +#define LFD 50 +#define LFDU 51 +#define STFD 54 +#define STFDU 55 +#define FMR 63 + +/* + * We return 0 on success, 1 on unimplemented instruction, and EFAULT + * if a load/store faulted. + */ +int +Soft_emulate_8xx(struct pt_regs *regs) +{ + uint inst, instword; + uint flreg, idxreg, disp; + uint retval; + signed short sdisp; + uint *ea, *ip; + + retval = 0; + + instword = *((uint *)regs->nip); + inst = instword >> 26; + + flreg = (instword >> 21) & 0x1f; + idxreg = (instword >> 16) & 0x1f; + disp = instword & 0xffff; + + ea = (uint *)(regs->gpr[idxreg] + disp); + ip = (uint *)¤t->thread.fpr[flreg]; + + switch ( inst ) + { + case LFD: + /* this is a 16 bit quantity that is sign extended + * so use a signed short here -- Cort + */ + sdisp = (instword & 0xffff); + ea = (uint *)(regs->gpr[idxreg] + sdisp); + if (copy_from_user(ip, ea, sizeof(double))) + retval = -EFAULT; + break; + + case LFDU: + if (copy_from_user(ip, ea, sizeof(double))) + retval = -EFAULT; + else + regs->gpr[idxreg] = (uint)ea; + break; + case LFS: + sdisp = (instword & 0xffff); + ea = (uint *)(regs->gpr[idxreg] + sdisp); + if (copy_from_user(ip, ea, sizeof(float))) + retval = -EFAULT; + break; + case STFD: + /* this is a 16 bit quantity that is sign extended + * so use a signed short here -- Cort + */ + sdisp = (instword & 0xffff); + ea = (uint *)(regs->gpr[idxreg] + sdisp); + if (copy_to_user(ea, ip, sizeof(double))) + retval = -EFAULT; + break; + + case STFDU: + if (copy_to_user(ea, ip, sizeof(double))) + retval = -EFAULT; + else + regs->gpr[idxreg] = (uint)ea; + break; + case FMR: + /* assume this is a fp move -- Cort */ + memcpy( ip, ¤t->thread.fpr[(instword>>11)&0x1f], + sizeof(double) ); + break; + default: + retval = 1; + printk("Bad emulation %s/%d\n" + " NIP: %08lx instruction: %08x opcode: %x " + "A: %x B: %x C: %x code: %x rc: %x\n", + current->comm,current->pid, + regs->nip, + instword,inst, + (instword>>16)&0x1f, + (instword>>11)&0x1f, + (instword>>6)&0x1f, + (instword>>1)&0x3ff, + instword&1); + { + int pa; + print_8xx_pte(current->mm,regs->nip); + pa = get_8xx_pte(current->mm,regs->nip) & PAGE_MASK; + pa |= (regs->nip & ~PAGE_MASK); + pa = (unsigned long)__va(pa); + printk("Kernel VA for NIP %x ", pa); + print_8xx_pte(current->mm,pa); + } + + } + + if (retval == 0) + regs->nip += 4; + return(retval); +} + diff --git a/arch/ppc/kernel/swsusp.S b/arch/ppc/kernel/swsusp.S new file mode 100644 index 00000000000..55148bb88d3 --- /dev/null +++ b/arch/ppc/kernel/swsusp.S @@ -0,0 +1,349 @@ +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * Structure for storing CPU registers on the save area. + */ +#define SL_SP 0 +#define SL_PC 4 +#define SL_MSR 8 +#define SL_SDR1 0xc +#define SL_SPRG0 0x10 /* 4 sprg's */ +#define SL_DBAT0 0x20 +#define SL_IBAT0 0x28 +#define SL_DBAT1 0x30 +#define SL_IBAT1 0x38 +#define SL_DBAT2 0x40 +#define SL_IBAT2 0x48 +#define SL_DBAT3 0x50 +#define SL_IBAT3 0x58 +#define SL_TB 0x60 +#define SL_R2 0x68 +#define SL_CR 0x6c +#define SL_LR 0x70 +#define SL_R12 0x74 /* r12 to r31 */ +#define SL_SIZE (SL_R12 + 80) + + .section .data + .align 5 + +_GLOBAL(swsusp_save_area) + .space SL_SIZE + + + .section .text + .align 5 + +_GLOBAL(swsusp_arch_suspend) + + lis r11,swsusp_save_area@h + ori r11,r11,swsusp_save_area@l + + mflr r0 + stw r0,SL_LR(r11) + mfcr r0 + stw r0,SL_CR(r11) + stw r1,SL_SP(r11) + stw r2,SL_R2(r11) + stmw r12,SL_R12(r11) + + /* Save MSR & SDR1 */ + mfmsr r4 + stw r4,SL_MSR(r11) + mfsdr1 r4 + stw r4,SL_SDR1(r11) + + /* Get a stable timebase and save it */ +1: mftbu r4 + stw r4,SL_TB(r11) + mftb r5 + stw r5,SL_TB+4(r11) + mftbu r3 + cmpw r3,r4 + bne 1b + + /* Save SPRGs */ + mfsprg r4,0 + stw r4,SL_SPRG0(r11) + mfsprg r4,1 + stw r4,SL_SPRG0+4(r11) + mfsprg r4,2 + stw r4,SL_SPRG0+8(r11) + mfsprg r4,3 + stw r4,SL_SPRG0+12(r11) + + /* Save BATs */ + mfdbatu r4,0 + stw r4,SL_DBAT0(r11) + mfdbatl r4,0 + stw r4,SL_DBAT0+4(r11) + mfdbatu r4,1 + stw r4,SL_DBAT1(r11) + mfdbatl r4,1 + stw r4,SL_DBAT1+4(r11) + mfdbatu r4,2 + stw r4,SL_DBAT2(r11) + mfdbatl r4,2 + stw r4,SL_DBAT2+4(r11) + mfdbatu r4,3 + stw r4,SL_DBAT3(r11) + mfdbatl r4,3 + stw r4,SL_DBAT3+4(r11) + mfibatu r4,0 + stw r4,SL_IBAT0(r11) + mfibatl r4,0 + stw r4,SL_IBAT0+4(r11) + mfibatu r4,1 + stw r4,SL_IBAT1(r11) + mfibatl r4,1 + stw r4,SL_IBAT1+4(r11) + mfibatu r4,2 + stw r4,SL_IBAT2(r11) + mfibatl r4,2 + stw r4,SL_IBAT2+4(r11) + mfibatu r4,3 + stw r4,SL_IBAT3(r11) + mfibatl r4,3 + stw r4,SL_IBAT3+4(r11) + +#if 0 + /* Backup various CPU config stuffs */ + bl __save_cpu_setup +#endif + /* Call the low level suspend stuff (we should probably have made + * a stackframe... + */ + bl swsusp_save + + /* Restore LR from the save area */ + lis r11,swsusp_save_area@h + ori r11,r11,swsusp_save_area@l + lwz r0,SL_LR(r11) + mtlr r0 + + blr + + +/* Resume code */ +_GLOBAL(swsusp_arch_resume) + + /* Stop pending alitvec streams and memory accesses */ +BEGIN_FTR_SECTION + DSSALL +END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) + sync + + /* Disable MSR:DR to make sure we don't take a TLB or + * hash miss during the copy, as our hash table will + * for a while be unuseable. For .text, we assume we are + * covered by a BAT. This works only for non-G5 at this + * point. G5 will need a better approach, possibly using + * a small temporary hash table filled with large mappings, + * disabling the MMU completely isn't a good option for + * performance reasons. + * (Note that 750's may have the same performance issue as + * the G5 in this case, we should investigate using moving + * BATs for these CPUs) + */ + mfmsr r0 + sync + rlwinm r0,r0,0,28,26 /* clear MSR_DR */ + mtmsr r0 + sync + isync + + /* Load ptr the list of pages to copy in r3 */ + lis r11,(pagedir_nosave - KERNELBASE)@h + ori r11,r11,pagedir_nosave@l + lwz r10,0(r11) + + /* Copy the pages. This is a very basic implementation, to + * be replaced by something more cache efficient */ +1: + tophys(r3,r10) + li r0,256 + mtctr r0 + lwz r11,pbe_address(r3) /* source */ + tophys(r5,r11) + lwz r10,pbe_orig_address(r3) /* destination */ + tophys(r6,r10) +2: + lwz r8,0(r5) + lwz r9,4(r5) + lwz r10,8(r5) + lwz r11,12(r5) + addi r5,r5,16 + stw r8,0(r6) + stw r9,4(r6) + stw r10,8(r6) + stw r11,12(r6) + addi r6,r6,16 + bdnz 2b + lwz r10,pbe_next(r3) + cmpwi 0,r10,0 + bne 1b + + /* Do a very simple cache flush/inval of the L1 to ensure + * coherency of the icache + */ + lis r3,0x0002 + mtctr r3 + li r3, 0 +1: + lwz r0,0(r3) + addi r3,r3,0x0020 + bdnz 1b + isync + sync + + /* Now flush those cache lines */ + lis r3,0x0002 + mtctr r3 + li r3, 0 +1: + dcbf 0,r3 + addi r3,r3,0x0020 + bdnz 1b + sync + + /* Ok, we are now running with the kernel data of the old + * kernel fully restored. We can get to the save area + * easily now. As for the rest of the code, it assumes the + * loader kernel and the booted one are exactly identical + */ + lis r11,swsusp_save_area@h + ori r11,r11,swsusp_save_area@l + tophys(r11,r11) + +#if 0 + /* Restore various CPU config stuffs */ + bl __restore_cpu_setup +#endif + /* Restore the BATs, and SDR1. Then we can turn on the MMU. + * This is a bit hairy as we are running out of those BATs, + * but first, our code is probably in the icache, and we are + * writing the same value to the BAT, so that should be fine, + * though a better solution will have to be found long-term + */ + lwz r4,SL_SDR1(r11) + mtsdr1 r4 + lwz r4,SL_SPRG0(r11) + mtsprg 0,r4 + lwz r4,SL_SPRG0+4(r11) + mtsprg 1,r4 + lwz r4,SL_SPRG0+8(r11) + mtsprg 2,r4 + lwz r4,SL_SPRG0+12(r11) + mtsprg 3,r4 + +#if 0 + lwz r4,SL_DBAT0(r11) + mtdbatu 0,r4 + lwz r4,SL_DBAT0+4(r11) + mtdbatl 0,r4 + lwz r4,SL_DBAT1(r11) + mtdbatu 1,r4 + lwz r4,SL_DBAT1+4(r11) + mtdbatl 1,r4 + lwz r4,SL_DBAT2(r11) + mtdbatu 2,r4 + lwz r4,SL_DBAT2+4(r11) + mtdbatl 2,r4 + lwz r4,SL_DBAT3(r11) + mtdbatu 3,r4 + lwz r4,SL_DBAT3+4(r11) + mtdbatl 3,r4 + lwz r4,SL_IBAT0(r11) + mtibatu 0,r4 + lwz r4,SL_IBAT0+4(r11) + mtibatl 0,r4 + lwz r4,SL_IBAT1(r11) + mtibatu 1,r4 + lwz r4,SL_IBAT1+4(r11) + mtibatl 1,r4 + lwz r4,SL_IBAT2(r11) + mtibatu 2,r4 + lwz r4,SL_IBAT2+4(r11) + mtibatl 2,r4 + lwz r4,SL_IBAT3(r11) + mtibatu 3,r4 + lwz r4,SL_IBAT3+4(r11) + mtibatl 3,r4 +#endif + +BEGIN_FTR_SECTION + li r4,0 + mtspr SPRN_DBAT4U,r4 + mtspr SPRN_DBAT4L,r4 + mtspr SPRN_DBAT5U,r4 + mtspr SPRN_DBAT5L,r4 + mtspr SPRN_DBAT6U,r4 + mtspr SPRN_DBAT6L,r4 + mtspr SPRN_DBAT7U,r4 + mtspr SPRN_DBAT7L,r4 + mtspr SPRN_IBAT4U,r4 + mtspr SPRN_IBAT4L,r4 + mtspr SPRN_IBAT5U,r4 + mtspr SPRN_IBAT5L,r4 + mtspr SPRN_IBAT6U,r4 + mtspr SPRN_IBAT6L,r4 + mtspr SPRN_IBAT7U,r4 + mtspr SPRN_IBAT7L,r4 +END_FTR_SECTION_IFSET(CPU_FTR_HAS_HIGH_BATS) + + /* Flush all TLBs */ + lis r4,0x1000 +1: addic. r4,r4,-0x1000 + tlbie r4 + blt 1b + sync + + /* restore the MSR and turn on the MMU */ + lwz r3,SL_MSR(r11) + bl turn_on_mmu + tovirt(r11,r11) + + /* Restore TB */ + li r3,0 + mttbl r3 + lwz r3,SL_TB(r11) + lwz r4,SL_TB+4(r11) + mttbu r3 + mttbl r4 + + /* Kick decrementer */ + li r0,1 + mtdec r0 + + /* Restore the callee-saved registers and return */ + lwz r0,SL_CR(r11) + mtcr r0 + lwz r2,SL_R2(r11) + lmw r12,SL_R12(r11) + lwz r1,SL_SP(r11) + lwz r0,SL_LR(r11) + mtlr r0 + + // XXX Note: we don't really need to call swsusp_resume + + li r3,0 + blr + +/* FIXME:This construct is actually not useful since we don't shut + * down the instruction MMU, we could just flip back MSR-DR on. + */ +turn_on_mmu: + mflr r4 + mtsrr0 r4 + mtsrr1 r3 + sync + isync + rfi + diff --git a/arch/ppc/kernel/syscalls.c b/arch/ppc/kernel/syscalls.c new file mode 100644 index 00000000000..124313ce3c0 --- /dev/null +++ b/arch/ppc/kernel/syscalls.c @@ -0,0 +1,272 @@ +/* + * arch/ppc/kernel/sys_ppc.c + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Derived from "arch/i386/kernel/sys_i386.c" + * Adapted from the i386 version by Gary Thomas + * Modified by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras (paulus@cs.anu.edu.au). + * + * This file contains various random system calls that + * have a non-standard calling sequence on the Linux/PPC + * platform. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +void +check_bugs(void) +{ +} + +/* + * sys_ipc() is the de-multiplexer for the SysV IPC calls.. + * + * This is really horribly ugly. + */ +int +sys_ipc (uint call, int first, int second, int third, void __user *ptr, long fifth) +{ + int version, ret; + + version = call >> 16; /* hack for backward compatibility */ + call &= 0xffff; + + ret = -ENOSYS; + switch (call) { + case SEMOP: + ret = sys_semtimedop (first, (struct sembuf __user *)ptr, + second, NULL); + break; + case SEMTIMEDOP: + ret = sys_semtimedop (first, (struct sembuf __user *)ptr, + second, (const struct timespec __user *) fifth); + break; + case SEMGET: + ret = sys_semget (first, second, third); + break; + case SEMCTL: { + union semun fourth; + + if (!ptr) + break; + if ((ret = access_ok(VERIFY_READ, ptr, sizeof(long)) ? 0 : -EFAULT) + || (ret = get_user(fourth.__pad, (void __user *__user *)ptr))) + break; + ret = sys_semctl (first, second, third, fourth); + break; + } + case MSGSND: + ret = sys_msgsnd (first, (struct msgbuf __user *) ptr, second, third); + break; + case MSGRCV: + switch (version) { + case 0: { + struct ipc_kludge tmp; + + if (!ptr) + break; + if ((ret = access_ok(VERIFY_READ, ptr, sizeof(tmp)) ? 0 : -EFAULT) + || (ret = copy_from_user(&tmp, + (struct ipc_kludge __user *) ptr, + sizeof (tmp)) ? -EFAULT : 0)) + break; + ret = sys_msgrcv (first, tmp.msgp, second, tmp.msgtyp, + third); + break; + } + default: + ret = sys_msgrcv (first, (struct msgbuf __user *) ptr, + second, fifth, third); + break; + } + break; + case MSGGET: + ret = sys_msgget ((key_t) first, second); + break; + case MSGCTL: + ret = sys_msgctl (first, second, (struct msqid_ds __user *) ptr); + break; + case SHMAT: { + ulong raddr; + + if ((ret = access_ok(VERIFY_WRITE, (ulong __user *) third, + sizeof(ulong)) ? 0 : -EFAULT)) + break; + ret = do_shmat (first, (char __user *) ptr, second, &raddr); + if (ret) + break; + ret = put_user (raddr, (ulong __user *) third); + break; + } + case SHMDT: + ret = sys_shmdt ((char __user *)ptr); + break; + case SHMGET: + ret = sys_shmget (first, second, third); + break; + case SHMCTL: + ret = sys_shmctl (first, second, (struct shmid_ds __user *) ptr); + break; + } + + return ret; +} + +/* + * sys_pipe() is the normal C calling standard for creating + * a pipe. It's not the way unix traditionally does this, though. + */ +int sys_pipe(int __user *fildes) +{ + int fd[2]; + int error; + + error = do_pipe(fd); + if (!error) { + if (copy_to_user(fildes, fd, 2*sizeof(int))) + error = -EFAULT; + } + return error; +} + +static inline unsigned long +do_mmap2(unsigned long addr, size_t len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long pgoff) +{ + struct file * file = NULL; + int ret = -EBADF; + + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + if (!(flags & MAP_ANONYMOUS)) { + if (!(file = fget(fd))) + goto out; + } + + down_write(¤t->mm->mmap_sem); + ret = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); + up_write(¤t->mm->mmap_sem); + if (file) + fput(file); +out: + return ret; +} + +unsigned long sys_mmap2(unsigned long addr, size_t len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long pgoff) +{ + return do_mmap2(addr, len, prot, flags, fd, pgoff); +} + +unsigned long sys_mmap(unsigned long addr, size_t len, + unsigned long prot, unsigned long flags, + unsigned long fd, off_t offset) +{ + int err = -EINVAL; + + if (offset & ~PAGE_MASK) + goto out; + + err = do_mmap2(addr, len, prot, flags, fd, offset >> PAGE_SHIFT); +out: + return err; +} + +/* + * Due to some executables calling the wrong select we sometimes + * get wrong args. This determines how the args are being passed + * (a single ptr to them all args passed) then calls + * sys_select() with the appropriate args. -- Cort + */ +int +ppc_select(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp, struct timeval __user *tvp) +{ + if ( (unsigned long)n >= 4096 ) + { + unsigned long __user *buffer = (unsigned long __user *)n; + if (!access_ok(VERIFY_READ, buffer, 5*sizeof(unsigned long)) + || __get_user(n, buffer) + || __get_user(inp, ((fd_set __user * __user *)(buffer+1))) + || __get_user(outp, ((fd_set __user * __user *)(buffer+2))) + || __get_user(exp, ((fd_set __user * __user *)(buffer+3))) + || __get_user(tvp, ((struct timeval __user * __user *)(buffer+4)))) + return -EFAULT; + } + return sys_select(n, inp, outp, exp, tvp); +} + +int sys_uname(struct old_utsname __user * name) +{ + int err = -EFAULT; + + down_read(&uts_sem); + if (name && !copy_to_user(name, &system_utsname, sizeof (*name))) + err = 0; + up_read(&uts_sem); + return err; +} + +int sys_olduname(struct oldold_utsname __user * name) +{ + int error; + + if (!name) + return -EFAULT; + if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname))) + return -EFAULT; + + down_read(&uts_sem); + error = __copy_to_user(&name->sysname,&system_utsname.sysname,__OLD_UTS_LEN); + error -= __put_user(0,name->sysname+__OLD_UTS_LEN); + error -= __copy_to_user(&name->nodename,&system_utsname.nodename,__OLD_UTS_LEN); + error -= __put_user(0,name->nodename+__OLD_UTS_LEN); + error -= __copy_to_user(&name->release,&system_utsname.release,__OLD_UTS_LEN); + error -= __put_user(0,name->release+__OLD_UTS_LEN); + error -= __copy_to_user(&name->version,&system_utsname.version,__OLD_UTS_LEN); + error -= __put_user(0,name->version+__OLD_UTS_LEN); + error -= __copy_to_user(&name->machine,&system_utsname.machine,__OLD_UTS_LEN); + error = __put_user(0,name->machine+__OLD_UTS_LEN); + up_read(&uts_sem); + + error = error ? -EFAULT : 0; + return error; +} + +/* + * We put the arguments in a different order so we only use 6 + * registers for arguments, rather than 7 as sys_fadvise64_64 needs + * (because `offset' goes in r5/r6). + */ +long ppc_fadvise64_64(int fd, int advice, loff_t offset, loff_t len) +{ + return sys_fadvise64_64(fd, offset, len, advice); +} diff --git a/arch/ppc/kernel/temp.c b/arch/ppc/kernel/temp.c new file mode 100644 index 00000000000..fe8bb634ead --- /dev/null +++ b/arch/ppc/kernel/temp.c @@ -0,0 +1,272 @@ +/* + * temp.c Thermal management for cpu's with Thermal Assist Units + * + * Written by Troy Benjegerdes + * + * TODO: + * dynamic power management to limit peak CPU temp (using ICTC) + * calibration??? + * + * Silly, crazy ideas: use cpu load (from scheduler) and ICTC to extend battery + * life in portables, and add a 'performance/watt' metric somewhere in /proc + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +static struct tau_temp +{ + int interrupts; + unsigned char low; + unsigned char high; + unsigned char grew; +} tau[NR_CPUS]; + +struct timer_list tau_timer; + +#undef DEBUG + +/* TODO: put these in a /proc interface, with some sanity checks, and maybe + * dynamic adjustment to minimize # of interrupts */ +/* configurable values for step size and how much to expand the window when + * we get an interrupt. These are based on the limit that was out of range */ +#define step_size 2 /* step size when temp goes out of range */ +#define window_expand 1 /* expand the window by this much */ +/* configurable values for shrinking the window */ +#define shrink_timer 2*HZ /* period between shrinking the window */ +#define min_window 2 /* minimum window size, degrees C */ + +void set_thresholds(unsigned long cpu) +{ +#ifdef CONFIG_TAU_INT + /* + * setup THRM1, + * threshold, valid bit, enable interrupts, interrupt when below threshold + */ + mtspr(SPRN_THRM1, THRM1_THRES(tau[cpu].low) | THRM1_V | THRM1_TIE | THRM1_TID); + + /* setup THRM2, + * threshold, valid bit, enable interrupts, interrupt when above threshhold + */ + mtspr (SPRN_THRM2, THRM1_THRES(tau[cpu].high) | THRM1_V | THRM1_TIE); +#else + /* same thing but don't enable interrupts */ + mtspr(SPRN_THRM1, THRM1_THRES(tau[cpu].low) | THRM1_V | THRM1_TID); + mtspr(SPRN_THRM2, THRM1_THRES(tau[cpu].high) | THRM1_V); +#endif +} + +void TAUupdate(int cpu) +{ + unsigned thrm; + +#ifdef DEBUG + printk("TAUupdate "); +#endif + + /* if both thresholds are crossed, the step_sizes cancel out + * and the window winds up getting expanded twice. */ + if((thrm = mfspr(SPRN_THRM1)) & THRM1_TIV){ /* is valid? */ + if(thrm & THRM1_TIN){ /* crossed low threshold */ + if (tau[cpu].low >= step_size){ + tau[cpu].low -= step_size; + tau[cpu].high -= (step_size - window_expand); + } + tau[cpu].grew = 1; +#ifdef DEBUG + printk("low threshold crossed "); +#endif + } + } + if((thrm = mfspr(SPRN_THRM2)) & THRM1_TIV){ /* is valid? */ + if(thrm & THRM1_TIN){ /* crossed high threshold */ + if (tau[cpu].high <= 127-step_size){ + tau[cpu].low += (step_size - window_expand); + tau[cpu].high += step_size; + } + tau[cpu].grew = 1; +#ifdef DEBUG + printk("high threshold crossed "); +#endif + } + } + +#ifdef DEBUG + printk("grew = %d\n", tau[cpu].grew); +#endif + +#ifndef CONFIG_TAU_INT /* tau_timeout will do this if not using interrupts */ + set_thresholds(cpu); +#endif + +} + +#ifdef CONFIG_TAU_INT +/* + * TAU interrupts - called when we have a thermal assist unit interrupt + * with interrupts disabled + */ + +void TAUException(struct pt_regs * regs) +{ + int cpu = smp_processor_id(); + + irq_enter(); + tau[cpu].interrupts++; + + TAUupdate(cpu); + + irq_exit(); +} +#endif /* CONFIG_TAU_INT */ + +static void tau_timeout(void * info) +{ + int cpu; + unsigned long flags; + int size; + int shrink; + + /* disabling interrupts *should* be okay */ + local_irq_save(flags); + cpu = smp_processor_id(); + +#ifndef CONFIG_TAU_INT + TAUupdate(cpu); +#endif + + size = tau[cpu].high - tau[cpu].low; + if (size > min_window && ! tau[cpu].grew) { + /* do an exponential shrink of half the amount currently over size */ + shrink = (2 + size - min_window) / 4; + if (shrink) { + tau[cpu].low += shrink; + tau[cpu].high -= shrink; + } else { /* size must have been min_window + 1 */ + tau[cpu].low += 1; +#if 1 /* debug */ + if ((tau[cpu].high - tau[cpu].low) != min_window){ + printk(KERN_ERR "temp.c: line %d, logic error\n", __LINE__); + } +#endif + } + } + + tau[cpu].grew = 0; + + set_thresholds(cpu); + + /* + * Do the enable every time, since otherwise a bunch of (relatively) + * complex sleep code needs to be added. One mtspr every time + * tau_timeout is called is probably not a big deal. + * + * Enable thermal sensor and set up sample interval timer + * need 20 us to do the compare.. until a nice 'cpu_speed' function + * call is implemented, just assume a 500 mhz clock. It doesn't really + * matter if we take too long for a compare since it's all interrupt + * driven anyway. + * + * use a extra long time.. (60 us @ 500 mhz) + */ + mtspr(SPRN_THRM3, THRM3_SITV(500*60) | THRM3_E); + + local_irq_restore(flags); +} + +static void tau_timeout_smp(unsigned long unused) +{ + + /* schedule ourselves to be run again */ + mod_timer(&tau_timer, jiffies + shrink_timer) ; + on_each_cpu(tau_timeout, NULL, 1, 0); +} + +/* + * setup the TAU + * + * Set things up to use THRM1 as a temperature lower bound, and THRM2 as an upper bound. + * Start off at zero + */ + +int tau_initialized = 0; + +void __init TAU_init_smp(void * info) +{ + unsigned long cpu = smp_processor_id(); + + /* set these to a reasonable value and let the timer shrink the + * window */ + tau[cpu].low = 5; + tau[cpu].high = 120; + + set_thresholds(cpu); +} + +int __init TAU_init(void) +{ + /* We assume in SMP that if one CPU has TAU support, they + * all have it --BenH + */ + if (!cpu_has_feature(CPU_FTR_TAU)) { + printk("Thermal assist unit not available\n"); + tau_initialized = 0; + return 1; + } + + + /* first, set up the window shrinking timer */ + init_timer(&tau_timer); + tau_timer.function = tau_timeout_smp; + tau_timer.expires = jiffies + shrink_timer; + add_timer(&tau_timer); + + on_each_cpu(TAU_init_smp, NULL, 1, 0); + + printk("Thermal assist unit "); +#ifdef CONFIG_TAU_INT + printk("using interrupts, "); +#else + printk("using timers, "); +#endif + printk("shrink_timer: %d jiffies\n", shrink_timer); + tau_initialized = 1; + + return 0; +} + +__initcall(TAU_init); + +/* + * return current temp + */ + +u32 cpu_temp_both(unsigned long cpu) +{ + return ((tau[cpu].high << 16) | tau[cpu].low); +} + +int cpu_temp(unsigned long cpu) +{ + return ((tau[cpu].high + tau[cpu].low) / 2); +} + +int tau_interrupts(unsigned long cpu) +{ + return (tau[cpu].interrupts); +} diff --git a/arch/ppc/kernel/time.c b/arch/ppc/kernel/time.c new file mode 100644 index 00000000000..50724139402 --- /dev/null +++ b/arch/ppc/kernel/time.c @@ -0,0 +1,447 @@ +/* + * Common time routines among all ppc machines. + * + * Written by Cort Dougan (cort@cs.nmt.edu) to merge + * Paul Mackerras' version and mine for PReP and Pmac. + * MPC8xx/MBX changes by Dan Malek (dmalek@jlc.net). + * + * First round of bugfixes by Gabriel Paubert (paubert@iram.es) + * to make clock more stable (2.4.0-test5). The only thing + * that this code assumes is that the timebases have been synchronized + * by firmware on SMP and are never stopped (never do sleep + * on SMP then, nap and doze are OK). + * + * TODO (not necessarily in this file): + * - improve precision and reproducibility of timebase frequency + * measurement at boot time. + * - get rid of xtime_lock for gettimeofday (generic kernel problem + * to be implemented on all architectures for SMP scalability and + * eventually implementing gettimeofday without entering the kernel). + * - put all time/clock related variables in a single structure + * to minimize number of cache lines touched by gettimeofday() + * - for astronomical applications: add a new function to get + * non ambiguous timestamps even around leap seconds. This needs + * a new timestamp format and a good name. + * + * + * The following comment is partially obsolete (at least the long wait + * is no more a valid reason): + * Since the MPC8xx has a programmable interrupt timer, I decided to + * use that rather than the decrementer. Two reasons: 1.) the clock + * frequency is low, causing 2.) a long wait in the timer interrupt + * while ((d = get_dec()) == dval) + * loop. The MPC8xx can be driven from a variety of input clocks, + * so a number of assumptions have been made here because the kernel + * parameter HZ is a constant. We assume (correctly, today :-) that + * the MPC8xx on the MBX board is driven from a 32.768 kHz crystal. + * This is then divided by 4, providing a 8192 Hz clock into the PIT. + * Since it is not possible to get a nice 100 Hz clock out of this, without + * creating a software PLL, I have set HZ to 128. -- Dan + * + * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 + * "A Kernel Model for Precision Timekeeping" by Dave Mills + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +/* XXX false sharing with below? */ +u64 jiffies_64 = INITIAL_JIFFIES; + +EXPORT_SYMBOL(jiffies_64); + +unsigned long disarm_decr[NR_CPUS]; + +extern struct timezone sys_tz; + +/* keep track of when we need to update the rtc */ +time_t last_rtc_update; + +/* The decrementer counts down by 128 every 128ns on a 601. */ +#define DECREMENTER_COUNT_601 (1000000000 / HZ) + +unsigned tb_ticks_per_jiffy; +unsigned tb_to_us; +unsigned tb_last_stamp; +unsigned long tb_to_ns_scale; + +extern unsigned long wall_jiffies; + +static long time_offset; + +DEFINE_SPINLOCK(rtc_lock); + +EXPORT_SYMBOL(rtc_lock); + +/* Timer interrupt helper function */ +static inline int tb_delta(unsigned *jiffy_stamp) { + int delta; + if (__USE_RTC()) { + delta = get_rtcl(); + if (delta < *jiffy_stamp) *jiffy_stamp -= 1000000000; + delta -= *jiffy_stamp; + } else { + delta = get_tbl() - *jiffy_stamp; + } + return delta; +} + +#ifdef CONFIG_SMP +unsigned long profile_pc(struct pt_regs *regs) +{ + unsigned long pc = instruction_pointer(regs); + + if (in_lock_functions(pc)) + return regs->link; + + return pc; +} +EXPORT_SYMBOL(profile_pc); +#endif + +/* + * timer_interrupt - gets called when the decrementer overflows, + * with interrupts disabled. + * We set it up to overflow again in 1/HZ seconds. + */ +void timer_interrupt(struct pt_regs * regs) +{ + int next_dec; + unsigned long cpu = smp_processor_id(); + unsigned jiffy_stamp = last_jiffy_stamp(cpu); + extern void do_IRQ(struct pt_regs *); + + if (atomic_read(&ppc_n_lost_interrupts) != 0) + do_IRQ(regs); + + irq_enter(); + + while ((next_dec = tb_ticks_per_jiffy - tb_delta(&jiffy_stamp)) <= 0) { + jiffy_stamp += tb_ticks_per_jiffy; + + profile_tick(CPU_PROFILING, regs); + update_process_times(user_mode(regs)); + + if (smp_processor_id()) + continue; + + /* We are in an interrupt, no need to save/restore flags */ + write_seqlock(&xtime_lock); + tb_last_stamp = jiffy_stamp; + do_timer(regs); + + /* + * update the rtc when needed, this should be performed on the + * right fraction of a second. Half or full second ? + * Full second works on mk48t59 clocks, others need testing. + * Note that this update is basically only used through + * the adjtimex system calls. Setting the HW clock in + * any other way is a /dev/rtc and userland business. + * This is still wrong by -0.5/+1.5 jiffies because of the + * timer interrupt resolution and possible delay, but here we + * hit a quantization limit which can only be solved by higher + * resolution timers and decoupling time management from timer + * interrupts. This is also wrong on the clocks + * which require being written at the half second boundary. + * We should have an rtc call that only sets the minutes and + * seconds like on Intel to avoid problems with non UTC clocks. + */ + if ( ppc_md.set_rtc_time && (time_status & STA_UNSYNC) == 0 && + xtime.tv_sec - last_rtc_update >= 659 && + abs((xtime.tv_nsec / 1000) - (1000000-1000000/HZ)) < 500000/HZ && + jiffies - wall_jiffies == 1) { + if (ppc_md.set_rtc_time(xtime.tv_sec+1 + time_offset) == 0) + last_rtc_update = xtime.tv_sec+1; + else + /* Try again one minute later */ + last_rtc_update += 60; + } + write_sequnlock(&xtime_lock); + } + if ( !disarm_decr[smp_processor_id()] ) + set_dec(next_dec); + last_jiffy_stamp(cpu) = jiffy_stamp; + + if (ppc_md.heartbeat && !ppc_md.heartbeat_count--) + ppc_md.heartbeat(); + + irq_exit(); +} + +/* + * This version of gettimeofday has microsecond resolution. + */ +void do_gettimeofday(struct timeval *tv) +{ + unsigned long flags; + unsigned long seq; + unsigned delta, lost_ticks, usec, sec; + + do { + seq = read_seqbegin_irqsave(&xtime_lock, flags); + sec = xtime.tv_sec; + usec = (xtime.tv_nsec / 1000); + delta = tb_ticks_since(tb_last_stamp); +#ifdef CONFIG_SMP + /* As long as timebases are not in sync, gettimeofday can only + * have jiffy resolution on SMP. + */ + if (!smp_tb_synchronized) + delta = 0; +#endif /* CONFIG_SMP */ + lost_ticks = jiffies - wall_jiffies; + } while (read_seqretry_irqrestore(&xtime_lock, seq, flags)); + + usec += mulhwu(tb_to_us, tb_ticks_per_jiffy * lost_ticks + delta); + while (usec >= 1000000) { + sec++; + usec -= 1000000; + } + tv->tv_sec = sec; + tv->tv_usec = usec; +} + +EXPORT_SYMBOL(do_gettimeofday); + +int do_settimeofday(struct timespec *tv) +{ + time_t wtm_sec, new_sec = tv->tv_sec; + long wtm_nsec, new_nsec = tv->tv_nsec; + unsigned long flags; + int tb_delta; + + if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) + return -EINVAL; + + write_seqlock_irqsave(&xtime_lock, flags); + /* Updating the RTC is not the job of this code. If the time is + * stepped under NTP, the RTC will be update after STA_UNSYNC + * is cleared. Tool like clock/hwclock either copy the RTC + * to the system time, in which case there is no point in writing + * to the RTC again, or write to the RTC but then they don't call + * settimeofday to perform this operation. Note also that + * we don't touch the decrementer since: + * a) it would lose timer interrupt synchronization on SMP + * (if it is working one day) + * b) it could make one jiffy spuriously shorter or longer + * which would introduce another source of uncertainty potentially + * harmful to relatively short timers. + */ + + /* This works perfectly on SMP only if the tb are in sync but + * guarantees an error < 1 jiffy even if they are off by eons, + * still reasonable when gettimeofday resolution is 1 jiffy. + */ + tb_delta = tb_ticks_since(last_jiffy_stamp(smp_processor_id())); + tb_delta += (jiffies - wall_jiffies) * tb_ticks_per_jiffy; + + new_nsec -= 1000 * mulhwu(tb_to_us, tb_delta); + + wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - new_sec); + wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - new_nsec); + + set_normalized_timespec(&xtime, new_sec, new_nsec); + set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); + + /* In case of a large backwards jump in time with NTP, we want the + * clock to be updated as soon as the PLL is again in lock. + */ + last_rtc_update = new_sec - 658; + + time_adjust = 0; /* stop active adjtime() */ + time_status |= STA_UNSYNC; + time_state = TIME_ERROR; /* p. 24, (a) */ + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; + write_sequnlock_irqrestore(&xtime_lock, flags); + clock_was_set(); + return 0; +} + +EXPORT_SYMBOL(do_settimeofday); + +/* This function is only called on the boot processor */ +void __init time_init(void) +{ + time_t sec, old_sec; + unsigned old_stamp, stamp, elapsed; + + if (ppc_md.time_init != NULL) + time_offset = ppc_md.time_init(); + + if (__USE_RTC()) { + /* 601 processor: dec counts down by 128 every 128ns */ + tb_ticks_per_jiffy = DECREMENTER_COUNT_601; + /* mulhwu_scale_factor(1000000000, 1000000) is 0x418937 */ + tb_to_us = 0x418937; + } else { + ppc_md.calibrate_decr(); + tb_to_ns_scale = mulhwu(tb_to_us, 1000 << 10); + } + + /* Now that the decrementer is calibrated, it can be used in case the + * clock is stuck, but the fact that we have to handle the 601 + * makes things more complex. Repeatedly read the RTC until the + * next second boundary to try to achieve some precision. If there + * is no RTC, we still need to set tb_last_stamp and + * last_jiffy_stamp(cpu 0) to the current stamp. + */ + stamp = get_native_tbl(); + if (ppc_md.get_rtc_time) { + sec = ppc_md.get_rtc_time(); + elapsed = 0; + do { + old_stamp = stamp; + old_sec = sec; + stamp = get_native_tbl(); + if (__USE_RTC() && stamp < old_stamp) + old_stamp -= 1000000000; + elapsed += stamp - old_stamp; + sec = ppc_md.get_rtc_time(); + } while ( sec == old_sec && elapsed < 2*HZ*tb_ticks_per_jiffy); + if (sec==old_sec) + printk("Warning: real time clock seems stuck!\n"); + xtime.tv_sec = sec; + xtime.tv_nsec = 0; + /* No update now, we just read the time from the RTC ! */ + last_rtc_update = xtime.tv_sec; + } + last_jiffy_stamp(0) = tb_last_stamp = stamp; + + /* Not exact, but the timer interrupt takes care of this */ + set_dec(tb_ticks_per_jiffy); + + /* If platform provided a timezone (pmac), we correct the time */ + if (time_offset) { + sys_tz.tz_minuteswest = -time_offset / 60; + sys_tz.tz_dsttime = 0; + xtime.tv_sec -= time_offset; + } + set_normalized_timespec(&wall_to_monotonic, + -xtime.tv_sec, -xtime.tv_nsec); +} + +#define FEBRUARY 2 +#define STARTOFTIME 1970 +#define SECDAY 86400L +#define SECYR (SECDAY * 365) + +/* + * Note: this is wrong for 2100, but our signed 32-bit time_t will + * have overflowed long before that, so who cares. -- paulus + */ +#define leapyear(year) ((year) % 4 == 0) +#define days_in_year(a) (leapyear(a) ? 366 : 365) +#define days_in_month(a) (month_days[(a) - 1]) + +static int month_days[12] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +void to_tm(int tim, struct rtc_time * tm) +{ + register int i; + register long hms, day, gday; + + gday = day = tim / SECDAY; + hms = tim % SECDAY; + + /* Hours, minutes, seconds are easy */ + tm->tm_hour = hms / 3600; + tm->tm_min = (hms % 3600) / 60; + tm->tm_sec = (hms % 3600) % 60; + + /* Number of years in days */ + for (i = STARTOFTIME; day >= days_in_year(i); i++) + day -= days_in_year(i); + tm->tm_year = i; + + /* Number of months in days left */ + if (leapyear(tm->tm_year)) + days_in_month(FEBRUARY) = 29; + for (i = 1; day >= days_in_month(i); i++) + day -= days_in_month(i); + days_in_month(FEBRUARY) = 28; + tm->tm_mon = i; + + /* Days are what is left over (+1) from all that. */ + tm->tm_mday = day + 1; + + /* + * Determine the day of week. Jan. 1, 1970 was a Thursday. + */ + tm->tm_wday = (gday + 4) % 7; +} + +/* Auxiliary function to compute scaling factors */ +/* Actually the choice of a timebase running at 1/4 the of the bus + * frequency giving resolution of a few tens of nanoseconds is quite nice. + * It makes this computation very precise (27-28 bits typically) which + * is optimistic considering the stability of most processor clock + * oscillators and the precision with which the timebase frequency + * is measured but does not harm. + */ +unsigned mulhwu_scale_factor(unsigned inscale, unsigned outscale) { + unsigned mlt=0, tmp, err; + /* No concern for performance, it's done once: use a stupid + * but safe and compact method to find the multiplier. + */ + for (tmp = 1U<<31; tmp != 0; tmp >>= 1) { + if (mulhwu(inscale, mlt|tmp) < outscale) mlt|=tmp; + } + /* We might still be off by 1 for the best approximation. + * A side effect of this is that if outscale is too large + * the returned value will be zero. + * Many corner cases have been checked and seem to work, + * some might have been forgotten in the test however. + */ + err = inscale*(mlt+1); + if (err <= inscale/2) mlt++; + return mlt; +} + +unsigned long long sched_clock(void) +{ + unsigned long lo, hi, hi2; + unsigned long long tb; + + if (!__USE_RTC()) { + do { + hi = get_tbu(); + lo = get_tbl(); + hi2 = get_tbu(); + } while (hi2 != hi); + tb = ((unsigned long long) hi << 32) | lo; + tb = (tb * tb_to_ns_scale) >> 10; + } else { + do { + hi = get_rtcu(); + lo = get_rtcl(); + hi2 = get_rtcu(); + } while (hi2 != hi); + tb = ((unsigned long long) hi) * 1000000000 + lo; + } + return tb; +} diff --git a/arch/ppc/kernel/traps.c b/arch/ppc/kernel/traps.c new file mode 100644 index 00000000000..ed5c7acdca7 --- /dev/null +++ b/arch/ppc/kernel/traps.c @@ -0,0 +1,886 @@ +/* + * arch/ppc/kernel/traps.c + * + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Modified by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras (paulus@cs.anu.edu.au) + */ + +/* + * This file handles the architecture-dependent parts of hardware exceptions + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_PMAC_BACKLIGHT +#include +#endif +#include + +#ifdef CONFIG_XMON +void (*debugger)(struct pt_regs *regs) = xmon; +int (*debugger_bpt)(struct pt_regs *regs) = xmon_bpt; +int (*debugger_sstep)(struct pt_regs *regs) = xmon_sstep; +int (*debugger_iabr_match)(struct pt_regs *regs) = xmon_iabr_match; +int (*debugger_dabr_match)(struct pt_regs *regs) = xmon_dabr_match; +void (*debugger_fault_handler)(struct pt_regs *regs); +#else +#ifdef CONFIG_KGDB +void (*debugger)(struct pt_regs *regs); +int (*debugger_bpt)(struct pt_regs *regs); +int (*debugger_sstep)(struct pt_regs *regs); +int (*debugger_iabr_match)(struct pt_regs *regs); +int (*debugger_dabr_match)(struct pt_regs *regs); +void (*debugger_fault_handler)(struct pt_regs *regs); +#else +#define debugger(regs) do { } while (0) +#define debugger_bpt(regs) 0 +#define debugger_sstep(regs) 0 +#define debugger_iabr_match(regs) 0 +#define debugger_dabr_match(regs) 0 +#define debugger_fault_handler ((void (*)(struct pt_regs *))0) +#endif +#endif + +/* + * Trap & Exception support + */ + +DEFINE_SPINLOCK(die_lock); + +void die(const char * str, struct pt_regs * fp, long err) +{ + static int die_counter; + int nl = 0; + console_verbose(); + spin_lock_irq(&die_lock); +#ifdef CONFIG_PMAC_BACKLIGHT + set_backlight_enable(1); + set_backlight_level(BACKLIGHT_MAX); +#endif + printk("Oops: %s, sig: %ld [#%d]\n", str, err, ++die_counter); +#ifdef CONFIG_PREEMPT + printk("PREEMPT "); + nl = 1; +#endif +#ifdef CONFIG_SMP + printk("SMP NR_CPUS=%d ", NR_CPUS); + nl = 1; +#endif + if (nl) + printk("\n"); + show_regs(fp); + spin_unlock_irq(&die_lock); + /* do_exit() should take care of panic'ing from an interrupt + * context so we don't handle it here + */ + do_exit(err); +} + +void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr) +{ + siginfo_t info; + + if (!user_mode(regs)) { + debugger(regs); + die("Exception in kernel mode", regs, signr); + } + info.si_signo = signr; + info.si_errno = 0; + info.si_code = code; + info.si_addr = (void __user *) addr; + force_sig_info(signr, &info, current); +} + +/* + * I/O accesses can cause machine checks on powermacs. + * Check if the NIP corresponds to the address of a sync + * instruction for which there is an entry in the exception + * table. + * Note that the 601 only takes a machine check on TEA + * (transfer error ack) signal assertion, and does not + * set any of the top 16 bits of SRR1. + * -- paulus. + */ +static inline int check_io_access(struct pt_regs *regs) +{ +#ifdef CONFIG_PPC_PMAC + unsigned long msr = regs->msr; + const struct exception_table_entry *entry; + unsigned int *nip = (unsigned int *)regs->nip; + + if (((msr & 0xffff0000) == 0 || (msr & (0x80000 | 0x40000))) + && (entry = search_exception_tables(regs->nip)) != NULL) { + /* + * Check that it's a sync instruction, or somewhere + * in the twi; isync; nop sequence that inb/inw/inl uses. + * As the address is in the exception table + * we should be able to read the instr there. + * For the debug message, we look at the preceding + * load or store. + */ + if (*nip == 0x60000000) /* nop */ + nip -= 2; + else if (*nip == 0x4c00012c) /* isync */ + --nip; + if (*nip == 0x7c0004ac || (*nip >> 26) == 3) { + /* sync or twi */ + unsigned int rb; + + --nip; + rb = (*nip >> 11) & 0x1f; + printk(KERN_DEBUG "%s bad port %lx at %p\n", + (*nip & 0x100)? "OUT to": "IN from", + regs->gpr[rb] - _IO_BASE, nip); + regs->msr |= MSR_RI; + regs->nip = entry->fixup; + return 1; + } + } +#endif /* CONFIG_PPC_PMAC */ + return 0; +} + +#if defined(CONFIG_4xx) || defined(CONFIG_BOOKE) +/* On 4xx, the reason for the machine check or program exception + is in the ESR. */ +#define get_reason(regs) ((regs)->dsisr) +#ifndef CONFIG_E500 +#define get_mc_reason(regs) ((regs)->dsisr) +#else +#define get_mc_reason(regs) (mfspr(SPRN_MCSR)) +#endif +#define REASON_FP 0 +#define REASON_ILLEGAL ESR_PIL +#define REASON_PRIVILEGED ESR_PPR +#define REASON_TRAP ESR_PTR + +/* single-step stuff */ +#define single_stepping(regs) (current->thread.dbcr0 & DBCR0_IC) +#define clear_single_step(regs) (current->thread.dbcr0 &= ~DBCR0_IC) + +#else +/* On non-4xx, the reason for the machine check or program + exception is in the MSR. */ +#define get_reason(regs) ((regs)->msr) +#define get_mc_reason(regs) ((regs)->msr) +#define REASON_FP 0x100000 +#define REASON_ILLEGAL 0x80000 +#define REASON_PRIVILEGED 0x40000 +#define REASON_TRAP 0x20000 + +#define single_stepping(regs) ((regs)->msr & MSR_SE) +#define clear_single_step(regs) ((regs)->msr &= ~MSR_SE) +#endif + +/* + * This is "fall-back" implementation for configurations + * which don't provide platform-specific machine check info + */ +void __attribute__ ((weak)) +platform_machine_check(struct pt_regs *regs) +{ +} + +void MachineCheckException(struct pt_regs *regs) +{ + unsigned long reason = get_mc_reason(regs); + + if (user_mode(regs)) { + regs->msr |= MSR_RI; + _exception(SIGBUS, regs, BUS_ADRERR, regs->nip); + return; + } + +#if defined(CONFIG_8xx) && defined(CONFIG_PCI) + /* the qspan pci read routines can cause machine checks -- Cort */ + bad_page_fault(regs, regs->dar, SIGBUS); + return; +#endif + + if (debugger_fault_handler) { + debugger_fault_handler(regs); + regs->msr |= MSR_RI; + return; + } + + if (check_io_access(regs)) + return; + +#if defined(CONFIG_4xx) && !defined(CONFIG_440A) + if (reason & ESR_IMCP) { + printk("Instruction"); + mtspr(SPRN_ESR, reason & ~ESR_IMCP); + } else + printk("Data"); + printk(" machine check in kernel mode.\n"); +#elif defined(CONFIG_440A) + printk("Machine check in kernel mode.\n"); + if (reason & ESR_IMCP){ + printk("Instruction Synchronous Machine Check exception\n"); + mtspr(SPRN_ESR, reason & ~ESR_IMCP); + } + else { + u32 mcsr = mfspr(SPRN_MCSR); + if (mcsr & MCSR_IB) + printk("Instruction Read PLB Error\n"); + if (mcsr & MCSR_DRB) + printk("Data Read PLB Error\n"); + if (mcsr & MCSR_DWB) + printk("Data Write PLB Error\n"); + if (mcsr & MCSR_TLBP) + printk("TLB Parity Error\n"); + if (mcsr & MCSR_ICP){ + flush_instruction_cache(); + printk("I-Cache Parity Error\n"); + } + if (mcsr & MCSR_DCSP) + printk("D-Cache Search Parity Error\n"); + if (mcsr & MCSR_DCFP) + printk("D-Cache Flush Parity Error\n"); + if (mcsr & MCSR_IMPE) + printk("Machine Check exception is imprecise\n"); + + /* Clear MCSR */ + mtspr(SPRN_MCSR, mcsr); + } +#elif defined (CONFIG_E500) + printk("Machine check in kernel mode.\n"); + printk("Caused by (from MCSR=%lx): ", reason); + + if (reason & MCSR_MCP) + printk("Machine Check Signal\n"); + if (reason & MCSR_ICPERR) + printk("Instruction Cache Parity Error\n"); + if (reason & MCSR_DCP_PERR) + printk("Data Cache Push Parity Error\n"); + if (reason & MCSR_DCPERR) + printk("Data Cache Parity Error\n"); + if (reason & MCSR_GL_CI) + printk("Guarded Load or Cache-Inhibited stwcx.\n"); + if (reason & MCSR_BUS_IAERR) + printk("Bus - Instruction Address Error\n"); + if (reason & MCSR_BUS_RAERR) + printk("Bus - Read Address Error\n"); + if (reason & MCSR_BUS_WAERR) + printk("Bus - Write Address Error\n"); + if (reason & MCSR_BUS_IBERR) + printk("Bus - Instruction Data Error\n"); + if (reason & MCSR_BUS_RBERR) + printk("Bus - Read Data Bus Error\n"); + if (reason & MCSR_BUS_WBERR) + printk("Bus - Read Data Bus Error\n"); + if (reason & MCSR_BUS_IPERR) + printk("Bus - Instruction Parity Error\n"); + if (reason & MCSR_BUS_RPERR) + printk("Bus - Read Parity Error\n"); +#else /* !CONFIG_4xx && !CONFIG_E500 */ + printk("Machine check in kernel mode.\n"); + printk("Caused by (from SRR1=%lx): ", reason); + switch (reason & 0x601F0000) { + case 0x80000: + printk("Machine check signal\n"); + break; + case 0: /* for 601 */ + case 0x40000: + case 0x140000: /* 7450 MSS error and TEA */ + printk("Transfer error ack signal\n"); + break; + case 0x20000: + printk("Data parity error signal\n"); + break; + case 0x10000: + printk("Address parity error signal\n"); + break; + case 0x20000000: + printk("L1 Data Cache error\n"); + break; + case 0x40000000: + printk("L1 Instruction Cache error\n"); + break; + case 0x00100000: + printk("L2 data cache parity error\n"); + break; + default: + printk("Unknown values in msr\n"); + } +#endif /* CONFIG_4xx */ + + /* + * Optional platform-provided routine to print out + * additional info, e.g. bus error registers. + */ + platform_machine_check(regs); + + debugger(regs); + die("machine check", regs, SIGBUS); +} + +void SMIException(struct pt_regs *regs) +{ + debugger(regs); +#if !(defined(CONFIG_XMON) || defined(CONFIG_KGDB)) + show_regs(regs); + panic("System Management Interrupt"); +#endif +} + +void UnknownException(struct pt_regs *regs) +{ + printk("Bad trap at PC: %lx, MSR: %lx, vector=%lx %s\n", + regs->nip, regs->msr, regs->trap, print_tainted()); + _exception(SIGTRAP, regs, 0, 0); +} + +void InstructionBreakpoint(struct pt_regs *regs) +{ + if (debugger_iabr_match(regs)) + return; + _exception(SIGTRAP, regs, TRAP_BRKPT, 0); +} + +void RunModeException(struct pt_regs *regs) +{ + _exception(SIGTRAP, regs, 0, 0); +} + +/* Illegal instruction emulation support. Originally written to + * provide the PVR to user applications using the mfspr rd, PVR. + * Return non-zero if we can't emulate, or -EFAULT if the associated + * memory access caused an access fault. Return zero on success. + * + * There are a couple of ways to do this, either "decode" the instruction + * or directly match lots of bits. In this case, matching lots of + * bits is faster and easier. + * + */ +#define INST_MFSPR_PVR 0x7c1f42a6 +#define INST_MFSPR_PVR_MASK 0xfc1fffff + +#define INST_DCBA 0x7c0005ec +#define INST_DCBA_MASK 0x7c0007fe + +#define INST_MCRXR 0x7c000400 +#define INST_MCRXR_MASK 0x7c0007fe + +#define INST_STRING 0x7c00042a +#define INST_STRING_MASK 0x7c0007fe +#define INST_STRING_GEN_MASK 0x7c00067e +#define INST_LSWI 0x7c0004aa +#define INST_LSWX 0x7c00042a +#define INST_STSWI 0x7c0005aa +#define INST_STSWX 0x7c00052a + +static int emulate_string_inst(struct pt_regs *regs, u32 instword) +{ + u8 rT = (instword >> 21) & 0x1f; + u8 rA = (instword >> 16) & 0x1f; + u8 NB_RB = (instword >> 11) & 0x1f; + u32 num_bytes; + u32 EA; + int pos = 0; + + /* Early out if we are an invalid form of lswx */ + if ((instword & INST_STRING_MASK) == INST_LSWX) + if ((rA >= rT) || (NB_RB >= rT) || (rT == rA) || (rT == NB_RB)) + return -EINVAL; + + /* Early out if we are an invalid form of lswi */ + if ((instword & INST_STRING_MASK) == INST_LSWI) + if ((rA >= rT) || (rT == rA)) + return -EINVAL; + + EA = (rA == 0) ? 0 : regs->gpr[rA]; + + switch (instword & INST_STRING_MASK) { + case INST_LSWX: + case INST_STSWX: + EA += NB_RB; + num_bytes = regs->xer & 0x7f; + break; + case INST_LSWI: + case INST_STSWI: + num_bytes = (NB_RB == 0) ? 32 : NB_RB; + break; + default: + return -EINVAL; + } + + while (num_bytes != 0) + { + u8 val; + u32 shift = 8 * (3 - (pos & 0x3)); + + switch ((instword & INST_STRING_MASK)) { + case INST_LSWX: + case INST_LSWI: + if (get_user(val, (u8 __user *)EA)) + return -EFAULT; + /* first time updating this reg, + * zero it out */ + if (pos == 0) + regs->gpr[rT] = 0; + regs->gpr[rT] |= val << shift; + break; + case INST_STSWI: + case INST_STSWX: + val = regs->gpr[rT] >> shift; + if (put_user(val, (u8 __user *)EA)) + return -EFAULT; + break; + } + /* move EA to next address */ + EA += 1; + num_bytes--; + + /* manage our position within the register */ + if (++pos == 4) { + pos = 0; + if (++rT == 32) + rT = 0; + } + } + + return 0; +} + +static int emulate_instruction(struct pt_regs *regs) +{ + u32 instword; + u32 rd; + + if (!user_mode(regs)) + return -EINVAL; + CHECK_FULL_REGS(regs); + + if (get_user(instword, (u32 __user *)(regs->nip))) + return -EFAULT; + + /* Emulate the mfspr rD, PVR. + */ + if ((instword & INST_MFSPR_PVR_MASK) == INST_MFSPR_PVR) { + rd = (instword >> 21) & 0x1f; + regs->gpr[rd] = mfspr(SPRN_PVR); + return 0; + } + + /* Emulating the dcba insn is just a no-op. */ + if ((instword & INST_DCBA_MASK) == INST_DCBA) + return 0; + + /* Emulate the mcrxr insn. */ + if ((instword & INST_MCRXR_MASK) == INST_MCRXR) { + int shift = (instword >> 21) & 0x1c; + unsigned long msk = 0xf0000000UL >> shift; + + regs->ccr = (regs->ccr & ~msk) | ((regs->xer >> shift) & msk); + regs->xer &= ~0xf0000000UL; + return 0; + } + + /* Emulate load/store string insn. */ + if ((instword & INST_STRING_GEN_MASK) == INST_STRING) + return emulate_string_inst(regs, instword); + + return -EINVAL; +} + +/* + * After we have successfully emulated an instruction, we have to + * check if the instruction was being single-stepped, and if so, + * pretend we got a single-step exception. This was pointed out + * by Kumar Gala. -- paulus + */ +static void emulate_single_step(struct pt_regs *regs) +{ + if (single_stepping(regs)) { + clear_single_step(regs); + _exception(SIGTRAP, regs, TRAP_TRACE, 0); + } +} + +/* + * Look through the list of trap instructions that are used for BUG(), + * BUG_ON() and WARN_ON() and see if we hit one. At this point we know + * that the exception was caused by a trap instruction of some kind. + * Returns 1 if we should continue (i.e. it was a WARN_ON) or 0 + * otherwise. + */ +extern struct bug_entry __start___bug_table[], __stop___bug_table[]; + +#ifndef CONFIG_MODULES +#define module_find_bug(x) NULL +#endif + +static struct bug_entry *find_bug(unsigned long bugaddr) +{ + struct bug_entry *bug; + + for (bug = __start___bug_table; bug < __stop___bug_table; ++bug) + if (bugaddr == bug->bug_addr) + return bug; + return module_find_bug(bugaddr); +} + +int check_bug_trap(struct pt_regs *regs) +{ + struct bug_entry *bug; + unsigned long addr; + + if (regs->msr & MSR_PR) + return 0; /* not in kernel */ + addr = regs->nip; /* address of trap instruction */ + if (addr < PAGE_OFFSET) + return 0; + bug = find_bug(regs->nip); + if (bug == NULL) + return 0; + if (bug->line & BUG_WARNING_TRAP) { + /* this is a WARN_ON rather than BUG/BUG_ON */ +#ifdef CONFIG_XMON + xmon_printf(KERN_ERR "Badness in %s at %s:%d\n", + bug->function, bug->file, + bug->line & ~BUG_WARNING_TRAP); +#endif /* CONFIG_XMON */ + printk(KERN_ERR "Badness in %s at %s:%d\n", + bug->function, bug->file, + bug->line & ~BUG_WARNING_TRAP); + dump_stack(); + return 1; + } +#ifdef CONFIG_XMON + xmon_printf(KERN_CRIT "kernel BUG in %s at %s:%d!\n", + bug->function, bug->file, bug->line); + xmon(regs); +#endif /* CONFIG_XMON */ + printk(KERN_CRIT "kernel BUG in %s at %s:%d!\n", + bug->function, bug->file, bug->line); + + return 0; +} + +void ProgramCheckException(struct pt_regs *regs) +{ + unsigned int reason = get_reason(regs); + extern int do_mathemu(struct pt_regs *regs); + +#ifdef CONFIG_MATH_EMULATION + /* (reason & REASON_ILLEGAL) would be the obvious thing here, + * but there seems to be a hardware bug on the 405GP (RevD) + * that means ESR is sometimes set incorrectly - either to + * ESR_DST (!?) or 0. In the process of chasing this with the + * hardware people - not sure if it can happen on any illegal + * instruction or only on FP instructions, whether there is a + * pattern to occurences etc. -dgibson 31/Mar/2003 */ + if (!(reason & REASON_TRAP) && do_mathemu(regs) == 0) { + emulate_single_step(regs); + return; + } +#endif /* CONFIG_MATH_EMULATION */ + + if (reason & REASON_FP) { + /* IEEE FP exception */ + int code = 0; + u32 fpscr; + + /* We must make sure the FP state is consistent with + * our MSR_FP in regs + */ + preempt_disable(); + if (regs->msr & MSR_FP) + giveup_fpu(current); + preempt_enable(); + + fpscr = current->thread.fpscr; + fpscr &= fpscr << 22; /* mask summary bits with enables */ + if (fpscr & FPSCR_VX) + code = FPE_FLTINV; + else if (fpscr & FPSCR_OX) + code = FPE_FLTOVF; + else if (fpscr & FPSCR_UX) + code = FPE_FLTUND; + else if (fpscr & FPSCR_ZX) + code = FPE_FLTDIV; + else if (fpscr & FPSCR_XX) + code = FPE_FLTRES; + _exception(SIGFPE, regs, code, regs->nip); + return; + } + + if (reason & REASON_TRAP) { + /* trap exception */ + if (debugger_bpt(regs)) + return; + if (check_bug_trap(regs)) { + regs->nip += 4; + return; + } + _exception(SIGTRAP, regs, TRAP_BRKPT, 0); + return; + } + + /* Try to emulate it if we should. */ + if (reason & (REASON_ILLEGAL | REASON_PRIVILEGED)) { + switch (emulate_instruction(regs)) { + case 0: + regs->nip += 4; + emulate_single_step(regs); + return; + case -EFAULT: + _exception(SIGSEGV, regs, SEGV_MAPERR, regs->nip); + return; + } + } + + if (reason & REASON_PRIVILEGED) + _exception(SIGILL, regs, ILL_PRVOPC, regs->nip); + else + _exception(SIGILL, regs, ILL_ILLOPC, regs->nip); +} + +void SingleStepException(struct pt_regs *regs) +{ + regs->msr &= ~(MSR_SE | MSR_BE); /* Turn off 'trace' bits */ + if (debugger_sstep(regs)) + return; + _exception(SIGTRAP, regs, TRAP_TRACE, 0); +} + +void AlignmentException(struct pt_regs *regs) +{ + int fixed; + + fixed = fix_alignment(regs); + if (fixed == 1) { + regs->nip += 4; /* skip over emulated instruction */ + return; + } + if (fixed == -EFAULT) { + /* fixed == -EFAULT means the operand address was bad */ + if (user_mode(regs)) + _exception(SIGSEGV, regs, SEGV_ACCERR, regs->dar); + else + bad_page_fault(regs, regs->dar, SIGSEGV); + return; + } + _exception(SIGBUS, regs, BUS_ADRALN, regs->dar); +} + +void StackOverflow(struct pt_regs *regs) +{ + printk(KERN_CRIT "Kernel stack overflow in process %p, r1=%lx\n", + current, regs->gpr[1]); + debugger(regs); + show_regs(regs); + panic("kernel stack overflow"); +} + +void nonrecoverable_exception(struct pt_regs *regs) +{ + printk(KERN_ERR "Non-recoverable exception at PC=%lx MSR=%lx\n", + regs->nip, regs->msr); + debugger(regs); + die("nonrecoverable exception", regs, SIGKILL); +} + +void trace_syscall(struct pt_regs *regs) +{ + printk("Task: %p(%d), PC: %08lX/%08lX, Syscall: %3ld, Result: %s%ld %s\n", + current, current->pid, regs->nip, regs->link, regs->gpr[0], + regs->ccr&0x10000000?"Error=":"", regs->gpr[3], print_tainted()); +} + +#ifdef CONFIG_8xx +void SoftwareEmulation(struct pt_regs *regs) +{ + extern int do_mathemu(struct pt_regs *); + extern int Soft_emulate_8xx(struct pt_regs *); + int errcode; + + CHECK_FULL_REGS(regs); + + if (!user_mode(regs)) { + debugger(regs); + die("Kernel Mode Software FPU Emulation", regs, SIGFPE); + } + +#ifdef CONFIG_MATH_EMULATION + errcode = do_mathemu(regs); +#else + errcode = Soft_emulate_8xx(regs); +#endif + if (errcode) { + if (errcode > 0) + _exception(SIGFPE, regs, 0, 0); + else if (errcode == -EFAULT) + _exception(SIGSEGV, regs, 0, 0); + else + _exception(SIGILL, regs, ILL_ILLOPC, regs->nip); + } else + emulate_single_step(regs); +} +#endif /* CONFIG_8xx */ + +#if defined(CONFIG_40x) || defined(CONFIG_BOOKE) + +void DebugException(struct pt_regs *regs, unsigned long debug_status) +{ + if (debug_status & DBSR_IC) { /* instruction completion */ + regs->msr &= ~MSR_DE; + if (user_mode(regs)) { + current->thread.dbcr0 &= ~DBCR0_IC; + } else { + /* Disable instruction completion */ + mtspr(SPRN_DBCR0, mfspr(SPRN_DBCR0) & ~DBCR0_IC); + /* Clear the instruction completion event */ + mtspr(SPRN_DBSR, DBSR_IC); + if (debugger_sstep(regs)) + return; + } + _exception(SIGTRAP, regs, TRAP_TRACE, 0); + } +} +#endif /* CONFIG_4xx || CONFIG_BOOKE */ + +#if !defined(CONFIG_TAU_INT) +void TAUException(struct pt_regs *regs) +{ + printk("TAU trap at PC: %lx, MSR: %lx, vector=%lx %s\n", + regs->nip, regs->msr, regs->trap, print_tainted()); +} +#endif /* CONFIG_INT_TAU */ + +void AltivecUnavailException(struct pt_regs *regs) +{ + static int kernel_altivec_count; + +#ifndef CONFIG_ALTIVEC + if (user_mode(regs)) { + /* A user program has executed an altivec instruction, + but this kernel doesn't support altivec. */ + _exception(SIGILL, regs, ILL_ILLOPC, regs->nip); + return; + } +#endif + /* The kernel has executed an altivec instruction without + first enabling altivec. Whinge but let it do it. */ + if (++kernel_altivec_count < 10) + printk(KERN_ERR "AltiVec used in kernel (task=%p, pc=%lx)\n", + current, regs->nip); + regs->msr |= MSR_VEC; +} + +#ifdef CONFIG_ALTIVEC +void AltivecAssistException(struct pt_regs *regs) +{ + int err; + + preempt_disable(); + if (regs->msr & MSR_VEC) + giveup_altivec(current); + preempt_enable(); + + err = emulate_altivec(regs); + if (err == 0) { + regs->nip += 4; /* skip emulated instruction */ + emulate_single_step(regs); + return; + } + + if (err == -EFAULT) { + /* got an error reading the instruction */ + _exception(SIGSEGV, regs, SEGV_ACCERR, regs->nip); + } else { + /* didn't recognize the instruction */ + /* XXX quick hack for now: set the non-Java bit in the VSCR */ + printk(KERN_ERR "unrecognized altivec instruction " + "in %s at %lx\n", current->comm, regs->nip); + current->thread.vscr.u[3] |= 0x10000; + } +} +#endif /* CONFIG_ALTIVEC */ + +void PerformanceMonitorException(struct pt_regs *regs) +{ + perf_irq(regs); +} + +#ifdef CONFIG_FSL_BOOKE +void CacheLockingException(struct pt_regs *regs, unsigned long address, + unsigned long error_code) +{ + /* We treat cache locking instructions from the user + * as priv ops, in the future we could try to do + * something smarter + */ + if (error_code & (ESR_DLK|ESR_ILK)) + _exception(SIGILL, regs, ILL_PRVOPC, regs->nip); + return; +} +#endif /* CONFIG_FSL_BOOKE */ + +#ifdef CONFIG_SPE +void SPEFloatingPointException(struct pt_regs *regs) +{ + unsigned long spefscr; + int fpexc_mode; + int code = 0; + + spefscr = current->thread.spefscr; + fpexc_mode = current->thread.fpexc_mode; + + /* Hardware does not neccessarily set sticky + * underflow/overflow/invalid flags */ + if ((spefscr & SPEFSCR_FOVF) && (fpexc_mode & PR_FP_EXC_OVF)) { + code = FPE_FLTOVF; + spefscr |= SPEFSCR_FOVFS; + } + else if ((spefscr & SPEFSCR_FUNF) && (fpexc_mode & PR_FP_EXC_UND)) { + code = FPE_FLTUND; + spefscr |= SPEFSCR_FUNFS; + } + else if ((spefscr & SPEFSCR_FDBZ) && (fpexc_mode & PR_FP_EXC_DIV)) + code = FPE_FLTDIV; + else if ((spefscr & SPEFSCR_FINV) && (fpexc_mode & PR_FP_EXC_INV)) { + code = FPE_FLTINV; + spefscr |= SPEFSCR_FINVS; + } + else if ((spefscr & (SPEFSCR_FG | SPEFSCR_FX)) && (fpexc_mode & PR_FP_EXC_RES)) + code = FPE_FLTRES; + + current->thread.spefscr = spefscr; + + _exception(SIGFPE, regs, code, regs->nip); + return; +} +#endif + +void __init trap_init(void) +{ +} diff --git a/arch/ppc/kernel/vecemu.c b/arch/ppc/kernel/vecemu.c new file mode 100644 index 00000000000..604d0947cb2 --- /dev/null +++ b/arch/ppc/kernel/vecemu.c @@ -0,0 +1,345 @@ +/* + * Routines to emulate some Altivec/VMX instructions, specifically + * those that can trap when given denormalized operands in Java mode. + */ +#include +#include +#include +#include +#include +#include + +/* Functions in vector.S */ +extern void vaddfp(vector128 *dst, vector128 *a, vector128 *b); +extern void vsubfp(vector128 *dst, vector128 *a, vector128 *b); +extern void vmaddfp(vector128 *dst, vector128 *a, vector128 *b, vector128 *c); +extern void vnmsubfp(vector128 *dst, vector128 *a, vector128 *b, vector128 *c); +extern void vrefp(vector128 *dst, vector128 *src); +extern void vrsqrtefp(vector128 *dst, vector128 *src); +extern void vexptep(vector128 *dst, vector128 *src); + +static unsigned int exp2s[8] = { + 0x800000, + 0x8b95c2, + 0x9837f0, + 0xa5fed7, + 0xb504f3, + 0xc5672a, + 0xd744fd, + 0xeac0c7 +}; + +/* + * Computes an estimate of 2^x. The `s' argument is the 32-bit + * single-precision floating-point representation of x. + */ +static unsigned int eexp2(unsigned int s) +{ + int exp, pwr; + unsigned int mant, frac; + + /* extract exponent field from input */ + exp = ((s >> 23) & 0xff) - 127; + if (exp > 7) { + /* check for NaN input */ + if (exp == 128 && (s & 0x7fffff) != 0) + return s | 0x400000; /* return QNaN */ + /* 2^-big = 0, 2^+big = +Inf */ + return (s & 0x80000000)? 0: 0x7f800000; /* 0 or +Inf */ + } + if (exp < -23) + return 0x3f800000; /* 1.0 */ + + /* convert to fixed point integer in 9.23 representation */ + pwr = (s & 0x7fffff) | 0x800000; + if (exp > 0) + pwr <<= exp; + else + pwr >>= -exp; + if (s & 0x80000000) + pwr = -pwr; + + /* extract integer part, which becomes exponent part of result */ + exp = (pwr >> 23) + 126; + if (exp >= 254) + return 0x7f800000; + if (exp < -23) + return 0; + + /* table lookup on top 3 bits of fraction to get mantissa */ + mant = exp2s[(pwr >> 20) & 7]; + + /* linear interpolation using remaining 20 bits of fraction */ + asm("mulhwu %0,%1,%2" : "=r" (frac) + : "r" (pwr << 12), "r" (0x172b83ff)); + asm("mulhwu %0,%1,%2" : "=r" (frac) : "r" (frac), "r" (mant)); + mant += frac; + + if (exp >= 0) + return mant + (exp << 23); + + /* denormalized result */ + exp = -exp; + mant += 1 << (exp - 1); + return mant >> exp; +} + +/* + * Computes an estimate of log_2(x). The `s' argument is the 32-bit + * single-precision floating-point representation of x. + */ +static unsigned int elog2(unsigned int s) +{ + int exp, mant, lz, frac; + + exp = s & 0x7f800000; + mant = s & 0x7fffff; + if (exp == 0x7f800000) { /* Inf or NaN */ + if (mant != 0) + s |= 0x400000; /* turn NaN into QNaN */ + return s; + } + if ((exp | mant) == 0) /* +0 or -0 */ + return 0xff800000; /* return -Inf */ + + if (exp == 0) { + /* denormalized */ + asm("cntlzw %0,%1" : "=r" (lz) : "r" (mant)); + mant <<= lz - 8; + exp = (-118 - lz) << 23; + } else { + mant |= 0x800000; + exp -= 127 << 23; + } + + if (mant >= 0xb504f3) { /* 2^0.5 * 2^23 */ + exp |= 0x400000; /* 0.5 * 2^23 */ + asm("mulhwu %0,%1,%2" : "=r" (mant) + : "r" (mant), "r" (0xb504f334)); /* 2^-0.5 * 2^32 */ + } + if (mant >= 0x9837f0) { /* 2^0.25 * 2^23 */ + exp |= 0x200000; /* 0.25 * 2^23 */ + asm("mulhwu %0,%1,%2" : "=r" (mant) + : "r" (mant), "r" (0xd744fccb)); /* 2^-0.25 * 2^32 */ + } + if (mant >= 0x8b95c2) { /* 2^0.125 * 2^23 */ + exp |= 0x100000; /* 0.125 * 2^23 */ + asm("mulhwu %0,%1,%2" : "=r" (mant) + : "r" (mant), "r" (0xeac0c6e8)); /* 2^-0.125 * 2^32 */ + } + if (mant > 0x800000) { /* 1.0 * 2^23 */ + /* calculate (mant - 1) * 1.381097463 */ + /* 1.381097463 == 0.125 / (2^0.125 - 1) */ + asm("mulhwu %0,%1,%2" : "=r" (frac) + : "r" ((mant - 0x800000) << 1), "r" (0xb0c7cd3a)); + exp += frac; + } + s = exp & 0x80000000; + if (exp != 0) { + if (s) + exp = -exp; + asm("cntlzw %0,%1" : "=r" (lz) : "r" (exp)); + lz = 8 - lz; + if (lz > 0) + exp >>= lz; + else if (lz < 0) + exp <<= -lz; + s += ((lz + 126) << 23) + exp; + } + return s; +} + +#define VSCR_SAT 1 + +static int ctsxs(unsigned int x, int scale, unsigned int *vscrp) +{ + int exp, mant; + + exp = (x >> 23) & 0xff; + mant = x & 0x7fffff; + if (exp == 255 && mant != 0) + return 0; /* NaN -> 0 */ + exp = exp - 127 + scale; + if (exp < 0) + return 0; /* round towards zero */ + if (exp >= 31) { + /* saturate, unless the result would be -2^31 */ + if (x + (scale << 23) != 0xcf000000) + *vscrp |= VSCR_SAT; + return (x & 0x80000000)? 0x80000000: 0x7fffffff; + } + mant |= 0x800000; + mant = (mant << 7) >> (30 - exp); + return (x & 0x80000000)? -mant: mant; +} + +static unsigned int ctuxs(unsigned int x, int scale, unsigned int *vscrp) +{ + int exp; + unsigned int mant; + + exp = (x >> 23) & 0xff; + mant = x & 0x7fffff; + if (exp == 255 && mant != 0) + return 0; /* NaN -> 0 */ + exp = exp - 127 + scale; + if (exp < 0) + return 0; /* round towards zero */ + if (x & 0x80000000) { + /* negative => saturate to 0 */ + *vscrp |= VSCR_SAT; + return 0; + } + if (exp >= 32) { + /* saturate */ + *vscrp |= VSCR_SAT; + return 0xffffffff; + } + mant |= 0x800000; + mant = (mant << 8) >> (31 - exp); + return mant; +} + +/* Round to floating integer, towards 0 */ +static unsigned int rfiz(unsigned int x) +{ + int exp; + + exp = ((x >> 23) & 0xff) - 127; + if (exp == 128 && (x & 0x7fffff) != 0) + return x | 0x400000; /* NaN -> make it a QNaN */ + if (exp >= 23) + return x; /* it's an integer already (or Inf) */ + if (exp < 0) + return x & 0x80000000; /* |x| < 1.0 rounds to 0 */ + return x & ~(0x7fffff >> exp); +} + +/* Round to floating integer, towards +/- Inf */ +static unsigned int rfii(unsigned int x) +{ + int exp, mask; + + exp = ((x >> 23) & 0xff) - 127; + if (exp == 128 && (x & 0x7fffff) != 0) + return x | 0x400000; /* NaN -> make it a QNaN */ + if (exp >= 23) + return x; /* it's an integer already (or Inf) */ + if ((x & 0x7fffffff) == 0) + return x; /* +/-0 -> +/-0 */ + if (exp < 0) + /* 0 < |x| < 1.0 rounds to +/- 1.0 */ + return (x & 0x80000000) | 0x3f800000; + mask = 0x7fffff >> exp; + /* mantissa overflows into exponent - that's OK, + it can't overflow into the sign bit */ + return (x + mask) & ~mask; +} + +/* Round to floating integer, to nearest */ +static unsigned int rfin(unsigned int x) +{ + int exp, half; + + exp = ((x >> 23) & 0xff) - 127; + if (exp == 128 && (x & 0x7fffff) != 0) + return x | 0x400000; /* NaN -> make it a QNaN */ + if (exp >= 23) + return x; /* it's an integer already (or Inf) */ + if (exp < -1) + return x & 0x80000000; /* |x| < 0.5 -> +/-0 */ + if (exp == -1) + /* 0.5 <= |x| < 1.0 rounds to +/- 1.0 */ + return (x & 0x80000000) | 0x3f800000; + half = 0x400000 >> exp; + /* add 0.5 to the magnitude and chop off the fraction bits */ + return (x + half) & ~(0x7fffff >> exp); +} + +int emulate_altivec(struct pt_regs *regs) +{ + unsigned int instr, i; + unsigned int va, vb, vc, vd; + vector128 *vrs; + + if (get_user(instr, (unsigned int __user *) regs->nip)) + return -EFAULT; + if ((instr >> 26) != 4) + return -EINVAL; /* not an altivec instruction */ + vd = (instr >> 21) & 0x1f; + va = (instr >> 16) & 0x1f; + vb = (instr >> 11) & 0x1f; + vc = (instr >> 6) & 0x1f; + + vrs = current->thread.vr; + switch (instr & 0x3f) { + case 10: + switch (vc) { + case 0: /* vaddfp */ + vaddfp(&vrs[vd], &vrs[va], &vrs[vb]); + break; + case 1: /* vsubfp */ + vsubfp(&vrs[vd], &vrs[va], &vrs[vb]); + break; + case 4: /* vrefp */ + vrefp(&vrs[vd], &vrs[vb]); + break; + case 5: /* vrsqrtefp */ + vrsqrtefp(&vrs[vd], &vrs[vb]); + break; + case 6: /* vexptefp */ + for (i = 0; i < 4; ++i) + vrs[vd].u[i] = eexp2(vrs[vb].u[i]); + break; + case 7: /* vlogefp */ + for (i = 0; i < 4; ++i) + vrs[vd].u[i] = elog2(vrs[vb].u[i]); + break; + case 8: /* vrfin */ + for (i = 0; i < 4; ++i) + vrs[vd].u[i] = rfin(vrs[vb].u[i]); + break; + case 9: /* vrfiz */ + for (i = 0; i < 4; ++i) + vrs[vd].u[i] = rfiz(vrs[vb].u[i]); + break; + case 10: /* vrfip */ + for (i = 0; i < 4; ++i) { + u32 x = vrs[vb].u[i]; + x = (x & 0x80000000)? rfiz(x): rfii(x); + vrs[vd].u[i] = x; + } + break; + case 11: /* vrfim */ + for (i = 0; i < 4; ++i) { + u32 x = vrs[vb].u[i]; + x = (x & 0x80000000)? rfii(x): rfiz(x); + vrs[vd].u[i] = x; + } + break; + case 14: /* vctuxs */ + for (i = 0; i < 4; ++i) + vrs[vd].u[i] = ctuxs(vrs[vb].u[i], va, + ¤t->thread.vscr.u[3]); + break; + case 15: /* vctsxs */ + for (i = 0; i < 4; ++i) + vrs[vd].u[i] = ctsxs(vrs[vb].u[i], va, + ¤t->thread.vscr.u[3]); + break; + default: + return -EINVAL; + } + break; + case 46: /* vmaddfp */ + vmaddfp(&vrs[vd], &vrs[va], &vrs[vb], &vrs[vc]); + break; + case 47: /* vnmsubfp */ + vnmsubfp(&vrs[vd], &vrs[va], &vrs[vb], &vrs[vc]); + break; + default: + return -EINVAL; + } + + return 0; +} diff --git a/arch/ppc/kernel/vector.S b/arch/ppc/kernel/vector.S new file mode 100644 index 00000000000..82a21346bf8 --- /dev/null +++ b/arch/ppc/kernel/vector.S @@ -0,0 +1,217 @@ +#include +#include + +/* + * The routines below are in assembler so we can closely control the + * usage of floating-point registers. These routines must be called + * with preempt disabled. + */ + .data +fpzero: + .long 0 +fpone: + .long 0x3f800000 /* 1.0 in single-precision FP */ +fphalf: + .long 0x3f000000 /* 0.5 in single-precision FP */ + + .text +/* + * Internal routine to enable floating point and set FPSCR to 0. + * Don't call it from C; it doesn't use the normal calling convention. + */ +fpenable: + mfmsr r10 + ori r11,r10,MSR_FP + mtmsr r11 + isync + stfd fr0,24(r1) + stfd fr1,16(r1) + stfd fr31,8(r1) + lis r11,fpzero@ha + mffs fr31 + lfs fr1,fpzero@l(r11) + mtfsf 0xff,fr1 + blr + +fpdisable: + mtfsf 0xff,fr31 + lfd fr31,8(r1) + lfd fr1,16(r1) + lfd fr0,24(r1) + mtmsr r10 + isync + blr + +/* + * Vector add, floating point. + */ + .globl vaddfp +vaddfp: + stwu r1,-32(r1) + mflr r0 + stw r0,36(r1) + bl fpenable + li r0,4 + mtctr r0 + li r6,0 +1: lfsx fr0,r4,r6 + lfsx fr1,r5,r6 + fadds fr0,fr0,fr1 + stfsx fr0,r3,r6 + addi r6,r6,4 + bdnz 1b + bl fpdisable + lwz r0,36(r1) + mtlr r0 + addi r1,r1,32 + blr + +/* + * Vector subtract, floating point. + */ + .globl vsubfp +vsubfp: + stwu r1,-32(r1) + mflr r0 + stw r0,36(r1) + bl fpenable + li r0,4 + mtctr r0 + li r6,0 +1: lfsx fr0,r4,r6 + lfsx fr1,r5,r6 + fsubs fr0,fr0,fr1 + stfsx fr0,r3,r6 + addi r6,r6,4 + bdnz 1b + bl fpdisable + lwz r0,36(r1) + mtlr r0 + addi r1,r1,32 + blr + +/* + * Vector multiply and add, floating point. + */ + .globl vmaddfp +vmaddfp: + stwu r1,-48(r1) + mflr r0 + stw r0,52(r1) + bl fpenable + stfd fr2,32(r1) + li r0,4 + mtctr r0 + li r7,0 +1: lfsx fr0,r4,r7 + lfsx fr1,r5,r7 + lfsx fr2,r6,r7 + fmadds fr0,fr0,fr2,fr1 + stfsx fr0,r3,r7 + addi r7,r7,4 + bdnz 1b + lfd fr2,32(r1) + bl fpdisable + lwz r0,52(r1) + mtlr r0 + addi r1,r1,48 + blr + +/* + * Vector negative multiply and subtract, floating point. + */ + .globl vnmsubfp +vnmsubfp: + stwu r1,-48(r1) + mflr r0 + stw r0,52(r1) + bl fpenable + stfd fr2,32(r1) + li r0,4 + mtctr r0 + li r7,0 +1: lfsx fr0,r4,r7 + lfsx fr1,r5,r7 + lfsx fr2,r6,r7 + fnmsubs fr0,fr0,fr2,fr1 + stfsx fr0,r3,r7 + addi r7,r7,4 + bdnz 1b + lfd fr2,32(r1) + bl fpdisable + lwz r0,52(r1) + mtlr r0 + addi r1,r1,48 + blr + +/* + * Vector reciprocal estimate. We just compute 1.0/x. + * r3 -> destination, r4 -> source. + */ + .globl vrefp +vrefp: + stwu r1,-32(r1) + mflr r0 + stw r0,36(r1) + bl fpenable + lis r9,fpone@ha + li r0,4 + lfs fr1,fpone@l(r9) + mtctr r0 + li r6,0 +1: lfsx fr0,r4,r6 + fdivs fr0,fr1,fr0 + stfsx fr0,r3,r6 + addi r6,r6,4 + bdnz 1b + bl fpdisable + lwz r0,36(r1) + mtlr r0 + addi r1,r1,32 + blr + +/* + * Vector reciprocal square-root estimate, floating point. + * We use the frsqrte instruction for the initial estimate followed + * by 2 iterations of Newton-Raphson to get sufficient accuracy. + * r3 -> destination, r4 -> source. + */ + .globl vrsqrtefp +vrsqrtefp: + stwu r1,-48(r1) + mflr r0 + stw r0,52(r1) + bl fpenable + stfd fr2,32(r1) + stfd fr3,40(r1) + stfd fr4,48(r1) + stfd fr5,56(r1) + lis r9,fpone@ha + lis r8,fphalf@ha + li r0,4 + lfs fr4,fpone@l(r9) + lfs fr5,fphalf@l(r8) + mtctr r0 + li r6,0 +1: lfsx fr0,r4,r6 + frsqrte fr1,fr0 /* r = frsqrte(s) */ + fmuls fr3,fr1,fr0 /* r * s */ + fmuls fr2,fr1,fr5 /* r * 0.5 */ + fnmsubs fr3,fr1,fr3,fr4 /* 1 - s * r * r */ + fmadds fr1,fr2,fr3,fr1 /* r = r + 0.5 * r * (1 - s * r * r) */ + fmuls fr3,fr1,fr0 /* r * s */ + fmuls fr2,fr1,fr5 /* r * 0.5 */ + fnmsubs fr3,fr1,fr3,fr4 /* 1 - s * r * r */ + fmadds fr1,fr2,fr3,fr1 /* r = r + 0.5 * r * (1 - s * r * r) */ + stfsx fr1,r3,r6 + addi r6,r6,4 + bdnz 1b + lfd fr5,56(r1) + lfd fr4,48(r1) + lfd fr3,40(r1) + lfd fr2,32(r1) + bl fpdisable + lwz r0,36(r1) + mtlr r0 + addi r1,r1,32 + blr diff --git a/arch/ppc/kernel/vmlinux.lds.S b/arch/ppc/kernel/vmlinux.lds.S new file mode 100644 index 00000000000..0c0e714b84d --- /dev/null +++ b/arch/ppc/kernel/vmlinux.lds.S @@ -0,0 +1,192 @@ +#include + +OUTPUT_ARCH(powerpc:common) +jiffies = jiffies_64 + 4; +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = + SIZEOF_HEADERS; + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .rel.text : { *(.rel.text) } + .rela.text : { *(.rela.text) } + .rel.data : { *(.rel.data) } + .rela.data : { *(.rela.data) } + .rel.rodata : { *(.rel.rodata) } + .rela.rodata : { *(.rela.rodata) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } +/* .init : { *(.init) } =0*/ + .plt : { *(.plt) } + .text : + { + *(.text) + SCHED_TEXT + LOCK_TEXT + *(.fixup) + *(.got1) + __got2_start = .; + *(.got2) + __got2_end = .; + } + _etext = .; + PROVIDE (etext = .); + + RODATA + .fini : { *(.fini) } =0 + .ctors : { *(.ctors) } + .dtors : { *(.dtors) } + + .fixup : { *(.fixup) } + + __ex_table : { + __start___ex_table = .; + *(__ex_table) + __stop___ex_table = .; + } + + __bug_table : { + __start___bug_table = .; + *(__bug_table) + __stop___bug_table = .; + } + + /* Read-write section, merged into data segment: */ + . = ALIGN(4096); + .data : + { + *(.data) + *(.data1) + *(.sdata) + *(.sdata2) + *(.got.plt) *(.got) + *(.dynamic) + CONSTRUCTORS + } + + . = ALIGN(4096); + __nosave_begin = .; + .data_nosave : { *(.data.nosave) } + . = ALIGN(4096); + __nosave_end = .; + + . = ALIGN(32); + .data.cacheline_aligned : { *(.data.cacheline_aligned) } + + _edata = .; + PROVIDE (edata = .); + + . = ALIGN(8192); + .data.init_task : { *(.data.init_task) } + + . = ALIGN(4096); + __init_begin = .; + .init.text : { + _sinittext = .; + *(.init.text) + _einittext = .; + } + .init.data : { + *(.init.data); + __vtop_table_begin = .; + *(.vtop_fixup); + __vtop_table_end = .; + __ptov_table_begin = .; + *(.ptov_fixup); + __ptov_table_end = .; + } + . = ALIGN(16); + __setup_start = .; + .init.setup : { *(.init.setup) } + __setup_end = .; + __initcall_start = .; + .initcall.init : { + *(.initcall1.init) + *(.initcall2.init) + *(.initcall3.init) + *(.initcall4.init) + *(.initcall5.init) + *(.initcall6.init) + *(.initcall7.init) + } + __initcall_end = .; + + __con_initcall_start = .; + .con_initcall.init : { *(.con_initcall.init) } + __con_initcall_end = .; + + SECURITY_INIT + + __start___ftr_fixup = .; + __ftr_fixup : { *(__ftr_fixup) } + __stop___ftr_fixup = .; + + . = ALIGN(32); + __per_cpu_start = .; + .data.percpu : { *(.data.percpu) } + __per_cpu_end = .; + + . = ALIGN(4096); + __initramfs_start = .; + .init.ramfs : { *(.init.ramfs) } + __initramfs_end = .; + + . = ALIGN(4096); + __init_end = .; + + . = ALIGN(4096); + __pmac_begin = .; + .pmac.text : { *(.pmac.text) } + .pmac.data : { *(.pmac.data) } + . = ALIGN(4096); + __pmac_end = .; + + . = ALIGN(4096); + __prep_begin = .; + .prep.text : { *(.prep.text) } + .prep.data : { *(.prep.data) } + . = ALIGN(4096); + __prep_end = .; + + . = ALIGN(4096); + __chrp_begin = .; + .chrp.text : { *(.chrp.text) } + .chrp.data : { *(.chrp.data) } + . = ALIGN(4096); + __chrp_end = .; + + . = ALIGN(4096); + __openfirmware_begin = .; + .openfirmware.text : { *(.openfirmware.text) } + .openfirmware.data : { *(.openfirmware.data) } + . = ALIGN(4096); + __openfirmware_end = .; + + __bss_start = .; + .bss : + { + *(.sbss) *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + } + __bss_stop = .; + + _end = . ; + PROVIDE (end = .); + + /* Sections to be discarded. */ + /DISCARD/ : { + *(.exitcall.exit) + } +} diff --git a/arch/ppc/lib/Makefile b/arch/ppc/lib/Makefile new file mode 100644 index 00000000000..1c380e67d43 --- /dev/null +++ b/arch/ppc/lib/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for ppc-specific library files.. +# + +obj-y := checksum.o string.o strcase.o dec_and_lock.o div64.o + +obj-$(CONFIG_SMP) += locks.o +obj-$(CONFIG_8xx) += rheap.o +obj-$(CONFIG_CPM2) += rheap.o diff --git a/arch/ppc/lib/checksum.S b/arch/ppc/lib/checksum.S new file mode 100644 index 00000000000..7874e8a8045 --- /dev/null +++ b/arch/ppc/lib/checksum.S @@ -0,0 +1,225 @@ +/* + * This file contains assembly-language implementations + * of IP-style 1's complement checksum routines. + * + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Severely hacked about by Paul Mackerras (paulus@cs.anu.edu.au). + */ + +#include +#include +#include +#include + + .text + +/* + * ip_fast_csum(buf, len) -- Optimized for IP header + * len is in words and is always >= 5. + */ +_GLOBAL(ip_fast_csum) + lwz r0,0(r3) + lwzu r5,4(r3) + addic. r4,r4,-2 + addc r0,r0,r5 + mtctr r4 + blelr- +1: lwzu r4,4(r3) + adde r0,r0,r4 + bdnz 1b + addze r0,r0 /* add in final carry */ + rlwinm r3,r0,16,0,31 /* fold two halves together */ + add r3,r0,r3 + not r3,r3 + srwi r3,r3,16 + blr + +/* + * Compute checksum of TCP or UDP pseudo-header: + * csum_tcpudp_magic(saddr, daddr, len, proto, sum) + */ +_GLOBAL(csum_tcpudp_magic) + rlwimi r5,r6,16,0,15 /* put proto in upper half of len */ + addc r0,r3,r4 /* add 4 32-bit words together */ + adde r0,r0,r5 + adde r0,r0,r7 + addze r0,r0 /* add in final carry */ + rlwinm r3,r0,16,0,31 /* fold two halves together */ + add r3,r0,r3 + not r3,r3 + srwi r3,r3,16 + blr + +/* + * computes the checksum of a memory block at buff, length len, + * and adds in "sum" (32-bit) + * + * csum_partial(buff, len, sum) + */ +_GLOBAL(csum_partial) + addic r0,r5,0 + subi r3,r3,4 + srwi. r6,r4,2 + beq 3f /* if we're doing < 4 bytes */ + andi. r5,r3,2 /* Align buffer to longword boundary */ + beq+ 1f + lhz r5,4(r3) /* do 2 bytes to get aligned */ + addi r3,r3,2 + subi r4,r4,2 + addc r0,r0,r5 + srwi. r6,r4,2 /* # words to do */ + beq 3f +1: mtctr r6 +2: lwzu r5,4(r3) /* the bdnz has zero overhead, so it should */ + adde r0,r0,r5 /* be unnecessary to unroll this loop */ + bdnz 2b + andi. r4,r4,3 +3: cmpwi 0,r4,2 + blt+ 4f + lhz r5,4(r3) + addi r3,r3,2 + subi r4,r4,2 + adde r0,r0,r5 +4: cmpwi 0,r4,1 + bne+ 5f + lbz r5,4(r3) + slwi r5,r5,8 /* Upper byte of word */ + adde r0,r0,r5 +5: addze r3,r0 /* add in final carry */ + blr + +/* + * Computes the checksum of a memory block at src, length len, + * and adds in "sum" (32-bit), while copying the block to dst. + * If an access exception occurs on src or dst, it stores -EFAULT + * to *src_err or *dst_err respectively, and (for an error on + * src) zeroes the rest of dst. + * + * csum_partial_copy_generic(src, dst, len, sum, src_err, dst_err) + */ +_GLOBAL(csum_partial_copy_generic) + addic r0,r6,0 + subi r3,r3,4 + subi r4,r4,4 + srwi. r6,r5,2 + beq 3f /* if we're doing < 4 bytes */ + andi. r9,r4,2 /* Align dst to longword boundary */ + beq+ 1f +81: lhz r6,4(r3) /* do 2 bytes to get aligned */ + addi r3,r3,2 + subi r5,r5,2 +91: sth r6,4(r4) + addi r4,r4,2 + addc r0,r0,r6 + srwi. r6,r5,2 /* # words to do */ + beq 3f +1: srwi. r6,r5,4 /* # groups of 4 words to do */ + beq 10f + mtctr r6 +71: lwz r6,4(r3) +72: lwz r9,8(r3) +73: lwz r10,12(r3) +74: lwzu r11,16(r3) + adde r0,r0,r6 +75: stw r6,4(r4) + adde r0,r0,r9 +76: stw r9,8(r4) + adde r0,r0,r10 +77: stw r10,12(r4) + adde r0,r0,r11 +78: stwu r11,16(r4) + bdnz 71b +10: rlwinm. r6,r5,30,30,31 /* # words left to do */ + beq 13f + mtctr r6 +82: lwzu r9,4(r3) +92: stwu r9,4(r4) + adde r0,r0,r9 + bdnz 82b +13: andi. r5,r5,3 +3: cmpwi 0,r5,2 + blt+ 4f +83: lhz r6,4(r3) + addi r3,r3,2 + subi r5,r5,2 +93: sth r6,4(r4) + addi r4,r4,2 + adde r0,r0,r6 +4: cmpwi 0,r5,1 + bne+ 5f +84: lbz r6,4(r3) +94: stb r6,4(r4) + slwi r6,r6,8 /* Upper byte of word */ + adde r0,r0,r6 +5: addze r3,r0 /* add in final carry */ + blr + +/* These shouldn't go in the fixup section, since that would + cause the ex_table addresses to get out of order. */ + +src_error_4: + mfctr r6 /* update # bytes remaining from ctr */ + rlwimi r5,r6,4,0,27 + b 79f +src_error_1: + li r6,0 + subi r5,r5,2 +95: sth r6,4(r4) + addi r4,r4,2 +79: srwi. r6,r5,2 + beq 3f + mtctr r6 +src_error_2: + li r6,0 +96: stwu r6,4(r4) + bdnz 96b +3: andi. r5,r5,3 + beq src_error +src_error_3: + li r6,0 + mtctr r5 + addi r4,r4,3 +97: stbu r6,1(r4) + bdnz 97b +src_error: + cmpwi 0,r7,0 + beq 1f + li r6,-EFAULT + stw r6,0(r7) +1: addze r3,r0 + blr + +dst_error: + cmpwi 0,r8,0 + beq 1f + li r6,-EFAULT + stw r6,0(r8) +1: addze r3,r0 + blr + +.section __ex_table,"a" + .long 81b,src_error_1 + .long 91b,dst_error + .long 71b,src_error_4 + .long 72b,src_error_4 + .long 73b,src_error_4 + .long 74b,src_error_4 + .long 75b,dst_error + .long 76b,dst_error + .long 77b,dst_error + .long 78b,dst_error + .long 82b,src_error_2 + .long 92b,dst_error + .long 83b,src_error_3 + .long 93b,dst_error + .long 84b,src_error_3 + .long 94b,dst_error + .long 95b,dst_error + .long 96b,dst_error + .long 97b,dst_error diff --git a/arch/ppc/lib/dec_and_lock.c b/arch/ppc/lib/dec_and_lock.c new file mode 100644 index 00000000000..4ee888070d9 --- /dev/null +++ b/arch/ppc/lib/dec_and_lock.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include + +/* + * This is an implementation of the notion of "decrement a + * reference count, and return locked if it decremented to zero". + * + * This implementation can be used on any architecture that + * has a cmpxchg, and where atomic->value is an int holding + * the value of the atomic (i.e. the high bits aren't used + * for a lock or anything like that). + * + * N.B. ATOMIC_DEC_AND_LOCK gets defined in include/linux/spinlock.h + * if spinlocks are empty and thus atomic_dec_and_lock is defined + * to be atomic_dec_and_test - in that case we don't need it + * defined here as well. + */ + +#ifndef ATOMIC_DEC_AND_LOCK +int _atomic_dec_and_lock(atomic_t *atomic, spinlock_t *lock) +{ + int counter; + int newcount; + + for (;;) { + counter = atomic_read(atomic); + newcount = counter - 1; + if (!newcount) + break; /* do it the slow way */ + + newcount = cmpxchg(&atomic->counter, counter, newcount); + if (newcount == counter) + return 0; + } + + spin_lock(lock); + if (atomic_dec_and_test(atomic)) + return 1; + spin_unlock(lock); + return 0; +} + +EXPORT_SYMBOL(_atomic_dec_and_lock); +#endif /* ATOMIC_DEC_AND_LOCK */ diff --git a/arch/ppc/lib/div64.S b/arch/ppc/lib/div64.S new file mode 100644 index 00000000000..3527569e992 --- /dev/null +++ b/arch/ppc/lib/div64.S @@ -0,0 +1,58 @@ +/* + * Divide a 64-bit unsigned number by a 32-bit unsigned number. + * This routine assumes that the top 32 bits of the dividend are + * non-zero to start with. + * On entry, r3 points to the dividend, which get overwritten with + * the 64-bit quotient, and r4 contains the divisor. + * On exit, r3 contains the remainder. + * + * Copyright (C) 2002 Paul Mackerras, IBM Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include + +_GLOBAL(__div64_32) + lwz r5,0(r3) # get the dividend into r5/r6 + lwz r6,4(r3) + cmplw r5,r4 + li r7,0 + li r8,0 + blt 1f + divwu r7,r5,r4 # if dividend.hi >= divisor, + mullw r0,r7,r4 # quotient.hi = dividend.hi / divisor + subf. r5,r0,r5 # dividend.hi %= divisor + beq 3f +1: mr r11,r5 # here dividend.hi != 0 + andis. r0,r5,0xc000 + bne 2f + cntlzw r0,r5 # we are shifting the dividend right + li r10,-1 # to make it < 2^32, and shifting + srw r10,r10,r0 # the divisor right the same amount, + add r9,r4,r10 # rounding up (so the estimate cannot + andc r11,r6,r10 # ever be too large, only too small) + andc r9,r9,r10 + or r11,r5,r11 + rotlw r9,r9,r0 + rotlw r11,r11,r0 + divwu r11,r11,r9 # then we divide the shifted quantities +2: mullw r10,r11,r4 # to get an estimate of the quotient, + mulhwu r9,r11,r4 # multiply the estimate by the divisor, + subfc r6,r10,r6 # take the product from the divisor, + add r8,r8,r11 # and add the estimate to the accumulated + subfe. r5,r9,r5 # quotient + bne 1b +3: cmplw r6,r4 + blt 4f + divwu r0,r6,r4 # perform the remaining 32-bit division + mullw r10,r0,r4 # and get the remainder + add r8,r8,r0 + subf r6,r10,r6 +4: stw r7,0(r3) # return the quotient in *r3 + stw r8,4(r3) + mr r3,r6 # return the remainder in r3 + blr diff --git a/arch/ppc/lib/locks.c b/arch/ppc/lib/locks.c new file mode 100644 index 00000000000..694163d696d --- /dev/null +++ b/arch/ppc/lib/locks.c @@ -0,0 +1,190 @@ +/* + * Locks for smp ppc + * + * Written by Cort Dougan (cort@cs.nmt.edu) + */ + +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_DEBUG_SPINLOCK + +#undef INIT_STUCK +#define INIT_STUCK 200000000 /*0xffffffff*/ + +/* + * Try to acquire a spinlock. + * Only does the stwcx. if the load returned 0 - the Programming + * Environments Manual suggests not doing unnecessary stcwx.'s + * since they may inhibit forward progress by other CPUs in getting + * a lock. + */ +static inline unsigned long __spin_trylock(volatile unsigned long *lock) +{ + unsigned long ret; + + __asm__ __volatile__ ("\n\ +1: lwarx %0,0,%1\n\ + cmpwi 0,%0,0\n\ + bne 2f\n" + PPC405_ERR77(0,%1) +" stwcx. %2,0,%1\n\ + bne- 1b\n\ + isync\n\ +2:" + : "=&r"(ret) + : "r"(lock), "r"(1) + : "cr0", "memory"); + + return ret; +} + +void _raw_spin_lock(spinlock_t *lock) +{ + int cpu = smp_processor_id(); + unsigned int stuck = INIT_STUCK; + while (__spin_trylock(&lock->lock)) { + while ((unsigned volatile long)lock->lock != 0) { + if (!--stuck) { + printk("_spin_lock(%p) CPU#%d NIP %p" + " holder: cpu %ld pc %08lX\n", + lock, cpu, __builtin_return_address(0), + lock->owner_cpu,lock->owner_pc); + stuck = INIT_STUCK; + /* steal the lock */ + /*xchg_u32((void *)&lock->lock,0);*/ + } + } + } + lock->owner_pc = (unsigned long)__builtin_return_address(0); + lock->owner_cpu = cpu; +} +EXPORT_SYMBOL(_raw_spin_lock); + +int _raw_spin_trylock(spinlock_t *lock) +{ + if (__spin_trylock(&lock->lock)) + return 0; + lock->owner_cpu = smp_processor_id(); + lock->owner_pc = (unsigned long)__builtin_return_address(0); + return 1; +} +EXPORT_SYMBOL(_raw_spin_trylock); + +void _raw_spin_unlock(spinlock_t *lp) +{ + if ( !lp->lock ) + printk("_spin_unlock(%p): no lock cpu %d curr PC %p %s/%d\n", + lp, smp_processor_id(), __builtin_return_address(0), + current->comm, current->pid); + if ( lp->owner_cpu != smp_processor_id() ) + printk("_spin_unlock(%p): cpu %d trying clear of cpu %d pc %lx val %lx\n", + lp, smp_processor_id(), (int)lp->owner_cpu, + lp->owner_pc,lp->lock); + lp->owner_pc = lp->owner_cpu = 0; + wmb(); + lp->lock = 0; +} +EXPORT_SYMBOL(_raw_spin_unlock); + +/* + * For rwlocks, zero is unlocked, -1 is write-locked, + * positive is read-locked. + */ +static __inline__ int __read_trylock(rwlock_t *rw) +{ + signed int tmp; + + __asm__ __volatile__( +"2: lwarx %0,0,%1 # __read_trylock\n\ + addic. %0,%0,1\n\ + ble- 1f\n" + PPC405_ERR77(0,%1) +" stwcx. %0,0,%1\n\ + bne- 2b\n\ + isync\n\ +1:" + : "=&r"(tmp) + : "r"(&rw->lock) + : "cr0", "memory"); + + return tmp; +} + +int _raw_read_trylock(rwlock_t *rw) +{ + return __read_trylock(rw) > 0; +} +EXPORT_SYMBOL(_raw_read_trylock); + +void _raw_read_lock(rwlock_t *rw) +{ + unsigned int stuck; + + while (__read_trylock(rw) <= 0) { + stuck = INIT_STUCK; + while (!read_can_lock(rw)) { + if (--stuck == 0) { + printk("_read_lock(%p) CPU#%d lock %d\n", + rw, _smp_processor_id(), rw->lock); + stuck = INIT_STUCK; + } + } + } +} +EXPORT_SYMBOL(_raw_read_lock); + +void _raw_read_unlock(rwlock_t *rw) +{ + if ( rw->lock == 0 ) + printk("_read_unlock(): %s/%d (nip %08lX) lock %d\n", + current->comm,current->pid,current->thread.regs->nip, + rw->lock); + wmb(); + atomic_dec((atomic_t *) &(rw)->lock); +} +EXPORT_SYMBOL(_raw_read_unlock); + +void _raw_write_lock(rwlock_t *rw) +{ + unsigned int stuck; + + while (cmpxchg(&rw->lock, 0, -1) != 0) { + stuck = INIT_STUCK; + while (!write_can_lock(rw)) { + if (--stuck == 0) { + printk("write_lock(%p) CPU#%d lock %d)\n", + rw, _smp_processor_id(), rw->lock); + stuck = INIT_STUCK; + } + } + } + wmb(); +} +EXPORT_SYMBOL(_raw_write_lock); + +int _raw_write_trylock(rwlock_t *rw) +{ + if (cmpxchg(&rw->lock, 0, -1) != 0) + return 0; + wmb(); + return 1; +} +EXPORT_SYMBOL(_raw_write_trylock); + +void _raw_write_unlock(rwlock_t *rw) +{ + if (rw->lock >= 0) + printk("_write_lock(): %s/%d (nip %08lX) lock %d\n", + current->comm,current->pid,current->thread.regs->nip, + rw->lock); + wmb(); + rw->lock = 0; +} +EXPORT_SYMBOL(_raw_write_unlock); + +#endif diff --git a/arch/ppc/lib/rheap.c b/arch/ppc/lib/rheap.c new file mode 100644 index 00000000000..42c5de2c898 --- /dev/null +++ b/arch/ppc/lib/rheap.c @@ -0,0 +1,693 @@ +/* + * arch/ppc/syslib/rheap.c + * + * A Remote Heap. Remote means that we don't touch the memory that the + * heap points to. Normal heap implementations use the memory they manage + * to place their list. We cannot do that because the memory we manage may + * have special properties, for example it is uncachable or of different + * endianess. + * + * Author: Pantelis Antoniou + * + * 2004 (c) INTRACOM S.A. Greece. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#include +#include +#include +#include + +#include + +/* + * Fixup a list_head, needed when copying lists. If the pointers fall + * between s and e, apply the delta. This assumes that + * sizeof(struct list_head *) == sizeof(unsigned long *). + */ +static inline void fixup(unsigned long s, unsigned long e, int d, + struct list_head *l) +{ + unsigned long *pp; + + pp = (unsigned long *)&l->next; + if (*pp >= s && *pp < e) + *pp += d; + + pp = (unsigned long *)&l->prev; + if (*pp >= s && *pp < e) + *pp += d; +} + +/* Grow the allocated blocks */ +static int grow(rh_info_t * info, int max_blocks) +{ + rh_block_t *block, *blk; + int i, new_blocks; + int delta; + unsigned long blks, blke; + + if (max_blocks <= info->max_blocks) + return -EINVAL; + + new_blocks = max_blocks - info->max_blocks; + + block = kmalloc(sizeof(rh_block_t) * max_blocks, GFP_KERNEL); + if (block == NULL) + return -ENOMEM; + + if (info->max_blocks > 0) { + + /* copy old block area */ + memcpy(block, info->block, + sizeof(rh_block_t) * info->max_blocks); + + delta = (char *)block - (char *)info->block; + + /* and fixup list pointers */ + blks = (unsigned long)info->block; + blke = (unsigned long)(info->block + info->max_blocks); + + for (i = 0, blk = block; i < info->max_blocks; i++, blk++) + fixup(blks, blke, delta, &blk->list); + + fixup(blks, blke, delta, &info->empty_list); + fixup(blks, blke, delta, &info->free_list); + fixup(blks, blke, delta, &info->taken_list); + + /* free the old allocated memory */ + if ((info->flags & RHIF_STATIC_BLOCK) == 0) + kfree(info->block); + } + + info->block = block; + info->empty_slots += new_blocks; + info->max_blocks = max_blocks; + info->flags &= ~RHIF_STATIC_BLOCK; + + /* add all new blocks to the free list */ + for (i = 0, blk = block + info->max_blocks; i < new_blocks; i++, blk++) + list_add(&blk->list, &info->empty_list); + + return 0; +} + +/* + * Assure at least the required amount of empty slots. If this function + * causes a grow in the block area then all pointers kept to the block + * area are invalid! + */ +static int assure_empty(rh_info_t * info, int slots) +{ + int max_blocks; + + /* This function is not meant to be used to grow uncontrollably */ + if (slots >= 4) + return -EINVAL; + + /* Enough space */ + if (info->empty_slots >= slots) + return 0; + + /* Next 16 sized block */ + max_blocks = ((info->max_blocks + slots) + 15) & ~15; + + return grow(info, max_blocks); +} + +static rh_block_t *get_slot(rh_info_t * info) +{ + rh_block_t *blk; + + /* If no more free slots, and failure to extend. */ + /* XXX: You should have called assure_empty before */ + if (info->empty_slots == 0) { + printk(KERN_ERR "rh: out of slots; crash is imminent.\n"); + return NULL; + } + + /* Get empty slot to use */ + blk = list_entry(info->empty_list.next, rh_block_t, list); + list_del_init(&blk->list); + info->empty_slots--; + + /* Initialize */ + blk->start = NULL; + blk->size = 0; + blk->owner = NULL; + + return blk; +} + +static inline void release_slot(rh_info_t * info, rh_block_t * blk) +{ + list_add(&blk->list, &info->empty_list); + info->empty_slots++; +} + +static void attach_free_block(rh_info_t * info, rh_block_t * blkn) +{ + rh_block_t *blk; + rh_block_t *before; + rh_block_t *after; + rh_block_t *next; + int size; + unsigned long s, e, bs, be; + struct list_head *l; + + /* We assume that they are aligned properly */ + size = blkn->size; + s = (unsigned long)blkn->start; + e = s + size; + + /* Find the blocks immediately before and after the given one + * (if any) */ + before = NULL; + after = NULL; + next = NULL; + + list_for_each(l, &info->free_list) { + blk = list_entry(l, rh_block_t, list); + + bs = (unsigned long)blk->start; + be = bs + blk->size; + + if (next == NULL && s >= bs) + next = blk; + + if (be == s) + before = blk; + + if (e == bs) + after = blk; + + /* If both are not null, break now */ + if (before != NULL && after != NULL) + break; + } + + /* Now check if they are really adjacent */ + if (before != NULL && s != (unsigned long)before->start + before->size) + before = NULL; + + if (after != NULL && e != (unsigned long)after->start) + after = NULL; + + /* No coalescing; list insert and return */ + if (before == NULL && after == NULL) { + + if (next != NULL) + list_add(&blkn->list, &next->list); + else + list_add(&blkn->list, &info->free_list); + + return; + } + + /* We don't need it anymore */ + release_slot(info, blkn); + + /* Grow the before block */ + if (before != NULL && after == NULL) { + before->size += size; + return; + } + + /* Grow the after block backwards */ + if (before == NULL && after != NULL) { + after->start = (int8_t *)after->start - size; + after->size += size; + return; + } + + /* Grow the before block, and release the after block */ + before->size += size + after->size; + list_del(&after->list); + release_slot(info, after); +} + +static void attach_taken_block(rh_info_t * info, rh_block_t * blkn) +{ + rh_block_t *blk; + struct list_head *l; + + /* Find the block immediately before the given one (if any) */ + list_for_each(l, &info->taken_list) { + blk = list_entry(l, rh_block_t, list); + if (blk->start > blkn->start) { + list_add_tail(&blkn->list, &blk->list); + return; + } + } + + list_add_tail(&blkn->list, &info->taken_list); +} + +/* + * Create a remote heap dynamically. Note that no memory for the blocks + * are allocated. It will upon the first allocation + */ +rh_info_t *rh_create(unsigned int alignment) +{ + rh_info_t *info; + + /* Alignment must be a power of two */ + if ((alignment & (alignment - 1)) != 0) + return ERR_PTR(-EINVAL); + + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (info == NULL) + return ERR_PTR(-ENOMEM); + + info->alignment = alignment; + + /* Initially everything as empty */ + info->block = NULL; + info->max_blocks = 0; + info->empty_slots = 0; + info->flags = 0; + + INIT_LIST_HEAD(&info->empty_list); + INIT_LIST_HEAD(&info->free_list); + INIT_LIST_HEAD(&info->taken_list); + + return info; +} + +/* + * Destroy a dynamically created remote heap. Deallocate only if the areas + * are not static + */ +void rh_destroy(rh_info_t * info) +{ + if ((info->flags & RHIF_STATIC_BLOCK) == 0 && info->block != NULL) + kfree(info->block); + + if ((info->flags & RHIF_STATIC_INFO) == 0) + kfree(info); +} + +/* + * Initialize in place a remote heap info block. This is needed to support + * operation very early in the startup of the kernel, when it is not yet safe + * to call kmalloc. + */ +void rh_init(rh_info_t * info, unsigned int alignment, int max_blocks, + rh_block_t * block) +{ + int i; + rh_block_t *blk; + + /* Alignment must be a power of two */ + if ((alignment & (alignment - 1)) != 0) + return; + + info->alignment = alignment; + + /* Initially everything as empty */ + info->block = block; + info->max_blocks = max_blocks; + info->empty_slots = max_blocks; + info->flags = RHIF_STATIC_INFO | RHIF_STATIC_BLOCK; + + INIT_LIST_HEAD(&info->empty_list); + INIT_LIST_HEAD(&info->free_list); + INIT_LIST_HEAD(&info->taken_list); + + /* Add all new blocks to the free list */ + for (i = 0, blk = block; i < max_blocks; i++, blk++) + list_add(&blk->list, &info->empty_list); +} + +/* Attach a free memory region, coalesces regions if adjuscent */ +int rh_attach_region(rh_info_t * info, void *start, int size) +{ + rh_block_t *blk; + unsigned long s, e, m; + int r; + + /* The region must be aligned */ + s = (unsigned long)start; + e = s + size; + m = info->alignment - 1; + + /* Round start up */ + s = (s + m) & ~m; + + /* Round end down */ + e = e & ~m; + + /* Take final values */ + start = (void *)s; + size = (int)(e - s); + + /* Grow the blocks, if needed */ + r = assure_empty(info, 1); + if (r < 0) + return r; + + blk = get_slot(info); + blk->start = start; + blk->size = size; + blk->owner = NULL; + + attach_free_block(info, blk); + + return 0; +} + +/* Detatch given address range, splits free block if needed. */ +void *rh_detach_region(rh_info_t * info, void *start, int size) +{ + struct list_head *l; + rh_block_t *blk, *newblk; + unsigned long s, e, m, bs, be; + + /* Validate size */ + if (size <= 0) + return ERR_PTR(-EINVAL); + + /* The region must be aligned */ + s = (unsigned long)start; + e = s + size; + m = info->alignment - 1; + + /* Round start up */ + s = (s + m) & ~m; + + /* Round end down */ + e = e & ~m; + + if (assure_empty(info, 1) < 0) + return ERR_PTR(-ENOMEM); + + blk = NULL; + list_for_each(l, &info->free_list) { + blk = list_entry(l, rh_block_t, list); + /* The range must lie entirely inside one free block */ + bs = (unsigned long)blk->start; + be = (unsigned long)blk->start + blk->size; + if (s >= bs && e <= be) + break; + blk = NULL; + } + + if (blk == NULL) + return ERR_PTR(-ENOMEM); + + /* Perfect fit */ + if (bs == s && be == e) { + /* Delete from free list, release slot */ + list_del(&blk->list); + release_slot(info, blk); + return (void *)s; + } + + /* blk still in free list, with updated start and/or size */ + if (bs == s || be == e) { + if (bs == s) + blk->start = (int8_t *)blk->start + size; + blk->size -= size; + + } else { + /* The front free fragment */ + blk->size = s - bs; + + /* the back free fragment */ + newblk = get_slot(info); + newblk->start = (void *)e; + newblk->size = be - e; + + list_add(&newblk->list, &blk->list); + } + + return (void *)s; +} + +void *rh_alloc(rh_info_t * info, int size, const char *owner) +{ + struct list_head *l; + rh_block_t *blk; + rh_block_t *newblk; + void *start; + + /* Validate size */ + if (size <= 0) + return ERR_PTR(-EINVAL); + + /* Align to configured alignment */ + size = (size + (info->alignment - 1)) & ~(info->alignment - 1); + + if (assure_empty(info, 1) < 0) + return ERR_PTR(-ENOMEM); + + blk = NULL; + list_for_each(l, &info->free_list) { + blk = list_entry(l, rh_block_t, list); + if (size <= blk->size) + break; + blk = NULL; + } + + if (blk == NULL) + return ERR_PTR(-ENOMEM); + + /* Just fits */ + if (blk->size == size) { + /* Move from free list to taken list */ + list_del(&blk->list); + blk->owner = owner; + start = blk->start; + + attach_taken_block(info, blk); + + return start; + } + + newblk = get_slot(info); + newblk->start = blk->start; + newblk->size = size; + newblk->owner = owner; + + /* blk still in free list, with updated start, size */ + blk->start = (int8_t *)blk->start + size; + blk->size -= size; + + start = newblk->start; + + attach_taken_block(info, newblk); + + return start; +} + +/* allocate at precisely the given address */ +void *rh_alloc_fixed(rh_info_t * info, void *start, int size, const char *owner) +{ + struct list_head *l; + rh_block_t *blk, *newblk1, *newblk2; + unsigned long s, e, m, bs, be; + + /* Validate size */ + if (size <= 0) + return ERR_PTR(-EINVAL); + + /* The region must be aligned */ + s = (unsigned long)start; + e = s + size; + m = info->alignment - 1; + + /* Round start up */ + s = (s + m) & ~m; + + /* Round end down */ + e = e & ~m; + + if (assure_empty(info, 2) < 0) + return ERR_PTR(-ENOMEM); + + blk = NULL; + list_for_each(l, &info->free_list) { + blk = list_entry(l, rh_block_t, list); + /* The range must lie entirely inside one free block */ + bs = (unsigned long)blk->start; + be = (unsigned long)blk->start + blk->size; + if (s >= bs && e <= be) + break; + } + + if (blk == NULL) + return ERR_PTR(-ENOMEM); + + /* Perfect fit */ + if (bs == s && be == e) { + /* Move from free list to taken list */ + list_del(&blk->list); + blk->owner = owner; + + start = blk->start; + attach_taken_block(info, blk); + + return start; + + } + + /* blk still in free list, with updated start and/or size */ + if (bs == s || be == e) { + if (bs == s) + blk->start = (int8_t *)blk->start + size; + blk->size -= size; + + } else { + /* The front free fragment */ + blk->size = s - bs; + + /* The back free fragment */ + newblk2 = get_slot(info); + newblk2->start = (void *)e; + newblk2->size = be - e; + + list_add(&newblk2->list, &blk->list); + } + + newblk1 = get_slot(info); + newblk1->start = (void *)s; + newblk1->size = e - s; + newblk1->owner = owner; + + start = newblk1->start; + attach_taken_block(info, newblk1); + + return start; +} + +int rh_free(rh_info_t * info, void *start) +{ + rh_block_t *blk, *blk2; + struct list_head *l; + int size; + + /* Linear search for block */ + blk = NULL; + list_for_each(l, &info->taken_list) { + blk2 = list_entry(l, rh_block_t, list); + if (start < blk2->start) + break; + blk = blk2; + } + + if (blk == NULL || start > (blk->start + blk->size)) + return -EINVAL; + + /* Remove from taken list */ + list_del(&blk->list); + + /* Get size of freed block */ + size = blk->size; + attach_free_block(info, blk); + + return size; +} + +int rh_get_stats(rh_info_t * info, int what, int max_stats, rh_stats_t * stats) +{ + rh_block_t *blk; + struct list_head *l; + struct list_head *h; + int nr; + + switch (what) { + + case RHGS_FREE: + h = &info->free_list; + break; + + case RHGS_TAKEN: + h = &info->taken_list; + break; + + default: + return -EINVAL; + } + + /* Linear search for block */ + nr = 0; + list_for_each(l, h) { + blk = list_entry(l, rh_block_t, list); + if (stats != NULL && nr < max_stats) { + stats->start = blk->start; + stats->size = blk->size; + stats->owner = blk->owner; + stats++; + } + nr++; + } + + return nr; +} + +int rh_set_owner(rh_info_t * info, void *start, const char *owner) +{ + rh_block_t *blk, *blk2; + struct list_head *l; + int size; + + /* Linear search for block */ + blk = NULL; + list_for_each(l, &info->taken_list) { + blk2 = list_entry(l, rh_block_t, list); + if (start < blk2->start) + break; + blk = blk2; + } + + if (blk == NULL || start > (blk->start + blk->size)) + return -EINVAL; + + blk->owner = owner; + size = blk->size; + + return size; +} + +void rh_dump(rh_info_t * info) +{ + static rh_stats_t st[32]; /* XXX maximum 32 blocks */ + int maxnr; + int i, nr; + + maxnr = sizeof(st) / sizeof(st[0]); + + printk(KERN_INFO + "info @0x%p (%d slots empty / %d max)\n", + info, info->empty_slots, info->max_blocks); + + printk(KERN_INFO " Free:\n"); + nr = rh_get_stats(info, RHGS_FREE, maxnr, st); + if (nr > maxnr) + nr = maxnr; + for (i = 0; i < nr; i++) + printk(KERN_INFO + " 0x%p-0x%p (%u)\n", + st[i].start, (int8_t *) st[i].start + st[i].size, + st[i].size); + printk(KERN_INFO "\n"); + + printk(KERN_INFO " Taken:\n"); + nr = rh_get_stats(info, RHGS_TAKEN, maxnr, st); + if (nr > maxnr) + nr = maxnr; + for (i = 0; i < nr; i++) + printk(KERN_INFO + " 0x%p-0x%p (%u) %s\n", + st[i].start, (int8_t *) st[i].start + st[i].size, + st[i].size, st[i].owner != NULL ? st[i].owner : ""); + printk(KERN_INFO "\n"); +} + +void rh_dump_blk(rh_info_t * info, rh_block_t * blk) +{ + printk(KERN_INFO + "blk @0x%p: 0x%p-0x%p (%u)\n", + blk, blk->start, (int8_t *) blk->start + blk->size, blk->size); +} diff --git a/arch/ppc/lib/strcase.c b/arch/ppc/lib/strcase.c new file mode 100644 index 00000000000..36b521091bb --- /dev/null +++ b/arch/ppc/lib/strcase.c @@ -0,0 +1,23 @@ +#include + +int strcasecmp(const char *s1, const char *s2) +{ + int c1, c2; + + do { + c1 = tolower(*s1++); + c2 = tolower(*s2++); + } while (c1 == c2 && c1 != 0); + return c1 - c2; +} + +int strncasecmp(const char *s1, const char *s2, int n) +{ + int c1, c2; + + do { + c1 = tolower(*s1++); + c2 = tolower(*s2++); + } while ((--n > 0) && c1 == c2 && c1 != 0); + return c1 - c2; +} diff --git a/arch/ppc/lib/string.S b/arch/ppc/lib/string.S new file mode 100644 index 00000000000..8d08a2eb225 --- /dev/null +++ b/arch/ppc/lib/string.S @@ -0,0 +1,716 @@ +/* + * String handling functions for PowerPC. + * + * Copyright (C) 1996 Paul Mackerras. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include + +#define COPY_16_BYTES \ + lwz r7,4(r4); \ + lwz r8,8(r4); \ + lwz r9,12(r4); \ + lwzu r10,16(r4); \ + stw r7,4(r6); \ + stw r8,8(r6); \ + stw r9,12(r6); \ + stwu r10,16(r6) + +#define COPY_16_BYTES_WITHEX(n) \ +8 ## n ## 0: \ + lwz r7,4(r4); \ +8 ## n ## 1: \ + lwz r8,8(r4); \ +8 ## n ## 2: \ + lwz r9,12(r4); \ +8 ## n ## 3: \ + lwzu r10,16(r4); \ +8 ## n ## 4: \ + stw r7,4(r6); \ +8 ## n ## 5: \ + stw r8,8(r6); \ +8 ## n ## 6: \ + stw r9,12(r6); \ +8 ## n ## 7: \ + stwu r10,16(r6) + +#define COPY_16_BYTES_EXCODE(n) \ +9 ## n ## 0: \ + addi r5,r5,-(16 * n); \ + b 104f; \ +9 ## n ## 1: \ + addi r5,r5,-(16 * n); \ + b 105f; \ +.section __ex_table,"a"; \ + .align 2; \ + .long 8 ## n ## 0b,9 ## n ## 0b; \ + .long 8 ## n ## 1b,9 ## n ## 0b; \ + .long 8 ## n ## 2b,9 ## n ## 0b; \ + .long 8 ## n ## 3b,9 ## n ## 0b; \ + .long 8 ## n ## 4b,9 ## n ## 1b; \ + .long 8 ## n ## 5b,9 ## n ## 1b; \ + .long 8 ## n ## 6b,9 ## n ## 1b; \ + .long 8 ## n ## 7b,9 ## n ## 1b; \ + .text + + .text + .stabs "arch/ppc/lib/",N_SO,0,0,0f + .stabs "string.S",N_SO,0,0,0f + +CACHELINE_BYTES = L1_CACHE_LINE_SIZE +LG_CACHELINE_BYTES = LG_L1_CACHE_LINE_SIZE +CACHELINE_MASK = (L1_CACHE_LINE_SIZE-1) + +_GLOBAL(strcpy) + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r5) + bne 1b + blr + +/* This clears out any unused part of the destination buffer, + just as the libc version does. -- paulus */ +_GLOBAL(strncpy) + cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r6,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r6) + bdnzf 2,1b /* dec ctr, branch if ctr != 0 && !cr0.eq */ + bnelr /* if we didn't hit a null char, we're done */ + mfctr r5 + cmpwi 0,r5,0 /* any space left in destination buffer? */ + beqlr /* we know r0 == 0 here */ +2: stbu r0,1(r6) /* clear it out if so */ + bdnz 2b + blr + +_GLOBAL(strcat) + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r5) + cmpwi 0,r0,0 + bne 1b + addi r5,r5,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r5) + bne 1b + blr + +_GLOBAL(strcmp) + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r3,1(r5) + cmpwi 1,r3,0 + lbzu r0,1(r4) + subf. r3,r0,r3 + beqlr 1 + beq 1b + blr + +_GLOBAL(strlen) + addi r4,r3,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + bne 1b + subf r3,r3,r4 + blr + +/* + * Use dcbz on the complete cache lines in the destination + * to set them to zero. This requires that the destination + * area is cacheable. -- paulus + */ +_GLOBAL(cacheable_memzero) + mr r5,r4 + li r4,0 + addi r6,r3,-4 + cmplwi 0,r5,4 + blt 7f + stwu r4,4(r6) + beqlr + andi. r0,r6,3 + add r5,r0,r5 + subf r6,r0,r6 + clrlwi r7,r6,32-LG_CACHELINE_BYTES + add r8,r7,r5 + srwi r9,r8,LG_CACHELINE_BYTES + addic. r9,r9,-1 /* total number of complete cachelines */ + ble 2f + xori r0,r7,CACHELINE_MASK & ~3 + srwi. r0,r0,2 + beq 3f + mtctr r0 +4: stwu r4,4(r6) + bdnz 4b +3: mtctr r9 + li r7,4 +#if !defined(CONFIG_8xx) +10: dcbz r7,r6 +#else +10: stw r4, 4(r6) + stw r4, 8(r6) + stw r4, 12(r6) + stw r4, 16(r6) +#if CACHE_LINE_SIZE >= 32 + stw r4, 20(r6) + stw r4, 24(r6) + stw r4, 28(r6) + stw r4, 32(r6) +#endif /* CACHE_LINE_SIZE */ +#endif + addi r6,r6,CACHELINE_BYTES + bdnz 10b + clrlwi r5,r8,32-LG_CACHELINE_BYTES + addi r5,r5,4 +2: srwi r0,r5,2 + mtctr r0 + bdz 6f +1: stwu r4,4(r6) + bdnz 1b +6: andi. r5,r5,3 +7: cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r6,r6,3 +8: stbu r4,1(r6) + bdnz 8b + blr + +_GLOBAL(memset) + rlwimi r4,r4,8,16,23 + rlwimi r4,r4,16,0,15 + addi r6,r3,-4 + cmplwi 0,r5,4 + blt 7f + stwu r4,4(r6) + beqlr + andi. r0,r6,3 + add r5,r0,r5 + subf r6,r0,r6 + srwi r0,r5,2 + mtctr r0 + bdz 6f +1: stwu r4,4(r6) + bdnz 1b +6: andi. r5,r5,3 +7: cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r6,r6,3 +8: stbu r4,1(r6) + bdnz 8b + blr + +/* + * This version uses dcbz on the complete cache lines in the + * destination area to reduce memory traffic. This requires that + * the destination area is cacheable. + * We only use this version if the source and dest don't overlap. + * -- paulus. + */ +_GLOBAL(cacheable_memcpy) + add r7,r3,r5 /* test if the src & dst overlap */ + add r8,r4,r5 + cmplw 0,r4,r7 + cmplw 1,r3,r8 + crand 0,0,4 /* cr0.lt &= cr1.lt */ + blt memcpy /* if regions overlap */ + + addi r4,r4,-4 + addi r6,r3,-4 + neg r0,r3 + andi. r0,r0,CACHELINE_MASK /* # bytes to start of cache line */ + beq 58f + + cmplw 0,r5,r0 /* is this more than total to do? */ + blt 63f /* if not much to do */ + andi. r8,r0,3 /* get it word-aligned first */ + subf r5,r0,r5 + mtctr r8 + beq+ 61f +70: lbz r9,4(r4) /* do some bytes */ + stb r9,4(r6) + addi r4,r4,1 + addi r6,r6,1 + bdnz 70b +61: srwi. r0,r0,2 + mtctr r0 + beq 58f +72: lwzu r9,4(r4) /* do some words */ + stwu r9,4(r6) + bdnz 72b + +58: srwi. r0,r5,LG_CACHELINE_BYTES /* # complete cachelines */ + clrlwi r5,r5,32-LG_CACHELINE_BYTES + li r11,4 + mtctr r0 + beq 63f +53: +#if !defined(CONFIG_8xx) + dcbz r11,r6 +#endif + COPY_16_BYTES +#if L1_CACHE_LINE_SIZE >= 32 + COPY_16_BYTES +#if L1_CACHE_LINE_SIZE >= 64 + COPY_16_BYTES + COPY_16_BYTES +#if L1_CACHE_LINE_SIZE >= 128 + COPY_16_BYTES + COPY_16_BYTES + COPY_16_BYTES + COPY_16_BYTES +#endif +#endif +#endif + bdnz 53b + +63: srwi. r0,r5,2 + mtctr r0 + beq 64f +30: lwzu r0,4(r4) + stwu r0,4(r6) + bdnz 30b + +64: andi. r0,r5,3 + mtctr r0 + beq+ 65f +40: lbz r0,4(r4) + stb r0,4(r6) + addi r4,r4,1 + addi r6,r6,1 + bdnz 40b +65: blr + +_GLOBAL(memmove) + cmplw 0,r3,r4 + bgt backwards_memcpy + /* fall through */ + +_GLOBAL(memcpy) + srwi. r7,r5,3 + addi r6,r3,-4 + addi r4,r4,-4 + beq 2f /* if less than 8 bytes to do */ + andi. r0,r6,3 /* get dest word aligned */ + mtctr r7 + bne 5f +1: lwz r7,4(r4) + lwzu r8,8(r4) + stw r7,4(r6) + stwu r8,8(r6) + bdnz 1b + andi. r5,r5,7 +2: cmplwi 0,r5,4 + blt 3f + lwzu r0,4(r4) + addi r5,r5,-4 + stwu r0,4(r6) +3: cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r4,r4,3 + addi r6,r6,3 +4: lbzu r0,1(r4) + stbu r0,1(r6) + bdnz 4b + blr +5: subfic r0,r0,4 + mtctr r0 +6: lbz r7,4(r4) + addi r4,r4,1 + stb r7,4(r6) + addi r6,r6,1 + bdnz 6b + subf r5,r0,r5 + rlwinm. r7,r5,32-3,3,31 + beq 2b + mtctr r7 + b 1b + +_GLOBAL(backwards_memcpy) + rlwinm. r7,r5,32-3,3,31 /* r0 = r5 >> 3 */ + add r6,r3,r5 + add r4,r4,r5 + beq 2f + andi. r0,r6,3 + mtctr r7 + bne 5f +1: lwz r7,-4(r4) + lwzu r8,-8(r4) + stw r7,-4(r6) + stwu r8,-8(r6) + bdnz 1b + andi. r5,r5,7 +2: cmplwi 0,r5,4 + blt 3f + lwzu r0,-4(r4) + subi r5,r5,4 + stwu r0,-4(r6) +3: cmpwi 0,r5,0 + beqlr + mtctr r5 +4: lbzu r0,-1(r4) + stbu r0,-1(r6) + bdnz 4b + blr +5: mtctr r0 +6: lbzu r7,-1(r4) + stbu r7,-1(r6) + bdnz 6b + subf r5,r0,r5 + rlwinm. r7,r5,32-3,3,31 + beq 2b + mtctr r7 + b 1b + +_GLOBAL(memcmp) + cmpwi 0,r5,0 + ble- 2f + mtctr r5 + addi r6,r3,-1 + addi r4,r4,-1 +1: lbzu r3,1(r6) + lbzu r0,1(r4) + subf. r3,r0,r3 + bdnzt 2,1b + blr +2: li r3,0 + blr + +_GLOBAL(memchr) + cmpwi 0,r5,0 + ble- 2f + mtctr r5 + addi r3,r3,-1 +1: lbzu r0,1(r3) + cmpw 0,r0,r4 + bdnzf 2,1b + beqlr +2: li r3,0 + blr + +_GLOBAL(__copy_tofrom_user) + addi r4,r4,-4 + addi r6,r3,-4 + neg r0,r3 + andi. r0,r0,CACHELINE_MASK /* # bytes to start of cache line */ + beq 58f + + cmplw 0,r5,r0 /* is this more than total to do? */ + blt 63f /* if not much to do */ + andi. r8,r0,3 /* get it word-aligned first */ + mtctr r8 + beq+ 61f +70: lbz r9,4(r4) /* do some bytes */ +71: stb r9,4(r6) + addi r4,r4,1 + addi r6,r6,1 + bdnz 70b +61: subf r5,r0,r5 + srwi. r0,r0,2 + mtctr r0 + beq 58f +72: lwzu r9,4(r4) /* do some words */ +73: stwu r9,4(r6) + bdnz 72b + + .section __ex_table,"a" + .align 2 + .long 70b,100f + .long 71b,101f + .long 72b,102f + .long 73b,103f + .text + +58: srwi. r0,r5,LG_CACHELINE_BYTES /* # complete cachelines */ + clrlwi r5,r5,32-LG_CACHELINE_BYTES + li r11,4 + beq 63f + +#ifdef CONFIG_8xx + /* Don't use prefetch on 8xx */ + mtctr r0 +53: COPY_16_BYTES_WITHEX(0) + bdnz 53b + +#else /* not CONFIG_8xx */ + /* Here we decide how far ahead to prefetch the source */ + li r3,4 + cmpwi r0,1 + li r7,0 + ble 114f + li r7,1 +#if MAX_COPY_PREFETCH > 1 + /* Heuristically, for large transfers we prefetch + MAX_COPY_PREFETCH cachelines ahead. For small transfers + we prefetch 1 cacheline ahead. */ + cmpwi r0,MAX_COPY_PREFETCH + ble 112f + li r7,MAX_COPY_PREFETCH +112: mtctr r7 +111: dcbt r3,r4 + addi r3,r3,CACHELINE_BYTES + bdnz 111b +#else + dcbt r3,r4 + addi r3,r3,CACHELINE_BYTES +#endif /* MAX_COPY_PREFETCH > 1 */ + +114: subf r8,r7,r0 + mr r0,r7 + mtctr r8 + +53: dcbt r3,r4 +54: dcbz r11,r6 + .section __ex_table,"a" + .align 2 + .long 54b,105f + .text +/* the main body of the cacheline loop */ + COPY_16_BYTES_WITHEX(0) +#if L1_CACHE_LINE_SIZE >= 32 + COPY_16_BYTES_WITHEX(1) +#if L1_CACHE_LINE_SIZE >= 64 + COPY_16_BYTES_WITHEX(2) + COPY_16_BYTES_WITHEX(3) +#if L1_CACHE_LINE_SIZE >= 128 + COPY_16_BYTES_WITHEX(4) + COPY_16_BYTES_WITHEX(5) + COPY_16_BYTES_WITHEX(6) + COPY_16_BYTES_WITHEX(7) +#endif +#endif +#endif + bdnz 53b + cmpwi r0,0 + li r3,4 + li r7,0 + bne 114b +#endif /* CONFIG_8xx */ + +63: srwi. r0,r5,2 + mtctr r0 + beq 64f +30: lwzu r0,4(r4) +31: stwu r0,4(r6) + bdnz 30b + +64: andi. r0,r5,3 + mtctr r0 + beq+ 65f +40: lbz r0,4(r4) +41: stb r0,4(r6) + addi r4,r4,1 + addi r6,r6,1 + bdnz 40b +65: li r3,0 + blr + +/* read fault, initial single-byte copy */ +100: li r9,0 + b 90f +/* write fault, initial single-byte copy */ +101: li r9,1 +90: subf r5,r8,r5 + li r3,0 + b 99f +/* read fault, initial word copy */ +102: li r9,0 + b 91f +/* write fault, initial word copy */ +103: li r9,1 +91: li r3,2 + b 99f + +/* + * this stuff handles faults in the cacheline loop and branches to either + * 104f (if in read part) or 105f (if in write part), after updating r5 + */ + COPY_16_BYTES_EXCODE(0) +#if L1_CACHE_LINE_SIZE >= 32 + COPY_16_BYTES_EXCODE(1) +#if L1_CACHE_LINE_SIZE >= 64 + COPY_16_BYTES_EXCODE(2) + COPY_16_BYTES_EXCODE(3) +#if L1_CACHE_LINE_SIZE >= 128 + COPY_16_BYTES_EXCODE(4) + COPY_16_BYTES_EXCODE(5) + COPY_16_BYTES_EXCODE(6) + COPY_16_BYTES_EXCODE(7) +#endif +#endif +#endif + +/* read fault in cacheline loop */ +104: li r9,0 + b 92f +/* fault on dcbz (effectively a write fault) */ +/* or write fault in cacheline loop */ +105: li r9,1 +92: li r3,LG_CACHELINE_BYTES + b 99f +/* read fault in final word loop */ +108: li r9,0 + b 93f +/* write fault in final word loop */ +109: li r9,1 +93: andi. r5,r5,3 + li r3,2 + b 99f +/* read fault in final byte loop */ +110: li r9,0 + b 94f +/* write fault in final byte loop */ +111: li r9,1 +94: li r5,0 + li r3,0 +/* + * At this stage the number of bytes not copied is + * r5 + (ctr << r3), and r9 is 0 for read or 1 for write. + */ +99: mfctr r0 + slw r3,r0,r3 + add. r3,r3,r5 + beq 120f /* shouldn't happen */ + cmpwi 0,r9,0 + bne 120f +/* for a read fault, first try to continue the copy one byte at a time */ + mtctr r3 +130: lbz r0,4(r4) +131: stb r0,4(r6) + addi r4,r4,1 + addi r6,r6,1 + bdnz 130b +/* then clear out the destination: r3 bytes starting at 4(r6) */ +132: mfctr r3 + srwi. r0,r3,2 + li r9,0 + mtctr r0 + beq 113f +112: stwu r9,4(r6) + bdnz 112b +113: andi. r0,r3,3 + mtctr r0 + beq 120f +114: stb r9,4(r6) + addi r6,r6,1 + bdnz 114b +120: blr + + .section __ex_table,"a" + .align 2 + .long 30b,108b + .long 31b,109b + .long 40b,110b + .long 41b,111b + .long 130b,132b + .long 131b,120b + .long 112b,120b + .long 114b,120b + .text + +_GLOBAL(__clear_user) + addi r6,r3,-4 + li r3,0 + li r5,0 + cmplwi 0,r4,4 + blt 7f + /* clear a single word */ +11: stwu r5,4(r6) + beqlr + /* clear word sized chunks */ + andi. r0,r6,3 + add r4,r0,r4 + subf r6,r0,r6 + srwi r0,r4,2 + andi. r4,r4,3 + mtctr r0 + bdz 7f +1: stwu r5,4(r6) + bdnz 1b + /* clear byte sized chunks */ +7: cmpwi 0,r4,0 + beqlr + mtctr r4 + addi r6,r6,3 +8: stbu r5,1(r6) + bdnz 8b + blr +90: mr r3,r4 + blr +91: mfctr r3 + slwi r3,r3,2 + add r3,r3,r4 + blr +92: mfctr r3 + blr + + .section __ex_table,"a" + .align 2 + .long 11b,90b + .long 1b,91b + .long 8b,92b + .text + +_GLOBAL(__strncpy_from_user) + addi r6,r3,-1 + addi r4,r4,-1 + cmpwi 0,r5,0 + beq 2f + mtctr r5 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r6) + bdnzf 2,1b /* dec ctr, branch if ctr != 0 && !cr0.eq */ + beq 3f +2: addi r6,r6,1 +3: subf r3,r3,r6 + blr +99: li r3,-EFAULT + blr + + .section __ex_table,"a" + .align 2 + .long 1b,99b + .text + +/* r3 = str, r4 = len (> 0), r5 = top (highest addr) */ +_GLOBAL(__strnlen_user) + addi r7,r3,-1 + subf r6,r7,r5 /* top+1 - str */ + cmplw 0,r4,r6 + bge 0f + mr r6,r4 +0: mtctr r6 /* ctr = min(len, top - str) */ +1: lbzu r0,1(r7) /* get next byte */ + cmpwi 0,r0,0 + bdnzf 2,1b /* loop if --ctr != 0 && byte != 0 */ + addi r7,r7,1 + subf r3,r3,r7 /* number of bytes we have looked at */ + beqlr /* return if we found a 0 byte */ + cmpw 0,r3,r4 /* did we look at all len bytes? */ + blt 99f /* if not, must have hit top */ + addi r3,r4,1 /* return len + 1 to indicate no null found */ + blr +99: li r3,0 /* bad address, return 0 */ + blr + + .section __ex_table,"a" + .align 2 + .long 1b,99b diff --git a/arch/ppc/math-emu/Makefile b/arch/ppc/math-emu/Makefile new file mode 100644 index 00000000000..754143e8936 --- /dev/null +++ b/arch/ppc/math-emu/Makefile @@ -0,0 +1,13 @@ + +obj-y := math.o fmr.o lfd.o stfd.o + +obj-$(CONFIG_MATH_EMULATION) += fabs.o fadd.o fadds.o fcmpo.o fcmpu.o \ + fctiw.o fctiwz.o fdiv.o fdivs.o \ + fmadd.o fmadds.o fmsub.o fmsubs.o \ + fmul.o fmuls.o fnabs.o fneg.o types.o \ + fnmadd.o fnmadds.o fnmsub.o fnmsubs.o \ + fres.o frsp.o frsqrte.o fsel.o lfs.o \ + fsqrt.o fsqrts.o fsub.o fsubs.o \ + mcrfs.o mffs.o mtfsb0.o mtfsb1.o \ + mtfsf.o mtfsfi.o stfiwx.o stfs.o \ + udivmodti4.o diff --git a/arch/ppc/math-emu/double.h b/arch/ppc/math-emu/double.h new file mode 100644 index 00000000000..ffba8b67f05 --- /dev/null +++ b/arch/ppc/math-emu/double.h @@ -0,0 +1,129 @@ +/* + * Definitions for IEEE Double Precision + */ + +#if _FP_W_TYPE_SIZE < 32 +#error "Here's a nickel kid. Go buy yourself a real computer." +#endif + +#if _FP_W_TYPE_SIZE < 64 +#define _FP_FRACTBITS_D (2 * _FP_W_TYPE_SIZE) +#else +#define _FP_FRACTBITS_D _FP_W_TYPE_SIZE +#endif + +#define _FP_FRACBITS_D 53 +#define _FP_FRACXBITS_D (_FP_FRACTBITS_D - _FP_FRACBITS_D) +#define _FP_WFRACBITS_D (_FP_WORKBITS + _FP_FRACBITS_D) +#define _FP_WFRACXBITS_D (_FP_FRACTBITS_D - _FP_WFRACBITS_D) +#define _FP_EXPBITS_D 11 +#define _FP_EXPBIAS_D 1023 +#define _FP_EXPMAX_D 2047 + +#define _FP_QNANBIT_D \ + ((_FP_W_TYPE)1 << ((_FP_FRACBITS_D-2) % _FP_W_TYPE_SIZE)) +#define _FP_IMPLBIT_D \ + ((_FP_W_TYPE)1 << ((_FP_FRACBITS_D-1) % _FP_W_TYPE_SIZE)) +#define _FP_OVERFLOW_D \ + ((_FP_W_TYPE)1 << (_FP_WFRACBITS_D % _FP_W_TYPE_SIZE)) + +#if _FP_W_TYPE_SIZE < 64 + +union _FP_UNION_D +{ + double flt; + struct { +#if __BYTE_ORDER == __BIG_ENDIAN + unsigned sign : 1; + unsigned exp : _FP_EXPBITS_D; + unsigned frac1 : _FP_FRACBITS_D - (_FP_IMPLBIT_D != 0) - _FP_W_TYPE_SIZE; + unsigned frac0 : _FP_W_TYPE_SIZE; +#else + unsigned frac0 : _FP_W_TYPE_SIZE; + unsigned frac1 : _FP_FRACBITS_D - (_FP_IMPLBIT_D != 0) - _FP_W_TYPE_SIZE; + unsigned exp : _FP_EXPBITS_D; + unsigned sign : 1; +#endif + } bits __attribute__((packed)); +}; + +#define FP_DECL_D(X) _FP_DECL(2,X) +#define FP_UNPACK_RAW_D(X,val) _FP_UNPACK_RAW_2(D,X,val) +#define FP_PACK_RAW_D(val,X) _FP_PACK_RAW_2(D,val,X) + +#define FP_UNPACK_D(X,val) \ + do { \ + _FP_UNPACK_RAW_2(D,X,val); \ + _FP_UNPACK_CANONICAL(D,2,X); \ + } while (0) + +#define FP_PACK_D(val,X) \ + do { \ + _FP_PACK_CANONICAL(D,2,X); \ + _FP_PACK_RAW_2(D,val,X); \ + } while (0) + +#define FP_NEG_D(R,X) _FP_NEG(D,2,R,X) +#define FP_ADD_D(R,X,Y) _FP_ADD(D,2,R,X,Y) +#define FP_SUB_D(R,X,Y) _FP_SUB(D,2,R,X,Y) +#define FP_MUL_D(R,X,Y) _FP_MUL(D,2,R,X,Y) +#define FP_DIV_D(R,X,Y) _FP_DIV(D,2,R,X,Y) +#define FP_SQRT_D(R,X) _FP_SQRT(D,2,R,X) + +#define FP_CMP_D(r,X,Y,un) _FP_CMP(D,2,r,X,Y,un) +#define FP_CMP_EQ_D(r,X,Y) _FP_CMP_EQ(D,2,r,X,Y) + +#define FP_TO_INT_D(r,X,rsz,rsg) _FP_TO_INT(D,2,r,X,rsz,rsg) +#define FP_FROM_INT_D(X,r,rs,rt) _FP_FROM_INT(D,2,X,r,rs,rt) + +#else + +union _FP_UNION_D +{ + double flt; + struct { +#if __BYTE_ORDER == __BIG_ENDIAN + unsigned sign : 1; + unsigned exp : _FP_EXPBITS_D; + unsigned long frac : _FP_FRACBITS_D - (_FP_IMPLBIT_D != 0); +#else + unsigned long frac : _FP_FRACBITS_D - (_FP_IMPLBIT_D != 0); + unsigned exp : _FP_EXPBITS_D; + unsigned sign : 1; +#endif + } bits __attribute__((packed)); +}; + +#define FP_DECL_D(X) _FP_DECL(1,X) +#define FP_UNPACK_RAW_D(X,val) _FP_UNPACK_RAW_1(D,X,val) +#define FP_PACK_RAW_D(val,X) _FP_PACK_RAW_1(D,val,X) + +#define FP_UNPACK_D(X,val) \ + do { \ + _FP_UNPACK_RAW_1(D,X,val); \ + _FP_UNPACK_CANONICAL(D,1,X); \ + } while (0) + +#define FP_PACK_D(val,X) \ + do { \ + _FP_PACK_CANONICAL(D,1,X); \ + _FP_PACK_RAW_1(D,val,X); \ + } while (0) + +#define FP_NEG_D(R,X) _FP_NEG(D,1,R,X) +#define FP_ADD_D(R,X,Y) _FP_ADD(D,1,R,X,Y) +#define FP_SUB_D(R,X,Y) _FP_SUB(D,1,R,X,Y) +#define FP_MUL_D(R,X,Y) _FP_MUL(D,1,R,X,Y) +#define FP_DIV_D(R,X,Y) _FP_DIV(D,1,R,X,Y) +#define FP_SQRT_D(R,X) _FP_SQRT(D,1,R,X) + +/* The implementation of _FP_MUL_D and _FP_DIV_D should be chosen by + the target machine. */ + +#define FP_CMP_D(r,X,Y,un) _FP_CMP(D,1,r,X,Y,un) +#define FP_CMP_EQ_D(r,X,Y) _FP_CMP_EQ(D,1,r,X,Y) + +#define FP_TO_INT_D(r,X,rsz,rsg) _FP_TO_INT(D,1,r,X,rsz,rsg) +#define FP_FROM_INT_D(X,r,rs,rt) _FP_FROM_INT(D,1,X,r,rs,rt) + +#endif /* W_TYPE_SIZE < 64 */ diff --git a/arch/ppc/math-emu/fabs.c b/arch/ppc/math-emu/fabs.c new file mode 100644 index 00000000000..41f0617f3d3 --- /dev/null +++ b/arch/ppc/math-emu/fabs.c @@ -0,0 +1,18 @@ +#include +#include +#include + +int +fabs(u32 *frD, u32 *frB) +{ + frD[0] = frB[0] & 0x7fffffff; + frD[1] = frB[1]; + +#ifdef DEBUG + printk("%s: D %p, B %p: ", __FUNCTION__, frD, frB); + dump_double(frD); + printk("\n"); +#endif + + return 0; +} diff --git a/arch/ppc/math-emu/fadd.c b/arch/ppc/math-emu/fadd.c new file mode 100644 index 00000000000..fc8836488b6 --- /dev/null +++ b/arch/ppc/math-emu/fadd.c @@ -0,0 +1,38 @@ +#include +#include +#include + +#include "soft-fp.h" +#include "double.h" + +int +fadd(void *frD, void *frA, void *frB) +{ + FP_DECL_D(A); + FP_DECL_D(B); + FP_DECL_D(R); + int ret = 0; + +#ifdef DEBUG + printk("%s: %p %p %p\n", __FUNCTION__, frD, frA, frB); +#endif + + __FP_UNPACK_D(A, frA); + __FP_UNPACK_D(B, frB); + +#ifdef DEBUG + printk("A: %ld %lu %lu %ld (%ld)\n", A_s, A_f1, A_f0, A_e, A_c); + printk("B: %ld %lu %lu %ld (%ld)\n", B_s, B_f1, B_f0, B_e, B_c); +#endif + + if (A_s != B_s && A_c == FP_CLS_INF && B_c == FP_CLS_INF) + ret |= EFLAG_VXISI; + + FP_ADD_D(R, A, B); + +#ifdef DEBUG + printk("D: %ld %lu %lu %ld (%ld)\n", R_s, R_f1, R_f0, R_e, R_c); +#endif + + return (ret | __FP_PACK_D(frD, R)); +} diff --git a/arch/ppc/math-emu/fadds.c b/arch/ppc/math-emu/fadds.c new file mode 100644 index 00000000000..93025b6c8f3 --- /dev/null +++ b/arch/ppc/math-emu/fadds.c @@ -0,0 +1,39 @@ +#include +#include +#include + +#include "soft-fp.h" +#include "double.h" +#include "single.h" + +int +fadds(void *frD, void *frA, void *frB) +{ + FP_DECL_D(A); + FP_DECL_D(B); + FP_DECL_D(R); + int ret = 0; + +#ifdef DEBUG + printk("%s: %p %p %p\n", __FUNCTION__, frD, frA, frB); +#endif + + __FP_UNPACK_D(A, frA); + __FP_UNPACK_D(B, frB); + +#ifdef DEBUG + printk("A: %ld %lu %lu %ld (%ld)\n", A_s, A_f1, A_f0, A_e, A_c); + printk("B: %ld %lu %lu %ld (%ld)\n", B_s, B_f1, B_f0, B_e, B_c); +#endif + + if (A_s != B_s && A_c == FP_CLS_INF && B_c == FP_CLS_INF) + ret |= EFLAG_VXISI; + + FP_ADD_D(R, A, B); + +#ifdef DEBUG + printk("D: %ld %lu %lu %ld (%ld)\n", R_s, R_f1, R_f0, R_e, R_c); +#endif + + return (ret | __FP_PACK_DS(frD, R)); +} diff --git a/arch/ppc/math-emu/fcmpo.c b/arch/ppc/math-emu/fcmpo.c new file mode 100644 index 00000000000..4efac394b4c --- /dev/null +++ b/arch/ppc/math-emu/fcmpo.c @@ -0,0 +1,46 @@ +#include +#include +#include + +#include "soft-fp.h" +#include "double.h" + +int +fcmpo(u32 *ccr, int crfD, void *frA, void *frB) +{ + FP_DECL_D(A); + FP_DECL_D(B); + int code[4] = { (1 << 3), (1 << 1), (1 << 2), (1 << 0) }; + long cmp; + int ret = 0; + +#ifdef DEBUG + printk("%s: %p (%08x) %d %p %p\n", __FUNCTION__, ccr, *ccr, crfD, frA, frB); +#endif + + __FP_UNPACK_D(A, frA); + __FP_UNPACK_D(B, frB); + +#ifdef DEBUG + printk("A: %ld %lu %lu %ld (%ld)\n", A_s, A_f1, A_f0, A_e, A_c); + printk("B: %ld %lu %lu %ld (%ld)\n", B_s, B_f1, B_f0, B_e, B_c); +#endif + + if (A_c == FP_CLS_NAN || B_c == FP_CLS_NAN) + ret |= EFLAG_VXVC; + + FP_CMP_D(cmp, A, B, 2); + cmp = code[(cmp + 1) & 3]; + + __FPU_FPSCR &= ~(0x1f000); + __FPU_FPSCR |= (cmp << 12); + + *ccr &= ~(15 << ((7 - crfD) << 2)); + *ccr |= (cmp << ((7 - crfD) << 2)); + +#ifdef DEBUG + printk("CR: %08x\n", *ccr); +#endif + + return ret; +} diff --git a/arch/ppc/math-emu/fcmpu.c b/arch/ppc/math-emu/fcmpu.c new file mode 100644 index 00000000000..b7e33176e61 --- /dev/null +++ b/arch/ppc/math-emu/fcmpu.c @@ -0,0 +1,42 @@ +#include +#include +#include + +#include "soft-fp.h" +#include "double.h" + +int +fcmpu(u32 *ccr, int crfD, void *frA, void *frB) +{ + FP_DECL_D(A); + FP_DECL_D(B); + int code[4] = { (1 << 3), (1 << 1), (1 << 2), (1 << 0) }; + long cmp; + +#ifdef DEBUG + printk("%s: %p (%08x) %d %p %p\n", __FUNCTION__, ccr, *ccr, crfD, frA, frB); +#endif + + __FP_UNPACK_D(A, frA); + __FP_UNPACK_D(B, frB); + +#ifdef DEBUG + printk("A: %ld %lu %lu %ld (%ld)\n", A_s, A_f1, A_f0, A_e, A_c); + printk("B: %ld %lu %lu %ld (%ld)\n", B_s, B_f1, B_f0, B_e, B_c); +#endif + + FP_CMP_D(cmp, A, B, 2); + cmp = code[(cmp + 1) & 3]; + + __FPU_FPSCR &= ~(0x1f000); + __FPU_FPSCR |= (cmp << 12); + + *ccr &= ~(15 << ((7 - crfD) << 2)); + *ccr |= (cmp << ((7 - crfD) << 2)); + +#ifdef DEBUG + printk("CR: %08x\n", *ccr); +#endif + + return 0; +} diff --git a/arch/ppc/math-emu/fctiw.c b/arch/ppc/math-emu/fctiw.c new file mode 100644 index 00000000000..3b3c98b840c --- /dev/null +++ b/arch/ppc/math-emu/fctiw.c @@ -0,0 +1,25 @@ +#include +#include +#include + +#include "soft-fp.h" +#include "double.h" + +int +fctiw(u32 *frD, void *frB) +{ + FP_DECL_D(B); + unsigned int r; + + __FP_UNPACK_D(B, frB); + FP_TO_INT_D(r, B, 32, 1); + frD[1] = r; + +#ifdef DEBUG + printk("%s: D %p, B %p: ", __FUNCTION__, frD, frB); + dump_double(frD); + printk("\n"); +#endif + + return 0; +} diff --git a/arch/ppc/math-emu/fctiwz.c b/arch/ppc/math-emu/fctiwz.c new file mode 100644 index 00000000000..7717eb6fcfb --- /dev/null +++ b/arch/ppc/math-emu/fctiwz.c @@ -0,0 +1,32 @@ +#include +#include +#include + +#include "soft-fp.h" +#include "double.h" + +int +fctiwz(u32 *frD, void *frB) +{ + FP_DECL_D(B); + u32 fpscr; + unsigned int r; + + fpscr = __FPU_FPSCR; + __FPU_FPSCR &= ~(3); + __FPU_FPSCR |= FP_RND_ZERO; + + __FP_UNPACK_D(B, frB); + FP_TO_INT_D(r, B, 32, 1); + frD[1] = r; + + __FPU_FPSCR = fpscr; + +#ifdef DEBUG + printk("%s: D %p, B %p: ", __FUNCTION__, frD, frB); + dump_double(frD); + printk("\n"); +#endif + + return 0; +} diff --git a/arch/ppc/math-emu/fdiv.c b/arch/ppc/math-emu/fdiv.c new file mode 100644 index 00000000000..f2fba825b2d --- /dev/null +++ b/arch/ppc/math-emu/fdiv.c @@ -0,0 +1,53 @@ +#include +#include +#include + +#include "soft-fp.h" +#include "double.h" + +int +fdiv(void *frD, void *frA, void *frB) +{ + FP_DECL_D(A); + FP_DECL_D(B); + FP_DECL_D(R); + int ret = 0; + +#ifdef DEBUG + printk("%s: %p %p %p\n", __FUNCTION__, frD, frA, frB); +#endif + + __FP_UNPACK_D(A, frA); + __FP_UNPACK_D(B, frB); + +#ifdef DEBUG + printk("A: %ld %lu %lu %ld (%ld)\n", A_s, A_f1, A_f0, A_e, A_c); + printk("B: %ld %lu %lu %ld (%ld)\n", B_s, B_f1, B_f0, B_e, B_c); +#endif + + if (A_c == FP_CLS_ZERO && B_c == FP_CLS_ZERO) { + ret |= EFLAG_VXZDZ; +#ifdef DEBUG + printk("%s: FPSCR_VXZDZ raised\n", __FUNCTION__); +#endif + } + if (A_c == FP_CLS_INF && B_c == FP_CLS_INF) { + ret |= EFLAG_VXIDI; +#ifdef DEBUG + printk("%s: FPSCR_VXIDI raised\n", __FUNCTION__); +#endif + } + + if (B_c == FP_CLS_ZERO && A_c != FP_CLS_ZERO) { + ret |= EFLAG_DIVZERO; + if (__FPU_TRAP_P(EFLAG_DIVZERO)) + return ret; + } + FP_DIV_D(R, A, B); + +#ifdef DEBUG + printk("D: %ld %lu %lu %ld (%ld)\n", R_s, R_f1, R_f0, R_e, R_c); +#endif + + return (ret | __FP_PACK_D(frD, R)); +} diff --git a/arch/ppc/math-emu/fdivs.c b/arch/ppc/math-emu/fdivs.c new file mode 100644 index 00000000000..b971196e317 --- /dev/null +++ b/arch/ppc/math-emu/fdivs.c @@ -0,0 +1,55 @@ +#include +#include +#include + +#include "soft-fp.h" +#include "double.h" +#include "single.h" + +int +fdivs(void *frD, void *frA, void *frB) +{ + FP_DECL_D(A); + FP_DECL_D(B); + FP_DECL_D(R); + int ret = 0; + +#ifdef DEBUG + printk("%s: %p %p %p\n", __FUNCTION__, frD, frA, frB); +#endif + + __FP_UNPACK_D(A, frA); + __FP_UNPACK_D(B, frB); + +#ifdef DEBUG + printk("A: %ld %lu %lu %ld (%ld)\n", A_s, A_f1, A_f0, A_e, A_c); + printk("B: %ld %lu %lu %ld (%ld)\n", B_s, B_f1, B_f0, B_e, B_c); +#endif + + if (A_c == FP_CLS_ZERO && B_c == FP_CLS_ZERO) { + ret |= EFLAG_VXZDZ; +#ifdef DEBUG + printk("%s: FPSCR_VXZDZ raised\n", __FUNCTION__); +#endif + } + if (A_c == FP_CLS_INF && B_c == FP_CLS_INF) { + ret |= EFLAG_VXIDI; +#ifdef DEBUG + printk("%s: FPSCR_VXIDI raised\n", __FUNCTION__); +#endif + } + + if (B_c == FP_CLS_ZERO && A_c != FP_CLS_ZERO) { + ret |= EFLAG_DIVZERO; + if (__FPU_TRAP_P(EFLAG_DIVZERO)) + return ret; + } + + FP_DIV_D(R, A, B); + +#ifdef DEBUG + printk("D: %ld %lu %lu %ld (%ld)\n", R_s, R_f1, R_f0, R_e, R_c); +#endif + + return (ret | __FP_PACK_DS(frD, R)); +} diff --git a/arch/ppc/math-emu/fmadd.c b/arch/ppc/math-emu/fmadd.c new file mode 100644 index 00000000000..0a1dbce793e --- /dev/null +++ b/arch/ppc/math-emu/fmadd.c @@ -0,0 +1,48 @@ +#include +#include +#include + +#include "soft-fp.h" +#include "double.h" + +int +fmadd(void *frD, void *frA, void *frB, void *frC) +{ + FP_DECL_D(R); + FP_DECL_D(A); + FP_DECL_D(B); + FP_DECL_D(C); + FP_DECL_D(T); + int ret = 0; + +#ifdef DEBUG + printk("%s: %p %p %p %p\n", __FUNCTION__, frD, frA, frB, frC); +#endif + + __FP_UNPACK_D(A, frA); + __FP_UNPACK_D(B, frB); + __FP_UNPACK_D(C, frC); + +#ifdef DEBUG + printk("A: %ld %lu %lu %ld (%ld)\n", A_s, A_f1, A_f0, A_e, A_c); + printk("B: %ld %lu %lu %ld (%ld)\n", B_s, B_f1, B_f0, B_e, B_c); + printk("C: %ld %lu %lu %ld (%ld)\n", C_s, C_f1, C_f0, C_e, C_c); +#endif + + if ((A_c == FP_CLS_INF && C_c == FP_CLS_ZERO) || + (A_c == FP_CLS_ZERO && C_c == FP_CLS_INF)) + ret |= EFLAG_VXIMZ; + + FP_MUL_D(T, A, C); + + if (T_s != B_s && T_c == FP_CLS_INF && B_c == FP_CLS_INF) + ret |= EFLAG_VXISI; + + FP_ADD_D(R, T, B); + +#ifdef DEBUG + printk("D: %ld %lu %lu %ld (%ld)\n", R_s, R_f1, R_f0, R_e, R_c); +#endif + + return (ret | __FP_PACK_D(frD, R)); +} diff --git a/arch/ppc/math-emu/fmadds.c b/arch/ppc/math-emu/fmadds.c new file mode 100644 index 00000000000..0f70bba9445 --- /dev/null +++ b/arch/ppc/math-emu/fmadds.c @@ -0,0 +1,49 @@ +#include +#include +#include + +#include "soft-fp.h" +#include "double.h" +#include "single.h" + +int +fmadds(void *frD, void *frA, void *frB, void *frC) +{ + FP_DECL_D(R); + FP_DECL_D(A); + FP_DECL_D(B); + FP_DECL_D(C); + FP_DECL_D(T); + int ret = 0; + +#ifdef DEBUG + printk("%s: %p %p %p %p\n", __FUNCTION__, frD, frA, frB, frC); +#endif + + __FP_UNPACK_D(A, frA); + __FP_UNPACK_D(B, frB); + __FP_UNPACK_D(C, frC); + +#ifdef DEBUG + printk("A: %ld %lu %lu %ld (%ld)\n", A_s, A_f1, A_f0, A_e, A_c); + printk("B: %ld %lu %lu %ld (%ld)\n", B_s, B_f1, B_f0, B_e, B_c); + printk("C: %ld %lu %lu %ld (%ld)\n", C_s, C_f1, C_f0, C_e, C_c); +#endif + + if ((A_c == FP_CLS_INF && C_c == FP_CLS_ZERO) || + (A_c == FP_CLS_ZERO && C_c == FP_CLS_INF)) + ret |= EFLAG_VXIMZ; + + FP_MUL_D(T, A, C); + + if (T_s != B_s && T_c == FP_CLS_INF && B_c == FP_CLS_INF) + ret |= EFLAG_VXISI; + + FP_ADD_D(R, T, B); + +#ifdef DEBUG + printk("D: %ld %lu %lu %ld (%ld)\n", R_s, R_f1, R_f0, R_e, R_c); +#endif + + return (ret | __FP_PACK_DS(frD, R)); +} diff --git a/arch/ppc/math-emu/fmr.c b/arch/ppc/math-emu/fmr.c new file mode 100644 index 00000000000..28df700c0c7 --- /dev/null +++ b/arch/ppc/math-emu/fmr.c @@ -0,0 +1,18 @@ +#include +#include +#include + +int +fmr(u32 *frD, u32 *frB) +{ + frD[0] = frB[0]; + frD[1] = frB[1]; + +#ifdef DEBUG + printk("%s: D %p, B %p: ", __FUNCTION__, frD, frB); + dump_double(frD); + printk("\n"); +#endif + + return 0; +} diff --git a/arch/ppc/math-emu/fmsub.c b/arch/ppc/math-emu/fmsub.c new file mode 100644 index 00000000000..203fd48a6fe --- /dev/null +++ b/arch/ppc/math-emu/fmsub.c @@ -0,0 +1,51 @@ +#include +#include +#include + +#include "soft-fp.h" +#include "double.h" + +int +fmsub(void *frD, void *frA, void *frB, void *frC) +{ + FP_DECL_D(R); + FP_DECL_D(A); + FP_DECL_D(B); + FP_DECL_D(C); + FP_DECL_D(T); + int ret = 0; + +#ifdef DEBUG + printk("%s: %p %p %p %p\n", __FUNCTION__, frD, frA, frB, frC); +#endif + + __FP_UNPACK_D(A, frA); + __FP_UNPACK_D(B, frB); + __FP_UNPACK_D(C, frC); + +#ifdef DEBUG + printk("A: %ld %lu %lu %ld (%ld)\n", A_s, A_f1, A_f0, A_e, A_c); + printk("B: %ld %lu %lu %ld (%ld)\n", B_s, B_f1, B_f0, B_e, B_c); + printk("C: %ld %lu %lu %ld (%ld)\n", C_s, C_f1, C_f0, C_e, C_c); +#endif + + if ((A_c == FP_CLS_INF && C_c == FP_CLS_ZERO) || + (A_c == FP_CLS_ZERO && C_c == FP_CLS_INF)) + ret |= EFLAG_VXIMZ; + + FP_MUL_D(T, A, C); + + if (B_c != FP_CLS_NAN) + B_s ^= 1; + + if (T_s != B_s && T_c == FP_CLS_INF && B_c == FP_CLS_INF) + ret |= EFLAG_VXISI; + + FP_ADD_D(R, T, B); + +#ifdef DEBUG + printk("D: %ld %lu %lu %ld (%ld)\n", R_s, R_f1, R_f0, R_e, R_c); +#endif + + return (ret | __FP_PACK_D(frD, R)); +} diff --git a/arch/ppc/math-emu/fmsubs.c b/arch/ppc/math-emu/fmsubs.c new file mode 100644 index 00000000000..8ce68624c18 --- /dev/null +++ b/arch/ppc/math-emu/fmsubs.c @@ -0,0 +1,52 @@ +#include +#include +#include + +#include "soft-fp.h" +#include "double.h" +#include "single.h" + +int +fmsubs(void *frD, void *frA, void *frB, void *frC) +{ + FP_DECL_D(R); + FP_DECL_D(A); + FP_DECL_D(B); + FP_DECL_D(C); + FP_DECL_D(T); + int ret = 0; + +#ifdef DEBUG + printk("%s: %p %p %p %p\n", __FUNCTION__, frD, frA, frB, frC); +#endif + + __FP_UNPACK_D(A, frA); + __FP_UNPACK_D(B, frB); + __FP_UNPACK_D(C, frC); + +#ifdef DEBUG + printk("A: %ld %lu %lu %ld (%ld)\n", A_s, A_f1, A_f0, A_e, A_c); + printk("B: %ld %lu %lu %ld (%ld)\n", B_s, B_f1, B_f0, B_e, B_c); + printk("C: %ld %lu %lu %ld (%ld)\n", C_s, C_f1, C_f0, C_e, C_c); +#endif + + if ((A_c == FP_CLS_INF && C_c == FP_CLS_ZERO) || + (A_c == FP_CLS_ZERO && C_c == FP_CLS_INF)) + ret |= EFLAG_VXIMZ; + + FP_MUL_D(T, A, C); + + if (B_c != FP_CLS_NAN) + B_s ^= 1; + + if (T_s != B_s && T_c == FP_CLS_INF && B_c == FP_CLS_INF) + ret |= EFLAG_VXISI; + + FP_ADD_D(R, T, B); + +#ifdef DEBUG + printk("D: %ld %lu %lu %ld (%ld)\n", R_s, R_f1, R_f0, R_e, R_c); +#endif + + return (ret | __FP_PACK_DS(frD, R)); +} diff --git a/arch/ppc/math-emu/fmul.c b/arch/ppc/math-emu/fmul.c new file mode 100644 index 00000000000..66c7e79aae2 --- /dev/null +++ b/arch/ppc/math-emu/fmul.c @@ -0,0 +1,42 @@ +#include +#include +#include + +#include "soft-fp.h" +#include "double.h" + +int +fmul(void *frD, void *frA, void *frB) +{ + FP_DECL_D(A); + FP_DECL_D(B); + FP_DECL_D(R); + int ret = 0; + +#ifdef DEBUG + printk("%s: %p %p %p\n", __FUNCTION__, frD, frA, frB); +#endif + + __FP_UNPACK_D(A, frA); + __FP_UNPACK_D(B, frB); + +#ifdef DEBUG + printk("A: %ld %lu %lu %ld (%ld) [%08lx.%08lx %lx]\n", + A_s, A_f1, A_f0, A_e, A_c, A_f1, A_f0, A_e + 1023); + printk("B: %ld %lu %lu %ld (%ld) [%08lx.%08lx %lx]\n", + B_s, B_f1, B_f0, B_e, B_c, B_f1, B_f0, B_e + 1023); +#endif + + if ((A_c == FP_CLS_INF && B_c == FP_CLS_ZERO) || + (A_c == FP_CLS_ZERO && B_c == FP_CLS_INF)) + ret |= EFLAG_VXIMZ; + + FP_MUL_D(R, A, B); + +#ifdef DEBUG + printk("D: %ld %lu %lu %ld (%ld) [%08lx.%08lx %lx]\n", + R_s, R_f1, R_f0, R_e, R_c, R_f1, R_f0, R_e + 1023); +#endif + + return (ret | __FP_PACK_D(frD, R)); +} diff --git a/arch/ppc/math-emu/fmuls.c b/arch/ppc/math-emu/fmuls.c new file mode 100644 index 00000000000..26bc4278271 --- /dev/null +++ b/arch/ppc/math-emu/fmuls.c @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "soft-fp.h" +#include "double.h" +#include "single.h" + +int +fmuls(void *frD, void *frA, void *frB) +{ + FP_DECL_D(A); + FP_DECL_D(B); + FP_DECL_D(R); + int ret = 0; + +#ifdef DEBUG + printk("%s: %p %p %p\n", __FUNCTION__, frD, frA, frB); +#endif + + __FP_UNPACK_D(A, frA); + __FP_UNPACK_D(B, frB); + +#ifdef DEBUG + printk("A: %ld %lu %lu %ld (%ld) [%08lx.%08lx %lx]\n", + A_s, A_f1, A_f0, A_e, A_c, A_f1, A_f0, A_e + 1023); + printk("B: %ld %lu %lu %ld (%ld) [%08lx.%08lx %lx]\n", + B_s, B_f1, B_f0, B_e, B_c, B_f1, B_f0, B_e + 1023); +#endif + + if ((A_c == FP_CLS_INF && B_c == FP_CLS_ZERO) || + (A_c == FP_CLS_ZERO && B_c == FP_CLS_INF)) + ret |= EFLAG_VXIMZ; + + FP_MUL_D(R, A, B); + +#ifdef DEBUG + printk("D: %ld %lu %lu %ld (%ld) [%08lx.%08lx %lx]\n", + R_s, R_f1, R_f0, R_e, R_c, R_f1, R_f0, R_e + 1023); +#endif + + return (ret | __FP_PACK_DS(frD, R)); +} diff --git a/arch/ppc/math-emu/fnabs.c b/arch/ppc/math-emu/fnabs.c new file mode 100644 index 00000000000..c6b913d179e --- /dev/null +++ b/arch/ppc/math-emu/fnabs.c @@ -0,0 +1,18 @@ +#include +#include +#include + +int +fnabs(u32 *frD, u32 *frB) +{ + frD[0] = frB[0] | 0x80000000; + frD[1] = frB[1]; + +#ifdef DEBUG + printk("%s: D %p, B %p: ", __FUNCTION__, frD, frB); + dump_double(frD); + printk("\n"); +#endif + + return 0; +} diff --git a/arch/ppc/math-emu/fneg.c b/arch/ppc/math-emu/fneg.c new file mode 100644 index 00000000000..fe9a98deff6 --- /dev/null +++ b/arch/ppc/math-emu/fneg.c @@ -0,0 +1,18 @@ +#include +#include +#include + +int +fneg(u32 *frD, u32 *frB) +{ + frD[0] = frB[0] ^ 0x80000000; + frD[1] = frB[1]; + +#ifdef DEBUG + printk("%s: D %p, B %p: ", __FUNCTION__, frD, frB); + dump_double(frD); + printk("\n"); +#endif + + return 0; +} diff --git a/arch/ppc/math-emu/fnmadd.c b/arch/ppc/math-emu/fnmadd.c new file mode 100644 index 00000000000..7f312276d92 --- /dev/null +++ b/arch/ppc/math-emu/fnmadd.c @@ -0,0 +1,51 @@ +#include +#include +#include + +#include "soft-fp.h" +#include "double.h" + +int +fnmadd(void *frD, void *frA, void *frB, void *frC) +{ + FP_DECL_D(R); + FP_DECL_D(A); + FP_DECL_D(B); + FP_DECL_D(C); + FP_DECL_D(T); + int ret = 0; + +#ifdef DEBUG + printk("%s: %p %p %p %p\n", __FUNCTION__, frD, frA, frB, frC); +#endif + + __FP_UNPACK_D(A, frA); + __FP_UNPACK_D(B, frB); + __FP_UNPACK_D(C, frC); + +#ifdef DEBUG + printk("A: %ld %lu %lu %ld (%ld)\n", A_s, A_f1, A_f0, A_e, A_c); + printk("B: %ld %lu %lu %ld (%ld)\n", B_s, B_f1, B_f0, B_e, B_c); + printk("C: %ld %lu %lu %ld (%ld)\n", C_s, C_f1, C_f0, C_e, C_c); +#endif + + if ((A_c == FP_CLS_INF && C_c == FP_CLS_ZERO) || + (A_c == FP_CLS_ZERO && C_c == FP_CLS_INF)) + ret |= EFLAG_VXIMZ; + + FP_MUL_D(T, A, C); + + if (T_s != B_s && T_c == FP_CLS_INF && B_c == FP_CLS_INF) + ret |= EFLAG_VXISI; + + FP_ADD_D(R, T, B); + + if (R_c != FP_CLS_NAN) + R_s ^= 1; + +#ifdef DEBUG + printk("D: %ld %lu %lu %ld (%ld)\n", R_s, R_f1, R_f0, R_e, R_c); +#endif + + return (ret | __FP_PACK_D(frD, R)); +} diff --git a/arch/ppc/math-emu/fnmadds.c b/arch/ppc/math-emu/fnmadds.c new file mode 100644 index 00000000000..65454c9c70b --- /dev/null +++ b/arch/ppc/math-emu/fnmadds.c @@ -0,0 +1,52 @@ +#include +#include +#include + +#include "soft-fp.h" +#include "double.h" +#include "single.h" + +int +fnmadds(void *frD, void *frA, void *frB, void *frC) +{ + FP_DECL_D(R); + FP_DECL_D(A); + FP_DECL_D(B); + FP_DECL_D(C); + FP_DECL_D(T); + int ret = 0; + +#ifdef DEBUG + printk("%s: %p %p %p %p\n", __FUNCTION__, frD, frA, frB, frC); +#endif + + __FP_UNPACK_D(A, frA); + __FP_UNPACK_D(B, frB); + __FP_UNPACK_D(C, frC); + +#ifdef DEBUG + printk("A: %ld %lu %lu %ld (%ld)\n", A_s, A_f1, A_f0, A_e, A_c); + printk("B: %ld %lu %lu %ld (%ld)\n", B_s, B_f1, B_f0, B_e, B_c); + printk("C: %ld %lu %lu %ld (%ld)\n", C_s, C_f1, C_f0, C_e, C_c); +#endif + + if ((A_c == FP_CLS_INF && C_c == FP_CLS_ZERO) || + (A_c == FP_CLS_ZERO && C_c == FP_CLS_INF)) + ret |= EFLAG_VXIMZ; + + FP_MUL_D(T, A, C); + + if (T_s != B_s && T_c == FP_CLS_INF && B_c == FP_CLS_INF) + ret |= EFLAG_VXISI; + + FP_ADD_D(R, T, B); + + if (R_c != FP_CLS_NAN) + R_s ^= 1; + +#ifdef DEBUG + printk("D: %ld %lu %lu %ld (%ld)\n", R_s, R_f1, R_f0, R_e, R_c); +#endif + + return (ret | __FP_PACK_DS(frD, R)); +} diff --git a/arch/ppc/math-emu/fnmsub.c b/arch/ppc/math-emu/fnmsub.c new file mode 100644 index 00000000000..f1ca7482b5f --- /dev/null +++ b/arch/ppc/math-emu/fnmsub.c @@ -0,0 +1,54 @@ +#include +#include +#include + +#include "soft-fp.h" +#include "double.h" + +int +fnmsub(void *frD, void *frA, void *frB, void *frC) +{ + FP_DECL_D(R); + FP_DECL_D(A); + FP_DECL_D(B); + FP_DECL_D(C); + FP_DECL_D(T); + int ret = 0; + +#ifdef DEBUG + printk("%s: %p %p %p %p\n", __FUNCTION__, frD, frA, frB, frC); +#endif + + __FP_UNPACK_D(A, frA); + __FP_UNPACK_D(B, frB); + __FP_UNPACK_D(C, frC); + +#ifdef DEBUG + printk("A: %ld %lu %lu %ld (%ld)\n", A_s, A_f1, A_f0, A_e, A_c); + printk("B: %ld %lu %lu %ld (%ld)\n", B_s, B_f1, B_f0, B_e, B_c); + printk("C: %ld %lu %lu %ld (%ld)\n", C_s, C_f1, C_f0, C_e, C_c); +#endif + + if ((A_c == FP_CLS_INF && C_c == FP_CLS_ZERO) || + (A_c == FP_CLS_ZERO && C_c == FP_CLS_INF)) + ret |= EFLAG_VXIMZ; + + FP_MUL_D(T, A, C); + + if (B_c != FP_CLS_NAN) + B_s ^= 1; + + if (T_s != B_s && T_c == FP_CLS_INF && B_c == FP_CLS_INF) + ret |= EFLAG_VXISI; + + FP_ADD_D(R, T, B); + + if (R_c != FP_CLS_NAN) + R_s ^= 1; + +#ifdef DEBUG + printk("D: %ld %lu %lu %ld (%ld)\n", R_s, R_f1, R_f0, R_e, R_c); +#endif + + return (ret | __FP_PACK_D(frD, R)); +} diff --git a/arch/ppc/math-emu/fnmsubs.c b/arch/ppc/math-emu/fnmsubs.c new file mode 100644 index 00000000000..5c9a09a87dc --- /dev/null +++ b/arch/ppc/math-emu/fnmsubs.c @@ -0,0 +1,55 @@ +#include +#include +#include + +#include "soft-fp.h" +#include "double.h" +#include "single.h" + +int +fnmsubs(void *frD, void *frA, void *frB, void *frC) +{ + FP_DECL_D(R); + FP_DECL_D(A); + FP_DECL_D(B); + FP_DECL_D(C); + FP_DECL_D(T); + int ret = 0; + +#ifdef DEBUG + printk("%s: %p %p %p %p\n", __FUNCTION__, frD, frA, frB, frC); +#endif + + __FP_UNPACK_D(A, frA); + __FP_UNPACK_D(B, frB); + __FP_UNPACK_D(C, frC); + +#ifdef DEBUG + printk("A: %ld %lu %lu %ld (%ld)\n", A_s, A_f1, A_f0, A_e, A_c); + printk("B: %ld %lu %lu %ld (%ld)\n", B_s, B_f1, B_f0, B_e, B_c); + printk("C: %ld %lu %lu %ld (%ld)\n", C_s, C_f1, C_f0, C_e, C_c); +#endif + + if ((A_c == FP_CLS_INF && C_c == FP_CLS_ZERO) || + (A_c == FP_CLS_ZERO && C_c == FP_CLS_INF)) + ret |= EFLAG_VXIMZ; + + FP_MUL_D(T, A, C); + + if (B_c != FP_CLS_NAN) + B_s ^= 1; + + if (T_s != B_s && T_c == FP_CLS_INF && B_c == FP_CLS_INF) + ret |= EFLAG_VXISI; + + FP_ADD_D(R, T, B); + + if (R_c != FP_CLS_NAN) + R_s ^= 1; + +#ifdef DEBUG + printk("D: %ld %lu %lu %ld (%ld)\n", R_s, R_f1, R_f0, R_e, R_c); +#endif + + return (ret | __FP_PACK_DS(frD, R)); +} diff --git a/arch/ppc/math-emu/fres.c b/arch/ppc/math-emu/fres.c new file mode 100644 index 00000000000..ec11e46d20a --- /dev/null +++ b/arch/ppc/math-emu/fres.c @@ -0,0 +1,12 @@ +#include +#include +#include + +int +fres(void *frD, void *frB) +{ +#ifdef DEBUG + printk("%s: %p %p\n", __FUNCTION__, frD, frB); +#endif + return -ENOSYS; +} diff --git a/arch/ppc/math-emu/frsp.c b/arch/ppc/math-emu/frsp.c new file mode 100644 index 00000000000..d879b2a3d0c --- /dev/null +++ b/arch/ppc/math-emu/frsp.c @@ -0,0 +1,25 @@ +#include +#include +#include + +#include "soft-fp.h" +#include "double.h" +#include "single.h" + +int +frsp(void *frD, void *frB) +{ + FP_DECL_D(B); + +#ifdef DEBUG + printk("%s: D %p, B %p\n", __FUNCTION__, frD, frB); +#endif + + __FP_UNPACK_D(B, frB); + +#ifdef DEBUG + printk("B: %ld %lu %lu %ld (%ld)\n", B_s, B_f1, B_f0, B_e, B_c); +#endif + + return __FP_PACK_DS(frD, B); +} diff --git a/arch/ppc/math-emu/frsqrte.c b/arch/ppc/math-emu/frsqrte.c new file mode 100644 index 00000000000..a11ae182985 --- /dev/null +++ b/arch/ppc/math-emu/frsqrte.c @@ -0,0 +1,12 @@ +#include +#include +#include + +int +frsqrte(void *frD, void *frB) +{ +#ifdef DEBUG + printk("%s: %p %p\n", __FUNCTION__, frD, frB); +#endif + return 0; +} diff --git a/arch/ppc/math-emu/fsel.c b/arch/ppc/math-emu/fsel.c new file mode 100644 index 00000000000..e36e6e72819 --- /dev/null +++ b/arch/ppc/math-emu/fsel.c @@ -0,0 +1,38 @@ +#include +#include +#include + +#include "soft-fp.h" +#include "double.h" + +int +fsel(u32 *frD, void *frA, u32 *frB, u32 *frC) +{ + FP_DECL_D(A); + +#ifdef DEBUG + printk("%s: %p %p %p %p\n", __FUNCTION__, frD, frA, frB, frC); +#endif + + __FP_UNPACK_D(A, frA); + +#ifdef DEBUG + printk("A: %ld %lu %lu %ld (%ld)\n", A_s, A_f1, A_f0, A_e, A_c); + printk("B: %08x %08x\n", frB[0], frB[1]); + printk("C: %08x %08x\n", frC[0], frC[1]); +#endif + + if (A_c == FP_CLS_NAN || (A_c != FP_CLS_ZERO && A_s)) { + frD[0] = frB[0]; + frD[1] = frB[1]; + } else { + frD[0] = frC[0]; + frD[1] = frC[1]; + } + +#ifdef DEBUG + printk("D: %08x.%08x\n", frD[0], frD[1]); +#endif + + return 0; +} diff --git a/arch/ppc/math-emu/fsqrt.c b/arch/ppc/math-emu/fsqrt.c new file mode 100644 index 00000000000..6f8319f64a8 --- /dev/null +++ b/arch/ppc/math-emu/fsqrt.c @@ -0,0 +1,37 @@ +#include +#include +#include + +#include "soft-fp.h" +#include "double.h" + +int +fsqrt(void *frD, void *frB) +{ + FP_DECL_D(B); + FP_DECL_D(R); + int ret = 0; + +#ifdef DEBUG + printk("%s: %p %p %p %p\n", __FUNCTION__, frD, frB); +#endif + + __FP_UNPACK_D(B, frB); + +#ifdef DEBUG + printk("B: %ld %lu %lu %ld (%ld)\n", B_s, B_f1, B_f0, B_e, B_c); +#endif + + if (B_s && B_c != FP_CLS_ZERO) + ret |= EFLAG_VXSQRT; + if (B_c == FP_CLS_NAN) + ret |= EFLAG_VXSNAN; + + FP_SQRT_D(R, B); + +#ifdef DEBUG + printk("R: %ld %lu %lu %ld (%ld)\n", R_s, R_f1, R_f0, R_e, R_c); +#endif + + return (ret | __FP_PACK_D(frD, R)); +} diff --git a/arch/ppc/math-emu/fsqrts.c b/arch/ppc/math-emu/fsqrts.c new file mode 100644 index 00000000000..3b2b1cf55c1 --- /dev/null +++ b/arch/ppc/math-emu/fsqrts.c @@ -0,0 +1,38 @@ +#include +#include +#include + +#include "soft-fp.h" +#include "double.h" +#include "single.h" + +int +fsqrts(void *frD, void *frB) +{ + FP_DECL_D(B); + FP_DECL_D(R); + int ret = 0; + +#ifdef DEBUG + printk("%s: %p %p %p %p\n", __FUNCTION__, frD, frB); +#endif + + __FP_UNPACK_D(B, frB); + +#ifdef DEBUG + printk("B: %ld %lu %lu %ld (%ld)\n", B_s, B_f1, B_f0, B_e, B_c); +#endif + + if (B_s && B_c != FP_CLS_ZERO) + ret |= EFLAG_VXSQRT; + if (B_c == FP_CLS_NAN) + ret |= EFLAG_VXSNAN; + + FP_SQRT_D(R, B); + +#ifdef DEBUG + printk("R: %ld %lu %lu %ld (%ld)\n", R_s, R_f1, R_f0, R_e, R_c); +#endif + + return (ret | __FP_PACK_DS(frD, R)); +} diff --git a/arch/ppc/math-emu/fsub.c b/arch/ppc/math-emu/fsub.c new file mode 100644 index 00000000000..956679042bb --- /dev/null +++ b/arch/ppc/math-emu/fsub.c @@ -0,0 +1,41 @@ +#include +#include +#include + +#include "soft-fp.h" +#include "double.h" + +int +fsub(void *frD, void *frA, void *frB) +{ + FP_DECL_D(A); + FP_DECL_D(B); + FP_DECL_D(R); + int ret = 0; + +#ifdef DEBUG + printk("%s: %p %p %p\n", __FUNCTION__, frD, frA, frB); +#endif + + __FP_UNPACK_D(A, frA); + __FP_UNPACK_D(B, frB); + +#ifdef DEBUG + printk("A: %ld %lu %lu %ld (%ld)\n", A_s, A_f1, A_f0, A_e, A_c); + printk("B: %ld %lu %lu %ld (%ld)\n", B_s, B_f1, B_f0, B_e, B_c); +#endif + + if (B_c != FP_CLS_NAN) + B_s ^= 1; + + if (A_s != B_s && A_c == FP_CLS_INF && B_c == FP_CLS_INF) + ret |= EFLAG_VXISI; + + FP_ADD_D(R, A, B); + +#ifdef DEBUG + printk("D: %ld %lu %lu %ld (%ld)\n", R_s, R_f1, R_f0, R_e, R_c); +#endif + + return (ret | __FP_PACK_D(frD, R)); +} diff --git a/arch/ppc/math-emu/fsubs.c b/arch/ppc/math-emu/fsubs.c new file mode 100644 index 00000000000..3428117dfe8 --- /dev/null +++ b/arch/ppc/math-emu/fsubs.c @@ -0,0 +1,42 @@ +#include +#include +#include + +#include "soft-fp.h" +#include "double.h" +#include "single.h" + +int +fsubs(void *frD, void *frA, void *frB) +{ + FP_DECL_D(A); + FP_DECL_D(B); + FP_DECL_D(R); + int ret = 0; + +#ifdef DEBUG + printk("%s: %p %p %p\n", __FUNCTION__, frD, frA, frB); +#endif + + __FP_UNPACK_D(A, frA); + __FP_UNPACK_D(B, frB); + +#ifdef DEBUG + printk("A: %ld %lu %lu %ld (%ld)\n", A_s, A_f1, A_f0, A_e, A_c); + printk("B: %ld %lu %lu %ld (%ld)\n", B_s, B_f1, B_f0, B_e, B_c); +#endif + + if (B_c != FP_CLS_NAN) + B_s ^= 1; + + if (A_s != B_s && A_c == FP_CLS_INF && B_c == FP_CLS_INF) + ret |= EFLAG_VXISI; + + FP_ADD_D(R, A, B); + +#ifdef DEBUG + printk("D: %ld %lu %lu %ld (%ld)\n", R_s, R_f1, R_f0, R_e, R_c); +#endif + + return (ret | __FP_PACK_DS(frD, R)); +} diff --git a/arch/ppc/math-emu/lfd.c b/arch/ppc/math-emu/lfd.c new file mode 100644 index 00000000000..7d38101c329 --- /dev/null +++ b/arch/ppc/math-emu/lfd.c @@ -0,0 +1,19 @@ +#include +#include +#include + +#include "sfp-machine.h" +#include "double.h" + +int +lfd(void *frD, void *ea) +{ + if (copy_from_user(frD, ea, sizeof(double))) + return -EFAULT; +#ifdef DEBUG + printk("%s: D %p, ea %p: ", __FUNCTION__, frD, ea); + dump_double(frD); + printk("\n"); +#endif + return 0; +} diff --git a/arch/ppc/math-emu/lfs.c b/arch/ppc/math-emu/lfs.c new file mode 100644 index 00000000000..c86dee3d765 --- /dev/null +++ b/arch/ppc/math-emu/lfs.c @@ -0,0 +1,37 @@ +#include +#include +#include + +#include "soft-fp.h" +#include "double.h" +#include "single.h" + +int +lfs(void *frD, void *ea) +{ + FP_DECL_D(R); + FP_DECL_S(A); + float f; + +#ifdef DEBUG + printk("%s: D %p, ea %p\n", __FUNCTION__, frD, ea); +#endif + + if (copy_from_user(&f, ea, sizeof(float))) + return -EFAULT; + + __FP_UNPACK_S(A, &f); + +#ifdef DEBUG + printk("A: %ld %lu %ld (%ld) [%08lx]\n", A_s, A_f, A_e, A_c, + *(unsigned long *)&f); +#endif + + FP_CONV(D, S, 2, 1, R, A); + +#ifdef DEBUG + printk("R: %ld %lu %lu %ld (%ld)\n", R_s, R_f1, R_f0, R_e, R_c); +#endif + + return __FP_PACK_D(frD, R); +} diff --git a/arch/ppc/math-emu/math.c b/arch/ppc/math-emu/math.c new file mode 100644 index 00000000000..b7dff53a710 --- /dev/null +++ b/arch/ppc/math-emu/math.c @@ -0,0 +1,485 @@ +/* + * arch/ppc/math-emu/math.c + * + * Copyright (C) 1999 Eddie C. Dost (ecd@atecom.com) + */ + +#include +#include +#include + +#include +#include + +#include "sfp-machine.h" +#include "double.h" + +#define FLOATFUNC(x) extern int x(void *, void *, void *, void *) + +FLOATFUNC(fadd); +FLOATFUNC(fadds); +FLOATFUNC(fdiv); +FLOATFUNC(fdivs); +FLOATFUNC(fmul); +FLOATFUNC(fmuls); +FLOATFUNC(fsub); +FLOATFUNC(fsubs); + +FLOATFUNC(fmadd); +FLOATFUNC(fmadds); +FLOATFUNC(fmsub); +FLOATFUNC(fmsubs); +FLOATFUNC(fnmadd); +FLOATFUNC(fnmadds); +FLOATFUNC(fnmsub); +FLOATFUNC(fnmsubs); + +FLOATFUNC(fctiw); +FLOATFUNC(fctiwz); +FLOATFUNC(frsp); + +FLOATFUNC(fcmpo); +FLOATFUNC(fcmpu); + +FLOATFUNC(mcrfs); +FLOATFUNC(mffs); +FLOATFUNC(mtfsb0); +FLOATFUNC(mtfsb1); +FLOATFUNC(mtfsf); +FLOATFUNC(mtfsfi); + +FLOATFUNC(lfd); +FLOATFUNC(lfs); + +FLOATFUNC(stfd); +FLOATFUNC(stfs); +FLOATFUNC(stfiwx); + +FLOATFUNC(fabs); +FLOATFUNC(fmr); +FLOATFUNC(fnabs); +FLOATFUNC(fneg); + +/* Optional */ +FLOATFUNC(fres); +FLOATFUNC(frsqrte); +FLOATFUNC(fsel); +FLOATFUNC(fsqrt); +FLOATFUNC(fsqrts); + + +#define OP31 0x1f /* 31 */ +#define LFS 0x30 /* 48 */ +#define LFSU 0x31 /* 49 */ +#define LFD 0x32 /* 50 */ +#define LFDU 0x33 /* 51 */ +#define STFS 0x34 /* 52 */ +#define STFSU 0x35 /* 53 */ +#define STFD 0x36 /* 54 */ +#define STFDU 0x37 /* 55 */ +#define OP59 0x3b /* 59 */ +#define OP63 0x3f /* 63 */ + +/* Opcode 31: */ +/* X-Form: */ +#define LFSX 0x217 /* 535 */ +#define LFSUX 0x237 /* 567 */ +#define LFDX 0x257 /* 599 */ +#define LFDUX 0x277 /* 631 */ +#define STFSX 0x297 /* 663 */ +#define STFSUX 0x2b7 /* 695 */ +#define STFDX 0x2d7 /* 727 */ +#define STFDUX 0x2f7 /* 759 */ +#define STFIWX 0x3d7 /* 983 */ + +/* Opcode 59: */ +/* A-Form: */ +#define FDIVS 0x012 /* 18 */ +#define FSUBS 0x014 /* 20 */ +#define FADDS 0x015 /* 21 */ +#define FSQRTS 0x016 /* 22 */ +#define FRES 0x018 /* 24 */ +#define FMULS 0x019 /* 25 */ +#define FMSUBS 0x01c /* 28 */ +#define FMADDS 0x01d /* 29 */ +#define FNMSUBS 0x01e /* 30 */ +#define FNMADDS 0x01f /* 31 */ + +/* Opcode 63: */ +/* A-Form: */ +#define FDIV 0x012 /* 18 */ +#define FSUB 0x014 /* 20 */ +#define FADD 0x015 /* 21 */ +#define FSQRT 0x016 /* 22 */ +#define FSEL 0x017 /* 23 */ +#define FMUL 0x019 /* 25 */ +#define FRSQRTE 0x01a /* 26 */ +#define FMSUB 0x01c /* 28 */ +#define FMADD 0x01d /* 29 */ +#define FNMSUB 0x01e /* 30 */ +#define FNMADD 0x01f /* 31 */ + +/* X-Form: */ +#define FCMPU 0x000 /* 0 */ +#define FRSP 0x00c /* 12 */ +#define FCTIW 0x00e /* 14 */ +#define FCTIWZ 0x00f /* 15 */ +#define FCMPO 0x020 /* 32 */ +#define MTFSB1 0x026 /* 38 */ +#define FNEG 0x028 /* 40 */ +#define MCRFS 0x040 /* 64 */ +#define MTFSB0 0x046 /* 70 */ +#define FMR 0x048 /* 72 */ +#define MTFSFI 0x086 /* 134 */ +#define FNABS 0x088 /* 136 */ +#define FABS 0x108 /* 264 */ +#define MFFS 0x247 /* 583 */ +#define MTFSF 0x2c7 /* 711 */ + + +#define AB 2 +#define AC 3 +#define ABC 4 +#define D 5 +#define DU 6 +#define X 7 +#define XA 8 +#define XB 9 +#define XCR 11 +#define XCRB 12 +#define XCRI 13 +#define XCRL 16 +#define XE 14 +#define XEU 15 +#define XFLB 10 + +#ifdef CONFIG_MATH_EMULATION +static int +record_exception(struct pt_regs *regs, int eflag) +{ + u32 fpscr; + + fpscr = __FPU_FPSCR; + + if (eflag) { + fpscr |= FPSCR_FX; + if (eflag & EFLAG_OVERFLOW) + fpscr |= FPSCR_OX; + if (eflag & EFLAG_UNDERFLOW) + fpscr |= FPSCR_UX; + if (eflag & EFLAG_DIVZERO) + fpscr |= FPSCR_ZX; + if (eflag & EFLAG_INEXACT) + fpscr |= FPSCR_XX; + if (eflag & EFLAG_VXSNAN) + fpscr |= FPSCR_VXSNAN; + if (eflag & EFLAG_VXISI) + fpscr |= FPSCR_VXISI; + if (eflag & EFLAG_VXIDI) + fpscr |= FPSCR_VXIDI; + if (eflag & EFLAG_VXZDZ) + fpscr |= FPSCR_VXZDZ; + if (eflag & EFLAG_VXIMZ) + fpscr |= FPSCR_VXIMZ; + if (eflag & EFLAG_VXVC) + fpscr |= FPSCR_VXVC; + if (eflag & EFLAG_VXSOFT) + fpscr |= FPSCR_VXSOFT; + if (eflag & EFLAG_VXSQRT) + fpscr |= FPSCR_VXSQRT; + if (eflag & EFLAG_VXCVI) + fpscr |= FPSCR_VXCVI; + } + + fpscr &= ~(FPSCR_VX); + if (fpscr & (FPSCR_VXSNAN | FPSCR_VXISI | FPSCR_VXIDI | + FPSCR_VXZDZ | FPSCR_VXIMZ | FPSCR_VXVC | + FPSCR_VXSOFT | FPSCR_VXSQRT | FPSCR_VXCVI)) + fpscr |= FPSCR_VX; + + fpscr &= ~(FPSCR_FEX); + if (((fpscr & FPSCR_VX) && (fpscr & FPSCR_VE)) || + ((fpscr & FPSCR_OX) && (fpscr & FPSCR_OE)) || + ((fpscr & FPSCR_UX) && (fpscr & FPSCR_UE)) || + ((fpscr & FPSCR_ZX) && (fpscr & FPSCR_ZE)) || + ((fpscr & FPSCR_XX) && (fpscr & FPSCR_XE))) + fpscr |= FPSCR_FEX; + + __FPU_FPSCR = fpscr; + + return (fpscr & FPSCR_FEX) ? 1 : 0; +} +#endif /* CONFIG_MATH_EMULATION */ + +int +do_mathemu(struct pt_regs *regs) +{ + void *op0 = 0, *op1 = 0, *op2 = 0, *op3 = 0; + unsigned long pc = regs->nip; + signed short sdisp; + u32 insn = 0; + int idx = 0; +#ifdef CONFIG_MATH_EMULATION + int (*func)(void *, void *, void *, void *); + int type = 0; + int eflag, trap; +#endif + + if (get_user(insn, (u32 *)pc)) + return -EFAULT; + +#ifndef CONFIG_MATH_EMULATION + switch (insn >> 26) { + case LFD: + idx = (insn >> 16) & 0x1f; + sdisp = (insn & 0xffff); + op0 = (void *)¤t->thread.fpr[(insn >> 21) & 0x1f]; + op1 = (void *)((idx ? regs->gpr[idx] : 0) + sdisp); + lfd(op0, op1, op2, op3); + break; + case LFDU: + idx = (insn >> 16) & 0x1f; + sdisp = (insn & 0xffff); + op0 = (void *)¤t->thread.fpr[(insn >> 21) & 0x1f]; + op1 = (void *)((idx ? regs->gpr[idx] : 0) + sdisp); + lfd(op0, op1, op2, op3); + regs->gpr[idx] = (unsigned long)op1; + break; + case STFD: + idx = (insn >> 16) & 0x1f; + sdisp = (insn & 0xffff); + op0 = (void *)¤t->thread.fpr[(insn >> 21) & 0x1f]; + op1 = (void *)((idx ? regs->gpr[idx] : 0) + sdisp); + stfd(op0, op1, op2, op3); + break; + case STFDU: + idx = (insn >> 16) & 0x1f; + sdisp = (insn & 0xffff); + op0 = (void *)¤t->thread.fpr[(insn >> 21) & 0x1f]; + op1 = (void *)((idx ? regs->gpr[idx] : 0) + sdisp); + stfd(op0, op1, op2, op3); + regs->gpr[idx] = (unsigned long)op1; + break; + case OP63: + op0 = (void *)¤t->thread.fpr[(insn >> 21) & 0x1f]; + op1 = (void *)¤t->thread.fpr[(insn >> 11) & 0x1f]; + fmr(op0, op1, op2, op3); + break; + default: + goto illegal; + } +#else /* CONFIG_MATH_EMULATION */ + switch (insn >> 26) { + case LFS: func = lfs; type = D; break; + case LFSU: func = lfs; type = DU; break; + case LFD: func = lfd; type = D; break; + case LFDU: func = lfd; type = DU; break; + case STFS: func = stfs; type = D; break; + case STFSU: func = stfs; type = DU; break; + case STFD: func = stfd; type = D; break; + case STFDU: func = stfd; type = DU; break; + + case OP31: + switch ((insn >> 1) & 0x3ff) { + case LFSX: func = lfs; type = XE; break; + case LFSUX: func = lfs; type = XEU; break; + case LFDX: func = lfd; type = XE; break; + case LFDUX: func = lfd; type = XEU; break; + case STFSX: func = stfs; type = XE; break; + case STFSUX: func = stfs; type = XEU; break; + case STFDX: func = stfd; type = XE; break; + case STFDUX: func = stfd; type = XEU; break; + case STFIWX: func = stfiwx; type = XE; break; + default: + goto illegal; + } + break; + + case OP59: + switch ((insn >> 1) & 0x1f) { + case FDIVS: func = fdivs; type = AB; break; + case FSUBS: func = fsubs; type = AB; break; + case FADDS: func = fadds; type = AB; break; + case FSQRTS: func = fsqrts; type = AB; break; + case FRES: func = fres; type = AB; break; + case FMULS: func = fmuls; type = AC; break; + case FMSUBS: func = fmsubs; type = ABC; break; + case FMADDS: func = fmadds; type = ABC; break; + case FNMSUBS: func = fnmsubs; type = ABC; break; + case FNMADDS: func = fnmadds; type = ABC; break; + default: + goto illegal; + } + break; + + case OP63: + if (insn & 0x20) { + switch ((insn >> 1) & 0x1f) { + case FDIV: func = fdiv; type = AB; break; + case FSUB: func = fsub; type = AB; break; + case FADD: func = fadd; type = AB; break; + case FSQRT: func = fsqrt; type = AB; break; + case FSEL: func = fsel; type = ABC; break; + case FMUL: func = fmul; type = AC; break; + case FRSQRTE: func = frsqrte; type = AB; break; + case FMSUB: func = fmsub; type = ABC; break; + case FMADD: func = fmadd; type = ABC; break; + case FNMSUB: func = fnmsub; type = ABC; break; + case FNMADD: func = fnmadd; type = ABC; break; + default: + goto illegal; + } + break; + } + + switch ((insn >> 1) & 0x3ff) { + case FCMPU: func = fcmpu; type = XCR; break; + case FRSP: func = frsp; type = XB; break; + case FCTIW: func = fctiw; type = XB; break; + case FCTIWZ: func = fctiwz; type = XB; break; + case FCMPO: func = fcmpo; type = XCR; break; + case MTFSB1: func = mtfsb1; type = XCRB; break; + case FNEG: func = fneg; type = XB; break; + case MCRFS: func = mcrfs; type = XCRL; break; + case MTFSB0: func = mtfsb0; type = XCRB; break; + case FMR: func = fmr; type = XB; break; + case MTFSFI: func = mtfsfi; type = XCRI; break; + case FNABS: func = fnabs; type = XB; break; + case FABS: func = fabs; type = XB; break; + case MFFS: func = mffs; type = X; break; + case MTFSF: func = mtfsf; type = XFLB; break; + default: + goto illegal; + } + break; + + default: + goto illegal; + } + + switch (type) { + case AB: + op0 = (void *)¤t->thread.fpr[(insn >> 21) & 0x1f]; + op1 = (void *)¤t->thread.fpr[(insn >> 16) & 0x1f]; + op2 = (void *)¤t->thread.fpr[(insn >> 11) & 0x1f]; + break; + + case AC: + op0 = (void *)¤t->thread.fpr[(insn >> 21) & 0x1f]; + op1 = (void *)¤t->thread.fpr[(insn >> 16) & 0x1f]; + op2 = (void *)¤t->thread.fpr[(insn >> 6) & 0x1f]; + break; + + case ABC: + op0 = (void *)¤t->thread.fpr[(insn >> 21) & 0x1f]; + op1 = (void *)¤t->thread.fpr[(insn >> 16) & 0x1f]; + op2 = (void *)¤t->thread.fpr[(insn >> 11) & 0x1f]; + op3 = (void *)¤t->thread.fpr[(insn >> 6) & 0x1f]; + break; + + case D: + idx = (insn >> 16) & 0x1f; + sdisp = (insn & 0xffff); + op0 = (void *)¤t->thread.fpr[(insn >> 21) & 0x1f]; + op1 = (void *)((idx ? regs->gpr[idx] : 0) + sdisp); + break; + + case DU: + idx = (insn >> 16) & 0x1f; + if (!idx) + goto illegal; + + sdisp = (insn & 0xffff); + op0 = (void *)¤t->thread.fpr[(insn >> 21) & 0x1f]; + op1 = (void *)(regs->gpr[idx] + sdisp); + break; + + case X: + op0 = (void *)¤t->thread.fpr[(insn >> 21) & 0x1f]; + break; + + case XA: + op0 = (void *)¤t->thread.fpr[(insn >> 21) & 0x1f]; + op1 = (void *)¤t->thread.fpr[(insn >> 16) & 0x1f]; + break; + + case XB: + op0 = (void *)¤t->thread.fpr[(insn >> 21) & 0x1f]; + op1 = (void *)¤t->thread.fpr[(insn >> 11) & 0x1f]; + break; + + case XE: + idx = (insn >> 16) & 0x1f; + if (!idx) + goto illegal; + + op0 = (void *)¤t->thread.fpr[(insn >> 21) & 0x1f]; + op1 = (void *)(regs->gpr[idx] + regs->gpr[(insn >> 11) & 0x1f]); + break; + + case XEU: + idx = (insn >> 16) & 0x1f; + op0 = (void *)¤t->thread.fpr[(insn >> 21) & 0x1f]; + op1 = (void *)((idx ? regs->gpr[idx] : 0) + + regs->gpr[(insn >> 11) & 0x1f]); + break; + + case XCR: + op0 = (void *)®s->ccr; + op1 = (void *)((insn >> 23) & 0x7); + op2 = (void *)¤t->thread.fpr[(insn >> 16) & 0x1f]; + op3 = (void *)¤t->thread.fpr[(insn >> 11) & 0x1f]; + break; + + case XCRL: + op0 = (void *)®s->ccr; + op1 = (void *)((insn >> 23) & 0x7); + op2 = (void *)((insn >> 18) & 0x7); + break; + + case XCRB: + op0 = (void *)((insn >> 21) & 0x1f); + break; + + case XCRI: + op0 = (void *)((insn >> 23) & 0x7); + op1 = (void *)((insn >> 12) & 0xf); + break; + + case XFLB: + op0 = (void *)((insn >> 17) & 0xff); + op1 = (void *)¤t->thread.fpr[(insn >> 11) & 0x1f]; + break; + + default: + goto illegal; + } + + eflag = func(op0, op1, op2, op3); + + if (insn & 1) { + regs->ccr &= ~(0x0f000000); + regs->ccr |= (__FPU_FPSCR >> 4) & 0x0f000000; + } + + trap = record_exception(regs, eflag); + if (trap) + return 1; + + switch (type) { + case DU: + case XEU: + regs->gpr[idx] = (unsigned long)op1; + break; + + default: + break; + } +#endif /* CONFIG_MATH_EMULATION */ + + regs->nip += 4; + return 0; + +illegal: + return -ENOSYS; +} diff --git a/arch/ppc/math-emu/mcrfs.c b/arch/ppc/math-emu/mcrfs.c new file mode 100644 index 00000000000..106dd912914 --- /dev/null +++ b/arch/ppc/math-emu/mcrfs.c @@ -0,0 +1,31 @@ +#include +#include +#include + +#include "soft-fp.h" + +int +mcrfs(u32 *ccr, u32 crfD, u32 crfS) +{ + u32 value, clear; + +#ifdef DEBUG + printk("%s: %p (%08x) %d %d\n", __FUNCTION__, ccr, *ccr, crfD, crfS); +#endif + + clear = 15 << ((7 - crfS) << 2); + if (!crfS) + clear = 0x90000000; + + value = (__FPU_FPSCR >> ((7 - crfS) << 2)) & 15; + __FPU_FPSCR &= ~(clear); + + *ccr &= ~(15 << ((7 - crfD) << 2)); + *ccr |= (value << ((7 - crfD) << 2)); + +#ifdef DEBUG + printk("CR: %08x\n", __FUNCTION__, *ccr); +#endif + + return 0; +} diff --git a/arch/ppc/math-emu/mffs.c b/arch/ppc/math-emu/mffs.c new file mode 100644 index 00000000000..f477c9170e7 --- /dev/null +++ b/arch/ppc/math-emu/mffs.c @@ -0,0 +1,17 @@ +#include +#include +#include + +#include "soft-fp.h" + +int +mffs(u32 *frD) +{ + frD[1] = __FPU_FPSCR; + +#ifdef DEBUG + printk("%s: frD %p: %08x.%08x\n", __FUNCTION__, frD, frD[0], frD[1]); +#endif + + return 0; +} diff --git a/arch/ppc/math-emu/mtfsb0.c b/arch/ppc/math-emu/mtfsb0.c new file mode 100644 index 00000000000..99bfd80f4af --- /dev/null +++ b/arch/ppc/math-emu/mtfsb0.c @@ -0,0 +1,18 @@ +#include +#include +#include + +#include "soft-fp.h" + +int +mtfsb0(int crbD) +{ + if ((crbD != 1) && (crbD != 2)) + __FPU_FPSCR &= ~(1 << (31 - crbD)); + +#ifdef DEBUG + printk("%s: %d %08lx\n", __FUNCTION__, crbD, __FPU_FPSCR); +#endif + + return 0; +} diff --git a/arch/ppc/math-emu/mtfsb1.c b/arch/ppc/math-emu/mtfsb1.c new file mode 100644 index 00000000000..3d9e7ed92d2 --- /dev/null +++ b/arch/ppc/math-emu/mtfsb1.c @@ -0,0 +1,18 @@ +#include +#include +#include + +#include "soft-fp.h" + +int +mtfsb1(int crbD) +{ + if ((crbD != 1) && (crbD != 2)) + __FPU_FPSCR |= (1 << (31 - crbD)); + +#ifdef DEBUG + printk("%s: %d %08lx\n", __FUNCTION__, crbD, __FPU_FPSCR); +#endif + + return 0; +} diff --git a/arch/ppc/math-emu/mtfsf.c b/arch/ppc/math-emu/mtfsf.c new file mode 100644 index 00000000000..d70cf714994 --- /dev/null +++ b/arch/ppc/math-emu/mtfsf.c @@ -0,0 +1,45 @@ +#include +#include +#include + +#include "soft-fp.h" + +int +mtfsf(unsigned int FM, u32 *frB) +{ + u32 mask; + + if (FM == 0) + return 0; + + if (FM == 0xff) + mask = 0x9fffffff; + else { + mask = 0; + if (FM & (1 << 0)) + mask |= 0x90000000; + if (FM & (1 << 1)) + mask |= 0x0f000000; + if (FM & (1 << 2)) + mask |= 0x00f00000; + if (FM & (1 << 3)) + mask |= 0x000f0000; + if (FM & (1 << 4)) + mask |= 0x0000f000; + if (FM & (1 << 5)) + mask |= 0x00000f00; + if (FM & (1 << 6)) + mask |= 0x000000f0; + if (FM & (1 << 7)) + mask |= 0x0000000f; + } + + __FPU_FPSCR &= ~(mask); + __FPU_FPSCR |= (frB[1] & mask); + +#ifdef DEBUG + printk("%s: %02x %p: %08lx\n", __FUNCTION__, FM, frB, __FPU_FPSCR); +#endif + + return 0; +} diff --git a/arch/ppc/math-emu/mtfsfi.c b/arch/ppc/math-emu/mtfsfi.c new file mode 100644 index 00000000000..71df854baa7 --- /dev/null +++ b/arch/ppc/math-emu/mtfsfi.c @@ -0,0 +1,23 @@ +#include +#include +#include + +#include "soft-fp.h" + +int +mtfsfi(unsigned int crfD, unsigned int IMM) +{ + u32 mask = 0xf; + + if (!crfD) + mask = 9; + + __FPU_FPSCR &= ~(mask << ((7 - crfD) << 2)); + __FPU_FPSCR |= (IMM & 0xf) << ((7 - crfD) << 2); + +#ifdef DEBUG + printk("%s: %d %x: %08lx\n", __FUNCTION__, crfD, IMM, __FPU_FPSCR); +#endif + + return 0; +} diff --git a/arch/ppc/math-emu/op-1.h b/arch/ppc/math-emu/op-1.h new file mode 100644 index 00000000000..c92fa95f562 --- /dev/null +++ b/arch/ppc/math-emu/op-1.h @@ -0,0 +1,245 @@ +/* + * Basic one-word fraction declaration and manipulation. + */ + +#define _FP_FRAC_DECL_1(X) _FP_W_TYPE X##_f +#define _FP_FRAC_COPY_1(D,S) (D##_f = S##_f) +#define _FP_FRAC_SET_1(X,I) (X##_f = I) +#define _FP_FRAC_HIGH_1(X) (X##_f) +#define _FP_FRAC_LOW_1(X) (X##_f) +#define _FP_FRAC_WORD_1(X,w) (X##_f) + +#define _FP_FRAC_ADDI_1(X,I) (X##_f += I) +#define _FP_FRAC_SLL_1(X,N) \ + do { \ + if (__builtin_constant_p(N) && (N) == 1) \ + X##_f += X##_f; \ + else \ + X##_f <<= (N); \ + } while (0) +#define _FP_FRAC_SRL_1(X,N) (X##_f >>= N) + +/* Right shift with sticky-lsb. */ +#define _FP_FRAC_SRS_1(X,N,sz) __FP_FRAC_SRS_1(X##_f, N, sz) + +#define __FP_FRAC_SRS_1(X,N,sz) \ + (X = (X >> (N) | (__builtin_constant_p(N) && (N) == 1 \ + ? X & 1 : (X << (_FP_W_TYPE_SIZE - (N))) != 0))) + +#define _FP_FRAC_ADD_1(R,X,Y) (R##_f = X##_f + Y##_f) +#define _FP_FRAC_SUB_1(R,X,Y) (R##_f = X##_f - Y##_f) +#define _FP_FRAC_CLZ_1(z, X) __FP_CLZ(z, X##_f) + +/* Predicates */ +#define _FP_FRAC_NEGP_1(X) ((_FP_WS_TYPE)X##_f < 0) +#define _FP_FRAC_ZEROP_1(X) (X##_f == 0) +#define _FP_FRAC_OVERP_1(fs,X) (X##_f & _FP_OVERFLOW_##fs) +#define _FP_FRAC_EQ_1(X, Y) (X##_f == Y##_f) +#define _FP_FRAC_GE_1(X, Y) (X##_f >= Y##_f) +#define _FP_FRAC_GT_1(X, Y) (X##_f > Y##_f) + +#define _FP_ZEROFRAC_1 0 +#define _FP_MINFRAC_1 1 + +/* + * Unpack the raw bits of a native fp value. Do not classify or + * normalize the data. + */ + +#define _FP_UNPACK_RAW_1(fs, X, val) \ + do { \ + union _FP_UNION_##fs _flo; _flo.flt = (val); \ + \ + X##_f = _flo.bits.frac; \ + X##_e = _flo.bits.exp; \ + X##_s = _flo.bits.sign; \ + } while (0) + + +/* + * Repack the raw bits of a native fp value. + */ + +#define _FP_PACK_RAW_1(fs, val, X) \ + do { \ + union _FP_UNION_##fs _flo; \ + \ + _flo.bits.frac = X##_f; \ + _flo.bits.exp = X##_e; \ + _flo.bits.sign = X##_s; \ + \ + (val) = _flo.flt; \ + } while (0) + + +/* + * Multiplication algorithms: + */ + +/* Basic. Assuming the host word size is >= 2*FRACBITS, we can do the + multiplication immediately. */ + +#define _FP_MUL_MEAT_1_imm(fs, R, X, Y) \ + do { \ + R##_f = X##_f * Y##_f; \ + /* Normalize since we know where the msb of the multiplicands \ + were (bit B), we know that the msb of the of the product is \ + at either 2B or 2B-1. */ \ + _FP_FRAC_SRS_1(R, _FP_WFRACBITS_##fs-1, 2*_FP_WFRACBITS_##fs); \ + } while (0) + +/* Given a 1W * 1W => 2W primitive, do the extended multiplication. */ + +#define _FP_MUL_MEAT_1_wide(fs, R, X, Y, doit) \ + do { \ + _FP_W_TYPE _Z_f0, _Z_f1; \ + doit(_Z_f1, _Z_f0, X##_f, Y##_f); \ + /* Normalize since we know where the msb of the multiplicands \ + were (bit B), we know that the msb of the of the product is \ + at either 2B or 2B-1. */ \ + _FP_FRAC_SRS_2(_Z, _FP_WFRACBITS_##fs-1, 2*_FP_WFRACBITS_##fs); \ + R##_f = _Z_f0; \ + } while (0) + +/* Finally, a simple widening multiply algorithm. What fun! */ + +#define _FP_MUL_MEAT_1_hard(fs, R, X, Y) \ + do { \ + _FP_W_TYPE _xh, _xl, _yh, _yl, _z_f0, _z_f1, _a_f0, _a_f1; \ + \ + /* split the words in half */ \ + _xh = X##_f >> (_FP_W_TYPE_SIZE/2); \ + _xl = X##_f & (((_FP_W_TYPE)1 << (_FP_W_TYPE_SIZE/2)) - 1); \ + _yh = Y##_f >> (_FP_W_TYPE_SIZE/2); \ + _yl = Y##_f & (((_FP_W_TYPE)1 << (_FP_W_TYPE_SIZE/2)) - 1); \ + \ + /* multiply the pieces */ \ + _z_f0 = _xl * _yl; \ + _a_f0 = _xh * _yl; \ + _a_f1 = _xl * _yh; \ + _z_f1 = _xh * _yh; \ + \ + /* reassemble into two full words */ \ + if ((_a_f0 += _a_f1) < _a_f1) \ + _z_f1 += (_FP_W_TYPE)1 << (_FP_W_TYPE_SIZE/2); \ + _a_f1 = _a_f0 >> (_FP_W_TYPE_SIZE/2); \ + _a_f0 = _a_f0 << (_FP_W_TYPE_SIZE/2); \ + _FP_FRAC_ADD_2(_z, _z, _a); \ + \ + /* normalize */ \ + _FP_FRAC_SRS_2(_z, _FP_WFRACBITS_##fs - 1, 2*_FP_WFRACBITS_##fs); \ + R##_f = _z_f0; \ + } while (0) + + +/* + * Division algorithms: + */ + +/* Basic. Assuming the host word size is >= 2*FRACBITS, we can do the + division immediately. Give this macro either _FP_DIV_HELP_imm for + C primitives or _FP_DIV_HELP_ldiv for the ISO function. Which you + choose will depend on what the compiler does with divrem4. */ + +#define _FP_DIV_MEAT_1_imm(fs, R, X, Y, doit) \ + do { \ + _FP_W_TYPE _q, _r; \ + X##_f <<= (X##_f < Y##_f \ + ? R##_e--, _FP_WFRACBITS_##fs \ + : _FP_WFRACBITS_##fs - 1); \ + doit(_q, _r, X##_f, Y##_f); \ + R##_f = _q | (_r != 0); \ + } while (0) + +/* GCC's longlong.h defines a 2W / 1W => (1W,1W) primitive udiv_qrnnd + that may be useful in this situation. This first is for a primitive + that requires normalization, the second for one that does not. Look + for UDIV_NEEDS_NORMALIZATION to tell which your machine needs. */ + +#define _FP_DIV_MEAT_1_udiv_norm(fs, R, X, Y) \ + do { \ + _FP_W_TYPE _nh, _nl, _q, _r; \ + \ + /* Normalize Y -- i.e. make the most significant bit set. */ \ + Y##_f <<= _FP_WFRACXBITS_##fs - 1; \ + \ + /* Shift X op correspondingly high, that is, up one full word. */ \ + if (X##_f <= Y##_f) \ + { \ + _nl = 0; \ + _nh = X##_f; \ + } \ + else \ + { \ + R##_e++; \ + _nl = X##_f << (_FP_W_TYPE_SIZE-1); \ + _nh = X##_f >> 1; \ + } \ + \ + udiv_qrnnd(_q, _r, _nh, _nl, Y##_f); \ + R##_f = _q | (_r != 0); \ + } while (0) + +#define _FP_DIV_MEAT_1_udiv(fs, R, X, Y) \ + do { \ + _FP_W_TYPE _nh, _nl, _q, _r; \ + if (X##_f < Y##_f) \ + { \ + R##_e--; \ + _nl = X##_f << _FP_WFRACBITS_##fs; \ + _nh = X##_f >> _FP_WFRACXBITS_##fs; \ + } \ + else \ + { \ + _nl = X##_f << (_FP_WFRACBITS_##fs - 1); \ + _nh = X##_f >> (_FP_WFRACXBITS_##fs + 1); \ + } \ + udiv_qrnnd(_q, _r, _nh, _nl, Y##_f); \ + R##_f = _q | (_r != 0); \ + } while (0) + + +/* + * Square root algorithms: + * We have just one right now, maybe Newton approximation + * should be added for those machines where division is fast. + */ + +#define _FP_SQRT_MEAT_1(R, S, T, X, q) \ + do { \ + while (q) \ + { \ + T##_f = S##_f + q; \ + if (T##_f <= X##_f) \ + { \ + S##_f = T##_f + q; \ + X##_f -= T##_f; \ + R##_f += q; \ + } \ + _FP_FRAC_SLL_1(X, 1); \ + q >>= 1; \ + } \ + } while (0) + +/* + * Assembly/disassembly for converting to/from integral types. + * No shifting or overflow handled here. + */ + +#define _FP_FRAC_ASSEMBLE_1(r, X, rsize) (r = X##_f) +#define _FP_FRAC_DISASSEMBLE_1(X, r, rsize) (X##_f = r) + + +/* + * Convert FP values between word sizes + */ + +#define _FP_FRAC_CONV_1_1(dfs, sfs, D, S) \ + do { \ + D##_f = S##_f; \ + if (_FP_WFRACBITS_##sfs > _FP_WFRACBITS_##dfs) \ + _FP_FRAC_SRS_1(D, (_FP_WFRACBITS_##sfs-_FP_WFRACBITS_##dfs), \ + _FP_WFRACBITS_##sfs); \ + else \ + D##_f <<= _FP_WFRACBITS_##dfs - _FP_WFRACBITS_##sfs; \ + } while (0) diff --git a/arch/ppc/math-emu/op-2.h b/arch/ppc/math-emu/op-2.h new file mode 100644 index 00000000000..b9b06b4c6ea --- /dev/null +++ b/arch/ppc/math-emu/op-2.h @@ -0,0 +1,433 @@ +/* + * Basic two-word fraction declaration and manipulation. + */ + +#define _FP_FRAC_DECL_2(X) _FP_W_TYPE X##_f0, X##_f1 +#define _FP_FRAC_COPY_2(D,S) (D##_f0 = S##_f0, D##_f1 = S##_f1) +#define _FP_FRAC_SET_2(X,I) __FP_FRAC_SET_2(X, I) +#define _FP_FRAC_HIGH_2(X) (X##_f1) +#define _FP_FRAC_LOW_2(X) (X##_f0) +#define _FP_FRAC_WORD_2(X,w) (X##_f##w) + +#define _FP_FRAC_SLL_2(X,N) \ + do { \ + if ((N) < _FP_W_TYPE_SIZE) \ + { \ + if (__builtin_constant_p(N) && (N) == 1) \ + { \ + X##_f1 = X##_f1 + X##_f1 + (((_FP_WS_TYPE)(X##_f0)) < 0); \ + X##_f0 += X##_f0; \ + } \ + else \ + { \ + X##_f1 = X##_f1 << (N) | X##_f0 >> (_FP_W_TYPE_SIZE - (N)); \ + X##_f0 <<= (N); \ + } \ + } \ + else \ + { \ + X##_f1 = X##_f0 << ((N) - _FP_W_TYPE_SIZE); \ + X##_f0 = 0; \ + } \ + } while (0) + +#define _FP_FRAC_SRL_2(X,N) \ + do { \ + if ((N) < _FP_W_TYPE_SIZE) \ + { \ + X##_f0 = X##_f0 >> (N) | X##_f1 << (_FP_W_TYPE_SIZE - (N)); \ + X##_f1 >>= (N); \ + } \ + else \ + { \ + X##_f0 = X##_f1 >> ((N) - _FP_W_TYPE_SIZE); \ + X##_f1 = 0; \ + } \ + } while (0) + +/* Right shift with sticky-lsb. */ +#define _FP_FRAC_SRS_2(X,N,sz) \ + do { \ + if ((N) < _FP_W_TYPE_SIZE) \ + { \ + X##_f0 = (X##_f1 << (_FP_W_TYPE_SIZE - (N)) | X##_f0 >> (N) | \ + (__builtin_constant_p(N) && (N) == 1 \ + ? X##_f0 & 1 \ + : (X##_f0 << (_FP_W_TYPE_SIZE - (N))) != 0)); \ + X##_f1 >>= (N); \ + } \ + else \ + { \ + X##_f0 = (X##_f1 >> ((N) - _FP_W_TYPE_SIZE) | \ + (((X##_f1 << (sz - (N))) | X##_f0) != 0)); \ + X##_f1 = 0; \ + } \ + } while (0) + +#define _FP_FRAC_ADDI_2(X,I) \ + __FP_FRAC_ADDI_2(X##_f1, X##_f0, I) + +#define _FP_FRAC_ADD_2(R,X,Y) \ + __FP_FRAC_ADD_2(R##_f1, R##_f0, X##_f1, X##_f0, Y##_f1, Y##_f0) + +#define _FP_FRAC_SUB_2(R,X,Y) \ + __FP_FRAC_SUB_2(R##_f1, R##_f0, X##_f1, X##_f0, Y##_f1, Y##_f0) + +#define _FP_FRAC_CLZ_2(R,X) \ + do { \ + if (X##_f1) \ + __FP_CLZ(R,X##_f1); \ + else \ + { \ + __FP_CLZ(R,X##_f0); \ + R += _FP_W_TYPE_SIZE; \ + } \ + } while(0) + +/* Predicates */ +#define _FP_FRAC_NEGP_2(X) ((_FP_WS_TYPE)X##_f1 < 0) +#define _FP_FRAC_ZEROP_2(X) ((X##_f1 | X##_f0) == 0) +#define _FP_FRAC_OVERP_2(fs,X) (X##_f1 & _FP_OVERFLOW_##fs) +#define _FP_FRAC_EQ_2(X, Y) (X##_f1 == Y##_f1 && X##_f0 == Y##_f0) +#define _FP_FRAC_GT_2(X, Y) \ + ((X##_f1 > Y##_f1) || (X##_f1 == Y##_f1 && X##_f0 > Y##_f0)) +#define _FP_FRAC_GE_2(X, Y) \ + ((X##_f1 > Y##_f1) || (X##_f1 == Y##_f1 && X##_f0 >= Y##_f0)) + +#define _FP_ZEROFRAC_2 0, 0 +#define _FP_MINFRAC_2 0, 1 + +/* + * Internals + */ + +#define __FP_FRAC_SET_2(X,I1,I0) (X##_f0 = I0, X##_f1 = I1) + +#define __FP_CLZ_2(R, xh, xl) \ + do { \ + if (xh) \ + __FP_CLZ(R,xl); \ + else \ + { \ + __FP_CLZ(R,xl); \ + R += _FP_W_TYPE_SIZE; \ + } \ + } while(0) + +#if 0 + +#ifndef __FP_FRAC_ADDI_2 +#define __FP_FRAC_ADDI_2(xh, xl, i) \ + (xh += ((xl += i) < i)) +#endif +#ifndef __FP_FRAC_ADD_2 +#define __FP_FRAC_ADD_2(rh, rl, xh, xl, yh, yl) \ + (rh = xh + yh + ((rl = xl + yl) < xl)) +#endif +#ifndef __FP_FRAC_SUB_2 +#define __FP_FRAC_SUB_2(rh, rl, xh, xl, yh, yl) \ + (rh = xh - yh - ((rl = xl - yl) > xl)) +#endif + +#else + +#undef __FP_FRAC_ADDI_2 +#define __FP_FRAC_ADDI_2(xh, xl, i) add_ssaaaa(xh, xl, xh, xl, 0, i) +#undef __FP_FRAC_ADD_2 +#define __FP_FRAC_ADD_2 add_ssaaaa +#undef __FP_FRAC_SUB_2 +#define __FP_FRAC_SUB_2 sub_ddmmss + +#endif + +/* + * Unpack the raw bits of a native fp value. Do not classify or + * normalize the data. + */ + +#define _FP_UNPACK_RAW_2(fs, X, val) \ + do { \ + union _FP_UNION_##fs _flo; _flo.flt = (val); \ + \ + X##_f0 = _flo.bits.frac0; \ + X##_f1 = _flo.bits.frac1; \ + X##_e = _flo.bits.exp; \ + X##_s = _flo.bits.sign; \ + } while (0) + + +/* + * Repack the raw bits of a native fp value. + */ + +#define _FP_PACK_RAW_2(fs, val, X) \ + do { \ + union _FP_UNION_##fs _flo; \ + \ + _flo.bits.frac0 = X##_f0; \ + _flo.bits.frac1 = X##_f1; \ + _flo.bits.exp = X##_e; \ + _flo.bits.sign = X##_s; \ + \ + (val) = _flo.flt; \ + } while (0) + + +/* + * Multiplication algorithms: + */ + +/* Given a 1W * 1W => 2W primitive, do the extended multiplication. */ + +#define _FP_MUL_MEAT_2_wide(fs, R, X, Y, doit) \ + do { \ + _FP_FRAC_DECL_4(_z); _FP_FRAC_DECL_2(_b); _FP_FRAC_DECL_2(_c); \ + \ + doit(_FP_FRAC_WORD_4(_z,1), _FP_FRAC_WORD_4(_z,0), X##_f0, Y##_f0); \ + doit(_b_f1, _b_f0, X##_f0, Y##_f1); \ + doit(_c_f1, _c_f0, X##_f1, Y##_f0); \ + doit(_FP_FRAC_WORD_4(_z,3), _FP_FRAC_WORD_4(_z,2), X##_f1, Y##_f1); \ + \ + __FP_FRAC_ADD_4(_FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2), \ + _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0), \ + 0, _b_f1, _b_f0, 0, \ + _FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2), \ + _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0)); \ + __FP_FRAC_ADD_4(_FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2), \ + _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0), \ + 0, _c_f1, _c_f0, 0, \ + _FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2), \ + _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0)); \ + \ + /* Normalize since we know where the msb of the multiplicands \ + were (bit B), we know that the msb of the of the product is \ + at either 2B or 2B-1. */ \ + _FP_FRAC_SRS_4(_z, _FP_WFRACBITS_##fs-1, 2*_FP_WFRACBITS_##fs); \ + R##_f0 = _FP_FRAC_WORD_4(_z,0); \ + R##_f1 = _FP_FRAC_WORD_4(_z,1); \ + } while (0) + +/* This next macro appears to be totally broken. Fortunately nowhere + * seems to use it :-> The problem is that we define _z[4] but + * then use it in _FP_FRAC_SRS_4, which will attempt to access + * _z_f[n] which will cause an error. The fix probably involves + * declaring it with _FP_FRAC_DECL_4, see previous macro. -- PMM 02/1998 + */ +#define _FP_MUL_MEAT_2_gmp(fs, R, X, Y) \ + do { \ + _FP_W_TYPE _x[2], _y[2], _z[4]; \ + _x[0] = X##_f0; _x[1] = X##_f1; \ + _y[0] = Y##_f0; _y[1] = Y##_f1; \ + \ + mpn_mul_n(_z, _x, _y, 2); \ + \ + /* Normalize since we know where the msb of the multiplicands \ + were (bit B), we know that the msb of the of the product is \ + at either 2B or 2B-1. */ \ + _FP_FRAC_SRS_4(_z, _FP_WFRACBITS##_fs-1, 2*_FP_WFRACBITS_##fs); \ + R##_f0 = _z[0]; \ + R##_f1 = _z[1]; \ + } while (0) + + +/* + * Division algorithms: + * This seems to be giving me difficulties -- PMM + * Look, NetBSD seems to be able to comment algorithms. Can't you? + * I've thrown printks at the problem. + * This now appears to work, but I still don't really know why. + * Also, I don't think the result is properly normalised... + */ + +#define _FP_DIV_MEAT_2_udiv_64(fs, R, X, Y) \ + do { \ + extern void _fp_udivmodti4(_FP_W_TYPE q[2], _FP_W_TYPE r[2], \ + _FP_W_TYPE n1, _FP_W_TYPE n0, \ + _FP_W_TYPE d1, _FP_W_TYPE d0); \ + _FP_W_TYPE _n_f3, _n_f2, _n_f1, _n_f0, _r_f1, _r_f0; \ + _FP_W_TYPE _q_f1, _q_f0, _m_f1, _m_f0; \ + _FP_W_TYPE _rmem[2], _qmem[2]; \ + /* I think this check is to ensure that the result is normalised. \ + * Assuming X,Y normalised (ie in [1.0,2.0)) X/Y will be in \ + * [0.5,2.0). Furthermore, it will be less than 1.0 iff X < Y. \ + * In this case we tweak things. (this is based on comments in \ + * the NetBSD FPU emulation code. ) \ + * We know X,Y are normalised because we ensure this as part of \ + * the unpacking process. -- PMM \ + */ \ + if (_FP_FRAC_GT_2(X, Y)) \ + { \ +/* R##_e++; */ \ + _n_f3 = X##_f1 >> 1; \ + _n_f2 = X##_f1 << (_FP_W_TYPE_SIZE - 1) | X##_f0 >> 1; \ + _n_f1 = X##_f0 << (_FP_W_TYPE_SIZE - 1); \ + _n_f0 = 0; \ + } \ + else \ + { \ + R##_e--; \ + _n_f3 = X##_f1; \ + _n_f2 = X##_f0; \ + _n_f1 = _n_f0 = 0; \ + } \ + \ + /* Normalize, i.e. make the most significant bit of the \ + denominator set. CHANGED: - 1 to nothing -- PMM */ \ + _FP_FRAC_SLL_2(Y, _FP_WFRACXBITS_##fs /* -1 */); \ + \ + /* Do the 256/128 bit division given the 128-bit _fp_udivmodtf4 \ + primitive snagged from libgcc2.c. */ \ + \ + _fp_udivmodti4(_qmem, _rmem, _n_f3, _n_f2, 0, Y##_f1); \ + _q_f1 = _qmem[0]; \ + umul_ppmm(_m_f1, _m_f0, _q_f1, Y##_f0); \ + _r_f1 = _rmem[0]; \ + _r_f0 = _n_f1; \ + if (_FP_FRAC_GT_2(_m, _r)) \ + { \ + _q_f1--; \ + _FP_FRAC_ADD_2(_r, _r, Y); \ + if (_FP_FRAC_GE_2(_r, Y) && _FP_FRAC_GT_2(_m, _r)) \ + { \ + _q_f1--; \ + _FP_FRAC_ADD_2(_r, _r, Y); \ + } \ + } \ + _FP_FRAC_SUB_2(_r, _r, _m); \ + \ + _fp_udivmodti4(_qmem, _rmem, _r_f1, _r_f0, 0, Y##_f1); \ + _q_f0 = _qmem[0]; \ + umul_ppmm(_m_f1, _m_f0, _q_f0, Y##_f0); \ + _r_f1 = _rmem[0]; \ + _r_f0 = _n_f0; \ + if (_FP_FRAC_GT_2(_m, _r)) \ + { \ + _q_f0--; \ + _FP_FRAC_ADD_2(_r, _r, Y); \ + if (_FP_FRAC_GE_2(_r, Y) && _FP_FRAC_GT_2(_m, _r)) \ + { \ + _q_f0--; \ + _FP_FRAC_ADD_2(_r, _r, Y); \ + } \ + } \ + _FP_FRAC_SUB_2(_r, _r, _m); \ + \ + R##_f1 = _q_f1; \ + R##_f0 = _q_f0 | ((_r_f1 | _r_f0) != 0); \ + /* adjust so answer is normalized again. I'm not sure what the \ + * final sz param should be. In practice it's never used since \ + * N is 1 which is always going to be < _FP_W_TYPE_SIZE... \ + */ \ + /* _FP_FRAC_SRS_2(R,1,_FP_WFRACBITS_##fs); */ \ + } while (0) + + +#define _FP_DIV_MEAT_2_gmp(fs, R, X, Y) \ + do { \ + _FP_W_TYPE _x[4], _y[2], _z[4]; \ + _y[0] = Y##_f0; _y[1] = Y##_f1; \ + _x[0] = _x[3] = 0; \ + if (_FP_FRAC_GT_2(X, Y)) \ + { \ + R##_e++; \ + _x[1] = (X##_f0 << (_FP_WFRACBITS-1 - _FP_W_TYPE_SIZE) | \ + X##_f1 >> (_FP_W_TYPE_SIZE - \ + (_FP_WFRACBITS-1 - _FP_W_TYPE_SIZE))); \ + _x[2] = X##_f1 << (_FP_WFRACBITS-1 - _FP_W_TYPE_SIZE); \ + } \ + else \ + { \ + _x[1] = (X##_f0 << (_FP_WFRACBITS - _FP_W_TYPE_SIZE) | \ + X##_f1 >> (_FP_W_TYPE_SIZE - \ + (_FP_WFRACBITS - _FP_W_TYPE_SIZE))); \ + _x[2] = X##_f1 << (_FP_WFRACBITS - _FP_W_TYPE_SIZE); \ + } \ + \ + (void) mpn_divrem (_z, 0, _x, 4, _y, 2); \ + R##_f1 = _z[1]; \ + R##_f0 = _z[0] | ((_x[0] | _x[1]) != 0); \ + } while (0) + + +/* + * Square root algorithms: + * We have just one right now, maybe Newton approximation + * should be added for those machines where division is fast. + */ + +#define _FP_SQRT_MEAT_2(R, S, T, X, q) \ + do { \ + while (q) \ + { \ + T##_f1 = S##_f1 + q; \ + if (T##_f1 <= X##_f1) \ + { \ + S##_f1 = T##_f1 + q; \ + X##_f1 -= T##_f1; \ + R##_f1 += q; \ + } \ + _FP_FRAC_SLL_2(X, 1); \ + q >>= 1; \ + } \ + q = (_FP_W_TYPE)1 << (_FP_W_TYPE_SIZE - 1); \ + while (q) \ + { \ + T##_f0 = S##_f0 + q; \ + T##_f1 = S##_f1; \ + if (T##_f1 < X##_f1 || \ + (T##_f1 == X##_f1 && T##_f0 < X##_f0)) \ + { \ + S##_f0 = T##_f0 + q; \ + if (((_FP_WS_TYPE)T##_f0) < 0 && \ + ((_FP_WS_TYPE)S##_f0) >= 0) \ + S##_f1++; \ + _FP_FRAC_SUB_2(X, X, T); \ + R##_f0 += q; \ + } \ + _FP_FRAC_SLL_2(X, 1); \ + q >>= 1; \ + } \ + } while (0) + + +/* + * Assembly/disassembly for converting to/from integral types. + * No shifting or overflow handled here. + */ + +#define _FP_FRAC_ASSEMBLE_2(r, X, rsize) \ + do { \ + if (rsize <= _FP_W_TYPE_SIZE) \ + r = X##_f0; \ + else \ + { \ + r = X##_f1; \ + r <<= _FP_W_TYPE_SIZE; \ + r += X##_f0; \ + } \ + } while (0) + +#define _FP_FRAC_DISASSEMBLE_2(X, r, rsize) \ + do { \ + X##_f0 = r; \ + X##_f1 = (rsize <= _FP_W_TYPE_SIZE ? 0 : r >> _FP_W_TYPE_SIZE); \ + } while (0) + +/* + * Convert FP values between word sizes + */ + +#define _FP_FRAC_CONV_1_2(dfs, sfs, D, S) \ + do { \ + _FP_FRAC_SRS_2(S, (_FP_WFRACBITS_##sfs - _FP_WFRACBITS_##dfs), \ + _FP_WFRACBITS_##sfs); \ + D##_f = S##_f0; \ + } while (0) + +#define _FP_FRAC_CONV_2_1(dfs, sfs, D, S) \ + do { \ + D##_f0 = S##_f; \ + D##_f1 = 0; \ + _FP_FRAC_SLL_2(D, (_FP_WFRACBITS_##dfs - _FP_WFRACBITS_##sfs)); \ + } while (0) + diff --git a/arch/ppc/math-emu/op-4.h b/arch/ppc/math-emu/op-4.h new file mode 100644 index 00000000000..fcdd6d064c5 --- /dev/null +++ b/arch/ppc/math-emu/op-4.h @@ -0,0 +1,297 @@ +/* + * Basic four-word fraction declaration and manipulation. + * + * When adding quadword support for 32 bit machines, we need + * to be a little careful as double multiply uses some of these + * macros: (in op-2.h) + * _FP_MUL_MEAT_2_wide() uses _FP_FRAC_DECL_4, _FP_FRAC_WORD_4, + * _FP_FRAC_ADD_4, _FP_FRAC_SRS_4 + * _FP_MUL_MEAT_2_gmp() uses _FP_FRAC_SRS_4 (and should use + * _FP_FRAC_DECL_4: it appears to be broken and is not used + * anywhere anyway. ) + * + * I've now fixed all the macros that were here from the sparc64 code. + * [*none* of the shift macros were correct!] -- PMM 02/1998 + * + * The only quadword stuff that remains to be coded is: + * 1) the conversion to/from ints, which requires + * that we check (in op-common.h) that the following do the right thing + * for quadwords: _FP_TO_INT(Q,4,r,X,rsz,rsg), _FP_FROM_INT(Q,4,X,r,rs,rt) + * 2) multiply, divide and sqrt, which require: + * _FP_MUL_MEAT_4_*(R,X,Y), _FP_DIV_MEAT_4_*(R,X,Y), _FP_SQRT_MEAT_4(R,S,T,X,q), + * This also needs _FP_MUL_MEAT_Q and _FP_DIV_MEAT_Q to be defined to + * some suitable _FP_MUL_MEAT_4_* macros in sfp-machine.h. + * [we're free to choose whatever FP_MUL_MEAT_4_* macros we need for + * these; they are used nowhere else. ] + */ + +#define _FP_FRAC_DECL_4(X) _FP_W_TYPE X##_f[4] +#define _FP_FRAC_COPY_4(D,S) \ + (D##_f[0] = S##_f[0], D##_f[1] = S##_f[1], \ + D##_f[2] = S##_f[2], D##_f[3] = S##_f[3]) +/* The _FP_FRAC_SET_n(X,I) macro is intended for use with another + * macro such as _FP_ZEROFRAC_n which returns n comma separated values. + * The result is that we get an expansion of __FP_FRAC_SET_n(X,I0,I1,I2,I3) + * which just assigns the In values to the array X##_f[]. + * This is why the number of parameters doesn't appear to match + * at first glance... -- PMM + */ +#define _FP_FRAC_SET_4(X,I) __FP_FRAC_SET_4(X, I) +#define _FP_FRAC_HIGH_4(X) (X##_f[3]) +#define _FP_FRAC_LOW_4(X) (X##_f[0]) +#define _FP_FRAC_WORD_4(X,w) (X##_f[w]) + +#define _FP_FRAC_SLL_4(X,N) \ + do { \ + _FP_I_TYPE _up, _down, _skip, _i; \ + _skip = (N) / _FP_W_TYPE_SIZE; \ + _up = (N) % _FP_W_TYPE_SIZE; \ + _down = _FP_W_TYPE_SIZE - _up; \ + for (_i = 3; _i > _skip; --_i) \ + X##_f[_i] = X##_f[_i-_skip] << _up | X##_f[_i-_skip-1] >> _down; \ +/* bugfixed: was X##_f[_i] <<= _up; -- PMM 02/1998 */ \ + X##_f[_i] = X##_f[0] << _up; \ + for (--_i; _i >= 0; --_i) \ + X##_f[_i] = 0; \ + } while (0) + +/* This one was broken too */ +#define _FP_FRAC_SRL_4(X,N) \ + do { \ + _FP_I_TYPE _up, _down, _skip, _i; \ + _skip = (N) / _FP_W_TYPE_SIZE; \ + _down = (N) % _FP_W_TYPE_SIZE; \ + _up = _FP_W_TYPE_SIZE - _down; \ + for (_i = 0; _i < 3-_skip; ++_i) \ + X##_f[_i] = X##_f[_i+_skip] >> _down | X##_f[_i+_skip+1] << _up; \ + X##_f[_i] = X##_f[3] >> _down; \ + for (++_i; _i < 4; ++_i) \ + X##_f[_i] = 0; \ + } while (0) + + +/* Right shift with sticky-lsb. + * What this actually means is that we do a standard right-shift, + * but that if any of the bits that fall off the right hand side + * were one then we always set the LSbit. + */ +#define _FP_FRAC_SRS_4(X,N,size) \ + do { \ + _FP_I_TYPE _up, _down, _skip, _i; \ + _FP_W_TYPE _s; \ + _skip = (N) / _FP_W_TYPE_SIZE; \ + _down = (N) % _FP_W_TYPE_SIZE; \ + _up = _FP_W_TYPE_SIZE - _down; \ + for (_s = _i = 0; _i < _skip; ++_i) \ + _s |= X##_f[_i]; \ + _s |= X##_f[_i] << _up; \ +/* s is now != 0 if we want to set the LSbit */ \ + for (_i = 0; _i < 3-_skip; ++_i) \ + X##_f[_i] = X##_f[_i+_skip] >> _down | X##_f[_i+_skip+1] << _up; \ + X##_f[_i] = X##_f[3] >> _down; \ + for (++_i; _i < 4; ++_i) \ + X##_f[_i] = 0; \ + /* don't fix the LSB until the very end when we're sure f[0] is stable */ \ + X##_f[0] |= (_s != 0); \ + } while (0) + +#define _FP_FRAC_ADD_4(R,X,Y) \ + __FP_FRAC_ADD_4(R##_f[3], R##_f[2], R##_f[1], R##_f[0], \ + X##_f[3], X##_f[2], X##_f[1], X##_f[0], \ + Y##_f[3], Y##_f[2], Y##_f[1], Y##_f[0]) + +#define _FP_FRAC_SUB_4(R,X,Y) \ + __FP_FRAC_SUB_4(R##_f[3], R##_f[2], R##_f[1], R##_f[0], \ + X##_f[3], X##_f[2], X##_f[1], X##_f[0], \ + Y##_f[3], Y##_f[2], Y##_f[1], Y##_f[0]) + +#define _FP_FRAC_ADDI_4(X,I) \ + __FP_FRAC_ADDI_4(X##_f[3], X##_f[2], X##_f[1], X##_f[0], I) + +#define _FP_ZEROFRAC_4 0,0,0,0 +#define _FP_MINFRAC_4 0,0,0,1 + +#define _FP_FRAC_ZEROP_4(X) ((X##_f[0] | X##_f[1] | X##_f[2] | X##_f[3]) == 0) +#define _FP_FRAC_NEGP_4(X) ((_FP_WS_TYPE)X##_f[3] < 0) +#define _FP_FRAC_OVERP_4(fs,X) (X##_f[0] & _FP_OVERFLOW_##fs) + +#define _FP_FRAC_EQ_4(X,Y) \ + (X##_f[0] == Y##_f[0] && X##_f[1] == Y##_f[1] \ + && X##_f[2] == Y##_f[2] && X##_f[3] == Y##_f[3]) + +#define _FP_FRAC_GT_4(X,Y) \ + (X##_f[3] > Y##_f[3] || \ + (X##_f[3] == Y##_f[3] && (X##_f[2] > Y##_f[2] || \ + (X##_f[2] == Y##_f[2] && (X##_f[1] > Y##_f[1] || \ + (X##_f[1] == Y##_f[1] && X##_f[0] > Y##_f[0]) \ + )) \ + )) \ + ) + +#define _FP_FRAC_GE_4(X,Y) \ + (X##_f[3] > Y##_f[3] || \ + (X##_f[3] == Y##_f[3] && (X##_f[2] > Y##_f[2] || \ + (X##_f[2] == Y##_f[2] && (X##_f[1] > Y##_f[1] || \ + (X##_f[1] == Y##_f[1] && X##_f[0] >= Y##_f[0]) \ + )) \ + )) \ + ) + + +#define _FP_FRAC_CLZ_4(R,X) \ + do { \ + if (X##_f[3]) \ + { \ + __FP_CLZ(R,X##_f[3]); \ + } \ + else if (X##_f[2]) \ + { \ + __FP_CLZ(R,X##_f[2]); \ + R += _FP_W_TYPE_SIZE; \ + } \ + else if (X##_f[1]) \ + { \ + __FP_CLZ(R,X##_f[2]); \ + R += _FP_W_TYPE_SIZE*2; \ + } \ + else \ + { \ + __FP_CLZ(R,X##_f[0]); \ + R += _FP_W_TYPE_SIZE*3; \ + } \ + } while(0) + + +#define _FP_UNPACK_RAW_4(fs, X, val) \ + do { \ + union _FP_UNION_##fs _flo; _flo.flt = (val); \ + X##_f[0] = _flo.bits.frac0; \ + X##_f[1] = _flo.bits.frac1; \ + X##_f[2] = _flo.bits.frac2; \ + X##_f[3] = _flo.bits.frac3; \ + X##_e = _flo.bits.exp; \ + X##_s = _flo.bits.sign; \ + } while (0) + +#define _FP_PACK_RAW_4(fs, val, X) \ + do { \ + union _FP_UNION_##fs _flo; \ + _flo.bits.frac0 = X##_f[0]; \ + _flo.bits.frac1 = X##_f[1]; \ + _flo.bits.frac2 = X##_f[2]; \ + _flo.bits.frac3 = X##_f[3]; \ + _flo.bits.exp = X##_e; \ + _flo.bits.sign = X##_s; \ + (val) = _flo.flt; \ + } while (0) + + +/* + * Internals + */ + +#define __FP_FRAC_SET_4(X,I3,I2,I1,I0) \ + (X##_f[3] = I3, X##_f[2] = I2, X##_f[1] = I1, X##_f[0] = I0) + +#ifndef __FP_FRAC_ADD_4 +#define __FP_FRAC_ADD_4(r3,r2,r1,r0,x3,x2,x1,x0,y3,y2,y1,y0) \ + (r0 = x0 + y0, \ + r1 = x1 + y1 + (r0 < x0), \ + r2 = x2 + y2 + (r1 < x1), \ + r3 = x3 + y3 + (r2 < x2)) +#endif + +#ifndef __FP_FRAC_SUB_4 +#define __FP_FRAC_SUB_4(r3,r2,r1,r0,x3,x2,x1,x0,y3,y2,y1,y0) \ + (r0 = x0 - y0, \ + r1 = x1 - y1 - (r0 > x0), \ + r2 = x2 - y2 - (r1 > x1), \ + r3 = x3 - y3 - (r2 > x2)) +#endif + +#ifndef __FP_FRAC_ADDI_4 +/* I always wanted to be a lisp programmer :-> */ +#define __FP_FRAC_ADDI_4(x3,x2,x1,x0,i) \ + (x3 += ((x2 += ((x1 += ((x0 += i) < x0)) < x1) < x2))) +#endif + +/* Convert FP values between word sizes. This appears to be more + * complicated than I'd have expected it to be, so these might be + * wrong... These macros are in any case somewhat bogus because they + * use information about what various FRAC_n variables look like + * internally [eg, that 2 word vars are X_f0 and x_f1]. But so do + * the ones in op-2.h and op-1.h. + */ +#define _FP_FRAC_CONV_1_4(dfs, sfs, D, S) \ + do { \ + _FP_FRAC_SRS_4(S, (_FP_WFRACBITS_##sfs - _FP_WFRACBITS_##dfs), \ + _FP_WFRACBITS_##sfs); \ + D##_f = S##_f[0]; \ + } while (0) + +#define _FP_FRAC_CONV_2_4(dfs, sfs, D, S) \ + do { \ + _FP_FRAC_SRS_4(S, (_FP_WFRACBITS_##sfs - _FP_WFRACBITS_##dfs), \ + _FP_WFRACBITS_##sfs); \ + D##_f0 = S##_f[0]; \ + D##_f1 = S##_f[1]; \ + } while (0) + +/* Assembly/disassembly for converting to/from integral types. + * No shifting or overflow handled here. + */ +/* Put the FP value X into r, which is an integer of size rsize. */ +#define _FP_FRAC_ASSEMBLE_4(r, X, rsize) \ + do { \ + if (rsize <= _FP_W_TYPE_SIZE) \ + r = X##_f[0]; \ + else if (rsize <= 2*_FP_W_TYPE_SIZE) \ + { \ + r = X##_f[1]; \ + r <<= _FP_W_TYPE_SIZE; \ + r += X##_f[0]; \ + } \ + else \ + { \ + /* I'm feeling lazy so we deal with int == 3words (implausible)*/ \ + /* and int == 4words as a single case. */ \ + r = X##_f[3]; \ + r <<= _FP_W_TYPE_SIZE; \ + r += X##_f[2]; \ + r <<= _FP_W_TYPE_SIZE; \ + r += X##_f[1]; \ + r <<= _FP_W_TYPE_SIZE; \ + r += X##_f[0]; \ + } \ + } while (0) + +/* "No disassemble Number Five!" */ +/* move an integer of size rsize into X's fractional part. We rely on + * the _f[] array consisting of words of size _FP_W_TYPE_SIZE to avoid + * having to mask the values we store into it. + */ +#define _FP_FRAC_DISASSEMBLE_4(X, r, rsize) \ + do { \ + X##_f[0] = r; \ + X##_f[1] = (rsize <= _FP_W_TYPE_SIZE ? 0 : r >> _FP_W_TYPE_SIZE); \ + X##_f[2] = (rsize <= 2*_FP_W_TYPE_SIZE ? 0 : r >> 2*_FP_W_TYPE_SIZE); \ + X##_f[3] = (rsize <= 3*_FP_W_TYPE_SIZE ? 0 : r >> 3*_FP_W_TYPE_SIZE); \ + } while (0) + +#define _FP_FRAC_CONV_4_1(dfs, sfs, D, S) \ + do { \ + D##_f[0] = S##_f; \ + D##_f[1] = D##_f[2] = D##_f[3] = 0; \ + _FP_FRAC_SLL_4(D, (_FP_WFRACBITS_##dfs - _FP_WFRACBITS_##sfs)); \ + } while (0) + +#define _FP_FRAC_CONV_4_2(dfs, sfs, D, S) \ + do { \ + D##_f[0] = S##_f0; \ + D##_f[1] = S##_f1; \ + D##_f[2] = D##_f[3] = 0; \ + _FP_FRAC_SLL_4(D, (_FP_WFRACBITS_##dfs - _FP_WFRACBITS_##sfs)); \ + } while (0) + +/* FIXME! This has to be written */ +#define _FP_SQRT_MEAT_4(R, S, T, X, q) diff --git a/arch/ppc/math-emu/op-common.h b/arch/ppc/math-emu/op-common.h new file mode 100644 index 00000000000..afb82b6498c --- /dev/null +++ b/arch/ppc/math-emu/op-common.h @@ -0,0 +1,688 @@ +#define _FP_DECL(wc, X) \ + _FP_I_TYPE X##_c, X##_s, X##_e; \ + _FP_FRAC_DECL_##wc(X) + +/* + * Finish truely unpacking a native fp value by classifying the kind + * of fp value and normalizing both the exponent and the fraction. + */ + +#define _FP_UNPACK_CANONICAL(fs, wc, X) \ +do { \ + switch (X##_e) \ + { \ + default: \ + _FP_FRAC_HIGH_##wc(X) |= _FP_IMPLBIT_##fs; \ + _FP_FRAC_SLL_##wc(X, _FP_WORKBITS); \ + X##_e -= _FP_EXPBIAS_##fs; \ + X##_c = FP_CLS_NORMAL; \ + break; \ + \ + case 0: \ + if (_FP_FRAC_ZEROP_##wc(X)) \ + X##_c = FP_CLS_ZERO; \ + else \ + { \ + /* a denormalized number */ \ + _FP_I_TYPE _shift; \ + _FP_FRAC_CLZ_##wc(_shift, X); \ + _shift -= _FP_FRACXBITS_##fs; \ + _FP_FRAC_SLL_##wc(X, (_shift+_FP_WORKBITS)); \ + X##_e -= _FP_EXPBIAS_##fs - 1 + _shift; \ + X##_c = FP_CLS_NORMAL; \ + } \ + break; \ + \ + case _FP_EXPMAX_##fs: \ + if (_FP_FRAC_ZEROP_##wc(X)) \ + X##_c = FP_CLS_INF; \ + else \ + /* we don't differentiate between signaling and quiet nans */ \ + X##_c = FP_CLS_NAN; \ + break; \ + } \ +} while (0) + + +/* + * Before packing the bits back into the native fp result, take care + * of such mundane things as rounding and overflow. Also, for some + * kinds of fp values, the original parts may not have been fully + * extracted -- but that is ok, we can regenerate them now. + */ + +#define _FP_PACK_CANONICAL(fs, wc, X) \ +({int __ret = 0; \ + switch (X##_c) \ + { \ + case FP_CLS_NORMAL: \ + X##_e += _FP_EXPBIAS_##fs; \ + if (X##_e > 0) \ + { \ + __ret |= _FP_ROUND(wc, X); \ + if (_FP_FRAC_OVERP_##wc(fs, X)) \ + { \ + _FP_FRAC_SRL_##wc(X, (_FP_WORKBITS+1)); \ + X##_e++; \ + } \ + else \ + _FP_FRAC_SRL_##wc(X, _FP_WORKBITS); \ + if (X##_e >= _FP_EXPMAX_##fs) \ + { \ + /* overflow to infinity */ \ + X##_e = _FP_EXPMAX_##fs; \ + _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \ + __ret |= EFLAG_OVERFLOW; \ + } \ + } \ + else \ + { \ + /* we've got a denormalized number */ \ + X##_e = -X##_e + 1; \ + if (X##_e <= _FP_WFRACBITS_##fs) \ + { \ + _FP_FRAC_SRS_##wc(X, X##_e, _FP_WFRACBITS_##fs); \ + _FP_FRAC_SLL_##wc(X, 1); \ + if (_FP_FRAC_OVERP_##wc(fs, X)) \ + { \ + X##_e = 1; \ + _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \ + } \ + else \ + { \ + X##_e = 0; \ + _FP_FRAC_SRL_##wc(X, _FP_WORKBITS+1); \ + __ret |= EFLAG_UNDERFLOW; \ + } \ + } \ + else \ + { \ + /* underflow to zero */ \ + X##_e = 0; \ + _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \ + __ret |= EFLAG_UNDERFLOW; \ + } \ + } \ + break; \ + \ + case FP_CLS_ZERO: \ + X##_e = 0; \ + _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \ + break; \ + \ + case FP_CLS_INF: \ + X##_e = _FP_EXPMAX_##fs; \ + _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \ + break; \ + \ + case FP_CLS_NAN: \ + X##_e = _FP_EXPMAX_##fs; \ + if (!_FP_KEEPNANFRACP) \ + { \ + _FP_FRAC_SET_##wc(X, _FP_NANFRAC_##fs); \ + X##_s = 0; \ + } \ + else \ + _FP_FRAC_HIGH_##wc(X) |= _FP_QNANBIT_##fs; \ + break; \ + } \ + __ret; \ +}) + + +/* + * Main addition routine. The input values should be cooked. + */ + +#define _FP_ADD(fs, wc, R, X, Y) \ +do { \ + switch (_FP_CLS_COMBINE(X##_c, Y##_c)) \ + { \ + case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NORMAL): \ + { \ + /* shift the smaller number so that its exponent matches the larger */ \ + _FP_I_TYPE diff = X##_e - Y##_e; \ + \ + if (diff < 0) \ + { \ + diff = -diff; \ + if (diff <= _FP_WFRACBITS_##fs) \ + _FP_FRAC_SRS_##wc(X, diff, _FP_WFRACBITS_##fs); \ + else if (!_FP_FRAC_ZEROP_##wc(X)) \ + _FP_FRAC_SET_##wc(X, _FP_MINFRAC_##wc); \ + else \ + _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \ + R##_e = Y##_e; \ + } \ + else \ + { \ + if (diff > 0) \ + { \ + if (diff <= _FP_WFRACBITS_##fs) \ + _FP_FRAC_SRS_##wc(Y, diff, _FP_WFRACBITS_##fs); \ + else if (!_FP_FRAC_ZEROP_##wc(Y)) \ + _FP_FRAC_SET_##wc(Y, _FP_MINFRAC_##wc); \ + else \ + _FP_FRAC_SET_##wc(Y, _FP_ZEROFRAC_##wc); \ + } \ + R##_e = X##_e; \ + } \ + \ + R##_c = FP_CLS_NORMAL; \ + \ + if (X##_s == Y##_s) \ + { \ + R##_s = X##_s; \ + _FP_FRAC_ADD_##wc(R, X, Y); \ + if (_FP_FRAC_OVERP_##wc(fs, R)) \ + { \ + _FP_FRAC_SRS_##wc(R, 1, _FP_WFRACBITS_##fs); \ + R##_e++; \ + } \ + } \ + else \ + { \ + R##_s = X##_s; \ + _FP_FRAC_SUB_##wc(R, X, Y); \ + if (_FP_FRAC_ZEROP_##wc(R)) \ + { \ + /* return an exact zero */ \ + if (FP_ROUNDMODE == FP_RND_MINF) \ + R##_s |= Y##_s; \ + else \ + R##_s &= Y##_s; \ + R##_c = FP_CLS_ZERO; \ + } \ + else \ + { \ + if (_FP_FRAC_NEGP_##wc(R)) \ + { \ + _FP_FRAC_SUB_##wc(R, Y, X); \ + R##_s = Y##_s; \ + } \ + \ + /* renormalize after subtraction */ \ + _FP_FRAC_CLZ_##wc(diff, R); \ + diff -= _FP_WFRACXBITS_##fs; \ + if (diff) \ + { \ + R##_e -= diff; \ + _FP_FRAC_SLL_##wc(R, diff); \ + } \ + } \ + } \ + break; \ + } \ + \ + case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NAN): \ + _FP_CHOOSENAN(fs, wc, R, X, Y); \ + break; \ + \ + case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_ZERO): \ + R##_e = X##_e; \ + case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NORMAL): \ + case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_INF): \ + case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_ZERO): \ + _FP_FRAC_COPY_##wc(R, X); \ + R##_s = X##_s; \ + R##_c = X##_c; \ + break; \ + \ + case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NORMAL): \ + R##_e = Y##_e; \ + case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NAN): \ + case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NAN): \ + case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NAN): \ + _FP_FRAC_COPY_##wc(R, Y); \ + R##_s = Y##_s; \ + R##_c = Y##_c; \ + break; \ + \ + case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_INF): \ + if (X##_s != Y##_s) \ + { \ + /* +INF + -INF => NAN */ \ + _FP_FRAC_SET_##wc(R, _FP_NANFRAC_##fs); \ + R##_s = X##_s ^ Y##_s; \ + R##_c = FP_CLS_NAN; \ + break; \ + } \ + /* FALLTHRU */ \ + \ + case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NORMAL): \ + case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_ZERO): \ + R##_s = X##_s; \ + R##_c = FP_CLS_INF; \ + break; \ + \ + case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_INF): \ + case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_INF): \ + R##_s = Y##_s; \ + R##_c = FP_CLS_INF; \ + break; \ + \ + case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_ZERO): \ + /* make sure the sign is correct */ \ + if (FP_ROUNDMODE == FP_RND_MINF) \ + R##_s = X##_s | Y##_s; \ + else \ + R##_s = X##_s & Y##_s; \ + R##_c = FP_CLS_ZERO; \ + break; \ + \ + default: \ + abort(); \ + } \ +} while (0) + + +/* + * Main negation routine. FIXME -- when we care about setting exception + * bits reliably, this will not do. We should examine all of the fp classes. + */ + +#define _FP_NEG(fs, wc, R, X) \ + do { \ + _FP_FRAC_COPY_##wc(R, X); \ + R##_c = X##_c; \ + R##_e = X##_e; \ + R##_s = 1 ^ X##_s; \ + } while (0) + + +/* + * Main multiplication routine. The input values should be cooked. + */ + +#define _FP_MUL(fs, wc, R, X, Y) \ +do { \ + R##_s = X##_s ^ Y##_s; \ + switch (_FP_CLS_COMBINE(X##_c, Y##_c)) \ + { \ + case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NORMAL): \ + R##_c = FP_CLS_NORMAL; \ + R##_e = X##_e + Y##_e + 1; \ + \ + _FP_MUL_MEAT_##fs(R,X,Y); \ + \ + if (_FP_FRAC_OVERP_##wc(fs, R)) \ + _FP_FRAC_SRS_##wc(R, 1, _FP_WFRACBITS_##fs); \ + else \ + R##_e--; \ + break; \ + \ + case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NAN): \ + _FP_CHOOSENAN(fs, wc, R, X, Y); \ + break; \ + \ + case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NORMAL): \ + case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_INF): \ + case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_ZERO): \ + R##_s = X##_s; \ + \ + case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_INF): \ + case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NORMAL): \ + case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NORMAL): \ + case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_ZERO): \ + _FP_FRAC_COPY_##wc(R, X); \ + R##_c = X##_c; \ + break; \ + \ + case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NAN): \ + case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NAN): \ + case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NAN): \ + R##_s = Y##_s; \ + \ + case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_INF): \ + case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_ZERO): \ + _FP_FRAC_COPY_##wc(R, Y); \ + R##_c = Y##_c; \ + break; \ + \ + case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_ZERO): \ + case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_INF): \ + R##_c = FP_CLS_NAN; \ + _FP_FRAC_SET_##wc(R, _FP_NANFRAC_##fs); \ + break; \ + \ + default: \ + abort(); \ + } \ +} while (0) + + +/* + * Main division routine. The input values should be cooked. + */ + +#define _FP_DIV(fs, wc, R, X, Y) \ +do { \ + R##_s = X##_s ^ Y##_s; \ + switch (_FP_CLS_COMBINE(X##_c, Y##_c)) \ + { \ + case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NORMAL): \ + R##_c = FP_CLS_NORMAL; \ + R##_e = X##_e - Y##_e; \ + \ + _FP_DIV_MEAT_##fs(R,X,Y); \ + break; \ + \ + case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NAN): \ + _FP_CHOOSENAN(fs, wc, R, X, Y); \ + break; \ + \ + case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NORMAL): \ + case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_INF): \ + case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_ZERO): \ + R##_s = X##_s; \ + _FP_FRAC_COPY_##wc(R, X); \ + R##_c = X##_c; \ + break; \ + \ + case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NAN): \ + case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NAN): \ + case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NAN): \ + R##_s = Y##_s; \ + _FP_FRAC_COPY_##wc(R, Y); \ + R##_c = Y##_c; \ + break; \ + \ + case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_INF): \ + case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_INF): \ + case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NORMAL): \ + R##_c = FP_CLS_ZERO; \ + break; \ + \ + case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_ZERO): \ + case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_ZERO): \ + case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NORMAL): \ + R##_c = FP_CLS_INF; \ + break; \ + \ + case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_INF): \ + case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_ZERO): \ + R##_c = FP_CLS_NAN; \ + _FP_FRAC_SET_##wc(R, _FP_NANFRAC_##fs); \ + break; \ + \ + default: \ + abort(); \ + } \ +} while (0) + + +/* + * Main differential comparison routine. The inputs should be raw not + * cooked. The return is -1,0,1 for normal values, 2 otherwise. + */ + +#define _FP_CMP(fs, wc, ret, X, Y, un) \ + do { \ + /* NANs are unordered */ \ + if ((X##_e == _FP_EXPMAX_##fs && !_FP_FRAC_ZEROP_##wc(X)) \ + || (Y##_e == _FP_EXPMAX_##fs && !_FP_FRAC_ZEROP_##wc(Y))) \ + { \ + ret = un; \ + } \ + else \ + { \ + int __x_zero = (!X##_e && _FP_FRAC_ZEROP_##wc(X)) ? 1 : 0; \ + int __y_zero = (!Y##_e && _FP_FRAC_ZEROP_##wc(Y)) ? 1 : 0; \ + \ + if (__x_zero && __y_zero) \ + ret = 0; \ + else if (__x_zero) \ + ret = Y##_s ? 1 : -1; \ + else if (__y_zero) \ + ret = X##_s ? -1 : 1; \ + else if (X##_s != Y##_s) \ + ret = X##_s ? -1 : 1; \ + else if (X##_e > Y##_e) \ + ret = X##_s ? -1 : 1; \ + else if (X##_e < Y##_e) \ + ret = X##_s ? 1 : -1; \ + else if (_FP_FRAC_GT_##wc(X, Y)) \ + ret = X##_s ? -1 : 1; \ + else if (_FP_FRAC_GT_##wc(Y, X)) \ + ret = X##_s ? 1 : -1; \ + else \ + ret = 0; \ + } \ + } while (0) + + +/* Simplification for strict equality. */ + +#define _FP_CMP_EQ(fs, wc, ret, X, Y) \ + do { \ + /* NANs are unordered */ \ + if ((X##_e == _FP_EXPMAX_##fs && !_FP_FRAC_ZEROP_##wc(X)) \ + || (Y##_e == _FP_EXPMAX_##fs && !_FP_FRAC_ZEROP_##wc(Y))) \ + { \ + ret = 1; \ + } \ + else \ + { \ + ret = !(X##_e == Y##_e \ + && _FP_FRAC_EQ_##wc(X, Y) \ + && (X##_s == Y##_s || !X##_e && _FP_FRAC_ZEROP_##wc(X))); \ + } \ + } while (0) + +/* + * Main square root routine. The input value should be cooked. + */ + +#define _FP_SQRT(fs, wc, R, X) \ +do { \ + _FP_FRAC_DECL_##wc(T); _FP_FRAC_DECL_##wc(S); \ + _FP_W_TYPE q; \ + switch (X##_c) \ + { \ + case FP_CLS_NAN: \ + R##_s = 0; \ + R##_c = FP_CLS_NAN; \ + _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \ + break; \ + case FP_CLS_INF: \ + if (X##_s) \ + { \ + R##_s = 0; \ + R##_c = FP_CLS_NAN; /* sNAN */ \ + } \ + else \ + { \ + R##_s = 0; \ + R##_c = FP_CLS_INF; /* sqrt(+inf) = +inf */ \ + } \ + break; \ + case FP_CLS_ZERO: \ + R##_s = X##_s; \ + R##_c = FP_CLS_ZERO; /* sqrt(+-0) = +-0 */ \ + break; \ + case FP_CLS_NORMAL: \ + R##_s = 0; \ + if (X##_s) \ + { \ + R##_c = FP_CLS_NAN; /* sNAN */ \ + break; \ + } \ + R##_c = FP_CLS_NORMAL; \ + if (X##_e & 1) \ + _FP_FRAC_SLL_##wc(X, 1); \ + R##_e = X##_e >> 1; \ + _FP_FRAC_SET_##wc(S, _FP_ZEROFRAC_##wc); \ + _FP_FRAC_SET_##wc(R, _FP_ZEROFRAC_##wc); \ + q = _FP_OVERFLOW_##fs; \ + _FP_FRAC_SLL_##wc(X, 1); \ + _FP_SQRT_MEAT_##wc(R, S, T, X, q); \ + _FP_FRAC_SRL_##wc(R, 1); \ + } \ + } while (0) + +/* + * Convert from FP to integer + */ + +/* "When a NaN, infinity, large positive argument >= 2147483648.0, or + * large negative argument <= -2147483649.0 is converted to an integer, + * the invalid_current bit...should be set and fp_exception_IEEE_754 should + * be raised. If the floating point invalid trap is disabled, no trap occurs + * and a numerical result is generated: if the sign bit of the operand + * is 0, the result is 2147483647; if the sign bit of the operand is 1, + * the result is -2147483648." + * Similarly for conversion to extended ints, except that the boundaries + * are >= 2^63, <= -(2^63 + 1), and the results are 2^63 + 1 for s=0 and + * -2^63 for s=1. + * -- SPARC Architecture Manual V9, Appendix B, which specifies how + * SPARCs resolve implementation dependencies in the IEEE-754 spec. + * I don't believe that the code below follows this. I'm not even sure + * it's right! + * It doesn't cope with needing to convert to an n bit integer when there + * is no n bit integer type. Fortunately gcc provides long long so this + * isn't a problem for sparc32. + * I have, however, fixed its NaN handling to conform as above. + * -- PMM 02/1998 + * NB: rsigned is not 'is r declared signed?' but 'should the value stored + * in r be signed or unsigned?'. r is always(?) declared unsigned. + * Comments below are mine, BTW -- PMM + */ +#define _FP_TO_INT(fs, wc, r, X, rsize, rsigned) \ + do { \ + switch (X##_c) \ + { \ + case FP_CLS_NORMAL: \ + if (X##_e < 0) \ + { \ + /* case FP_CLS_NAN: see above! */ \ + case FP_CLS_ZERO: \ + r = 0; \ + } \ + else if (X##_e >= rsize - (rsigned != 0)) \ + { /* overflow */ \ + case FP_CLS_NAN: \ + case FP_CLS_INF: \ + if (rsigned) \ + { \ + r = 1; \ + r <<= rsize - 1; \ + r -= 1 - X##_s; \ + } \ + else \ + { \ + r = 0; \ + if (!X##_s) \ + r = ~r; \ + } \ + } \ + else \ + { \ + if (_FP_W_TYPE_SIZE*wc < rsize) \ + { \ + _FP_FRAC_ASSEMBLE_##wc(r, X, rsize); \ + r <<= X##_e - _FP_WFRACBITS_##fs; \ + } \ + else \ + { \ + if (X##_e >= _FP_WFRACBITS_##fs) \ + _FP_FRAC_SLL_##wc(X, (X##_e - _FP_WFRACBITS_##fs + 1));\ + else \ + _FP_FRAC_SRL_##wc(X, (_FP_WFRACBITS_##fs - X##_e - 1));\ + _FP_FRAC_ASSEMBLE_##wc(r, X, rsize); \ + } \ + if (rsigned && X##_s) \ + r = -r; \ + } \ + break; \ + } \ + } while (0) + +#define _FP_FROM_INT(fs, wc, X, r, rsize, rtype) \ + do { \ + if (r) \ + { \ + X##_c = FP_CLS_NORMAL; \ + \ + if ((X##_s = (r < 0))) \ + r = -r; \ + /* Note that `r' is now considered unsigned, so we don't have \ + to worry about the single signed overflow case. */ \ + \ + if (rsize <= _FP_W_TYPE_SIZE) \ + __FP_CLZ(X##_e, r); \ + else \ + __FP_CLZ_2(X##_e, (_FP_W_TYPE)(r >> _FP_W_TYPE_SIZE), \ + (_FP_W_TYPE)r); \ + if (rsize < _FP_W_TYPE_SIZE) \ + X##_e -= (_FP_W_TYPE_SIZE - rsize); \ + X##_e = rsize - X##_e - 1; \ + \ + if (_FP_FRACBITS_##fs < rsize && _FP_WFRACBITS_##fs < X##_e) \ + __FP_FRAC_SRS_1(r, (X##_e - _FP_WFRACBITS_##fs), rsize); \ + r &= ~((_FP_W_TYPE)1 << X##_e); \ + _FP_FRAC_DISASSEMBLE_##wc(X, ((unsigned rtype)r), rsize); \ + _FP_FRAC_SLL_##wc(X, (_FP_WFRACBITS_##fs - X##_e - 1)); \ + } \ + else \ + { \ + X##_c = FP_CLS_ZERO, X##_s = 0; \ + } \ + } while (0) + + +#define FP_CONV(dfs,sfs,dwc,swc,D,S) \ + do { \ + _FP_FRAC_CONV_##dwc##_##swc(dfs, sfs, D, S); \ + D##_e = S##_e; \ + D##_c = S##_c; \ + D##_s = S##_s; \ + } while (0) + +/* + * Helper primitives. + */ + +/* Count leading zeros in a word. */ + +#ifndef __FP_CLZ +#if _FP_W_TYPE_SIZE < 64 +/* this is just to shut the compiler up about shifts > word length -- PMM 02/1998 */ +#define __FP_CLZ(r, x) \ + do { \ + _FP_W_TYPE _t = (x); \ + r = _FP_W_TYPE_SIZE - 1; \ + if (_t > 0xffff) r -= 16; \ + if (_t > 0xffff) _t >>= 16; \ + if (_t > 0xff) r -= 8; \ + if (_t > 0xff) _t >>= 8; \ + if (_t & 0xf0) r -= 4; \ + if (_t & 0xf0) _t >>= 4; \ + if (_t & 0xc) r -= 2; \ + if (_t & 0xc) _t >>= 2; \ + if (_t & 0x2) r -= 1; \ + } while (0) +#else /* not _FP_W_TYPE_SIZE < 64 */ +#define __FP_CLZ(r, x) \ + do { \ + _FP_W_TYPE _t = (x); \ + r = _FP_W_TYPE_SIZE - 1; \ + if (_t > 0xffffffff) r -= 32; \ + if (_t > 0xffffffff) _t >>= 32; \ + if (_t > 0xffff) r -= 16; \ + if (_t > 0xffff) _t >>= 16; \ + if (_t > 0xff) r -= 8; \ + if (_t > 0xff) _t >>= 8; \ + if (_t & 0xf0) r -= 4; \ + if (_t & 0xf0) _t >>= 4; \ + if (_t & 0xc) r -= 2; \ + if (_t & 0xc) _t >>= 2; \ + if (_t & 0x2) r -= 1; \ + } while (0) +#endif /* not _FP_W_TYPE_SIZE < 64 */ +#endif /* ndef __FP_CLZ */ + +#define _FP_DIV_HELP_imm(q, r, n, d) \ + do { \ + q = n / d, r = n % d; \ + } while (0) + diff --git a/arch/ppc/math-emu/sfp-machine.h b/arch/ppc/math-emu/sfp-machine.h new file mode 100644 index 00000000000..686e06d2918 --- /dev/null +++ b/arch/ppc/math-emu/sfp-machine.h @@ -0,0 +1,377 @@ +/* Machine-dependent software floating-point definitions. PPC version. + Copyright (C) 1997 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Actually, this is a PPC (32bit) version, written based on the + i386, sparc, and sparc64 versions, by me, + Peter Maydell (pmaydell@chiark.greenend.org.uk). + Comments are by and large also mine, although they may be inaccurate. + + In picking out asm fragments I've gone with the lowest common + denominator, which also happens to be the hardware I have :-> + That is, a SPARC without hardware multiply and divide. + */ + +/* basic word size definitions */ +#define _FP_W_TYPE_SIZE 32 +#define _FP_W_TYPE unsigned long +#define _FP_WS_TYPE signed long +#define _FP_I_TYPE long + +#define __ll_B ((UWtype) 1 << (W_TYPE_SIZE / 2)) +#define __ll_lowpart(t) ((UWtype) (t) & (__ll_B - 1)) +#define __ll_highpart(t) ((UWtype) (t) >> (W_TYPE_SIZE / 2)) + +/* You can optionally code some things like addition in asm. For + * example, i386 defines __FP_FRAC_ADD_2 as asm. If you don't + * then you get a fragment of C code [if you change an #ifdef 0 + * in op-2.h] or a call to add_ssaaaa (see below). + * Good places to look for asm fragments to use are gcc and glibc. + * gcc's longlong.h is useful. + */ + +/* We need to know how to multiply and divide. If the host word size + * is >= 2*fracbits you can use FP_MUL_MEAT_n_imm(t,R,X,Y) which + * codes the multiply with whatever gcc does to 'a * b'. + * _FP_MUL_MEAT_n_wide(t,R,X,Y,f) is used when you have an asm + * function that can multiply two 1W values and get a 2W result. + * Otherwise you're stuck with _FP_MUL_MEAT_n_hard(t,R,X,Y) which + * does bitshifting to avoid overflow. + * For division there is FP_DIV_MEAT_n_imm(t,R,X,Y,f) for word size + * >= 2*fracbits, where f is either _FP_DIV_HELP_imm or + * _FP_DIV_HELP_ldiv (see op-1.h). + * _FP_DIV_MEAT_udiv() is if you have asm to do 2W/1W => (1W, 1W). + * [GCC and glibc have longlong.h which has the asm macro udiv_qrnnd + * to do this.] + * In general, 'n' is the number of words required to hold the type, + * and 't' is either S, D or Q for single/double/quad. + * -- PMM + */ +/* Example: SPARC64: + * #define _FP_MUL_MEAT_S(R,X,Y) _FP_MUL_MEAT_1_imm(S,R,X,Y) + * #define _FP_MUL_MEAT_D(R,X,Y) _FP_MUL_MEAT_1_wide(D,R,X,Y,umul_ppmm) + * #define _FP_MUL_MEAT_Q(R,X,Y) _FP_MUL_MEAT_2_wide(Q,R,X,Y,umul_ppmm) + * + * #define _FP_DIV_MEAT_S(R,X,Y) _FP_DIV_MEAT_1_imm(S,R,X,Y,_FP_DIV_HELP_imm) + * #define _FP_DIV_MEAT_D(R,X,Y) _FP_DIV_MEAT_1_udiv(D,R,X,Y) + * #define _FP_DIV_MEAT_Q(R,X,Y) _FP_DIV_MEAT_2_udiv_64(Q,R,X,Y) + * + * Example: i386: + * #define _FP_MUL_MEAT_S(R,X,Y) _FP_MUL_MEAT_1_wide(S,R,X,Y,_i386_mul_32_64) + * #define _FP_MUL_MEAT_D(R,X,Y) _FP_MUL_MEAT_2_wide(D,R,X,Y,_i386_mul_32_64) + * + * #define _FP_DIV_MEAT_S(R,X,Y) _FP_DIV_MEAT_1_udiv(S,R,X,Y,_i386_div_64_32) + * #define _FP_DIV_MEAT_D(R,X,Y) _FP_DIV_MEAT_2_udiv_64(D,R,X,Y) + */ + +#define _FP_MUL_MEAT_S(R,X,Y) _FP_MUL_MEAT_1_wide(S,R,X,Y,umul_ppmm) +#define _FP_MUL_MEAT_D(R,X,Y) _FP_MUL_MEAT_2_wide(D,R,X,Y,umul_ppmm) + +#define _FP_DIV_MEAT_S(R,X,Y) _FP_DIV_MEAT_1_udiv(S,R,X,Y) +#define _FP_DIV_MEAT_D(R,X,Y) _FP_DIV_MEAT_2_udiv_64(D,R,X,Y) + +/* These macros define what NaN looks like. They're supposed to expand to + * a comma-separated set of 32bit unsigned ints that encode NaN. + */ +#define _FP_NANFRAC_S _FP_QNANBIT_S +#define _FP_NANFRAC_D _FP_QNANBIT_D, 0 +#define _FP_NANFRAC_Q _FP_QNANBIT_Q, 0, 0, 0 + +#define _FP_KEEPNANFRACP 1 + +/* This macro appears to be called when both X and Y are NaNs, and + * has to choose one and copy it to R. i386 goes for the larger of the + * two, sparc64 just picks Y. I don't understand this at all so I'll + * go with sparc64 because it's shorter :-> -- PMM + */ +#define _FP_CHOOSENAN(fs, wc, R, X, Y) \ + do { \ + R##_s = Y##_s; \ + _FP_FRAC_COPY_##wc(R,Y); \ + R##_c = FP_CLS_NAN; \ + } while (0) + + +extern void fp_unpack_d(long *, unsigned long *, unsigned long *, + long *, long *, void *); +extern int fp_pack_d(void *, long, unsigned long, unsigned long, long, long); +extern int fp_pack_ds(void *, long, unsigned long, unsigned long, long, long); + +#define __FP_UNPACK_RAW_1(fs, X, val) \ + do { \ + union _FP_UNION_##fs *_flo = \ + (union _FP_UNION_##fs *)val; \ + \ + X##_f = _flo->bits.frac; \ + X##_e = _flo->bits.exp; \ + X##_s = _flo->bits.sign; \ + } while (0) + +#define __FP_UNPACK_RAW_2(fs, X, val) \ + do { \ + union _FP_UNION_##fs *_flo = \ + (union _FP_UNION_##fs *)val; \ + \ + X##_f0 = _flo->bits.frac0; \ + X##_f1 = _flo->bits.frac1; \ + X##_e = _flo->bits.exp; \ + X##_s = _flo->bits.sign; \ + } while (0) + +#define __FP_UNPACK_S(X,val) \ + do { \ + __FP_UNPACK_RAW_1(S,X,val); \ + _FP_UNPACK_CANONICAL(S,1,X); \ + } while (0) + +#define __FP_UNPACK_D(X,val) \ + fp_unpack_d(&X##_s, &X##_f1, &X##_f0, &X##_e, &X##_c, val) + +#define __FP_PACK_RAW_1(fs, val, X) \ + do { \ + union _FP_UNION_##fs *_flo = \ + (union _FP_UNION_##fs *)val; \ + \ + _flo->bits.frac = X##_f; \ + _flo->bits.exp = X##_e; \ + _flo->bits.sign = X##_s; \ + } while (0) + +#define __FP_PACK_RAW_2(fs, val, X) \ + do { \ + union _FP_UNION_##fs *_flo = \ + (union _FP_UNION_##fs *)val; \ + \ + _flo->bits.frac0 = X##_f0; \ + _flo->bits.frac1 = X##_f1; \ + _flo->bits.exp = X##_e; \ + _flo->bits.sign = X##_s; \ + } while (0) + +#include +#include + +#define __FPU_FPSCR (current->thread.fpscr) + +/* We only actually write to the destination register + * if exceptions signalled (if any) will not trap. + */ +#define __FPU_ENABLED_EXC \ +({ \ + (__FPU_FPSCR >> 3) & 0x1f; \ +}) + +#define __FPU_TRAP_P(bits) \ + ((__FPU_ENABLED_EXC & (bits)) != 0) + +#define __FP_PACK_S(val,X) \ +({ int __exc = _FP_PACK_CANONICAL(S,1,X); \ + if(!__exc || !__FPU_TRAP_P(__exc)) \ + __FP_PACK_RAW_1(S,val,X); \ + __exc; \ +}) + +#define __FP_PACK_D(val,X) \ + fp_pack_d(val, X##_s, X##_f1, X##_f0, X##_e, X##_c) + +#define __FP_PACK_DS(val,X) \ + fp_pack_ds(val, X##_s, X##_f1, X##_f0, X##_e, X##_c) + +/* Obtain the current rounding mode. */ +#define FP_ROUNDMODE \ +({ \ + __FPU_FPSCR & 0x3; \ +}) + +/* the asm fragments go here: all these are taken from glibc-2.0.5's + * stdlib/longlong.h + */ + +#include +#include + +/* add_ssaaaa is used in op-2.h and should be equivalent to + * #define add_ssaaaa(sh,sl,ah,al,bh,bl) (sh = ah+bh+ (( sl = al+bl) < al)) + * add_ssaaaa(high_sum, low_sum, high_addend_1, low_addend_1, + * high_addend_2, low_addend_2) adds two UWtype integers, composed by + * HIGH_ADDEND_1 and LOW_ADDEND_1, and HIGH_ADDEND_2 and LOW_ADDEND_2 + * respectively. The result is placed in HIGH_SUM and LOW_SUM. Overflow + * (i.e. carry out) is not stored anywhere, and is lost. + */ +#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ + do { \ + if (__builtin_constant_p (bh) && (bh) == 0) \ + __asm__ ("{a%I4|add%I4c} %1,%3,%4\n\t{aze|addze} %0,%2" \ + : "=r" ((USItype)(sh)), \ + "=&r" ((USItype)(sl)) \ + : "%r" ((USItype)(ah)), \ + "%r" ((USItype)(al)), \ + "rI" ((USItype)(bl))); \ + else if (__builtin_constant_p (bh) && (bh) ==~(USItype) 0) \ + __asm__ ("{a%I4|add%I4c} %1,%3,%4\n\t{ame|addme} %0,%2" \ + : "=r" ((USItype)(sh)), \ + "=&r" ((USItype)(sl)) \ + : "%r" ((USItype)(ah)), \ + "%r" ((USItype)(al)), \ + "rI" ((USItype)(bl))); \ + else \ + __asm__ ("{a%I5|add%I5c} %1,%4,%5\n\t{ae|adde} %0,%2,%3" \ + : "=r" ((USItype)(sh)), \ + "=&r" ((USItype)(sl)) \ + : "%r" ((USItype)(ah)), \ + "r" ((USItype)(bh)), \ + "%r" ((USItype)(al)), \ + "rI" ((USItype)(bl))); \ + } while (0) + +/* sub_ddmmss is used in op-2.h and udivmodti4.c and should be equivalent to + * #define sub_ddmmss(sh, sl, ah, al, bh, bl) (sh = ah-bh - ((sl = al-bl) > al)) + * sub_ddmmss(high_difference, low_difference, high_minuend, low_minuend, + * high_subtrahend, low_subtrahend) subtracts two two-word UWtype integers, + * composed by HIGH_MINUEND_1 and LOW_MINUEND_1, and HIGH_SUBTRAHEND_2 and + * LOW_SUBTRAHEND_2 respectively. The result is placed in HIGH_DIFFERENCE + * and LOW_DIFFERENCE. Overflow (i.e. carry out) is not stored anywhere, + * and is lost. + */ +#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ + do { \ + if (__builtin_constant_p (ah) && (ah) == 0) \ + __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{sfze|subfze} %0,%2" \ + : "=r" ((USItype)(sh)), \ + "=&r" ((USItype)(sl)) \ + : "r" ((USItype)(bh)), \ + "rI" ((USItype)(al)), \ + "r" ((USItype)(bl))); \ + else if (__builtin_constant_p (ah) && (ah) ==~(USItype) 0) \ + __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{sfme|subfme} %0,%2" \ + : "=r" ((USItype)(sh)), \ + "=&r" ((USItype)(sl)) \ + : "r" ((USItype)(bh)), \ + "rI" ((USItype)(al)), \ + "r" ((USItype)(bl))); \ + else if (__builtin_constant_p (bh) && (bh) == 0) \ + __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{ame|addme} %0,%2" \ + : "=r" ((USItype)(sh)), \ + "=&r" ((USItype)(sl)) \ + : "r" ((USItype)(ah)), \ + "rI" ((USItype)(al)), \ + "r" ((USItype)(bl))); \ + else if (__builtin_constant_p (bh) && (bh) ==~(USItype) 0) \ + __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{aze|addze} %0,%2" \ + : "=r" ((USItype)(sh)), \ + "=&r" ((USItype)(sl)) \ + : "r" ((USItype)(ah)), \ + "rI" ((USItype)(al)), \ + "r" ((USItype)(bl))); \ + else \ + __asm__ ("{sf%I4|subf%I4c} %1,%5,%4\n\t{sfe|subfe} %0,%3,%2" \ + : "=r" ((USItype)(sh)), \ + "=&r" ((USItype)(sl)) \ + : "r" ((USItype)(ah)), \ + "r" ((USItype)(bh)), \ + "rI" ((USItype)(al)), \ + "r" ((USItype)(bl))); \ + } while (0) + +/* asm fragments for mul and div */ + +/* umul_ppmm(high_prod, low_prod, multipler, multiplicand) multiplies two + * UWtype integers MULTIPLER and MULTIPLICAND, and generates a two UWtype + * word product in HIGH_PROD and LOW_PROD. + */ +#define umul_ppmm(ph, pl, m0, m1) \ + do { \ + USItype __m0 = (m0), __m1 = (m1); \ + __asm__ ("mulhwu %0,%1,%2" \ + : "=r" ((USItype)(ph)) \ + : "%r" (__m0), \ + "r" (__m1)); \ + (pl) = __m0 * __m1; \ + } while (0) + +/* udiv_qrnnd(quotient, remainder, high_numerator, low_numerator, + * denominator) divides a UDWtype, composed by the UWtype integers + * HIGH_NUMERATOR and LOW_NUMERATOR, by DENOMINATOR and places the quotient + * in QUOTIENT and the remainder in REMAINDER. HIGH_NUMERATOR must be less + * than DENOMINATOR for correct operation. If, in addition, the most + * significant bit of DENOMINATOR must be 1, then the pre-processor symbol + * UDIV_NEEDS_NORMALIZATION is defined to 1. + */ +#define udiv_qrnnd(q, r, n1, n0, d) \ + do { \ + UWtype __d1, __d0, __q1, __q0, __r1, __r0, __m; \ + __d1 = __ll_highpart (d); \ + __d0 = __ll_lowpart (d); \ + \ + __r1 = (n1) % __d1; \ + __q1 = (n1) / __d1; \ + __m = (UWtype) __q1 * __d0; \ + __r1 = __r1 * __ll_B | __ll_highpart (n0); \ + if (__r1 < __m) \ + { \ + __q1--, __r1 += (d); \ + if (__r1 >= (d)) /* we didn't get carry when adding to __r1 */ \ + if (__r1 < __m) \ + __q1--, __r1 += (d); \ + } \ + __r1 -= __m; \ + \ + __r0 = __r1 % __d1; \ + __q0 = __r1 / __d1; \ + __m = (UWtype) __q0 * __d0; \ + __r0 = __r0 * __ll_B | __ll_lowpart (n0); \ + if (__r0 < __m) \ + { \ + __q0--, __r0 += (d); \ + if (__r0 >= (d)) \ + if (__r0 < __m) \ + __q0--, __r0 += (d); \ + } \ + __r0 -= __m; \ + \ + (q) = (UWtype) __q1 * __ll_B | __q0; \ + (r) = __r0; \ + } while (0) + +#define UDIV_NEEDS_NORMALIZATION 1 + +#define abort() \ + return 0 + +#ifdef __BIG_ENDIAN +#define __BYTE_ORDER __BIG_ENDIAN +#else +#define __BYTE_ORDER __LITTLE_ENDIAN +#endif + +/* Exception flags. */ +#define EFLAG_INVALID (1 << (31 - 2)) +#define EFLAG_OVERFLOW (1 << (31 - 3)) +#define EFLAG_UNDERFLOW (1 << (31 - 4)) +#define EFLAG_DIVZERO (1 << (31 - 5)) +#define EFLAG_INEXACT (1 << (31 - 6)) + +#define EFLAG_VXSNAN (1 << (31 - 7)) +#define EFLAG_VXISI (1 << (31 - 8)) +#define EFLAG_VXIDI (1 << (31 - 9)) +#define EFLAG_VXZDZ (1 << (31 - 10)) +#define EFLAG_VXIMZ (1 << (31 - 11)) +#define EFLAG_VXVC (1 << (31 - 12)) +#define EFLAG_VXSOFT (1 << (31 - 21)) +#define EFLAG_VXSQRT (1 << (31 - 22)) +#define EFLAG_VXCVI (1 << (31 - 23)) diff --git a/arch/ppc/math-emu/single.h b/arch/ppc/math-emu/single.h new file mode 100644 index 00000000000..f19d9945181 --- /dev/null +++ b/arch/ppc/math-emu/single.h @@ -0,0 +1,66 @@ +/* + * Definitions for IEEE Single Precision + */ + +#if _FP_W_TYPE_SIZE < 32 +#error "Here's a nickel kid. Go buy yourself a real computer." +#endif + +#define _FP_FRACBITS_S 24 +#define _FP_FRACXBITS_S (_FP_W_TYPE_SIZE - _FP_FRACBITS_S) +#define _FP_WFRACBITS_S (_FP_WORKBITS + _FP_FRACBITS_S) +#define _FP_WFRACXBITS_S (_FP_W_TYPE_SIZE - _FP_WFRACBITS_S) +#define _FP_EXPBITS_S 8 +#define _FP_EXPBIAS_S 127 +#define _FP_EXPMAX_S 255 +#define _FP_QNANBIT_S ((_FP_W_TYPE)1 << (_FP_FRACBITS_S-2)) +#define _FP_IMPLBIT_S ((_FP_W_TYPE)1 << (_FP_FRACBITS_S-1)) +#define _FP_OVERFLOW_S ((_FP_W_TYPE)1 << (_FP_WFRACBITS_S)) + +/* The implementation of _FP_MUL_MEAT_S and _FP_DIV_MEAT_S should be + chosen by the target machine. */ + +union _FP_UNION_S +{ + float flt; + struct { +#if __BYTE_ORDER == __BIG_ENDIAN + unsigned sign : 1; + unsigned exp : _FP_EXPBITS_S; + unsigned frac : _FP_FRACBITS_S - (_FP_IMPLBIT_S != 0); +#else + unsigned frac : _FP_FRACBITS_S - (_FP_IMPLBIT_S != 0); + unsigned exp : _FP_EXPBITS_S; + unsigned sign : 1; +#endif + } bits __attribute__((packed)); +}; + +#define FP_DECL_S(X) _FP_DECL(1,X) +#define FP_UNPACK_RAW_S(X,val) _FP_UNPACK_RAW_1(S,X,val) +#define FP_PACK_RAW_S(val,X) _FP_PACK_RAW_1(S,val,X) + +#define FP_UNPACK_S(X,val) \ + do { \ + _FP_UNPACK_RAW_1(S,X,val); \ + _FP_UNPACK_CANONICAL(S,1,X); \ + } while (0) + +#define FP_PACK_S(val,X) \ + do { \ + _FP_PACK_CANONICAL(S,1,X); \ + _FP_PACK_RAW_1(S,val,X); \ + } while (0) + +#define FP_NEG_S(R,X) _FP_NEG(S,1,R,X) +#define FP_ADD_S(R,X,Y) _FP_ADD(S,1,R,X,Y) +#define FP_SUB_S(R,X,Y) _FP_SUB(S,1,R,X,Y) +#define FP_MUL_S(R,X,Y) _FP_MUL(S,1,R,X,Y) +#define FP_DIV_S(R,X,Y) _FP_DIV(S,1,R,X,Y) +#define FP_SQRT_S(R,X) _FP_SQRT(S,1,R,X) + +#define FP_CMP_S(r,X,Y,un) _FP_CMP(S,1,r,X,Y,un) +#define FP_CMP_EQ_S(r,X,Y) _FP_CMP_EQ(S,1,r,X,Y) + +#define FP_TO_INT_S(r,X,rsz,rsg) _FP_TO_INT(S,1,r,X,rsz,rsg) +#define FP_FROM_INT_S(X,r,rs,rt) _FP_FROM_INT(S,1,X,r,rs,rt) diff --git a/arch/ppc/math-emu/soft-fp.h b/arch/ppc/math-emu/soft-fp.h new file mode 100644 index 00000000000..cca39598f87 --- /dev/null +++ b/arch/ppc/math-emu/soft-fp.h @@ -0,0 +1,104 @@ +#ifndef SOFT_FP_H +#define SOFT_FP_H + +#include "sfp-machine.h" + +#define _FP_WORKBITS 3 +#define _FP_WORK_LSB ((_FP_W_TYPE)1 << 3) +#define _FP_WORK_ROUND ((_FP_W_TYPE)1 << 2) +#define _FP_WORK_GUARD ((_FP_W_TYPE)1 << 1) +#define _FP_WORK_STICKY ((_FP_W_TYPE)1 << 0) + +#ifndef FP_RND_NEAREST +# define FP_RND_NEAREST 0 +# define FP_RND_ZERO 1 +# define FP_RND_PINF 2 +# define FP_RND_MINF 3 +#ifndef FP_ROUNDMODE +# define FP_ROUNDMODE FP_RND_NEAREST +#endif +#endif + +#define _FP_ROUND_NEAREST(wc, X) \ +({ int __ret = 0; \ + int __frac = _FP_FRAC_LOW_##wc(X) & 15; \ + if (__frac & 7) { \ + __ret = EFLAG_INEXACT; \ + if ((__frac & 7) != _FP_WORK_ROUND) \ + _FP_FRAC_ADDI_##wc(X, _FP_WORK_ROUND); \ + else if (__frac & _FP_WORK_LSB) \ + _FP_FRAC_ADDI_##wc(X, _FP_WORK_ROUND); \ + } \ + __ret; \ +}) + +#define _FP_ROUND_ZERO(wc, X) \ +({ int __ret = 0; \ + if (_FP_FRAC_LOW_##wc(X) & 7) \ + __ret = EFLAG_INEXACT; \ + __ret; \ +}) + +#define _FP_ROUND_PINF(wc, X) \ +({ int __ret = EFLAG_INEXACT; \ + if (!X##_s && (_FP_FRAC_LOW_##wc(X) & 7)) \ + _FP_FRAC_ADDI_##wc(X, _FP_WORK_LSB); \ + else __ret = 0; \ + __ret; \ +}) + +#define _FP_ROUND_MINF(wc, X) \ +({ int __ret = EFLAG_INEXACT; \ + if (X##_s && (_FP_FRAC_LOW_##wc(X) & 7)) \ + _FP_FRAC_ADDI_##wc(X, _FP_WORK_LSB); \ + else __ret = 0; \ + __ret; \ +}) + +#define _FP_ROUND(wc, X) \ +({ int __ret = 0; \ + switch (FP_ROUNDMODE) \ + { \ + case FP_RND_NEAREST: \ + __ret |= _FP_ROUND_NEAREST(wc,X); \ + break; \ + case FP_RND_ZERO: \ + __ret |= _FP_ROUND_ZERO(wc,X); \ + break; \ + case FP_RND_PINF: \ + __ret |= _FP_ROUND_PINF(wc,X); \ + break; \ + case FP_RND_MINF: \ + __ret |= _FP_ROUND_MINF(wc,X); \ + break; \ + }; \ + __ret; \ +}) + +#define FP_CLS_NORMAL 0 +#define FP_CLS_ZERO 1 +#define FP_CLS_INF 2 +#define FP_CLS_NAN 3 + +#define _FP_CLS_COMBINE(x,y) (((x) << 2) | (y)) + +#include "op-1.h" +#include "op-2.h" +#include "op-4.h" +#include "op-common.h" + +/* Sigh. Silly things longlong.h needs. */ +#define UWtype _FP_W_TYPE +#define W_TYPE_SIZE _FP_W_TYPE_SIZE + +typedef int SItype __attribute__((mode(SI))); +typedef int DItype __attribute__((mode(DI))); +typedef unsigned int USItype __attribute__((mode(SI))); +typedef unsigned int UDItype __attribute__((mode(DI))); +#if _FP_W_TYPE_SIZE == 32 +typedef unsigned int UHWtype __attribute__((mode(HI))); +#elif _FP_W_TYPE_SIZE == 64 +typedef USItype UHWtype; +#endif + +#endif diff --git a/arch/ppc/math-emu/stfd.c b/arch/ppc/math-emu/stfd.c new file mode 100644 index 00000000000..3f8c2558a9e --- /dev/null +++ b/arch/ppc/math-emu/stfd.c @@ -0,0 +1,20 @@ +#include +#include +#include + +int +stfd(void *frS, void *ea) +{ +#if 0 +#ifdef DEBUG + printk("%s: S %p, ea %p: ", __FUNCTION__, frS, ea); + dump_double(frS); + printk("\n"); +#endif +#endif + + if (copy_to_user(ea, frS, sizeof(double))) + return -EFAULT; + + return 0; +} diff --git a/arch/ppc/math-emu/stfiwx.c b/arch/ppc/math-emu/stfiwx.c new file mode 100644 index 00000000000..95caaeec6a0 --- /dev/null +++ b/arch/ppc/math-emu/stfiwx.c @@ -0,0 +1,16 @@ +#include +#include +#include + +int +stfiwx(u32 *frS, void *ea) +{ +#ifdef DEBUG + printk("%s: %p %p\n", __FUNCTION__, frS, ea); +#endif + + if (copy_to_user(ea, &frS[1], sizeof(frS[1]))) + return -EFAULT; + + return 0; +} diff --git a/arch/ppc/math-emu/stfs.c b/arch/ppc/math-emu/stfs.c new file mode 100644 index 00000000000..e87ca23c6dc --- /dev/null +++ b/arch/ppc/math-emu/stfs.c @@ -0,0 +1,41 @@ +#include +#include +#include + +#include "soft-fp.h" +#include "double.h" +#include "single.h" + +int +stfs(void *frS, void *ea) +{ + FP_DECL_D(A); + FP_DECL_S(R); + float f; + int err; + +#ifdef DEBUG + printk("%s: S %p, ea %p\n", __FUNCTION__, frS, ea); +#endif + + __FP_UNPACK_D(A, frS); + +#ifdef DEBUG + printk("A: %ld %lu %lu %ld (%ld)\n", A_s, A_f1, A_f0, A_e, A_c); +#endif + + FP_CONV(S, D, 1, 2, R, A); + +#ifdef DEBUG + printk("R: %ld %lu %ld (%ld)\n", R_s, R_f, R_e, R_c); +#endif + + err = _FP_PACK_CANONICAL(S, 1, R); + if (!err || !__FPU_TRAP_P(err)) { + __FP_PACK_RAW_1(S, &f, R); + if (copy_to_user(ea, &f, sizeof(float))) + return -EFAULT; + } + + return err; +} diff --git a/arch/ppc/math-emu/types.c b/arch/ppc/math-emu/types.c new file mode 100644 index 00000000000..e1ed15d829d --- /dev/null +++ b/arch/ppc/math-emu/types.c @@ -0,0 +1,51 @@ +#include "soft-fp.h" +#include "double.h" +#include "single.h" + +void +fp_unpack_d(long *_s, unsigned long *_f1, unsigned long *_f0, + long *_e, long *_c, void *val) +{ + FP_DECL_D(X); + + __FP_UNPACK_RAW_2(D, X, val); + + _FP_UNPACK_CANONICAL(D, 2, X); + + *_s = X_s; + *_f1 = X_f1; + *_f0 = X_f0; + *_e = X_e; + *_c = X_c; +} + +int +fp_pack_d(void *val, long X_s, unsigned long X_f1, + unsigned long X_f0, long X_e, long X_c) +{ + int exc; + + exc = _FP_PACK_CANONICAL(D, 2, X); + if (!exc || !__FPU_TRAP_P(exc)) + __FP_PACK_RAW_2(D, val, X); + return exc; +} + +int +fp_pack_ds(void *val, long X_s, unsigned long X_f1, + unsigned long X_f0, long X_e, long X_c) +{ + FP_DECL_S(__X); + int exc; + + FP_CONV(S, D, 1, 2, __X, X); + exc = _FP_PACK_CANONICAL(S, 1, __X); + if (!exc || !__FPU_TRAP_P(exc)) { + _FP_UNPACK_CANONICAL(S, 1, __X); + FP_CONV(D, S, 2, 1, X, __X); + exc |= _FP_PACK_CANONICAL(D, 2, X); + if (!exc || !__FPU_TRAP_P(exc)) + __FP_PACK_RAW_2(D, val, X); + } + return exc; +} diff --git a/arch/ppc/math-emu/udivmodti4.c b/arch/ppc/math-emu/udivmodti4.c new file mode 100644 index 00000000000..7e112dc1e2f --- /dev/null +++ b/arch/ppc/math-emu/udivmodti4.c @@ -0,0 +1,191 @@ +/* This has so very few changes over libgcc2's __udivmoddi4 it isn't funny. */ + +#include "soft-fp.h" + +#undef count_leading_zeros +#define count_leading_zeros __FP_CLZ + +void +_fp_udivmodti4(_FP_W_TYPE q[2], _FP_W_TYPE r[2], + _FP_W_TYPE n1, _FP_W_TYPE n0, + _FP_W_TYPE d1, _FP_W_TYPE d0) +{ + _FP_W_TYPE q0, q1, r0, r1; + _FP_I_TYPE b, bm; + + if (d1 == 0) + { +#if !UDIV_NEEDS_NORMALIZATION + if (d0 > n1) + { + /* 0q = nn / 0D */ + + udiv_qrnnd (q0, n0, n1, n0, d0); + q1 = 0; + + /* Remainder in n0. */ + } + else + { + /* qq = NN / 0d */ + + if (d0 == 0) + d0 = 1 / d0; /* Divide intentionally by zero. */ + + udiv_qrnnd (q1, n1, 0, n1, d0); + udiv_qrnnd (q0, n0, n1, n0, d0); + + /* Remainder in n0. */ + } + + r0 = n0; + r1 = 0; + +#else /* UDIV_NEEDS_NORMALIZATION */ + + if (d0 > n1) + { + /* 0q = nn / 0D */ + + count_leading_zeros (bm, d0); + + if (bm != 0) + { + /* Normalize, i.e. make the most significant bit of the + denominator set. */ + + d0 = d0 << bm; + n1 = (n1 << bm) | (n0 >> (_FP_W_TYPE_SIZE - bm)); + n0 = n0 << bm; + } + + udiv_qrnnd (q0, n0, n1, n0, d0); + q1 = 0; + + /* Remainder in n0 >> bm. */ + } + else + { + /* qq = NN / 0d */ + + if (d0 == 0) + d0 = 1 / d0; /* Divide intentionally by zero. */ + + count_leading_zeros (bm, d0); + + if (bm == 0) + { + /* From (n1 >= d0) /\ (the most significant bit of d0 is set), + conclude (the most significant bit of n1 is set) /\ (the + leading quotient digit q1 = 1). + + This special case is necessary, not an optimization. + (Shifts counts of SI_TYPE_SIZE are undefined.) */ + + n1 -= d0; + q1 = 1; + } + else + { + _FP_W_TYPE n2; + + /* Normalize. */ + + b = _FP_W_TYPE_SIZE - bm; + + d0 = d0 << bm; + n2 = n1 >> b; + n1 = (n1 << bm) | (n0 >> b); + n0 = n0 << bm; + + udiv_qrnnd (q1, n1, n2, n1, d0); + } + + /* n1 != d0... */ + + udiv_qrnnd (q0, n0, n1, n0, d0); + + /* Remainder in n0 >> bm. */ + } + + r0 = n0 >> bm; + r1 = 0; +#endif /* UDIV_NEEDS_NORMALIZATION */ + } + else + { + if (d1 > n1) + { + /* 00 = nn / DD */ + + q0 = 0; + q1 = 0; + + /* Remainder in n1n0. */ + r0 = n0; + r1 = n1; + } + else + { + /* 0q = NN / dd */ + + count_leading_zeros (bm, d1); + if (bm == 0) + { + /* From (n1 >= d1) /\ (the most significant bit of d1 is set), + conclude (the most significant bit of n1 is set) /\ (the + quotient digit q0 = 0 or 1). + + This special case is necessary, not an optimization. */ + + /* The condition on the next line takes advantage of that + n1 >= d1 (true due to program flow). */ + if (n1 > d1 || n0 >= d0) + { + q0 = 1; + sub_ddmmss (n1, n0, n1, n0, d1, d0); + } + else + q0 = 0; + + q1 = 0; + + r0 = n0; + r1 = n1; + } + else + { + _FP_W_TYPE m1, m0, n2; + + /* Normalize. */ + + b = _FP_W_TYPE_SIZE - bm; + + d1 = (d1 << bm) | (d0 >> b); + d0 = d0 << bm; + n2 = n1 >> b; + n1 = (n1 << bm) | (n0 >> b); + n0 = n0 << bm; + + udiv_qrnnd (q0, n1, n2, n1, d1); + umul_ppmm (m1, m0, q0, d0); + + if (m1 > n1 || (m1 == n1 && m0 > n0)) + { + q0--; + sub_ddmmss (m1, m0, m1, m0, d1, d0); + } + + q1 = 0; + + /* Remainder in (n1n0 - m1m0) >> bm. */ + sub_ddmmss (n1, n0, n1, n0, m1, m0); + r0 = (n1 << b) | (n0 >> bm); + r1 = n1 >> bm; + } + } + } + + q[0] = q0; q[1] = q1; + r[0] = r0, r[1] = r1; +} diff --git a/arch/ppc/mm/44x_mmu.c b/arch/ppc/mm/44x_mmu.c new file mode 100644 index 00000000000..72f7c0d1c0e --- /dev/null +++ b/arch/ppc/mm/44x_mmu.c @@ -0,0 +1,121 @@ +/* + * Modifications by Matt Porter (mporter@mvista.com) to support + * PPC44x Book E processors. + * + * This file contains the routines for initializing the MMU + * on the 4xx series of chips. + * -- paulus + * + * Derived from arch/ppc/mm/init.c: + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) + * and Cort Dougan (PReP) (cort@cs.nmt.edu) + * Copyright (C) 1996 Paul Mackerras + * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). + * + * Derived from "arch/i386/mm/init.c" + * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mmu_decl.h" + +extern char etext[], _stext[]; + +/* Used by the 44x TLB replacement exception handler. + * Just needed it declared someplace. + */ +unsigned int tlb_44x_index = 0; +unsigned int tlb_44x_hwater = 62; + +/* + * "Pins" a 256MB TLB entry in AS0 for kernel lowmem + */ +static void __init +ppc44x_pin_tlb(int slot, unsigned int virt, unsigned int phys) +{ + unsigned long attrib = 0; + + __asm__ __volatile__("\ + clrrwi %2,%2,10\n\ + ori %2,%2,%4\n\ + clrrwi %1,%1,10\n\ + li %0,0\n\ + ori %0,%0,%5\n\ + tlbwe %2,%3,%6\n\ + tlbwe %1,%3,%7\n\ + tlbwe %0,%3,%8" + : + : "r" (attrib), "r" (phys), "r" (virt), "r" (slot), + "i" (PPC44x_TLB_VALID | PPC44x_TLB_256M), + "i" (PPC44x_TLB_SW | PPC44x_TLB_SR | PPC44x_TLB_SX | PPC44x_TLB_G), + "i" (PPC44x_TLB_PAGEID), + "i" (PPC44x_TLB_XLAT), + "i" (PPC44x_TLB_ATTRIB)); +} + +/* + * MMU_init_hw does the chip-specific initialization of the MMU hardware. + */ +void __init MMU_init_hw(void) +{ + flush_instruction_cache(); +} + +unsigned long __init mmu_mapin_ram(void) +{ + unsigned int pinned_tlbs = 1; + int i; + + /* Determine number of entries necessary to cover lowmem */ + pinned_tlbs = (unsigned int) + (_ALIGN(total_lowmem, PPC44x_PIN_SIZE) >> PPC44x_PIN_SHIFT); + + /* Write upper watermark to save location */ + tlb_44x_hwater = PPC44x_LOW_SLOT - pinned_tlbs; + + /* If necessary, set additional pinned TLBs */ + if (pinned_tlbs > 1) + for (i = (PPC44x_LOW_SLOT-(pinned_tlbs-1)); i < PPC44x_LOW_SLOT; i++) { + unsigned int phys_addr = (PPC44x_LOW_SLOT-i) * PPC44x_PIN_SIZE; + ppc44x_pin_tlb(i, phys_addr+PAGE_OFFSET, phys_addr); + } + + return total_lowmem; +} diff --git a/arch/ppc/mm/4xx_mmu.c b/arch/ppc/mm/4xx_mmu.c new file mode 100644 index 00000000000..a7f61614038 --- /dev/null +++ b/arch/ppc/mm/4xx_mmu.c @@ -0,0 +1,142 @@ +/* + * This file contains the routines for initializing the MMU + * on the 4xx series of chips. + * -- paulus + * + * Derived from arch/ppc/mm/init.c: + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) + * and Cort Dougan (PReP) (cort@cs.nmt.edu) + * Copyright (C) 1996 Paul Mackerras + * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). + * + * Derived from "arch/i386/mm/init.c" + * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mmu_decl.h" + +extern int __map_without_ltlbs; +/* + * MMU_init_hw does the chip-specific initialization of the MMU hardware. + */ +void __init MMU_init_hw(void) +{ + /* + * The Zone Protection Register (ZPR) defines how protection will + * be applied to every page which is a member of a given zone. At + * present, we utilize only two of the 4xx's zones. + * The zone index bits (of ZSEL) in the PTE are used for software + * indicators, except the LSB. For user access, zone 1 is used, + * for kernel access, zone 0 is used. We set all but zone 1 + * to zero, allowing only kernel access as indicated in the PTE. + * For zone 1, we set a 01 binary (a value of 10 will not work) + * to allow user access as indicated in the PTE. This also allows + * kernel access as indicated in the PTE. + */ + + mtspr(SPRN_ZPR, 0x10000000); + + flush_instruction_cache(); + + /* + * Set up the real-mode cache parameters for the exception vector + * handlers (which are run in real-mode). + */ + + mtspr(SPRN_DCWR, 0x00000000); /* All caching is write-back */ + + /* + * Cache instruction and data space where the exception + * vectors and the kernel live in real-mode. + */ + + mtspr(SPRN_DCCR, 0xF0000000); /* 512 MB of data space at 0x0. */ + mtspr(SPRN_ICCR, 0xF0000000); /* 512 MB of instr. space at 0x0. */ +} + +#define LARGE_PAGE_SIZE_16M (1<<24) +#define LARGE_PAGE_SIZE_4M (1<<22) + +unsigned long __init mmu_mapin_ram(void) +{ + unsigned long v, s; + phys_addr_t p; + + v = KERNELBASE; + p = PPC_MEMSTART; + s = 0; + + if (__map_without_ltlbs) { + return s; + } + + while (s <= (total_lowmem - LARGE_PAGE_SIZE_16M)) { + pmd_t *pmdp; + unsigned long val = p | _PMD_SIZE_16M | _PAGE_HWEXEC | _PAGE_HWWRITE; + + spin_lock(&init_mm.page_table_lock); + pmdp = pmd_offset(pgd_offset_k(v), v); + pmd_val(*pmdp++) = val; + pmd_val(*pmdp++) = val; + pmd_val(*pmdp++) = val; + pmd_val(*pmdp++) = val; + spin_unlock(&init_mm.page_table_lock); + + v += LARGE_PAGE_SIZE_16M; + p += LARGE_PAGE_SIZE_16M; + s += LARGE_PAGE_SIZE_16M; + } + + while (s <= (total_lowmem - LARGE_PAGE_SIZE_4M)) { + pmd_t *pmdp; + unsigned long val = p | _PMD_SIZE_4M | _PAGE_HWEXEC | _PAGE_HWWRITE; + + spin_lock(&init_mm.page_table_lock); + pmdp = pmd_offset(pgd_offset_k(v), v); + pmd_val(*pmdp) = val; + spin_unlock(&init_mm.page_table_lock); + + v += LARGE_PAGE_SIZE_4M; + p += LARGE_PAGE_SIZE_4M; + s += LARGE_PAGE_SIZE_4M; + } + + return s; +} diff --git a/arch/ppc/mm/Makefile b/arch/ppc/mm/Makefile new file mode 100644 index 00000000000..cd3eae147cf --- /dev/null +++ b/arch/ppc/mm/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for the linux ppc-specific parts of the memory manager. +# + +obj-y := fault.o init.o mem_pieces.o \ + mmu_context.o pgtable.o + +obj-$(CONFIG_PPC_STD_MMU) += hashtable.o ppc_mmu.o tlb.o +obj-$(CONFIG_40x) += 4xx_mmu.o +obj-$(CONFIG_44x) += 44x_mmu.o +obj-$(CONFIG_FSL_BOOKE) += fsl_booke_mmu.o diff --git a/arch/ppc/mm/fault.c b/arch/ppc/mm/fault.c new file mode 100644 index 00000000000..57d9930843a --- /dev/null +++ b/arch/ppc/mm/fault.c @@ -0,0 +1,440 @@ +/* + * arch/ppc/mm/fault.c + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Derived from "arch/i386/mm/fault.c" + * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds + * + * Modified by Cort Dougan and Paul Mackerras. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) +extern void (*debugger)(struct pt_regs *); +extern void (*debugger_fault_handler)(struct pt_regs *); +extern int (*debugger_dabr_match)(struct pt_regs *); +int debugger_kernel_faults = 1; +#endif + +unsigned long htab_reloads; /* updated by hashtable.S:hash_page() */ +unsigned long htab_evicts; /* updated by hashtable.S:hash_page() */ +unsigned long htab_preloads; /* updated by hashtable.S:add_hash_page() */ +unsigned long pte_misses; /* updated by do_page_fault() */ +unsigned long pte_errors; /* updated by do_page_fault() */ +unsigned int probingmem; + +/* + * Check whether the instruction at regs->nip is a store using + * an update addressing form which will update r1. + */ +static int store_updates_sp(struct pt_regs *regs) +{ + unsigned int inst; + + if (get_user(inst, (unsigned int __user *)regs->nip)) + return 0; + /* check for 1 in the rA field */ + if (((inst >> 16) & 0x1f) != 1) + return 0; + /* check major opcode */ + switch (inst >> 26) { + case 37: /* stwu */ + case 39: /* stbu */ + case 45: /* sthu */ + case 53: /* stfsu */ + case 55: /* stfdu */ + return 1; + case 31: + /* check minor opcode */ + switch ((inst >> 1) & 0x3ff) { + case 183: /* stwux */ + case 247: /* stbux */ + case 439: /* sthux */ + case 695: /* stfsux */ + case 759: /* stfdux */ + return 1; + } + } + return 0; +} + +/* + * For 600- and 800-family processors, the error_code parameter is DSISR + * for a data fault, SRR1 for an instruction fault. For 400-family processors + * the error_code parameter is ESR for a data fault, 0 for an instruction + * fault. + */ +int do_page_fault(struct pt_regs *regs, unsigned long address, + unsigned long error_code) +{ + struct vm_area_struct * vma; + struct mm_struct *mm = current->mm; + siginfo_t info; + int code = SEGV_MAPERR; +#if defined(CONFIG_4xx) || defined (CONFIG_BOOKE) + int is_write = error_code & ESR_DST; +#else + int is_write = 0; + + /* + * Fortunately the bit assignments in SRR1 for an instruction + * fault and DSISR for a data fault are mostly the same for the + * bits we are interested in. But there are some bits which + * indicate errors in DSISR but can validly be set in SRR1. + */ + if (TRAP(regs) == 0x400) + error_code &= 0x48200000; + else + is_write = error_code & 0x02000000; +#endif /* CONFIG_4xx || CONFIG_BOOKE */ + +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_fault_handler && TRAP(regs) == 0x300) { + debugger_fault_handler(regs); + return 0; + } +#if !(defined(CONFIG_4xx) || defined(CONFIG_BOOKE)) + if (error_code & 0x00400000) { + /* DABR match */ + if (debugger_dabr_match(regs)) + return 0; + } +#endif /* !(CONFIG_4xx || CONFIG_BOOKE)*/ +#endif /* CONFIG_XMON || CONFIG_KGDB */ + + if (in_atomic() || mm == NULL) + return SIGSEGV; + + down_read(&mm->mmap_sem); + vma = find_vma(mm, address); + if (!vma) + goto bad_area; + if (vma->vm_start <= address) + goto good_area; + if (!(vma->vm_flags & VM_GROWSDOWN)) + goto bad_area; + if (!is_write) + goto bad_area; + + /* + * N.B. The rs6000/xcoff ABI allows programs to access up to + * a few hundred bytes below the stack pointer. + * The kernel signal delivery code writes up to about 1.5kB + * below the stack pointer (r1) before decrementing it. + * The exec code can write slightly over 640kB to the stack + * before setting the user r1. Thus we allow the stack to + * expand to 1MB without further checks. + */ + if (address + 0x100000 < vma->vm_end) { + /* get user regs even if this fault is in kernel mode */ + struct pt_regs *uregs = current->thread.regs; + if (uregs == NULL) + goto bad_area; + + /* + * A user-mode access to an address a long way below + * the stack pointer is only valid if the instruction + * is one which would update the stack pointer to the + * address accessed if the instruction completed, + * i.e. either stwu rs,n(r1) or stwux rs,r1,rb + * (or the byte, halfword, float or double forms). + * + * If we don't check this then any write to the area + * between the last mapped region and the stack will + * expand the stack rather than segfaulting. + */ + if (address + 2048 < uregs->gpr[1] + && (!user_mode(regs) || !store_updates_sp(regs))) + goto bad_area; + } + if (expand_stack(vma, address)) + goto bad_area; + +good_area: + code = SEGV_ACCERR; +#if defined(CONFIG_6xx) + if (error_code & 0x95700000) + /* an error such as lwarx to I/O controller space, + address matching DABR, eciwx, etc. */ + goto bad_area; +#endif /* CONFIG_6xx */ +#if defined(CONFIG_8xx) + /* The MPC8xx seems to always set 0x80000000, which is + * "undefined". Of those that can be set, this is the only + * one which seems bad. + */ + if (error_code & 0x10000000) + /* Guarded storage error. */ + goto bad_area; +#endif /* CONFIG_8xx */ + + /* a write */ + if (is_write) { + if (!(vma->vm_flags & VM_WRITE)) + goto bad_area; +#if defined(CONFIG_4xx) || defined(CONFIG_BOOKE) + /* an exec - 4xx/Book-E allows for per-page execute permission */ + } else if (TRAP(regs) == 0x400) { + pte_t *ptep; + +#if 0 + /* It would be nice to actually enforce the VM execute + permission on CPUs which can do so, but far too + much stuff in userspace doesn't get the permissions + right, so we let any page be executed for now. */ + if (! (vma->vm_flags & VM_EXEC)) + goto bad_area; +#endif + + /* Since 4xx/Book-E supports per-page execute permission, + * we lazily flush dcache to icache. */ + ptep = NULL; + if (get_pteptr(mm, address, &ptep) && pte_present(*ptep)) { + struct page *page = pte_page(*ptep); + + if (! test_bit(PG_arch_1, &page->flags)) { + flush_dcache_icache_page(page); + set_bit(PG_arch_1, &page->flags); + } + pte_update(ptep, 0, _PAGE_HWEXEC); + _tlbie(address); + pte_unmap(ptep); + up_read(&mm->mmap_sem); + return 0; + } + if (ptep != NULL) + pte_unmap(ptep); +#endif + /* a read */ + } else { + /* protection fault */ + if (error_code & 0x08000000) + goto bad_area; + if (!(vma->vm_flags & (VM_READ | VM_EXEC))) + goto bad_area; + } + + /* + * If for any reason at all we couldn't handle the fault, + * make sure we exit gracefully rather than endlessly redo + * the fault. + */ + survive: + switch (handle_mm_fault(mm, vma, address, is_write)) { + case VM_FAULT_MINOR: + current->min_flt++; + break; + case VM_FAULT_MAJOR: + current->maj_flt++; + break; + case VM_FAULT_SIGBUS: + goto do_sigbus; + case VM_FAULT_OOM: + goto out_of_memory; + default: + BUG(); + } + + up_read(&mm->mmap_sem); + /* + * keep track of tlb+htab misses that are good addrs but + * just need pte's created via handle_mm_fault() + * -- Cort + */ + pte_misses++; + return 0; + +bad_area: + up_read(&mm->mmap_sem); + pte_errors++; + + /* User mode accesses cause a SIGSEGV */ + if (user_mode(regs)) { + info.si_signo = SIGSEGV; + info.si_errno = 0; + info.si_code = code; + info.si_addr = (void __user *) address; + force_sig_info(SIGSEGV, &info, current); + return 0; + } + + return SIGSEGV; + +/* + * We ran out of memory, or some other thing happened to us that made + * us unable to handle the page fault gracefully. + */ +out_of_memory: + up_read(&mm->mmap_sem); + if (current->pid == 1) { + yield(); + down_read(&mm->mmap_sem); + goto survive; + } + printk("VM: killing process %s\n", current->comm); + if (user_mode(regs)) + do_exit(SIGKILL); + return SIGKILL; + +do_sigbus: + up_read(&mm->mmap_sem); + info.si_signo = SIGBUS; + info.si_errno = 0; + info.si_code = BUS_ADRERR; + info.si_addr = (void __user *)address; + force_sig_info (SIGBUS, &info, current); + if (!user_mode(regs)) + return SIGBUS; + return 0; +} + +/* + * bad_page_fault is called when we have a bad access from the kernel. + * It is called from the DSI and ISI handlers in head.S and from some + * of the procedures in traps.c. + */ +void +bad_page_fault(struct pt_regs *regs, unsigned long address, int sig) +{ + const struct exception_table_entry *entry; + + /* Are we prepared to handle this fault? */ + if ((entry = search_exception_tables(regs->nip)) != NULL) { + regs->nip = entry->fixup; + return; + } + + /* kernel has accessed a bad area */ +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_kernel_faults) + debugger(regs); +#endif + die("kernel access of bad area", regs, sig); +} + +#ifdef CONFIG_8xx + +/* The pgtable.h claims some functions generically exist, but I + * can't find them...... + */ +pte_t *va_to_pte(unsigned long address) +{ + pgd_t *dir; + pmd_t *pmd; + pte_t *pte; + + if (address < TASK_SIZE) + return NULL; + + dir = pgd_offset(&init_mm, address); + if (dir) { + pmd = pmd_offset(dir, address & PAGE_MASK); + if (pmd && pmd_present(*pmd)) { + pte = pte_offset_kernel(pmd, address & PAGE_MASK); + if (pte && pte_present(*pte)) + return(pte); + } + } + return NULL; +} + +unsigned long va_to_phys(unsigned long address) +{ + pte_t *pte; + + pte = va_to_pte(address); + if (pte) + return(((unsigned long)(pte_val(*pte)) & PAGE_MASK) | (address & ~(PAGE_MASK))); + return (0); +} + +void +print_8xx_pte(struct mm_struct *mm, unsigned long addr) +{ + pgd_t * pgd; + pmd_t * pmd; + pte_t * pte; + + printk(" pte @ 0x%8lx: ", addr); + pgd = pgd_offset(mm, addr & PAGE_MASK); + if (pgd) { + pmd = pmd_offset(pgd, addr & PAGE_MASK); + if (pmd && pmd_present(*pmd)) { + pte = pte_offset_kernel(pmd, addr & PAGE_MASK); + if (pte) { + printk(" (0x%08lx)->(0x%08lx)->0x%08lx\n", + (long)pgd, (long)pte, (long)pte_val(*pte)); +#define pp ((long)pte_val(*pte)) + printk(" RPN: %05lx PP: %lx SPS: %lx SH: %lx " + "CI: %lx v: %lx\n", + pp>>12, /* rpn */ + (pp>>10)&3, /* pp */ + (pp>>3)&1, /* small */ + (pp>>2)&1, /* shared */ + (pp>>1)&1, /* cache inhibit */ + pp&1 /* valid */ + ); +#undef pp + } + else { + printk("no pte\n"); + } + } + else { + printk("no pmd\n"); + } + } + else { + printk("no pgd\n"); + } +} + +int +get_8xx_pte(struct mm_struct *mm, unsigned long addr) +{ + pgd_t * pgd; + pmd_t * pmd; + pte_t * pte; + int retval = 0; + + pgd = pgd_offset(mm, addr & PAGE_MASK); + if (pgd) { + pmd = pmd_offset(pgd, addr & PAGE_MASK); + if (pmd && pmd_present(*pmd)) { + pte = pte_offset_kernel(pmd, addr & PAGE_MASK); + if (pte) { + retval = (int)pte_val(*pte); + } + } + } + return(retval); +} +#endif /* CONFIG_8xx */ diff --git a/arch/ppc/mm/fsl_booke_mmu.c b/arch/ppc/mm/fsl_booke_mmu.c new file mode 100644 index 00000000000..36233bdcdf8 --- /dev/null +++ b/arch/ppc/mm/fsl_booke_mmu.c @@ -0,0 +1,236 @@ +/* + * Modifications by Kumar Gala (kumar.gala@freescale.com) to support + * E500 Book E processors. + * + * Copyright 2004 Freescale Semiconductor, Inc + * + * This file contains the routines for initializing the MMU + * on the 4xx series of chips. + * -- paulus + * + * Derived from arch/ppc/mm/init.c: + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) + * and Cort Dougan (PReP) (cort@cs.nmt.edu) + * Copyright (C) 1996 Paul Mackerras + * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). + * + * Derived from "arch/i386/mm/init.c" + * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern void loadcam_entry(unsigned int index); +unsigned int tlbcam_index; +unsigned int num_tlbcam_entries; +static unsigned long __cam0, __cam1, __cam2; +extern unsigned long total_lowmem; +extern unsigned long __max_low_memory; +#define MAX_LOW_MEM CONFIG_LOWMEM_SIZE + +struct tlbcam { + u32 MAS0; + u32 MAS1; + u32 MAS2; + u32 MAS3; + u32 MAS7; +} TLBCAM[NUM_TLBCAMS]; + +struct tlbcamrange { + unsigned long start; + unsigned long limit; + phys_addr_t phys; +} tlbcam_addrs[NUM_TLBCAMS]; + +extern unsigned int tlbcam_index; + +/* + * Return PA for this VA if it is mapped by a CAM, or 0 + */ +unsigned long v_mapped_by_tlbcam(unsigned long va) +{ + int b; + for (b = 0; b < tlbcam_index; ++b) + if (va >= tlbcam_addrs[b].start && va < tlbcam_addrs[b].limit) + return tlbcam_addrs[b].phys + (va - tlbcam_addrs[b].start); + return 0; +} + +/* + * Return VA for a given PA or 0 if not mapped + */ +unsigned long p_mapped_by_tlbcam(unsigned long pa) +{ + int b; + for (b = 0; b < tlbcam_index; ++b) + if (pa >= tlbcam_addrs[b].phys + && pa < (tlbcam_addrs[b].limit-tlbcam_addrs[b].start) + +tlbcam_addrs[b].phys) + return tlbcam_addrs[b].start+(pa-tlbcam_addrs[b].phys); + return 0; +} + +/* + * Set up one of the I/D BAT (block address translation) register pairs. + * The parameters are not checked; in particular size must be a power + * of 4 between 4k and 256M. + */ +void settlbcam(int index, unsigned long virt, phys_addr_t phys, + unsigned int size, int flags, unsigned int pid) +{ + unsigned int tsize, lz; + + asm ("cntlzw %0,%1" : "=r" (lz) : "r" (size)); + tsize = (21 - lz) / 2; + +#ifdef CONFIG_SMP + if ((flags & _PAGE_NO_CACHE) == 0) + flags |= _PAGE_COHERENT; +#endif + + TLBCAM[index].MAS0 = MAS0_TLBSEL(1) | MAS0_ESEL(index); + TLBCAM[index].MAS1 = MAS1_VALID | MAS1_IPROT | MAS1_TSIZE(tsize) | MAS1_TID(pid); + TLBCAM[index].MAS2 = virt & PAGE_MASK; + + TLBCAM[index].MAS2 |= (flags & _PAGE_WRITETHRU) ? MAS2_W : 0; + TLBCAM[index].MAS2 |= (flags & _PAGE_NO_CACHE) ? MAS2_I : 0; + TLBCAM[index].MAS2 |= (flags & _PAGE_COHERENT) ? MAS2_M : 0; + TLBCAM[index].MAS2 |= (flags & _PAGE_GUARDED) ? MAS2_G : 0; + TLBCAM[index].MAS2 |= (flags & _PAGE_ENDIAN) ? MAS2_E : 0; + + TLBCAM[index].MAS3 = (phys & PAGE_MASK) | MAS3_SX | MAS3_SR; + TLBCAM[index].MAS3 |= ((flags & _PAGE_RW) ? MAS3_SW : 0); + +#ifndef CONFIG_KGDB /* want user access for breakpoints */ + if (flags & _PAGE_USER) { + TLBCAM[index].MAS3 |= MAS3_UX | MAS3_UR; + TLBCAM[index].MAS3 |= ((flags & _PAGE_RW) ? MAS3_UW : 0); + } +#else + TLBCAM[index].MAS3 |= MAS3_UX | MAS3_UR; + TLBCAM[index].MAS3 |= ((flags & _PAGE_RW) ? MAS3_UW : 0); +#endif + + tlbcam_addrs[index].start = virt; + tlbcam_addrs[index].limit = virt + size - 1; + tlbcam_addrs[index].phys = phys; + + loadcam_entry(index); +} + +void invalidate_tlbcam_entry(int index) +{ + TLBCAM[index].MAS0 = MAS0_TLBSEL(1) | MAS0_ESEL(index); + TLBCAM[index].MAS1 = ~MAS1_VALID; + + loadcam_entry(index); +} + +void __init cam_mapin_ram(unsigned long cam0, unsigned long cam1, + unsigned long cam2) +{ + settlbcam(0, KERNELBASE, PPC_MEMSTART, cam0, _PAGE_KERNEL, 0); + tlbcam_index++; + if (cam1) { + tlbcam_index++; + settlbcam(1, KERNELBASE+cam0, PPC_MEMSTART+cam0, cam1, _PAGE_KERNEL, 0); + } + if (cam2) { + tlbcam_index++; + settlbcam(2, KERNELBASE+cam0+cam1, PPC_MEMSTART+cam0+cam1, cam2, _PAGE_KERNEL, 0); + } +} + +/* + * MMU_init_hw does the chip-specific initialization of the MMU hardware. + */ +void __init MMU_init_hw(void) +{ + flush_instruction_cache(); +} + +unsigned long __init mmu_mapin_ram(void) +{ + cam_mapin_ram(__cam0, __cam1, __cam2); + + return __cam0 + __cam1 + __cam2; +} + + +void __init +adjust_total_lowmem(void) +{ + unsigned long max_low_mem = MAX_LOW_MEM; + unsigned long cam_max = 0x10000000; + unsigned long ram; + + /* adjust CAM size to max_low_mem */ + if (max_low_mem < cam_max) + cam_max = max_low_mem; + + /* adjust lowmem size to max_low_mem */ + if (max_low_mem < total_lowmem) + ram = max_low_mem; + else + ram = total_lowmem; + + /* Calculate CAM values */ + __cam0 = 1UL << 2 * (__ilog2(ram) / 2); + if (__cam0 > cam_max) + __cam0 = cam_max; + ram -= __cam0; + if (ram) { + __cam1 = 1UL << 2 * (__ilog2(ram) / 2); + if (__cam1 > cam_max) + __cam1 = cam_max; + ram -= __cam1; + } + if (ram) { + __cam2 = 1UL << 2 * (__ilog2(ram) / 2); + if (__cam2 > cam_max) + __cam2 = cam_max; + ram -= __cam2; + } + + printk(KERN_INFO "Memory CAM mapping: CAM0=%ldMb, CAM1=%ldMb," + " CAM2=%ldMb residual: %ldMb\n", + __cam0 >> 20, __cam1 >> 20, __cam2 >> 20, + (total_lowmem - __cam0 - __cam1 - __cam2) >> 20); + __max_low_memory = max_low_mem = __cam0 + __cam1 + __cam2; +} diff --git a/arch/ppc/mm/hashtable.S b/arch/ppc/mm/hashtable.S new file mode 100644 index 00000000000..ab83132a7ed --- /dev/null +++ b/arch/ppc/mm/hashtable.S @@ -0,0 +1,642 @@ +/* + * arch/ppc/kernel/hashtable.S + * + * $Id: hashtable.S,v 1.6 1999/10/08 01:56:15 paulus Exp $ + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * Rewritten by Cort Dougan (cort@cs.nmt.edu) for PReP + * Copyright (C) 1996 Cort Dougan + * Adapted for Power Macintosh by Paul Mackerras. + * Low-level exception handlers and MMU support + * rewritten by Paul Mackerras. + * Copyright (C) 1996 Paul Mackerras. + * + * This file contains low-level assembler routines for managing + * the PowerPC MMU hash table. (PPC 8xx processors don't use a + * hash table, so this file is not used on them.) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_SMP + .comm mmu_hash_lock,4 +#endif /* CONFIG_SMP */ + +/* + * Sync CPUs with hash_page taking & releasing the hash + * table lock + */ +#ifdef CONFIG_SMP + .text +_GLOBAL(hash_page_sync) + lis r8,mmu_hash_lock@h + ori r8,r8,mmu_hash_lock@l + lis r0,0x0fff + b 10f +11: lwz r6,0(r8) + cmpwi 0,r6,0 + bne 11b +10: lwarx r6,0,r8 + cmpwi 0,r6,0 + bne- 11b + stwcx. r0,0,r8 + bne- 10b + isync + eieio + li r0,0 + stw r0,0(r8) + blr +#endif + +/* + * Load a PTE into the hash table, if possible. + * The address is in r4, and r3 contains an access flag: + * _PAGE_RW (0x400) if a write. + * r9 contains the SRR1 value, from which we use the MSR_PR bit. + * SPRG3 contains the physical address of the current task's thread. + * + * Returns to the caller if the access is illegal or there is no + * mapping for the address. Otherwise it places an appropriate PTE + * in the hash table and returns from the exception. + * Uses r0, r3 - r8, ctr, lr. + */ + .text +_GLOBAL(hash_page) +#ifdef CONFIG_PPC64BRIDGE + mfmsr r0 + clrldi r0,r0,1 /* make sure it's in 32-bit mode */ + MTMSRD(r0) + isync +#endif + tophys(r7,0) /* gets -KERNELBASE into r7 */ +#ifdef CONFIG_SMP + addis r8,r7,mmu_hash_lock@h + ori r8,r8,mmu_hash_lock@l + lis r0,0x0fff + b 10f +11: lwz r6,0(r8) + cmpwi 0,r6,0 + bne 11b +10: lwarx r6,0,r8 + cmpwi 0,r6,0 + bne- 11b + stwcx. r0,0,r8 + bne- 10b + isync +#endif + /* Get PTE (linux-style) and check access */ + lis r0,KERNELBASE@h /* check if kernel address */ + cmplw 0,r4,r0 + mfspr r8,SPRN_SPRG3 /* current task's THREAD (phys) */ + ori r3,r3,_PAGE_USER|_PAGE_PRESENT /* test low addresses as user */ + lwz r5,PGDIR(r8) /* virt page-table root */ + blt+ 112f /* assume user more likely */ + lis r5,swapper_pg_dir@ha /* if kernel address, use */ + addi r5,r5,swapper_pg_dir@l /* kernel page table */ + rlwimi r3,r9,32-12,29,29 /* MSR_PR -> _PAGE_USER */ +112: add r5,r5,r7 /* convert to phys addr */ + rlwimi r5,r4,12,20,29 /* insert top 10 bits of address */ + lwz r8,0(r5) /* get pmd entry */ + rlwinm. r8,r8,0,0,19 /* extract address of pte page */ +#ifdef CONFIG_SMP + beq- hash_page_out /* return if no mapping */ +#else + /* XXX it seems like the 601 will give a machine fault on the + rfi if its alignment is wrong (bottom 4 bits of address are + 8 or 0xc) and we have had a not-taken conditional branch + to the address following the rfi. */ + beqlr- +#endif + rlwimi r8,r4,22,20,29 /* insert next 10 bits of address */ + rlwinm r0,r3,32-3,24,24 /* _PAGE_RW access -> _PAGE_DIRTY */ + ori r0,r0,_PAGE_ACCESSED|_PAGE_HASHPTE + + /* + * Update the linux PTE atomically. We do the lwarx up-front + * because almost always, there won't be a permission violation + * and there won't already be an HPTE, and thus we will have + * to update the PTE to set _PAGE_HASHPTE. -- paulus. + */ +retry: + lwarx r6,0,r8 /* get linux-style pte */ + andc. r5,r3,r6 /* check access & ~permission */ +#ifdef CONFIG_SMP + bne- hash_page_out /* return if access not permitted */ +#else + bnelr- +#endif + or r5,r0,r6 /* set accessed/dirty bits */ + stwcx. r5,0,r8 /* attempt to update PTE */ + bne- retry /* retry if someone got there first */ + + mfsrin r3,r4 /* get segment reg for segment */ + mfctr r0 + stw r0,_CTR(r11) + bl create_hpte /* add the hash table entry */ + +/* + * htab_reloads counts the number of times we have to fault an + * HPTE into the hash table. This should only happen after a + * fork (because fork does a flush_tlb_mm) or a vmalloc or ioremap. + * Where a page is faulted into a process's address space, + * update_mmu_cache gets called to put the HPTE into the hash table + * and those are counted as preloads rather than reloads. + */ + addis r8,r7,htab_reloads@ha + lwz r3,htab_reloads@l(r8) + addi r3,r3,1 + stw r3,htab_reloads@l(r8) + +#ifdef CONFIG_SMP + eieio + addis r8,r7,mmu_hash_lock@ha + li r0,0 + stw r0,mmu_hash_lock@l(r8) +#endif + + /* Return from the exception */ + lwz r5,_CTR(r11) + mtctr r5 + lwz r0,GPR0(r11) + lwz r7,GPR7(r11) + lwz r8,GPR8(r11) + b fast_exception_return + +#ifdef CONFIG_SMP +hash_page_out: + eieio + addis r8,r7,mmu_hash_lock@ha + li r0,0 + stw r0,mmu_hash_lock@l(r8) + blr +#endif /* CONFIG_SMP */ + +/* + * Add an entry for a particular page to the hash table. + * + * add_hash_page(unsigned context, unsigned long va, unsigned long pmdval) + * + * We assume any necessary modifications to the pte (e.g. setting + * the accessed bit) have already been done and that there is actually + * a hash table in use (i.e. we're not on a 603). + */ +_GLOBAL(add_hash_page) + mflr r0 + stw r0,4(r1) + + /* Convert context and va to VSID */ + mulli r3,r3,897*16 /* multiply context by context skew */ + rlwinm r0,r4,4,28,31 /* get ESID (top 4 bits of va) */ + mulli r0,r0,0x111 /* multiply by ESID skew */ + add r3,r3,r0 /* note create_hpte trims to 24 bits */ + +#ifdef CONFIG_SMP + rlwinm r8,r1,0,0,18 /* use cpu number to make tag */ + lwz r8,TI_CPU(r8) /* to go in mmu_hash_lock */ + oris r8,r8,12 +#endif /* CONFIG_SMP */ + + /* + * We disable interrupts here, even on UP, because we don't + * want to race with hash_page, and because we want the + * _PAGE_HASHPTE bit to be a reliable indication of whether + * the HPTE exists (or at least whether one did once). + * We also turn off the MMU for data accesses so that we + * we can't take a hash table miss (assuming the code is + * covered by a BAT). -- paulus + */ + mfmsr r10 + SYNC + rlwinm r0,r10,0,17,15 /* clear bit 16 (MSR_EE) */ + rlwinm r0,r0,0,28,26 /* clear MSR_DR */ + mtmsr r0 + SYNC_601 + isync + + tophys(r7,0) + +#ifdef CONFIG_SMP + addis r9,r7,mmu_hash_lock@ha + addi r9,r9,mmu_hash_lock@l +10: lwarx r0,0,r9 /* take the mmu_hash_lock */ + cmpi 0,r0,0 + bne- 11f + stwcx. r8,0,r9 + beq+ 12f +11: lwz r0,0(r9) + cmpi 0,r0,0 + beq 10b + b 11b +12: isync +#endif + + /* + * Fetch the linux pte and test and set _PAGE_HASHPTE atomically. + * If _PAGE_HASHPTE was already set, we don't replace the existing + * HPTE, so we just unlock and return. + */ + mr r8,r5 + rlwimi r8,r4,22,20,29 +1: lwarx r6,0,r8 + andi. r0,r6,_PAGE_HASHPTE + bne 9f /* if HASHPTE already set, done */ + ori r5,r6,_PAGE_HASHPTE + stwcx. r5,0,r8 + bne- 1b + + bl create_hpte + + addis r8,r7,htab_preloads@ha + lwz r3,htab_preloads@l(r8) + addi r3,r3,1 + stw r3,htab_preloads@l(r8) + +9: +#ifdef CONFIG_SMP + eieio + li r0,0 + stw r0,0(r9) /* clear mmu_hash_lock */ +#endif + + /* reenable interrupts and DR */ + mtmsr r10 + SYNC_601 + isync + + lwz r0,4(r1) + mtlr r0 + blr + +/* + * This routine adds a hardware PTE to the hash table. + * It is designed to be called with the MMU either on or off. + * r3 contains the VSID, r4 contains the virtual address, + * r5 contains the linux PTE, r6 contains the old value of the + * linux PTE (before setting _PAGE_HASHPTE) and r7 contains the + * offset to be added to addresses (0 if the MMU is on, + * -KERNELBASE if it is off). + * On SMP, the caller should have the mmu_hash_lock held. + * We assume that the caller has (or will) set the _PAGE_HASHPTE + * bit in the linux PTE in memory. The value passed in r6 should + * be the old linux PTE value; if it doesn't have _PAGE_HASHPTE set + * this routine will skip the search for an existing HPTE. + * This procedure modifies r0, r3 - r6, r8, cr0. + * -- paulus. + * + * For speed, 4 of the instructions get patched once the size and + * physical address of the hash table are known. These definitions + * of Hash_base and Hash_bits below are just an example. + */ +Hash_base = 0xc0180000 +Hash_bits = 12 /* e.g. 256kB hash table */ +Hash_msk = (((1 << Hash_bits) - 1) * 64) + +#ifndef CONFIG_PPC64BRIDGE +/* defines for the PTE format for 32-bit PPCs */ +#define PTE_SIZE 8 +#define PTEG_SIZE 64 +#define LG_PTEG_SIZE 6 +#define LDPTEu lwzu +#define STPTE stw +#define CMPPTE cmpw +#define PTE_H 0x40 +#define PTE_V 0x80000000 +#define TST_V(r) rlwinm. r,r,0,0,0 +#define SET_V(r) oris r,r,PTE_V@h +#define CLR_V(r,t) rlwinm r,r,0,1,31 + +#else +/* defines for the PTE format for 64-bit PPCs */ +#define PTE_SIZE 16 +#define PTEG_SIZE 128 +#define LG_PTEG_SIZE 7 +#define LDPTEu ldu +#define STPTE std +#define CMPPTE cmpd +#define PTE_H 2 +#define PTE_V 1 +#define TST_V(r) andi. r,r,PTE_V +#define SET_V(r) ori r,r,PTE_V +#define CLR_V(r,t) li t,PTE_V; andc r,r,t +#endif /* CONFIG_PPC64BRIDGE */ + +#define HASH_LEFT 31-(LG_PTEG_SIZE+Hash_bits-1) +#define HASH_RIGHT 31-LG_PTEG_SIZE + +_GLOBAL(create_hpte) + /* Convert linux-style PTE (r5) to low word of PPC-style PTE (r8) */ + rlwinm r8,r5,32-10,31,31 /* _PAGE_RW -> PP lsb */ + rlwinm r0,r5,32-7,31,31 /* _PAGE_DIRTY -> PP lsb */ + and r8,r8,r0 /* writable if _RW & _DIRTY */ + rlwimi r5,r5,32-1,30,30 /* _PAGE_USER -> PP msb */ + rlwimi r5,r5,32-2,31,31 /* _PAGE_USER -> PP lsb */ + ori r8,r8,0xe14 /* clear out reserved bits and M */ + andc r8,r5,r8 /* PP = user? (rw&dirty? 2: 3): 0 */ +BEGIN_FTR_SECTION + ori r8,r8,_PAGE_COHERENT /* set M (coherence required) */ +END_FTR_SECTION_IFSET(CPU_FTR_NEED_COHERENT) + + /* Construct the high word of the PPC-style PTE (r5) */ +#ifndef CONFIG_PPC64BRIDGE + rlwinm r5,r3,7,1,24 /* put VSID in 0x7fffff80 bits */ + rlwimi r5,r4,10,26,31 /* put in API (abbrev page index) */ +#else /* CONFIG_PPC64BRIDGE */ + clrlwi r3,r3,8 /* reduce vsid to 24 bits */ + sldi r5,r3,12 /* shift vsid into position */ + rlwimi r5,r4,16,20,24 /* put in API (abbrev page index) */ +#endif /* CONFIG_PPC64BRIDGE */ + SET_V(r5) /* set V (valid) bit */ + + /* Get the address of the primary PTE group in the hash table (r3) */ +_GLOBAL(hash_page_patch_A) + addis r0,r7,Hash_base@h /* base address of hash table */ + rlwimi r0,r3,LG_PTEG_SIZE,HASH_LEFT,HASH_RIGHT /* VSID -> hash */ + rlwinm r3,r4,20+LG_PTEG_SIZE,HASH_LEFT,HASH_RIGHT /* PI -> hash */ + xor r3,r3,r0 /* make primary hash */ + li r0,8 /* PTEs/group */ + + /* + * Test the _PAGE_HASHPTE bit in the old linux PTE, and skip the search + * if it is clear, meaning that the HPTE isn't there already... + */ + andi. r6,r6,_PAGE_HASHPTE + beq+ 10f /* no PTE: go look for an empty slot */ + tlbie r4 + + addis r4,r7,htab_hash_searches@ha + lwz r6,htab_hash_searches@l(r4) + addi r6,r6,1 /* count how many searches we do */ + stw r6,htab_hash_searches@l(r4) + + /* Search the primary PTEG for a PTE whose 1st (d)word matches r5 */ + mtctr r0 + addi r4,r3,-PTE_SIZE +1: LDPTEu r6,PTE_SIZE(r4) /* get next PTE */ + CMPPTE 0,r6,r5 + bdnzf 2,1b /* loop while ctr != 0 && !cr0.eq */ + beq+ found_slot + + /* Search the secondary PTEG for a matching PTE */ + ori r5,r5,PTE_H /* set H (secondary hash) bit */ +_GLOBAL(hash_page_patch_B) + xoris r4,r3,Hash_msk>>16 /* compute secondary hash */ + xori r4,r4,(-PTEG_SIZE & 0xffff) + addi r4,r4,-PTE_SIZE + mtctr r0 +2: LDPTEu r6,PTE_SIZE(r4) + CMPPTE 0,r6,r5 + bdnzf 2,2b + beq+ found_slot + xori r5,r5,PTE_H /* clear H bit again */ + + /* Search the primary PTEG for an empty slot */ +10: mtctr r0 + addi r4,r3,-PTE_SIZE /* search primary PTEG */ +1: LDPTEu r6,PTE_SIZE(r4) /* get next PTE */ + TST_V(r6) /* test valid bit */ + bdnzf 2,1b /* loop while ctr != 0 && !cr0.eq */ + beq+ found_empty + + /* update counter of times that the primary PTEG is full */ + addis r4,r7,primary_pteg_full@ha + lwz r6,primary_pteg_full@l(r4) + addi r6,r6,1 + stw r6,primary_pteg_full@l(r4) + + /* Search the secondary PTEG for an empty slot */ + ori r5,r5,PTE_H /* set H (secondary hash) bit */ +_GLOBAL(hash_page_patch_C) + xoris r4,r3,Hash_msk>>16 /* compute secondary hash */ + xori r4,r4,(-PTEG_SIZE & 0xffff) + addi r4,r4,-PTE_SIZE + mtctr r0 +2: LDPTEu r6,PTE_SIZE(r4) + TST_V(r6) + bdnzf 2,2b + beq+ found_empty + xori r5,r5,PTE_H /* clear H bit again */ + + /* + * Choose an arbitrary slot in the primary PTEG to overwrite. + * Since both the primary and secondary PTEGs are full, and we + * have no information that the PTEs in the primary PTEG are + * more important or useful than those in the secondary PTEG, + * and we know there is a definite (although small) speed + * advantage to putting the PTE in the primary PTEG, we always + * put the PTE in the primary PTEG. + */ + addis r4,r7,next_slot@ha + lwz r6,next_slot@l(r4) + addi r6,r6,PTE_SIZE + andi. r6,r6,7*PTE_SIZE + stw r6,next_slot@l(r4) + add r4,r3,r6 + + /* update counter of evicted pages */ + addis r6,r7,htab_evicts@ha + lwz r3,htab_evicts@l(r6) + addi r3,r3,1 + stw r3,htab_evicts@l(r6) + +#ifndef CONFIG_SMP + /* Store PTE in PTEG */ +found_empty: + STPTE r5,0(r4) +found_slot: + STPTE r8,PTE_SIZE/2(r4) + +#else /* CONFIG_SMP */ +/* + * Between the tlbie above and updating the hash table entry below, + * another CPU could read the hash table entry and put it in its TLB. + * There are 3 cases: + * 1. using an empty slot + * 2. updating an earlier entry to change permissions (i.e. enable write) + * 3. taking over the PTE for an unrelated address + * + * In each case it doesn't really matter if the other CPUs have the old + * PTE in their TLB. So we don't need to bother with another tlbie here, + * which is convenient as we've overwritten the register that had the + * address. :-) The tlbie above is mainly to make sure that this CPU comes + * and gets the new PTE from the hash table. + * + * We do however have to make sure that the PTE is never in an invalid + * state with the V bit set. + */ +found_empty: +found_slot: + CLR_V(r5,r0) /* clear V (valid) bit in PTE */ + STPTE r5,0(r4) + sync + TLBSYNC + STPTE r8,PTE_SIZE/2(r4) /* put in correct RPN, WIMG, PP bits */ + sync + SET_V(r5) + STPTE r5,0(r4) /* finally set V bit in PTE */ +#endif /* CONFIG_SMP */ + + sync /* make sure pte updates get to memory */ + blr + + .comm next_slot,4 + .comm primary_pteg_full,4 + .comm htab_hash_searches,4 + +/* + * Flush the entry for a particular page from the hash table. + * + * flush_hash_pages(unsigned context, unsigned long va, unsigned long pmdval, + * int count) + * + * We assume that there is a hash table in use (Hash != 0). + */ +_GLOBAL(flush_hash_pages) + tophys(r7,0) + + /* + * We disable interrupts here, even on UP, because we want + * the _PAGE_HASHPTE bit to be a reliable indication of + * whether the HPTE exists (or at least whether one did once). + * We also turn off the MMU for data accesses so that we + * we can't take a hash table miss (assuming the code is + * covered by a BAT). -- paulus + */ + mfmsr r10 + SYNC + rlwinm r0,r10,0,17,15 /* clear bit 16 (MSR_EE) */ + rlwinm r0,r0,0,28,26 /* clear MSR_DR */ + mtmsr r0 + SYNC_601 + isync + + /* First find a PTE in the range that has _PAGE_HASHPTE set */ + rlwimi r5,r4,22,20,29 +1: lwz r0,0(r5) + cmpwi cr1,r6,1 + andi. r0,r0,_PAGE_HASHPTE + bne 2f + ble cr1,19f + addi r4,r4,0x1000 + addi r5,r5,4 + addi r6,r6,-1 + b 1b + + /* Convert context and va to VSID */ +2: mulli r3,r3,897*16 /* multiply context by context skew */ + rlwinm r0,r4,4,28,31 /* get ESID (top 4 bits of va) */ + mulli r0,r0,0x111 /* multiply by ESID skew */ + add r3,r3,r0 /* note code below trims to 24 bits */ + + /* Construct the high word of the PPC-style PTE (r11) */ +#ifndef CONFIG_PPC64BRIDGE + rlwinm r11,r3,7,1,24 /* put VSID in 0x7fffff80 bits */ + rlwimi r11,r4,10,26,31 /* put in API (abbrev page index) */ +#else /* CONFIG_PPC64BRIDGE */ + clrlwi r3,r3,8 /* reduce vsid to 24 bits */ + sldi r11,r3,12 /* shift vsid into position */ + rlwimi r11,r4,16,20,24 /* put in API (abbrev page index) */ +#endif /* CONFIG_PPC64BRIDGE */ + SET_V(r11) /* set V (valid) bit */ + +#ifdef CONFIG_SMP + addis r9,r7,mmu_hash_lock@ha + addi r9,r9,mmu_hash_lock@l + rlwinm r8,r1,0,0,18 + add r8,r8,r7 + lwz r8,TI_CPU(r8) + oris r8,r8,9 +10: lwarx r0,0,r9 + cmpi 0,r0,0 + bne- 11f + stwcx. r8,0,r9 + beq+ 12f +11: lwz r0,0(r9) + cmpi 0,r0,0 + beq 10b + b 11b +12: isync +#endif + + /* + * Check the _PAGE_HASHPTE bit in the linux PTE. If it is + * already clear, we're done (for this pte). If not, + * clear it (atomically) and proceed. -- paulus. + */ +33: lwarx r8,0,r5 /* fetch the pte */ + andi. r0,r8,_PAGE_HASHPTE + beq 8f /* done if HASHPTE is already clear */ + rlwinm r8,r8,0,31,29 /* clear HASHPTE bit */ + stwcx. r8,0,r5 /* update the pte */ + bne- 33b + + /* Get the address of the primary PTE group in the hash table (r3) */ +_GLOBAL(flush_hash_patch_A) + addis r8,r7,Hash_base@h /* base address of hash table */ + rlwimi r8,r3,LG_PTEG_SIZE,HASH_LEFT,HASH_RIGHT /* VSID -> hash */ + rlwinm r0,r4,20+LG_PTEG_SIZE,HASH_LEFT,HASH_RIGHT /* PI -> hash */ + xor r8,r0,r8 /* make primary hash */ + + /* Search the primary PTEG for a PTE whose 1st (d)word matches r5 */ + li r0,8 /* PTEs/group */ + mtctr r0 + addi r12,r8,-PTE_SIZE +1: LDPTEu r0,PTE_SIZE(r12) /* get next PTE */ + CMPPTE 0,r0,r11 + bdnzf 2,1b /* loop while ctr != 0 && !cr0.eq */ + beq+ 3f + + /* Search the secondary PTEG for a matching PTE */ + ori r11,r11,PTE_H /* set H (secondary hash) bit */ + li r0,8 /* PTEs/group */ +_GLOBAL(flush_hash_patch_B) + xoris r12,r8,Hash_msk>>16 /* compute secondary hash */ + xori r12,r12,(-PTEG_SIZE & 0xffff) + addi r12,r12,-PTE_SIZE + mtctr r0 +2: LDPTEu r0,PTE_SIZE(r12) + CMPPTE 0,r0,r11 + bdnzf 2,2b + xori r11,r11,PTE_H /* clear H again */ + bne- 4f /* should rarely fail to find it */ + +3: li r0,0 + STPTE r0,0(r12) /* invalidate entry */ +4: sync + tlbie r4 /* in hw tlb too */ + sync + +8: ble cr1,9f /* if all ptes checked */ +81: addi r6,r6,-1 + addi r5,r5,4 /* advance to next pte */ + addi r4,r4,0x1000 + lwz r0,0(r5) /* check next pte */ + cmpwi cr1,r6,1 + andi. r0,r0,_PAGE_HASHPTE + bne 33b + bgt cr1,81b + +9: +#ifdef CONFIG_SMP + TLBSYNC + li r0,0 + stw r0,0(r9) /* clear mmu_hash_lock */ +#endif + +19: mtmsr r10 + SYNC_601 + isync + blr diff --git a/arch/ppc/mm/init.c b/arch/ppc/mm/init.c new file mode 100644 index 00000000000..be02a7fec2b --- /dev/null +++ b/arch/ppc/mm/init.c @@ -0,0 +1,667 @@ +/* + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) + * and Cort Dougan (PReP) (cort@cs.nmt.edu) + * Copyright (C) 1996 Paul Mackerras + * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). + * PPC44x/36-bit changes by Matt Porter (mporter@mvista.com) + * + * Derived from "arch/i386/mm/init.c" + * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mem_pieces.h" +#include "mmu_decl.h" + +#if defined(CONFIG_KERNEL_START_BOOL) || defined(CONFIG_LOWMEM_SIZE_BOOL) +/* The ammount of lowmem must be within 0xF0000000 - KERNELBASE. */ +#if (CONFIG_LOWMEM_SIZE > (0xF0000000 - KERNELBASE)) +#error "You must adjust CONFIG_LOWMEM_SIZE or CONFIG_START_KERNEL" +#endif +#endif +#define MAX_LOW_MEM CONFIG_LOWMEM_SIZE + +DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); + +unsigned long total_memory; +unsigned long total_lowmem; + +unsigned long ppc_memstart; +unsigned long ppc_memoffset = PAGE_OFFSET; + +int mem_init_done; +int init_bootmem_done; +int boot_mapsize; +#ifdef CONFIG_PPC_PMAC +unsigned long agp_special_page; +#endif + +extern char _end[]; +extern char etext[], _stext[]; +extern char __init_begin, __init_end; +extern char __prep_begin, __prep_end; +extern char __chrp_begin, __chrp_end; +extern char __pmac_begin, __pmac_end; +extern char __openfirmware_begin, __openfirmware_end; + +#ifdef CONFIG_HIGHMEM +pte_t *kmap_pte; +pgprot_t kmap_prot; + +EXPORT_SYMBOL(kmap_prot); +EXPORT_SYMBOL(kmap_pte); +#endif + +void MMU_init(void); +void set_phys_avail(unsigned long total_ram); + +/* XXX should be in current.h -- paulus */ +extern struct task_struct *current_set[NR_CPUS]; + +char *klimit = _end; +struct mem_pieces phys_avail; + +extern char *sysmap; +extern unsigned long sysmap_size; + +/* + * this tells the system to map all of ram with the segregs + * (i.e. page tables) instead of the bats. + * -- Cort + */ +int __map_without_bats; +int __map_without_ltlbs; + +/* max amount of RAM to use */ +unsigned long __max_memory; +/* max amount of low RAM to map in */ +unsigned long __max_low_memory = MAX_LOW_MEM; + +void show_mem(void) +{ + int i,free = 0,total = 0,reserved = 0; + int shared = 0, cached = 0; + int highmem = 0; + + printk("Mem-info:\n"); + show_free_areas(); + printk("Free swap: %6ldkB\n", nr_swap_pages<<(PAGE_SHIFT-10)); + i = max_mapnr; + while (i-- > 0) { + total++; + if (PageHighMem(mem_map+i)) + highmem++; + if (PageReserved(mem_map+i)) + reserved++; + else if (PageSwapCache(mem_map+i)) + cached++; + else if (!page_count(mem_map+i)) + free++; + else + shared += page_count(mem_map+i) - 1; + } + printk("%d pages of RAM\n",total); + printk("%d pages of HIGHMEM\n", highmem); + printk("%d free pages\n",free); + printk("%d reserved pages\n",reserved); + printk("%d pages shared\n",shared); + printk("%d pages swap cached\n",cached); +} + +/* Free up now-unused memory */ +static void free_sec(unsigned long start, unsigned long end, const char *name) +{ + unsigned long cnt = 0; + + while (start < end) { + ClearPageReserved(virt_to_page(start)); + set_page_count(virt_to_page(start), 1); + free_page(start); + cnt++; + start += PAGE_SIZE; + } + if (cnt) { + printk(" %ldk %s", cnt << (PAGE_SHIFT - 10), name); + totalram_pages += cnt; + } +} + +void free_initmem(void) +{ +#define FREESEC(TYPE) \ + free_sec((unsigned long)(&__ ## TYPE ## _begin), \ + (unsigned long)(&__ ## TYPE ## _end), \ + #TYPE); + + printk ("Freeing unused kernel memory:"); + FREESEC(init); + if (_machine != _MACH_Pmac) + FREESEC(pmac); + if (_machine != _MACH_chrp) + FREESEC(chrp); + if (_machine != _MACH_prep) + FREESEC(prep); + if (!have_of) + FREESEC(openfirmware); + printk("\n"); +#undef FREESEC +} + +#ifdef CONFIG_BLK_DEV_INITRD +void free_initrd_mem(unsigned long start, unsigned long end) +{ + printk ("Freeing initrd memory: %ldk freed\n", (end - start) >> 10); + + for (; start < end; start += PAGE_SIZE) { + ClearPageReserved(virt_to_page(start)); + set_page_count(virt_to_page(start), 1); + free_page(start); + totalram_pages++; + } +} +#endif + +/* + * Check for command-line options that affect what MMU_init will do. + */ +void MMU_setup(void) +{ + /* Check for nobats option (used in mapin_ram). */ + if (strstr(cmd_line, "nobats")) { + __map_without_bats = 1; + } + + if (strstr(cmd_line, "noltlbs")) { + __map_without_ltlbs = 1; + } + + /* Look for mem= option on command line */ + if (strstr(cmd_line, "mem=")) { + char *p, *q; + unsigned long maxmem = 0; + + for (q = cmd_line; (p = strstr(q, "mem=")) != 0; ) { + q = p + 4; + if (p > cmd_line && p[-1] != ' ') + continue; + maxmem = simple_strtoul(q, &q, 0); + if (*q == 'k' || *q == 'K') { + maxmem <<= 10; + ++q; + } else if (*q == 'm' || *q == 'M') { + maxmem <<= 20; + ++q; + } + } + __max_memory = maxmem; + } +} + +/* + * MMU_init sets up the basic memory mappings for the kernel, + * including both RAM and possibly some I/O regions, + * and sets up the page tables and the MMU hardware ready to go. + */ +void __init MMU_init(void) +{ + if (ppc_md.progress) + ppc_md.progress("MMU:enter", 0x111); + + /* parse args from command line */ + MMU_setup(); + + /* + * Figure out how much memory we have, how much + * is lowmem, and how much is highmem. If we were + * passed the total memory size from the bootloader, + * just use it. + */ + if (boot_mem_size) + total_memory = boot_mem_size; + else + total_memory = ppc_md.find_end_of_memory(); + + if (__max_memory && total_memory > __max_memory) + total_memory = __max_memory; + total_lowmem = total_memory; +#ifdef CONFIG_FSL_BOOKE + /* Freescale Book-E parts expect lowmem to be mapped by fixed TLB + * entries, so we need to adjust lowmem to match the amount we can map + * in the fixed entries */ + adjust_total_lowmem(); +#endif /* CONFIG_FSL_BOOKE */ + if (total_lowmem > __max_low_memory) { + total_lowmem = __max_low_memory; +#ifndef CONFIG_HIGHMEM + total_memory = total_lowmem; +#endif /* CONFIG_HIGHMEM */ + } + set_phys_avail(total_lowmem); + + /* Initialize the MMU hardware */ + if (ppc_md.progress) + ppc_md.progress("MMU:hw init", 0x300); + MMU_init_hw(); + + /* Map in all of RAM starting at KERNELBASE */ + if (ppc_md.progress) + ppc_md.progress("MMU:mapin", 0x301); + mapin_ram(); + +#ifdef CONFIG_HIGHMEM + ioremap_base = PKMAP_BASE; +#else + ioremap_base = 0xfe000000UL; /* for now, could be 0xfffff000 */ +#endif /* CONFIG_HIGHMEM */ + ioremap_bot = ioremap_base; + + /* Map in I/O resources */ + if (ppc_md.progress) + ppc_md.progress("MMU:setio", 0x302); + if (ppc_md.setup_io_mappings) + ppc_md.setup_io_mappings(); + + /* Initialize the context management stuff */ + mmu_context_init(); + + if (ppc_md.progress) + ppc_md.progress("MMU:exit", 0x211); + +#ifdef CONFIG_BOOTX_TEXT + /* By default, we are no longer mapped */ + boot_text_mapped = 0; + /* Must be done last, or ppc_md.progress will die. */ + map_boot_text(); +#endif +} + +/* This is only called until mem_init is done. */ +void __init *early_get_page(void) +{ + void *p; + + if (init_bootmem_done) { + p = alloc_bootmem_pages(PAGE_SIZE); + } else { + p = mem_pieces_find(PAGE_SIZE, PAGE_SIZE); + } + return p; +} + +/* + * Initialize the bootmem system and give it all the memory we + * have available. + */ +void __init do_init_bootmem(void) +{ + unsigned long start, size; + int i; + + /* + * Find an area to use for the bootmem bitmap. + * We look for the first area which is at least + * 128kB in length (128kB is enough for a bitmap + * for 4GB of memory, using 4kB pages), plus 1 page + * (in case the address isn't page-aligned). + */ + start = 0; + size = 0; + for (i = 0; i < phys_avail.n_regions; ++i) { + unsigned long a = phys_avail.regions[i].address; + unsigned long s = phys_avail.regions[i].size; + if (s <= size) + continue; + start = a; + size = s; + if (s >= 33 * PAGE_SIZE) + break; + } + start = PAGE_ALIGN(start); + + min_low_pfn = start >> PAGE_SHIFT; + max_low_pfn = (PPC_MEMSTART + total_lowmem) >> PAGE_SHIFT; + max_pfn = (PPC_MEMSTART + total_memory) >> PAGE_SHIFT; + boot_mapsize = init_bootmem_node(&contig_page_data, min_low_pfn, + PPC_MEMSTART >> PAGE_SHIFT, + max_low_pfn); + + /* remove the bootmem bitmap from the available memory */ + mem_pieces_remove(&phys_avail, start, boot_mapsize, 1); + + /* add everything in phys_avail into the bootmem map */ + for (i = 0; i < phys_avail.n_regions; ++i) + free_bootmem(phys_avail.regions[i].address, + phys_avail.regions[i].size); + + init_bootmem_done = 1; +} + +/* + * paging_init() sets up the page tables - in fact we've already done this. + */ +void __init paging_init(void) +{ + unsigned long zones_size[MAX_NR_ZONES], i; + +#ifdef CONFIG_HIGHMEM + map_page(PKMAP_BASE, 0, 0); /* XXX gross */ + pkmap_page_table = pte_offset_kernel(pmd_offset(pgd_offset_k + (PKMAP_BASE), PKMAP_BASE), PKMAP_BASE); + map_page(KMAP_FIX_BEGIN, 0, 0); /* XXX gross */ + kmap_pte = pte_offset_kernel(pmd_offset(pgd_offset_k + (KMAP_FIX_BEGIN), KMAP_FIX_BEGIN), KMAP_FIX_BEGIN); + kmap_prot = PAGE_KERNEL; +#endif /* CONFIG_HIGHMEM */ + + /* + * All pages are DMA-able so we put them all in the DMA zone. + */ + zones_size[ZONE_DMA] = total_lowmem >> PAGE_SHIFT; + for (i = 1; i < MAX_NR_ZONES; i++) + zones_size[i] = 0; + +#ifdef CONFIG_HIGHMEM + zones_size[ZONE_HIGHMEM] = (total_memory - total_lowmem) >> PAGE_SHIFT; +#endif /* CONFIG_HIGHMEM */ + + free_area_init(zones_size); +} + +void __init mem_init(void) +{ + unsigned long addr; + int codepages = 0; + int datapages = 0; + int initpages = 0; +#ifdef CONFIG_HIGHMEM + unsigned long highmem_mapnr; + + highmem_mapnr = total_lowmem >> PAGE_SHIFT; +#endif /* CONFIG_HIGHMEM */ + max_mapnr = total_memory >> PAGE_SHIFT; + + high_memory = (void *) __va(PPC_MEMSTART + total_lowmem); + num_physpages = max_mapnr; /* RAM is assumed contiguous */ + + totalram_pages += free_all_bootmem(); + +#ifdef CONFIG_BLK_DEV_INITRD + /* if we are booted from BootX with an initial ramdisk, + make sure the ramdisk pages aren't reserved. */ + if (initrd_start) { + for (addr = initrd_start; addr < initrd_end; addr += PAGE_SIZE) + ClearPageReserved(virt_to_page(addr)); + } +#endif /* CONFIG_BLK_DEV_INITRD */ + +#ifdef CONFIG_PPC_OF + /* mark the RTAS pages as reserved */ + if ( rtas_data ) + for (addr = (ulong)__va(rtas_data); + addr < PAGE_ALIGN((ulong)__va(rtas_data)+rtas_size) ; + addr += PAGE_SIZE) + SetPageReserved(virt_to_page(addr)); +#endif +#ifdef CONFIG_PPC_PMAC + if (agp_special_page) + SetPageReserved(virt_to_page(agp_special_page)); +#endif + if ( sysmap ) + for (addr = (unsigned long)sysmap; + addr < PAGE_ALIGN((unsigned long)sysmap+sysmap_size) ; + addr += PAGE_SIZE) + SetPageReserved(virt_to_page(addr)); + + for (addr = PAGE_OFFSET; addr < (unsigned long)high_memory; + addr += PAGE_SIZE) { + if (!PageReserved(virt_to_page(addr))) + continue; + if (addr < (ulong) etext) + codepages++; + else if (addr >= (unsigned long)&__init_begin + && addr < (unsigned long)&__init_end) + initpages++; + else if (addr < (ulong) klimit) + datapages++; + } + +#ifdef CONFIG_HIGHMEM + { + unsigned long pfn; + + for (pfn = highmem_mapnr; pfn < max_mapnr; ++pfn) { + struct page *page = mem_map + pfn; + + ClearPageReserved(page); + set_bit(PG_highmem, &page->flags); + set_page_count(page, 1); + __free_page(page); + totalhigh_pages++; + } + totalram_pages += totalhigh_pages; + } +#endif /* CONFIG_HIGHMEM */ + + printk("Memory: %luk available (%dk kernel code, %dk data, %dk init, %ldk highmem)\n", + (unsigned long)nr_free_pages()<< (PAGE_SHIFT-10), + codepages<< (PAGE_SHIFT-10), datapages<< (PAGE_SHIFT-10), + initpages<< (PAGE_SHIFT-10), + (unsigned long) (totalhigh_pages << (PAGE_SHIFT-10))); + if (sysmap) + printk("System.map loaded at 0x%08x for debugger, size: %ld bytes\n", + (unsigned int)sysmap, sysmap_size); +#ifdef CONFIG_PPC_PMAC + if (agp_special_page) + printk(KERN_INFO "AGP special page: 0x%08lx\n", agp_special_page); +#endif + + mem_init_done = 1; +} + +/* + * Set phys_avail to the amount of physical memory, + * less the kernel text/data/bss. + */ +void __init +set_phys_avail(unsigned long total_memory) +{ + unsigned long kstart, ksize; + + /* + * Initially, available physical memory is equivalent to all + * physical memory. + */ + + phys_avail.regions[0].address = PPC_MEMSTART; + phys_avail.regions[0].size = total_memory; + phys_avail.n_regions = 1; + + /* + * Map out the kernel text/data/bss from the available physical + * memory. + */ + + kstart = __pa(_stext); /* should be 0 */ + ksize = PAGE_ALIGN(klimit - _stext); + + mem_pieces_remove(&phys_avail, kstart, ksize, 0); + mem_pieces_remove(&phys_avail, 0, 0x4000, 0); + +#if defined(CONFIG_BLK_DEV_INITRD) + /* Remove the init RAM disk from the available memory. */ + if (initrd_start) { + mem_pieces_remove(&phys_avail, __pa(initrd_start), + initrd_end - initrd_start, 1); + } +#endif /* CONFIG_BLK_DEV_INITRD */ +#ifdef CONFIG_PPC_OF + /* remove the RTAS pages from the available memory */ + if (rtas_data) + mem_pieces_remove(&phys_avail, rtas_data, rtas_size, 1); +#endif + /* remove the sysmap pages from the available memory */ + if (sysmap) + mem_pieces_remove(&phys_avail, __pa(sysmap), sysmap_size, 1); +#ifdef CONFIG_PPC_PMAC + /* Because of some uninorth weirdness, we need a page of + * memory as high as possible (it must be outside of the + * bus address seen as the AGP aperture). It will be used + * by the r128 DRM driver + * + * FIXME: We need to make sure that page doesn't overlap any of the\ + * above. This could be done by improving mem_pieces_find to be able + * to do a backward search from the end of the list. + */ + if (_machine == _MACH_Pmac && find_devices("uni-north-agp")) { + agp_special_page = (total_memory - PAGE_SIZE); + mem_pieces_remove(&phys_avail, agp_special_page, PAGE_SIZE, 0); + agp_special_page = (unsigned long)__va(agp_special_page); + } +#endif /* CONFIG_PPC_PMAC */ +} + +/* Mark some memory as reserved by removing it from phys_avail. */ +void __init reserve_phys_mem(unsigned long start, unsigned long size) +{ + mem_pieces_remove(&phys_avail, start, size, 1); +} + +/* + * This is called when a page has been modified by the kernel. + * It just marks the page as not i-cache clean. We do the i-cache + * flush later when the page is given to a user process, if necessary. + */ +void flush_dcache_page(struct page *page) +{ + clear_bit(PG_arch_1, &page->flags); +} + +void flush_dcache_icache_page(struct page *page) +{ +#ifdef CONFIG_BOOKE + __flush_dcache_icache(kmap(page)); + kunmap(page); +#else + __flush_dcache_icache_phys(page_to_pfn(page) << PAGE_SHIFT); +#endif + +} +void clear_user_page(void *page, unsigned long vaddr, struct page *pg) +{ + clear_page(page); + clear_bit(PG_arch_1, &pg->flags); +} + +void copy_user_page(void *vto, void *vfrom, unsigned long vaddr, + struct page *pg) +{ + copy_page(vto, vfrom); + clear_bit(PG_arch_1, &pg->flags); +} + +void flush_icache_user_range(struct vm_area_struct *vma, struct page *page, + unsigned long addr, int len) +{ + unsigned long maddr; + + maddr = (unsigned long) kmap(page) + (addr & ~PAGE_MASK); + flush_icache_range(maddr, maddr + len); + kunmap(page); +} + +/* + * This is called at the end of handling a user page fault, when the + * fault has been handled by updating a PTE in the linux page tables. + * We use it to preload an HPTE into the hash table corresponding to + * the updated linux PTE. + */ +void update_mmu_cache(struct vm_area_struct *vma, unsigned long address, + pte_t pte) +{ + /* handle i-cache coherency */ + unsigned long pfn = pte_pfn(pte); + + if (pfn_valid(pfn)) { + struct page *page = pfn_to_page(pfn); + if (!PageReserved(page) + && !test_bit(PG_arch_1, &page->flags)) { + if (vma->vm_mm == current->active_mm) + __flush_dcache_icache((void *) address); + else + flush_dcache_icache_page(page); + set_bit(PG_arch_1, &page->flags); + } + } + +#ifdef CONFIG_PPC_STD_MMU + /* We only want HPTEs for linux PTEs that have _PAGE_ACCESSED set */ + if (Hash != 0 && pte_young(pte)) { + struct mm_struct *mm; + pmd_t *pmd; + + mm = (address < TASK_SIZE)? vma->vm_mm: &init_mm; + pmd = pmd_offset(pgd_offset(mm, address), address); + if (!pmd_none(*pmd)) + add_hash_page(mm->context, address, pmd_val(*pmd)); + } +#endif +} + +/* + * This is called by /dev/mem to know if a given address has to + * be mapped non-cacheable or not + */ +int page_is_ram(unsigned long pfn) +{ + unsigned long paddr = (pfn << PAGE_SHIFT); + + return paddr < __pa(high_memory); +} + +pgprot_t phys_mem_access_prot(struct file *file, unsigned long addr, + unsigned long size, pgprot_t vma_prot) +{ + if (ppc_md.phys_mem_access_prot) + return ppc_md.phys_mem_access_prot(file, addr, size, vma_prot); + + if (!page_is_ram(addr >> PAGE_SHIFT)) + vma_prot = __pgprot(pgprot_val(vma_prot) + | _PAGE_GUARDED | _PAGE_NO_CACHE); + return vma_prot; +} +EXPORT_SYMBOL(phys_mem_access_prot); diff --git a/arch/ppc/mm/mem_pieces.c b/arch/ppc/mm/mem_pieces.c new file mode 100644 index 00000000000..3d639052017 --- /dev/null +++ b/arch/ppc/mm/mem_pieces.c @@ -0,0 +1,163 @@ +/* + * Copyright (c) 1996 Paul Mackerras + * Changes to accommodate Power Macintoshes. + * Cort Dougan + * Rewrites. + * Grant Erickson + * General rework and split from mm/init.c. + * + * Module name: mem_pieces.c + * + * Description: + * Routines and data structures for manipulating and representing + * phyiscal memory extents (i.e. address/length pairs). + * + */ + +#include +#include +#include +#include +#include + +#include "mem_pieces.h" + +extern struct mem_pieces phys_avail; + +static void mem_pieces_print(struct mem_pieces *); + +/* + * Scan a region for a piece of a given size with the required alignment. + */ +void __init * +mem_pieces_find(unsigned int size, unsigned int align) +{ + int i; + unsigned a, e; + struct mem_pieces *mp = &phys_avail; + + for (i = 0; i < mp->n_regions; ++i) { + a = mp->regions[i].address; + e = a + mp->regions[i].size; + a = (a + align - 1) & -align; + if (a + size <= e) { + mem_pieces_remove(mp, a, size, 1); + return (void *) __va(a); + } + } + panic("Couldn't find %u bytes at %u alignment\n", size, align); + + return NULL; +} + +/* + * Remove some memory from an array of pieces + */ +void __init +mem_pieces_remove(struct mem_pieces *mp, unsigned int start, unsigned int size, + int must_exist) +{ + int i, j; + unsigned int end, rs, re; + struct reg_property *rp; + + end = start + size; + for (i = 0, rp = mp->regions; i < mp->n_regions; ++i, ++rp) { + if (end > rp->address && start < rp->address + rp->size) + break; + } + if (i >= mp->n_regions) { + if (must_exist) + printk("mem_pieces_remove: [%x,%x) not in any region\n", + start, end); + return; + } + for (; i < mp->n_regions && end > rp->address; ++i, ++rp) { + rs = rp->address; + re = rs + rp->size; + if (must_exist && (start < rs || end > re)) { + printk("mem_pieces_remove: bad overlap [%x,%x) with", + start, end); + mem_pieces_print(mp); + must_exist = 0; + } + if (start > rs) { + rp->size = start - rs; + if (end < re) { + /* need to split this entry */ + if (mp->n_regions >= MEM_PIECES_MAX) + panic("eek... mem_pieces overflow"); + for (j = mp->n_regions; j > i + 1; --j) + mp->regions[j] = mp->regions[j-1]; + ++mp->n_regions; + rp[1].address = end; + rp[1].size = re - end; + } + } else { + if (end < re) { + rp->address = end; + rp->size = re - end; + } else { + /* need to delete this entry */ + for (j = i; j < mp->n_regions - 1; ++j) + mp->regions[j] = mp->regions[j+1]; + --mp->n_regions; + --i; + --rp; + } + } + } +} + +static void __init +mem_pieces_print(struct mem_pieces *mp) +{ + int i; + + for (i = 0; i < mp->n_regions; ++i) + printk(" [%x, %x)", mp->regions[i].address, + mp->regions[i].address + mp->regions[i].size); + printk("\n"); +} + +void __init +mem_pieces_sort(struct mem_pieces *mp) +{ + unsigned long a, s; + int i, j; + + for (i = 1; i < mp->n_regions; ++i) { + a = mp->regions[i].address; + s = mp->regions[i].size; + for (j = i - 1; j >= 0; --j) { + if (a >= mp->regions[j].address) + break; + mp->regions[j+1] = mp->regions[j]; + } + mp->regions[j+1].address = a; + mp->regions[j+1].size = s; + } +} + +void __init +mem_pieces_coalesce(struct mem_pieces *mp) +{ + unsigned long a, s, ns; + int i, j, d; + + d = 0; + for (i = 0; i < mp->n_regions; i = j) { + a = mp->regions[i].address; + s = mp->regions[i].size; + for (j = i + 1; j < mp->n_regions + && mp->regions[j].address - a <= s; ++j) { + ns = mp->regions[j].address + mp->regions[j].size - a; + if (ns > s) + s = ns; + } + mp->regions[d].address = a; + mp->regions[d].size = s; + ++d; + } + mp->n_regions = d; +} diff --git a/arch/ppc/mm/mem_pieces.h b/arch/ppc/mm/mem_pieces.h new file mode 100644 index 00000000000..e2b700dc7f1 --- /dev/null +++ b/arch/ppc/mm/mem_pieces.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 1996 Paul Mackerras + * Changes to accommodate Power Macintoshes. + * Cort Dougan + * Rewrites. + * Grant Erickson + * General rework and split from mm/init.c. + * + * Module name: mem_pieces.h + * + * Description: + * Routines and data structures for manipulating and representing + * phyiscal memory extents (i.e. address/length pairs). + * + */ + +#ifndef __MEM_PIECES_H__ +#define __MEM_PIECES_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Type Definitions */ + +#define MEM_PIECES_MAX 32 + +struct mem_pieces { + int n_regions; + struct reg_property regions[MEM_PIECES_MAX]; +}; + +/* Function Prototypes */ + +extern void *mem_pieces_find(unsigned int size, unsigned int align); +extern void mem_pieces_remove(struct mem_pieces *mp, unsigned int start, + unsigned int size, int must_exist); +extern void mem_pieces_coalesce(struct mem_pieces *mp); +extern void mem_pieces_sort(struct mem_pieces *mp); + +#ifdef __cplusplus +} +#endif + +#endif /* __MEM_PIECES_H__ */ diff --git a/arch/ppc/mm/mmu_context.c b/arch/ppc/mm/mmu_context.c new file mode 100644 index 00000000000..a8816e0f6a8 --- /dev/null +++ b/arch/ppc/mm/mmu_context.c @@ -0,0 +1,86 @@ +/* + * This file contains the routines for handling the MMU on those + * PowerPC implementations where the MMU substantially follows the + * architecture specification. This includes the 6xx, 7xx, 7xxx, + * 8260, and POWER3 implementations but excludes the 8xx and 4xx. + * -- paulus + * + * Derived from arch/ppc/mm/init.c: + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) + * and Cort Dougan (PReP) (cort@cs.nmt.edu) + * Copyright (C) 1996 Paul Mackerras + * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). + * + * Derived from "arch/i386/mm/init.c" + * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include + +#include +#include + +mm_context_t next_mmu_context; +unsigned long context_map[LAST_CONTEXT / BITS_PER_LONG + 1]; +#ifdef FEW_CONTEXTS +atomic_t nr_free_contexts; +struct mm_struct *context_mm[LAST_CONTEXT+1]; +void steal_context(void); +#endif /* FEW_CONTEXTS */ + +/* + * Initialize the context management stuff. + */ +void __init +mmu_context_init(void) +{ + /* + * Some processors have too few contexts to reserve one for + * init_mm, and require using context 0 for a normal task. + * Other processors reserve the use of context zero for the kernel. + * This code assumes FIRST_CONTEXT < 32. + */ + context_map[0] = (1 << FIRST_CONTEXT) - 1; + next_mmu_context = FIRST_CONTEXT; +#ifdef FEW_CONTEXTS + atomic_set(&nr_free_contexts, LAST_CONTEXT - FIRST_CONTEXT + 1); +#endif /* FEW_CONTEXTS */ +} + +#ifdef FEW_CONTEXTS +/* + * Steal a context from a task that has one at the moment. + * This is only used on 8xx and 4xx and we presently assume that + * they don't do SMP. If they do then this will have to check + * whether the MM we steal is in use. + * We also assume that this is only used on systems that don't + * use an MMU hash table - this is true for 8xx and 4xx. + * This isn't an LRU system, it just frees up each context in + * turn (sort-of pseudo-random replacement :). This would be the + * place to implement an LRU scheme if anyone was motivated to do it. + * -- paulus + */ +void +steal_context(void) +{ + struct mm_struct *mm; + + /* free up context `next_mmu_context' */ + /* if we shouldn't free context 0, don't... */ + if (next_mmu_context < FIRST_CONTEXT) + next_mmu_context = FIRST_CONTEXT; + mm = context_mm[next_mmu_context]; + flush_tlb_mm(mm); + destroy_context(mm); +} +#endif /* FEW_CONTEXTS */ diff --git a/arch/ppc/mm/mmu_decl.h b/arch/ppc/mm/mmu_decl.h new file mode 100644 index 00000000000..ffcdb46997d --- /dev/null +++ b/arch/ppc/mm/mmu_decl.h @@ -0,0 +1,83 @@ +/* + * Declarations of procedures and variables shared between files + * in arch/ppc/mm/. + * + * Derived from arch/ppc/mm/init.c: + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) + * and Cort Dougan (PReP) (cort@cs.nmt.edu) + * Copyright (C) 1996 Paul Mackerras + * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). + * + * Derived from "arch/i386/mm/init.c" + * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ +#include +#include + +extern void mapin_ram(void); +extern int map_page(unsigned long va, phys_addr_t pa, int flags); +extern void setbat(int index, unsigned long virt, unsigned long phys, + unsigned int size, int flags); +extern void reserve_phys_mem(unsigned long start, unsigned long size); +extern void settlbcam(int index, unsigned long virt, phys_addr_t phys, + unsigned int size, int flags, unsigned int pid); +extern void invalidate_tlbcam_entry(int index); + +extern int __map_without_bats; +extern unsigned long ioremap_base; +extern unsigned long ioremap_bot; +extern unsigned int rtas_data, rtas_size; + +extern unsigned long total_memory; +extern unsigned long total_lowmem; +extern int mem_init_done; + +extern PTE *Hash, *Hash_end; +extern unsigned long Hash_size, Hash_mask; + +/* ...and now those things that may be slightly different between processor + * architectures. -- Dan + */ +#if defined(CONFIG_8xx) +#define flush_HPTE(X, va, pg) _tlbie(va) +#define MMU_init_hw() do { } while(0) +#define mmu_mapin_ram() (0UL) + +#elif defined(CONFIG_4xx) +#define flush_HPTE(X, va, pg) _tlbie(va) +extern void MMU_init_hw(void); +extern unsigned long mmu_mapin_ram(void); + +#elif defined(CONFIG_FSL_BOOKE) +#define flush_HPTE(X, va, pg) _tlbie(va) +extern void MMU_init_hw(void); +extern unsigned long mmu_mapin_ram(void); +extern void adjust_total_lowmem(void); + +#else +/* anything except 4xx or 8xx */ +extern void MMU_init_hw(void); +extern unsigned long mmu_mapin_ram(void); + +/* Be careful....this needs to be updated if we ever encounter 603 SMPs, + * which includes all new 82xx processors. We need tlbie/tlbsync here + * in that case (I think). -- Dan. + */ +static inline void flush_HPTE(unsigned context, unsigned long va, + unsigned long pdval) +{ + if ((Hash != 0) && + cpu_has_feature(CPU_FTR_HPTE_TABLE)) + flush_hash_pages(0, va, pdval, 1); + else + _tlbie(va); +} +#endif diff --git a/arch/ppc/mm/pgtable.c b/arch/ppc/mm/pgtable.c new file mode 100644 index 00000000000..0a5cd20275c --- /dev/null +++ b/arch/ppc/mm/pgtable.c @@ -0,0 +1,471 @@ +/* + * This file contains the routines setting up the linux page tables. + * -- paulus + * + * Derived from arch/ppc/mm/init.c: + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) + * and Cort Dougan (PReP) (cort@cs.nmt.edu) + * Copyright (C) 1996 Paul Mackerras + * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). + * + * Derived from "arch/i386/mm/init.c" + * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "mmu_decl.h" + +unsigned long ioremap_base; +unsigned long ioremap_bot; +int io_bat_index; + +#if defined(CONFIG_6xx) || defined(CONFIG_POWER3) +#define HAVE_BATS 1 +#endif + +#if defined(CONFIG_FSL_BOOKE) +#define HAVE_TLBCAM 1 +#endif + +extern char etext[], _stext[]; + +#ifdef CONFIG_SMP +extern void hash_page_sync(void); +#endif + +#ifdef HAVE_BATS +extern unsigned long v_mapped_by_bats(unsigned long va); +extern unsigned long p_mapped_by_bats(unsigned long pa); +void setbat(int index, unsigned long virt, unsigned long phys, + unsigned int size, int flags); + +#else /* !HAVE_BATS */ +#define v_mapped_by_bats(x) (0UL) +#define p_mapped_by_bats(x) (0UL) +#endif /* HAVE_BATS */ + +#ifdef HAVE_TLBCAM +extern unsigned int tlbcam_index; +extern unsigned int num_tlbcam_entries; +extern unsigned long v_mapped_by_tlbcam(unsigned long va); +extern unsigned long p_mapped_by_tlbcam(unsigned long pa); +#else /* !HAVE_TLBCAM */ +#define v_mapped_by_tlbcam(x) (0UL) +#define p_mapped_by_tlbcam(x) (0UL) +#endif /* HAVE_TLBCAM */ + +#ifdef CONFIG_44x +/* 44x uses an 8kB pgdir because it has 8-byte Linux PTEs. */ +#define PGDIR_ORDER 1 +#else +#define PGDIR_ORDER 0 +#endif + +pgd_t *pgd_alloc(struct mm_struct *mm) +{ + pgd_t *ret; + + ret = (pgd_t *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, PGDIR_ORDER); + return ret; +} + +void pgd_free(pgd_t *pgd) +{ + free_pages((unsigned long)pgd, PGDIR_ORDER); +} + +pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) +{ + pte_t *pte; + extern int mem_init_done; + extern void *early_get_page(void); + + if (mem_init_done) { + pte = (pte_t *)__get_free_page(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO); + } else { + pte = (pte_t *)early_get_page(); + if (pte) + clear_page(pte); + } + return pte; +} + +struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address) +{ + struct page *ptepage; + +#ifdef CONFIG_HIGHPTE + int flags = GFP_KERNEL | __GFP_HIGHMEM | __GFP_REPEAT; +#else + int flags = GFP_KERNEL | __GFP_REPEAT; +#endif + + ptepage = alloc_pages(flags, 0); + if (ptepage) + clear_highpage(ptepage); + return ptepage; +} + +void pte_free_kernel(pte_t *pte) +{ +#ifdef CONFIG_SMP + hash_page_sync(); +#endif + free_page((unsigned long)pte); +} + +void pte_free(struct page *ptepage) +{ +#ifdef CONFIG_SMP + hash_page_sync(); +#endif + __free_page(ptepage); +} + +#ifndef CONFIG_44x +void __iomem * +ioremap(phys_addr_t addr, unsigned long size) +{ + return __ioremap(addr, size, _PAGE_NO_CACHE); +} +#else /* CONFIG_44x */ +void __iomem * +ioremap64(unsigned long long addr, unsigned long size) +{ + return __ioremap(addr, size, _PAGE_NO_CACHE); +} + +void __iomem * +ioremap(phys_addr_t addr, unsigned long size) +{ + phys_addr_t addr64 = fixup_bigphys_addr(addr, size); + + return ioremap64(addr64, size); +} +#endif /* CONFIG_44x */ + +void __iomem * +__ioremap(phys_addr_t addr, unsigned long size, unsigned long flags) +{ + unsigned long v, i; + phys_addr_t p; + int err; + + /* + * Choose an address to map it to. + * Once the vmalloc system is running, we use it. + * Before then, we use space going down from ioremap_base + * (ioremap_bot records where we're up to). + */ + p = addr & PAGE_MASK; + size = PAGE_ALIGN(addr + size) - p; + + /* + * If the address lies within the first 16 MB, assume it's in ISA + * memory space + */ + if (p < 16*1024*1024) + p += _ISA_MEM_BASE; + + /* + * Don't allow anybody to remap normal RAM that we're using. + * mem_init() sets high_memory so only do the check after that. + */ + if ( mem_init_done && (p < virt_to_phys(high_memory)) ) + { + printk("__ioremap(): phys addr "PTE_FMT" is RAM lr %p\n", p, + __builtin_return_address(0)); + return NULL; + } + + if (size == 0) + return NULL; + + /* + * Is it already mapped? Perhaps overlapped by a previous + * BAT mapping. If the whole area is mapped then we're done, + * otherwise remap it since we want to keep the virt addrs for + * each request contiguous. + * + * We make the assumption here that if the bottom and top + * of the range we want are mapped then it's mapped to the + * same virt address (and this is contiguous). + * -- Cort + */ + if ((v = p_mapped_by_bats(p)) /*&& p_mapped_by_bats(p+size-1)*/ ) + goto out; + + if ((v = p_mapped_by_tlbcam(p))) + goto out; + + if (mem_init_done) { + struct vm_struct *area; + area = get_vm_area(size, VM_IOREMAP); + if (area == 0) + return NULL; + v = (unsigned long) area->addr; + } else { + v = (ioremap_bot -= size); + } + + if ((flags & _PAGE_PRESENT) == 0) + flags |= _PAGE_KERNEL; + if (flags & _PAGE_NO_CACHE) + flags |= _PAGE_GUARDED; + + /* + * Should check if it is a candidate for a BAT mapping + */ + + err = 0; + for (i = 0; i < size && err == 0; i += PAGE_SIZE) + err = map_page(v+i, p+i, flags); + if (err) { + if (mem_init_done) + vunmap((void *)v); + return NULL; + } + +out: + return (void __iomem *) (v + ((unsigned long)addr & ~PAGE_MASK)); +} + +void iounmap(volatile void __iomem *addr) +{ + /* + * If mapped by BATs then there is nothing to do. + * Calling vfree() generates a benign warning. + */ + if (v_mapped_by_bats((unsigned long)addr)) return; + + if (addr > high_memory && (unsigned long) addr < ioremap_bot) + vunmap((void *) (PAGE_MASK & (unsigned long)addr)); +} + +void __iomem *ioport_map(unsigned long port, unsigned int len) +{ + return (void __iomem *) (port + _IO_BASE); +} + +void ioport_unmap(void __iomem *addr) +{ + /* Nothing to do */ +} +EXPORT_SYMBOL(ioport_map); +EXPORT_SYMBOL(ioport_unmap); + +int +map_page(unsigned long va, phys_addr_t pa, int flags) +{ + pmd_t *pd; + pte_t *pg; + int err = -ENOMEM; + + spin_lock(&init_mm.page_table_lock); + /* Use upper 10 bits of VA to index the first level map */ + pd = pmd_offset(pgd_offset_k(va), va); + /* Use middle 10 bits of VA to index the second-level map */ + pg = pte_alloc_kernel(&init_mm, pd, va); + if (pg != 0) { + err = 0; + set_pte_at(&init_mm, va, pg, pfn_pte(pa >> PAGE_SHIFT, __pgprot(flags))); + if (mem_init_done) + flush_HPTE(0, va, pmd_val(*pd)); + } + spin_unlock(&init_mm.page_table_lock); + return err; +} + +/* + * Map in all of physical memory starting at KERNELBASE. + */ +void __init mapin_ram(void) +{ + unsigned long v, p, s, f; + + s = mmu_mapin_ram(); + v = KERNELBASE + s; + p = PPC_MEMSTART + s; + for (; s < total_lowmem; s += PAGE_SIZE) { + if ((char *) v >= _stext && (char *) v < etext) + f = _PAGE_RAM_TEXT; + else + f = _PAGE_RAM; + map_page(v, p, f); + v += PAGE_SIZE; + p += PAGE_SIZE; + } +} + +/* is x a power of 2? */ +#define is_power_of_2(x) ((x) != 0 && (((x) & ((x) - 1)) == 0)) + +/* is x a power of 4? */ +#define is_power_of_4(x) ((x) != 0 && (((x) & (x-1)) == 0) && (ffs(x) & 1)) + +/* + * Set up a mapping for a block of I/O. + * virt, phys, size must all be page-aligned. + * This should only be called before ioremap is called. + */ +void __init io_block_mapping(unsigned long virt, phys_addr_t phys, + unsigned int size, int flags) +{ + int i; + + if (virt > KERNELBASE && virt < ioremap_bot) + ioremap_bot = ioremap_base = virt; + +#ifdef HAVE_BATS + /* + * Use a BAT for this if possible... + */ + if (io_bat_index < 2 && is_power_of_2(size) + && (virt & (size - 1)) == 0 && (phys & (size - 1)) == 0) { + setbat(io_bat_index, virt, phys, size, flags); + ++io_bat_index; + return; + } +#endif /* HAVE_BATS */ + +#ifdef HAVE_TLBCAM + /* + * Use a CAM for this if possible... + */ + if (tlbcam_index < num_tlbcam_entries && is_power_of_4(size) + && (virt & (size - 1)) == 0 && (phys & (size - 1)) == 0) { + settlbcam(tlbcam_index, virt, phys, size, flags, 0); + ++tlbcam_index; + return; + } +#endif /* HAVE_TLBCAM */ + + /* No BATs available, put it in the page tables. */ + for (i = 0; i < size; i += PAGE_SIZE) + map_page(virt + i, phys + i, flags); +} + +/* Scan the real Linux page tables and return a PTE pointer for + * a virtual address in a context. + * Returns true (1) if PTE was found, zero otherwise. The pointer to + * the PTE pointer is unmodified if PTE is not found. + */ +int +get_pteptr(struct mm_struct *mm, unsigned long addr, pte_t **ptep) +{ + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + int retval = 0; + + pgd = pgd_offset(mm, addr & PAGE_MASK); + if (pgd) { + pmd = pmd_offset(pgd, addr & PAGE_MASK); + if (pmd_present(*pmd)) { + pte = pte_offset_map(pmd, addr & PAGE_MASK); + if (pte) { + retval = 1; + *ptep = pte; + /* XXX caller needs to do pte_unmap, yuck */ + } + } + } + return(retval); +} + +/* Find physical address for this virtual address. Normally used by + * I/O functions, but anyone can call it. + */ +unsigned long iopa(unsigned long addr) +{ + unsigned long pa; + + /* I don't know why this won't work on PMacs or CHRP. It + * appears there is some bug, or there is some implicit + * mapping done not properly represented by BATs or in page + * tables.......I am actively working on resolving this, but + * can't hold up other stuff. -- Dan + */ + pte_t *pte; + struct mm_struct *mm; + + /* Check the BATs */ + pa = v_mapped_by_bats(addr); + if (pa) + return pa; + + /* Allow mapping of user addresses (within the thread) + * for DMA if necessary. + */ + if (addr < TASK_SIZE) + mm = current->mm; + else + mm = &init_mm; + + pa = 0; + if (get_pteptr(mm, addr, &pte)) { + pa = (pte_val(*pte) & PAGE_MASK) | (addr & ~PAGE_MASK); + pte_unmap(pte); + } + + return(pa); +} + +/* This is will find the virtual address for a physical one.... + * Swiped from APUS, could be dangerous :-). + * This is only a placeholder until I really find a way to make this + * work. -- Dan + */ +unsigned long +mm_ptov (unsigned long paddr) +{ + unsigned long ret; +#if 0 + if (paddr < 16*1024*1024) + ret = ZTWO_VADDR(paddr); + else { + int i; + + for (i = 0; i < kmap_chunk_count;){ + unsigned long phys = kmap_chunks[i++]; + unsigned long size = kmap_chunks[i++]; + unsigned long virt = kmap_chunks[i++]; + if (paddr >= phys + && paddr < (phys + size)){ + ret = virt + paddr - phys; + goto exit; + } + } + + ret = (unsigned long) __va(paddr); + } +exit: +#ifdef DEBUGPV + printk ("PTOV(%lx)=%lx\n", paddr, ret); +#endif +#else + ret = (unsigned long)paddr + KERNELBASE; +#endif + return ret; +} + diff --git a/arch/ppc/mm/ppc_mmu.c b/arch/ppc/mm/ppc_mmu.c new file mode 100644 index 00000000000..9a381ed5eb2 --- /dev/null +++ b/arch/ppc/mm/ppc_mmu.c @@ -0,0 +1,296 @@ +/* + * This file contains the routines for handling the MMU on those + * PowerPC implementations where the MMU substantially follows the + * architecture specification. This includes the 6xx, 7xx, 7xxx, + * 8260, and POWER3 implementations but excludes the 8xx and 4xx. + * -- paulus + * + * Derived from arch/ppc/mm/init.c: + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) + * and Cort Dougan (PReP) (cort@cs.nmt.edu) + * Copyright (C) 1996 Paul Mackerras + * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). + * + * Derived from "arch/i386/mm/init.c" + * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "mmu_decl.h" +#include "mem_pieces.h" + +PTE *Hash, *Hash_end; +unsigned long Hash_size, Hash_mask; +unsigned long _SDR1; + +union ubat { /* BAT register values to be loaded */ + BAT bat; +#ifdef CONFIG_PPC64BRIDGE + u64 word[2]; +#else + u32 word[2]; +#endif +} BATS[4][2]; /* 4 pairs of IBAT, DBAT */ + +struct batrange { /* stores address ranges mapped by BATs */ + unsigned long start; + unsigned long limit; + unsigned long phys; +} bat_addrs[4]; + +/* + * Return PA for this VA if it is mapped by a BAT, or 0 + */ +unsigned long v_mapped_by_bats(unsigned long va) +{ + int b; + for (b = 0; b < 4; ++b) + if (va >= bat_addrs[b].start && va < bat_addrs[b].limit) + return bat_addrs[b].phys + (va - bat_addrs[b].start); + return 0; +} + +/* + * Return VA for a given PA or 0 if not mapped + */ +unsigned long p_mapped_by_bats(unsigned long pa) +{ + int b; + for (b = 0; b < 4; ++b) + if (pa >= bat_addrs[b].phys + && pa < (bat_addrs[b].limit-bat_addrs[b].start) + +bat_addrs[b].phys) + return bat_addrs[b].start+(pa-bat_addrs[b].phys); + return 0; +} + +unsigned long __init mmu_mapin_ram(void) +{ +#ifdef CONFIG_POWER4 + return 0; +#else + unsigned long tot, bl, done; + unsigned long max_size = (256<<20); + unsigned long align; + + if (__map_without_bats) + return 0; + + /* Set up BAT2 and if necessary BAT3 to cover RAM. */ + + /* Make sure we don't map a block larger than the + smallest alignment of the physical address. */ + /* alignment of PPC_MEMSTART */ + align = ~(PPC_MEMSTART-1) & PPC_MEMSTART; + /* set BAT block size to MIN(max_size, align) */ + if (align && align < max_size) + max_size = align; + + tot = total_lowmem; + for (bl = 128<<10; bl < max_size; bl <<= 1) { + if (bl * 2 > tot) + break; + } + + setbat(2, KERNELBASE, PPC_MEMSTART, bl, _PAGE_RAM); + done = (unsigned long)bat_addrs[2].limit - KERNELBASE + 1; + if ((done < tot) && !bat_addrs[3].limit) { + /* use BAT3 to cover a bit more */ + tot -= done; + for (bl = 128<<10; bl < max_size; bl <<= 1) + if (bl * 2 > tot) + break; + setbat(3, KERNELBASE+done, PPC_MEMSTART+done, bl, _PAGE_RAM); + done = (unsigned long)bat_addrs[3].limit - KERNELBASE + 1; + } + + return done; +#endif +} + +/* + * Set up one of the I/D BAT (block address translation) register pairs. + * The parameters are not checked; in particular size must be a power + * of 2 between 128k and 256M. + */ +void __init setbat(int index, unsigned long virt, unsigned long phys, + unsigned int size, int flags) +{ + unsigned int bl; + int wimgxpp; + union ubat *bat = BATS[index]; + + if (((flags & _PAGE_NO_CACHE) == 0) && + cpu_has_feature(CPU_FTR_NEED_COHERENT)) + flags |= _PAGE_COHERENT; + + bl = (size >> 17) - 1; + if (PVR_VER(mfspr(SPRN_PVR)) != 1) { + /* 603, 604, etc. */ + /* Do DBAT first */ + wimgxpp = flags & (_PAGE_WRITETHRU | _PAGE_NO_CACHE + | _PAGE_COHERENT | _PAGE_GUARDED); + wimgxpp |= (flags & _PAGE_RW)? BPP_RW: BPP_RX; + bat[1].word[0] = virt | (bl << 2) | 2; /* Vs=1, Vp=0 */ + bat[1].word[1] = phys | wimgxpp; +#ifndef CONFIG_KGDB /* want user access for breakpoints */ + if (flags & _PAGE_USER) +#endif + bat[1].bat.batu.vp = 1; + if (flags & _PAGE_GUARDED) { + /* G bit must be zero in IBATs */ + bat[0].word[0] = bat[0].word[1] = 0; + } else { + /* make IBAT same as DBAT */ + bat[0] = bat[1]; + } + } else { + /* 601 cpu */ + if (bl > BL_8M) + bl = BL_8M; + wimgxpp = flags & (_PAGE_WRITETHRU | _PAGE_NO_CACHE + | _PAGE_COHERENT); + wimgxpp |= (flags & _PAGE_RW)? + ((flags & _PAGE_USER)? PP_RWRW: PP_RWXX): PP_RXRX; + bat->word[0] = virt | wimgxpp | 4; /* Ks=0, Ku=1 */ + bat->word[1] = phys | bl | 0x40; /* V=1 */ + } + + bat_addrs[index].start = virt; + bat_addrs[index].limit = virt + ((bl + 1) << 17) - 1; + bat_addrs[index].phys = phys; +} + +/* + * Initialize the hash table and patch the instructions in hashtable.S. + */ +void __init MMU_init_hw(void) +{ + unsigned int hmask, mb, mb2; + unsigned int n_hpteg, lg_n_hpteg; + + extern unsigned int hash_page_patch_A[]; + extern unsigned int hash_page_patch_B[], hash_page_patch_C[]; + extern unsigned int hash_page[]; + extern unsigned int flush_hash_patch_A[], flush_hash_patch_B[]; + + if (!cpu_has_feature(CPU_FTR_HPTE_TABLE)) { + /* + * Put a blr (procedure return) instruction at the + * start of hash_page, since we can still get DSI + * exceptions on a 603. + */ + hash_page[0] = 0x4e800020; + flush_icache_range((unsigned long) &hash_page[0], + (unsigned long) &hash_page[1]); + return; + } + + if ( ppc_md.progress ) ppc_md.progress("hash:enter", 0x105); + +#ifdef CONFIG_PPC64BRIDGE +#define LG_HPTEG_SIZE 7 /* 128 bytes per HPTEG */ +#define SDR1_LOW_BITS (lg_n_hpteg - 11) +#define MIN_N_HPTEG 2048 /* min 256kB hash table */ +#else +#define LG_HPTEG_SIZE 6 /* 64 bytes per HPTEG */ +#define SDR1_LOW_BITS ((n_hpteg - 1) >> 10) +#define MIN_N_HPTEG 1024 /* min 64kB hash table */ +#endif + +#ifdef CONFIG_POWER4 + /* The hash table has already been allocated and initialized + in prom.c */ + n_hpteg = Hash_size >> LG_HPTEG_SIZE; + lg_n_hpteg = __ilog2(n_hpteg); + + /* Remove the hash table from the available memory */ + if (Hash) + reserve_phys_mem(__pa(Hash), Hash_size); + +#else /* CONFIG_POWER4 */ + /* + * Allow 1 HPTE (1/8 HPTEG) for each page of memory. + * This is less than the recommended amount, but then + * Linux ain't AIX. + */ + n_hpteg = total_memory / (PAGE_SIZE * 8); + if (n_hpteg < MIN_N_HPTEG) + n_hpteg = MIN_N_HPTEG; + lg_n_hpteg = __ilog2(n_hpteg); + if (n_hpteg & (n_hpteg - 1)) { + ++lg_n_hpteg; /* round up if not power of 2 */ + n_hpteg = 1 << lg_n_hpteg; + } + Hash_size = n_hpteg << LG_HPTEG_SIZE; + + /* + * Find some memory for the hash table. + */ + if ( ppc_md.progress ) ppc_md.progress("hash:find piece", 0x322); + Hash = mem_pieces_find(Hash_size, Hash_size); + cacheable_memzero(Hash, Hash_size); + _SDR1 = __pa(Hash) | SDR1_LOW_BITS; +#endif /* CONFIG_POWER4 */ + + Hash_end = (PTE *) ((unsigned long)Hash + Hash_size); + + printk("Total memory = %ldMB; using %ldkB for hash table (at %p)\n", + total_memory >> 20, Hash_size >> 10, Hash); + + + /* + * Patch up the instructions in hashtable.S:create_hpte + */ + if ( ppc_md.progress ) ppc_md.progress("hash:patch", 0x345); + Hash_mask = n_hpteg - 1; + hmask = Hash_mask >> (16 - LG_HPTEG_SIZE); + mb2 = mb = 32 - LG_HPTEG_SIZE - lg_n_hpteg; + if (lg_n_hpteg > 16) + mb2 = 16 - LG_HPTEG_SIZE; + + hash_page_patch_A[0] = (hash_page_patch_A[0] & ~0xffff) + | ((unsigned int)(Hash) >> 16); + hash_page_patch_A[1] = (hash_page_patch_A[1] & ~0x7c0) | (mb << 6); + hash_page_patch_A[2] = (hash_page_patch_A[2] & ~0x7c0) | (mb2 << 6); + hash_page_patch_B[0] = (hash_page_patch_B[0] & ~0xffff) | hmask; + hash_page_patch_C[0] = (hash_page_patch_C[0] & ~0xffff) | hmask; + + /* + * Ensure that the locations we've patched have been written + * out from the data cache and invalidated in the instruction + * cache, on those machines with split caches. + */ + flush_icache_range((unsigned long) &hash_page_patch_A[0], + (unsigned long) &hash_page_patch_C[1]); + + /* + * Patch up the instructions in hashtable.S:flush_hash_page + */ + flush_hash_patch_A[0] = (flush_hash_patch_A[0] & ~0xffff) + | ((unsigned int)(Hash) >> 16); + flush_hash_patch_A[1] = (flush_hash_patch_A[1] & ~0x7c0) | (mb << 6); + flush_hash_patch_A[2] = (flush_hash_patch_A[2] & ~0x7c0) | (mb2 << 6); + flush_hash_patch_B[0] = (flush_hash_patch_B[0] & ~0xffff) | hmask; + flush_icache_range((unsigned long) &flush_hash_patch_A[0], + (unsigned long) &flush_hash_patch_B[1]); + + if ( ppc_md.progress ) ppc_md.progress("hash:done", 0x205); +} diff --git a/arch/ppc/mm/tlb.c b/arch/ppc/mm/tlb.c new file mode 100644 index 00000000000..6c3dc3c44c8 --- /dev/null +++ b/arch/ppc/mm/tlb.c @@ -0,0 +1,183 @@ +/* + * This file contains the routines for TLB flushing. + * On machines where the MMU uses a hash table to store virtual to + * physical translations, these routines flush entries from the + * hash table also. + * -- paulus + * + * Derived from arch/ppc/mm/init.c: + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) + * and Cort Dougan (PReP) (cort@cs.nmt.edu) + * Copyright (C) 1996 Paul Mackerras + * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). + * + * Derived from "arch/i386/mm/init.c" + * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "mmu_decl.h" + +/* + * Called when unmapping pages to flush entries from the TLB/hash table. + */ +void flush_hash_entry(struct mm_struct *mm, pte_t *ptep, unsigned long addr) +{ + unsigned long ptephys; + + if (Hash != 0) { + ptephys = __pa(ptep) & PAGE_MASK; + flush_hash_pages(mm->context, addr, ptephys, 1); + } +} + +/* + * Called by ptep_set_access_flags, must flush on CPUs for which the + * DSI handler can't just "fixup" the TLB on a write fault + */ +void flush_tlb_page_nohash(struct vm_area_struct *vma, unsigned long addr) +{ + if (Hash != 0) + return; + _tlbie(addr); +} + +/* + * Called at the end of a mmu_gather operation to make sure the + * TLB flush is completely done. + */ +void tlb_flush(struct mmu_gather *tlb) +{ + if (Hash == 0) { + /* + * 603 needs to flush the whole TLB here since + * it doesn't use a hash table. + */ + _tlbia(); + } +} + +/* + * TLB flushing: + * + * - flush_tlb_mm(mm) flushes the specified mm context TLB's + * - flush_tlb_page(vma, vmaddr) flushes one page + * - flush_tlb_range(vma, start, end) flushes a range of pages + * - flush_tlb_kernel_range(start, end) flushes kernel pages + * + * since the hardware hash table functions as an extension of the + * tlb as far as the linux tables are concerned, flush it too. + * -- Cort + */ + +/* + * 750 SMP is a Bad Idea because the 750 doesn't broadcast all + * the cache operations on the bus. Hence we need to use an IPI + * to get the other CPU(s) to invalidate their TLBs. + */ +#ifdef CONFIG_SMP_750 +#define FINISH_FLUSH smp_send_tlb_invalidate(0) +#else +#define FINISH_FLUSH do { } while (0) +#endif + +static void flush_range(struct mm_struct *mm, unsigned long start, + unsigned long end) +{ + pmd_t *pmd; + unsigned long pmd_end; + int count; + unsigned int ctx = mm->context; + + if (Hash == 0) { + _tlbia(); + return; + } + start &= PAGE_MASK; + if (start >= end) + return; + end = (end - 1) | ~PAGE_MASK; + pmd = pmd_offset(pgd_offset(mm, start), start); + for (;;) { + pmd_end = ((start + PGDIR_SIZE) & PGDIR_MASK) - 1; + if (pmd_end > end) + pmd_end = end; + if (!pmd_none(*pmd)) { + count = ((pmd_end - start) >> PAGE_SHIFT) + 1; + flush_hash_pages(ctx, start, pmd_val(*pmd), count); + } + if (pmd_end == end) + break; + start = pmd_end + 1; + ++pmd; + } +} + +/* + * Flush kernel TLB entries in the given range + */ +void flush_tlb_kernel_range(unsigned long start, unsigned long end) +{ + flush_range(&init_mm, start, end); + FINISH_FLUSH; +} + +/* + * Flush all the (user) entries for the address space described by mm. + */ +void flush_tlb_mm(struct mm_struct *mm) +{ + struct vm_area_struct *mp; + + if (Hash == 0) { + _tlbia(); + return; + } + + for (mp = mm->mmap; mp != NULL; mp = mp->vm_next) + flush_range(mp->vm_mm, mp->vm_start, mp->vm_end); + FINISH_FLUSH; +} + +void flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr) +{ + struct mm_struct *mm; + pmd_t *pmd; + + if (Hash == 0) { + _tlbie(vmaddr); + return; + } + mm = (vmaddr < TASK_SIZE)? vma->vm_mm: &init_mm; + pmd = pmd_offset(pgd_offset(mm, vmaddr), vmaddr); + if (!pmd_none(*pmd)) + flush_hash_pages(mm->context, vmaddr, pmd_val(*pmd), 1); + FINISH_FLUSH; +} + +/* + * For each address in the range, find the pte for the address + * and check _PAGE_HASHPTE bit; if it is set, find and destroy + * the corresponding HPTE. + */ +void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, + unsigned long end) +{ + flush_range(vma->vm_mm, start, end); + FINISH_FLUSH; +} diff --git a/arch/ppc/oprofile/Kconfig b/arch/ppc/oprofile/Kconfig new file mode 100644 index 00000000000..19d37730b66 --- /dev/null +++ b/arch/ppc/oprofile/Kconfig @@ -0,0 +1,23 @@ + +menu "Profiling support" + depends on EXPERIMENTAL + +config PROFILING + bool "Profiling support (EXPERIMENTAL)" + help + Say Y here to enable the extended profiling support mechanisms used + by profilers such as OProfile. + + +config OPROFILE + tristate "OProfile system profiling (EXPERIMENTAL)" + depends on PROFILING + help + OProfile is a profiling system capable of profiling the + whole system, include the kernel, kernel modules, libraries, + and applications. + + If unsure, say N. + +endmenu + diff --git a/arch/ppc/oprofile/Makefile b/arch/ppc/oprofile/Makefile new file mode 100644 index 00000000000..e2218d32a4e --- /dev/null +++ b/arch/ppc/oprofile/Makefile @@ -0,0 +1,14 @@ +obj-$(CONFIG_OPROFILE) += oprofile.o + +DRIVER_OBJS := $(addprefix ../../../drivers/oprofile/, \ + oprof.o cpu_buffer.o buffer_sync.o \ + event_buffer.o oprofile_files.o \ + oprofilefs.o oprofile_stats.o \ + timer_int.o ) + +oprofile-y := $(DRIVER_OBJS) common.o + +ifeq ($(CONFIG_FSL_BOOKE),y) + oprofile-y += op_model_fsl_booke.o +endif + diff --git a/arch/ppc/oprofile/common.c b/arch/ppc/oprofile/common.c new file mode 100644 index 00000000000..3169c67abea --- /dev/null +++ b/arch/ppc/oprofile/common.c @@ -0,0 +1,161 @@ +/* + * PPC 32 oprofile support + * Based on PPC64 oprofile support + * Copyright (C) 2004 Anton Blanchard , IBM + * + * Copyright (C) Freescale Semiconductor, Inc 2004 + * + * Author: Andy Fleming + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "op_impl.h" + +static struct op_ppc32_model *model; + +static struct op_counter_config ctr[OP_MAX_COUNTER]; +static struct op_system_config sys; + +static void op_handle_interrupt(struct pt_regs *regs) +{ + model->handle_interrupt(regs, ctr); +} + +static int op_ppc32_setup(void) +{ + /* Install our interrupt handler into the existing hook. */ + if(request_perfmon_irq(&op_handle_interrupt)) + return -EBUSY; + + mb(); + + /* Pre-compute the values to stuff in the hardware registers. */ + model->reg_setup(ctr, &sys, model->num_counters); + +#if 0 + /* FIXME: Make multi-cpu work */ + /* Configure the registers on all cpus. */ + on_each_cpu(model->reg_setup, NULL, 0, 1); +#endif + + return 0; +} + +static void op_ppc32_shutdown(void) +{ + mb(); + + /* Remove our interrupt handler. We may be removing this module. */ + free_perfmon_irq(); +} + +static void op_ppc32_cpu_start(void *dummy) +{ + model->start(ctr); +} + +static int op_ppc32_start(void) +{ + on_each_cpu(op_ppc32_cpu_start, NULL, 0, 1); + return 0; +} + +static inline void op_ppc32_cpu_stop(void *dummy) +{ + model->stop(); +} + +static void op_ppc32_stop(void) +{ + on_each_cpu(op_ppc32_cpu_stop, NULL, 0, 1); +} + +static int op_ppc32_create_files(struct super_block *sb, struct dentry *root) +{ + int i; + + for (i = 0; i < model->num_counters; ++i) { + struct dentry *dir; + char buf[3]; + + snprintf(buf, sizeof buf, "%d", i); + dir = oprofilefs_mkdir(sb, root, buf); + + oprofilefs_create_ulong(sb, dir, "enabled", &ctr[i].enabled); + oprofilefs_create_ulong(sb, dir, "event", &ctr[i].event); + oprofilefs_create_ulong(sb, dir, "count", &ctr[i].count); + oprofilefs_create_ulong(sb, dir, "kernel", &ctr[i].kernel); + oprofilefs_create_ulong(sb, dir, "user", &ctr[i].user); + + /* FIXME: Not sure if this is used */ + oprofilefs_create_ulong(sb, dir, "unit_mask", &ctr[i].unit_mask); + } + + oprofilefs_create_ulong(sb, root, "enable_kernel", &sys.enable_kernel); + oprofilefs_create_ulong(sb, root, "enable_user", &sys.enable_user); + + /* Default to tracing both kernel and user */ + sys.enable_kernel = 1; + sys.enable_user = 1; + + return 0; +} + +static struct oprofile_operations oprof_ppc32_ops = { + .create_files = op_ppc32_create_files, + .setup = op_ppc32_setup, + .shutdown = op_ppc32_shutdown, + .start = op_ppc32_start, + .stop = op_ppc32_stop, + .cpu_type = NULL /* To be filled in below. */ +}; + +int __init oprofile_arch_init(struct oprofile_operations *ops) +{ + char *name; + int cpu_id = smp_processor_id(); + +#ifdef CONFIG_FSL_BOOKE + model = &op_model_fsl_booke; +#else + return -ENODEV; +#endif + + name = kmalloc(32, GFP_KERNEL); + + if (NULL == name) + return -ENOMEM; + + sprintf(name, "ppc/%s", cur_cpu_spec[cpu_id]->cpu_name); + + oprof_ppc32_ops.cpu_type = name; + + model->num_counters = cur_cpu_spec[cpu_id]->num_pmcs; + + *ops = oprof_ppc32_ops; + + printk(KERN_INFO "oprofile: using %s performance monitoring.\n", + oprof_ppc32_ops.cpu_type); + + return 0; +} + +void oprofile_arch_exit(void) +{ + kfree(oprof_ppc32_ops.cpu_type); + oprof_ppc32_ops.cpu_type = NULL; +} diff --git a/arch/ppc/oprofile/op_impl.h b/arch/ppc/oprofile/op_impl.h new file mode 100644 index 00000000000..bc336dc971e --- /dev/null +++ b/arch/ppc/oprofile/op_impl.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2004 Anton Blanchard , IBM + * + * Based on alpha version. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef OP_IMPL_H +#define OP_IMPL_H 1 + +#define OP_MAX_COUNTER 8 + +/* Per-counter configuration as set via oprofilefs. */ +struct op_counter_config { + unsigned long enabled; + unsigned long event; + unsigned long count; + unsigned long kernel; + unsigned long user; + unsigned long unit_mask; +}; + +/* System-wide configuration as set via oprofilefs. */ +struct op_system_config { + unsigned long enable_kernel; + unsigned long enable_user; +}; + +/* Per-arch configuration */ +struct op_ppc32_model { + void (*reg_setup) (struct op_counter_config *, + struct op_system_config *, + int num_counters); + void (*start) (struct op_counter_config *); + void (*stop) (void); + void (*handle_interrupt) (struct pt_regs *, + struct op_counter_config *); + int num_counters; +}; + +#endif /* OP_IMPL_H */ diff --git a/arch/ppc/oprofile/op_model_fsl_booke.c b/arch/ppc/oprofile/op_model_fsl_booke.c new file mode 100644 index 00000000000..fc9c859358c --- /dev/null +++ b/arch/ppc/oprofile/op_model_fsl_booke.c @@ -0,0 +1,184 @@ +/* + * oprofile/op_model_e500.c + * + * Freescale Book-E oprofile support, based on ppc64 oprofile support + * Copyright (C) 2004 Anton Blanchard , IBM + * + * Copyright (c) 2004 Freescale Semiconductor, Inc + * + * Author: Andy Fleming + * Maintainer: Kumar Gala + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "op_impl.h" + +static unsigned long reset_value[OP_MAX_COUNTER]; + +static int num_counters; +static int oprofile_running; + +static inline unsigned int ctr_read(unsigned int i) +{ + switch(i) { + case 0: + return mfpmr(PMRN_PMC0); + case 1: + return mfpmr(PMRN_PMC1); + case 2: + return mfpmr(PMRN_PMC2); + case 3: + return mfpmr(PMRN_PMC3); + default: + return 0; + } +} + +static inline void ctr_write(unsigned int i, unsigned int val) +{ + switch(i) { + case 0: + mtpmr(PMRN_PMC0, val); + break; + case 1: + mtpmr(PMRN_PMC1, val); + break; + case 2: + mtpmr(PMRN_PMC2, val); + break; + case 3: + mtpmr(PMRN_PMC3, val); + break; + default: + break; + } +} + + +static void fsl_booke_reg_setup(struct op_counter_config *ctr, + struct op_system_config *sys, + int num_ctrs) +{ + int i; + + num_counters = num_ctrs; + + /* freeze all counters */ + pmc_stop_ctrs(); + + /* Our counters count up, and "count" refers to + * how much before the next interrupt, and we interrupt + * on overflow. So we calculate the starting value + * which will give us "count" until overflow. + * Then we set the events on the enabled counters */ + for (i = 0; i < num_counters; ++i) { + reset_value[i] = 0x80000000UL - ctr[i].count; + + init_pmc_stop(i); + + set_pmc_event(i, ctr[i].event); + + set_pmc_user_kernel(i, ctr[i].user, ctr[i].kernel); + } +} + +static void fsl_booke_start(struct op_counter_config *ctr) +{ + int i; + + mtmsr(mfmsr() | MSR_PMM); + + for (i = 0; i < num_counters; ++i) { + if (ctr[i].enabled) { + ctr_write(i, reset_value[i]); + /* Set Each enabled counterd to only + * count when the Mark bit is not set */ + set_pmc_marked(i, 1, 0); + pmc_start_ctr(i, 1); + } else { + ctr_write(i, 0); + + /* Set the ctr to be stopped */ + pmc_start_ctr(i, 0); + } + } + + /* Clear the freeze bit, and enable the interrupt. + * The counters won't actually start until the rfi clears + * the PMM bit */ + pmc_start_ctrs(1); + + oprofile_running = 1; + + pr_debug("start on cpu %d, pmgc0 %x\n", smp_processor_id(), + mfpmr(PMRN_PMGC0)); +} + +static void fsl_booke_stop(void) +{ + /* freeze counters */ + pmc_stop_ctrs(); + + oprofile_running = 0; + + pr_debug("stop on cpu %d, pmgc0 %x\n", smp_processor_id(), + mfpmr(PMRN_PMGC0)); + + mb(); +} + + +static void fsl_booke_handle_interrupt(struct pt_regs *regs, + struct op_counter_config *ctr) +{ + unsigned long pc; + int is_kernel; + int val; + int i; + + /* set the PMM bit (see comment below) */ + mtmsr(mfmsr() | MSR_PMM); + + pc = regs->nip; + is_kernel = (pc >= KERNELBASE); + + for (i = 0; i < num_counters; ++i) { + val = ctr_read(i); + if (val < 0) { + if (oprofile_running && ctr[i].enabled) { + oprofile_add_pc(pc, is_kernel, i); + ctr_write(i, reset_value[i]); + } else { + ctr_write(i, 0); + } + } + } + + /* The freeze bit was set by the interrupt. */ + /* Clear the freeze bit, and reenable the interrupt. + * The counters won't actually start until the rfi clears + * the PMM bit */ + pmc_start_ctrs(1); +} + +struct op_ppc32_model op_model_fsl_booke = { + .reg_setup = fsl_booke_reg_setup, + .start = fsl_booke_start, + .stop = fsl_booke_stop, + .handle_interrupt = fsl_booke_handle_interrupt, +}; diff --git a/arch/ppc/platforms/4xx/Kconfig b/arch/ppc/platforms/4xx/Kconfig new file mode 100644 index 00000000000..a0612a86455 --- /dev/null +++ b/arch/ppc/platforms/4xx/Kconfig @@ -0,0 +1,247 @@ +config 4xx + bool + depends on 40x || 44x + default y + +menu "IBM 4xx options" + depends on 4xx + +choice + prompt "Machine Type" + depends on 40x + default WALNUT + +config ASH + bool "Ash" + help + This option enables support for the IBM NP405H evaluation board. + +config BUBINGA + bool "Bubinga" + help + This option enables support for the IBM 405EP evaluation board. + +config CPCI405 + bool "CPCI405" + help + This option enables support for the CPCI405 board. + +config EP405 + bool "EP405/EP405PC" + help + This option enables support for the EP405/EP405PC boards. + +config OAK + bool "Oak" + help + This option enables support for the IBM 403GCX evaluation board. + +config REDWOOD_5 + bool "Redwood-5" + help + This option enables support for the IBM STB04 evaluation board. + +config REDWOOD_6 + bool "Redwood-6" + help + This option enables support for the IBM STBx25xx evaluation board. + +config SYCAMORE + bool "Sycamore" + help + This option enables support for the IBM PPC405GPr evaluation board. + +config WALNUT + bool "Walnut" + help + This option enables support for the IBM PPC405GP evaluation board. + +config XILINX_ML300 + bool "Xilinx-ML300" + help + This option enables support for the Xilinx ML300 evaluation board. + +endchoice + +choice + prompt "Machine Type" + depends on 44x + default EBONY + +config EBONY + bool "Ebony" + help + This option enables support for the IBM PPC440GP evaluation board. + +config LUAN + bool "Luan" + help + This option enables support for the IBM PPC440SP evaluation board. + +config OCOTEA + bool "Ocotea" + help + This option enables support for the IBM PPC440GX evaluation board. + +endchoice + +config EP405PC + bool "EP405PC Support" + depends on EP405 + + +# It's often necessary to know the specific 4xx processor type. +# Fortunately, it is impled (so far) from the board type, so we +# don't need to ask more redundant questions. +config NP405H + bool + depends on ASH + default y + +config 440GP + bool + depends on EBONY + default y + +config 440GX + bool + depends on OCOTEA + default y + +config 440SP + bool + depends on LUAN + default y + +config 440 + bool + depends on 440GP || 440SP + default y + +config 440A + bool + depends on 440GX + default y + +# All 405-based cores up until the 405GPR and 405EP have this errata. +config IBM405_ERR77 + bool + depends on 40x && !403GCX && !405GPR + default y + +# All 40x-based cores, up until the 405GPR and 405EP have this errata. +config IBM405_ERR51 + bool + depends on 40x && !405GPR + default y + +config BOOKE + bool + depends on 44x + default y + +config IBM_OCP + bool + depends on ASH || BUBINGA || CPCI405 || EBONY || EP405 || LUAN || OCOTEA || REDWOOD_5 || REDWOOD_6 || SYCAMORE || WALNUT + default y + +config XILINX_OCP + bool + depends on XILINX_ML300 + default y + +config IBM_EMAC4 + bool + depends on 440GX || 440SP + default y + +config BIOS_FIXUP + bool + depends on BUBINGA || EP405 || SYCAMORE || WALNUT + default y + +config 403GCX + bool + depends OAK + default y + +config 405EP + bool + depends on BUBINGA + default y + +config 405GP + bool + depends on CPCI405 || EP405 || WALNUT + default y + +config 405GPR + bool + depends on SYCAMORE + default y + +config VIRTEX_II_PRO + bool + depends on XILINX_ML300 + default y + +config STB03xxx + bool + depends on REDWOOD_5 || REDWOOD_6 + default y + +config EMBEDDEDBOOT + bool + depends on EP405 || XILINX_ML300 + default y + +config IBM_OPENBIOS + bool + depends on ASH || BUBINGA || REDWOOD_5 || REDWOOD_6 || SYCAMORE || WALNUT + default y + +config PPC4xx_DMA + bool "PPC4xx DMA controller support" + depends on 4xx + +config PPC4xx_EDMA + bool + depends on !STB03xxx && PPC4xx_DMA + default y + +config PPC_GEN550 + bool + depends on 4xx + default y + +config PM + bool "Power Management support (EXPERIMENTAL)" + depends on 4xx && EXPERIMENTAL + +choice + prompt "TTYS0 device and default console" + depends on 40x + default UART0_TTYS0 + +config UART0_TTYS0 + bool "UART0" + +config UART0_TTYS1 + bool "UART1" + +endchoice + +config SERIAL_SICC + bool "SICC Serial port support" + depends on STB03xxx + +config UART1_DFLT_CONSOLE + bool + depends on SERIAL_SICC && UART0_TTYS1 + default y + +config SERIAL_SICC_CONSOLE + bool + depends on SERIAL_SICC && UART0_TTYS1 + default y +endmenu diff --git a/arch/ppc/platforms/4xx/Makefile b/arch/ppc/platforms/4xx/Makefile new file mode 100644 index 00000000000..ea470c6adbb --- /dev/null +++ b/arch/ppc/platforms/4xx/Makefile @@ -0,0 +1,27 @@ +# +# Makefile for the PowerPC 4xx linux kernel. + +obj-$(CONFIG_ASH) += ash.o +obj-$(CONFIG_CPCI405) += cpci405.o +obj-$(CONFIG_EBONY) += ebony.o +obj-$(CONFIG_EP405) += ep405.o +obj-$(CONFIG_BUBINGA) += bubinga.o +obj-$(CONFIG_LUAN) += luan.o +obj-$(CONFIG_OAK) += oak.o +obj-$(CONFIG_OCOTEA) += ocotea.o +obj-$(CONFIG_REDWOOD_5) += redwood5.o +obj-$(CONFIG_REDWOOD_6) += redwood6.o +obj-$(CONFIG_SYCAMORE) += sycamore.o +obj-$(CONFIG_WALNUT) += walnut.o +obj-$(CONFIG_XILINX_ML300) += xilinx_ml300.o + +obj-$(CONFIG_405GP) += ibm405gp.o +obj-$(CONFIG_REDWOOD_5) += ibmstb4.o +obj-$(CONFIG_NP405H) += ibmnp405h.o +obj-$(CONFIG_REDWOOD_6) += ibmstbx25.o +obj-$(CONFIG_440GP) += ibm440gp.o +obj-$(CONFIG_440GX) += ibm440gx.o +obj-$(CONFIG_440SP) += ibm440sp.o +obj-$(CONFIG_405EP) += ibm405ep.o +obj-$(CONFIG_405GPR) += ibm405gpr.o +obj-$(CONFIG_VIRTEX_II_PRO) += virtex-ii_pro.o diff --git a/arch/ppc/platforms/4xx/ash.c b/arch/ppc/platforms/4xx/ash.c new file mode 100644 index 00000000000..ce291179371 --- /dev/null +++ b/arch/ppc/platforms/4xx/ash.c @@ -0,0 +1,250 @@ +/* + * arch/ppc/platforms/4xx/ash.c + * + * Support for the IBM NP405H ash eval board + * + * Author: Armin Kuster + * + * 2001-2002 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +void *ash_rtc_base; + +/* Some IRQs unique to Walnut. + * Used by the generic 405 PCI setup functions in ppc4xx_pci.c + */ +int __init +ppc405_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {24, 24, 24, 24}, /* IDSEL 1 - PCI slot 1 */ + {25, 25, 25, 25}, /* IDSEL 2 - PCI slot 2 */ + {26, 26, 26, 26}, /* IDSEL 3 - PCI slot 3 */ + {27, 27, 27, 27}, /* IDSEL 4 - PCI slot 4 */ + }; + + const long min_idsel = 1, max_idsel = 4, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +} + +void __init +ash_setup_arch(void) +{ + ppc4xx_setup_arch(); + + ibm_ocp_set_emac(0, 3); + +#ifdef CONFIG_DEBUG_BRINGUP + int i; + printk("\n"); + printk("machine\t: %s\n", PPC4xx_MACHINE_NAME); + printk("\n"); + printk("bi_s_version\t %s\n", bip->bi_s_version); + printk("bi_r_version\t %s\n", bip->bi_r_version); + printk("bi_memsize\t 0x%8.8x\t %dMBytes\n", bip->bi_memsize, + bip->bi_memsize / (1024 * 1000)); + for (i = 0; i < EMAC_NUMS; i++) { + printk("bi_enetaddr %d\t %2.2x%2.2x%2.2x-%2.2x%2.2x%2.2x\n", i, + bip->bi_enetaddr[i][0], bip->bi_enetaddr[i][1], + bip->bi_enetaddr[i][2], bip->bi_enetaddr[i][3], + bip->bi_enetaddr[i][4], bip->bi_enetaddr[i][5]); + } + printk("bi_pci_enetaddr %d\t %2.2x%2.2x%2.2x-%2.2x%2.2x%2.2x\n", 0, + bip->bi_pci_enetaddr[0], bip->bi_pci_enetaddr[1], + bip->bi_pci_enetaddr[2], bip->bi_pci_enetaddr[3], + bip->bi_pci_enetaddr[4], bip->bi_pci_enetaddr[5]); + + printk("bi_intfreq\t 0x%8.8x\t clock:\t %dMhz\n", + bip->bi_intfreq, bip->bi_intfreq / 1000000); + + printk("bi_busfreq\t 0x%8.8x\t plb bus clock:\t %dMHz\n", + bip->bi_busfreq, bip->bi_busfreq / 1000000); + printk("bi_pci_busfreq\t 0x%8.8x\t pci bus clock:\t %dMHz\n", + bip->bi_pci_busfreq, bip->bi_pci_busfreq / 1000000); + + printk("\n"); +#endif + /* RTC step for ash */ + ash_rtc_base = (void *) ASH_RTC_VADDR; + TODC_INIT(TODC_TYPE_DS1743, ash_rtc_base, ash_rtc_base, ash_rtc_base, + 8); +} + +void __init +bios_fixup(struct pci_controller *hose, struct pcil0_regs *pcip) +{ + /* + * Expected PCI mapping: + * + * PLB addr PCI memory addr + * --------------------- --------------------- + * 0000'0000 - 7fff'ffff <--- 0000'0000 - 7fff'ffff + * 8000'0000 - Bfff'ffff ---> 8000'0000 - Bfff'ffff + * + * PLB addr PCI io addr + * --------------------- --------------------- + * e800'0000 - e800'ffff ---> 0000'0000 - 0001'0000 + * + * The following code is simplified by assuming that the bootrom + * has been well behaved in following this mapping. + */ + +#ifdef DEBUG + int i; + + printk("ioremap PCLIO_BASE = 0x%x\n", pcip); + printk("PCI bridge regs before fixup \n"); + for (i = 0; i <= 2; i++) { + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].ma))); + printk(" pmm%dla\t0x%x\n", i, in_le32(&(pcip->pmm[i].la))); + printk(" pmm%dpcila\t0x%x\n", i, + in_le32(&(pcip->pmm[i].pcila))); + printk(" pmm%dpciha\t0x%x\n", i, + in_le32(&(pcip->pmm[i].pciha))); + } + printk(" ptm1ms\t0x%x\n", in_le32(&(pcip->ptm1ms))); + printk(" ptm1la\t0x%x\n", in_le32(&(pcip->ptm1la))); + printk(" ptm2ms\t0x%x\n", in_le32(&(pcip->ptm2ms))); + printk(" ptm2la\t0x%x\n", in_le32(&(pcip->ptm2la))); + for (bar = PCI_BASE_ADDRESS_1; bar <= PCI_BASE_ADDRESS_2; bar += 4) { + early_read_config_dword(hose, hose->first_busno, + PCI_FUNC(hose->first_busno), bar, + &bar_response); + DBG("BUS %d, device %d, Function %d bar 0x%8.8x is 0x%8.8x\n", + hose->first_busno, PCI_SLOT(hose->first_busno), + PCI_FUNC(hose->first_busno), bar, bar_response); + } + +#endif + if (ppc_md.progress) + ppc_md.progress("bios_fixup(): enter", 0x800); + + /* added for IBM boot rom version 1.15 bios bar changes -AK */ + + /* Disable region first */ + out_le32((void *) &(pcip->pmm[0].ma), 0x00000000); + /* PLB starting addr, PCI: 0x80000000 */ + out_le32((void *) &(pcip->pmm[0].la), 0x80000000); + /* PCI start addr, 0x80000000 */ + out_le32((void *) &(pcip->pmm[0].pcila), PPC405_PCI_MEM_BASE); + /* 512MB range of PLB to PCI */ + out_le32((void *) &(pcip->pmm[0].pciha), 0x00000000); + /* Enable no pre-fetch, enable region */ + out_le32((void *) &(pcip->pmm[0].ma), ((0xffffffff - + (PPC405_PCI_UPPER_MEM - + PPC405_PCI_MEM_BASE)) | 0x01)); + + /* Disable region one */ + out_le32((void *) &(pcip->pmm[1].ma), 0x00000000); + out_le32((void *) &(pcip->pmm[1].la), 0x00000000); + out_le32((void *) &(pcip->pmm[1].pcila), 0x00000000); + out_le32((void *) &(pcip->pmm[1].pciha), 0x00000000); + out_le32((void *) &(pcip->pmm[1].ma), 0x00000000); + + /* Disable region two */ + out_le32((void *) &(pcip->pmm[2].ma), 0x00000000); + out_le32((void *) &(pcip->pmm[2].la), 0x00000000); + out_le32((void *) &(pcip->pmm[2].pcila), 0x00000000); + out_le32((void *) &(pcip->pmm[2].pciha), 0x00000000); + out_le32((void *) &(pcip->pmm[2].ma), 0x00000000); + + /* Enable PTM1 and PTM2, mapped to PLB address 0. */ + + out_le32((void *) &(pcip->ptm1la), 0x00000000); + out_le32((void *) &(pcip->ptm1ms), 0x00000001); + out_le32((void *) &(pcip->ptm2la), 0x00000000); + out_le32((void *) &(pcip->ptm2ms), 0x00000001); + + /* Write zero to PTM1 BAR. */ + + early_write_config_dword(hose, hose->first_busno, + PCI_FUNC(hose->first_busno), + PCI_BASE_ADDRESS_1, + 0x00000000); + + /* Disable PTM2 (unused) */ + + out_le32((void *) &(pcip->ptm2la), 0x00000000); + out_le32((void *) &(pcip->ptm2ms), 0x00000000); + + /* end work arround */ + if (ppc_md.progress) + ppc_md.progress("bios_fixup(): done", 0x800); + +#ifdef DEBUG + printk("PCI bridge regs after fixup \n"); + for (i = 0; i <= 2; i++) { + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].ma))); + printk(" pmm%dla\t0x%x\n", i, in_le32(&(pcip->pmm[i].la))); + printk(" pmm%dpcila\t0x%x\n", i, + in_le32(&(pcip->pmm[i].pcila))); + printk(" pmm%dpciha\t0x%x\n", i, + in_le32(&(pcip->pmm[i].pciha))); + } + printk(" ptm1ms\t0x%x\n", in_le32(&(pcip->ptm1ms))); + printk(" ptm1la\t0x%x\n", in_le32(&(pcip->ptm1la))); + printk(" ptm2ms\t0x%x\n", in_le32(&(pcip->ptm2ms))); + printk(" ptm2la\t0x%x\n", in_le32(&(pcip->ptm2la))); + + for (bar = PCI_BASE_ADDRESS_1; bar <= PCI_BASE_ADDRESS_2; bar += 4) { + early_read_config_dword(hose, hose->first_busno, + PCI_FUNC(hose->first_busno), bar, + &bar_response); + DBG("BUS %d, device %d, Function %d bar 0x%8.8x is 0x%8.8x\n", + hose->first_busno, PCI_SLOT(hose->first_busno), + PCI_FUNC(hose->first_busno), bar, bar_response); + } + + +#endif +} + +void __init +ash_map_io(void) +{ + ppc4xx_map_io(); + io_block_mapping(ASH_RTC_VADDR, ASH_RTC_PADDR, ASH_RTC_SIZE, _PAGE_IO); +} + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + ppc4xx_init(r3, r4, r5, r6, r7); + + ppc_md.setup_arch = ash_setup_arch; + ppc_md.setup_io_mappings = ash_map_io; + +#ifdef CONFIG_PPC_RTC + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; +#endif +} diff --git a/arch/ppc/platforms/4xx/ash.h b/arch/ppc/platforms/4xx/ash.h new file mode 100644 index 00000000000..5f7448ea418 --- /dev/null +++ b/arch/ppc/platforms/4xx/ash.h @@ -0,0 +1,83 @@ +/* + * arch/ppc/platforms/4xx/ash.h + * + * Macros, definitions, and data structures specific to the IBM PowerPC + * Ash eval board. + * + * Author: Armin Kuster + * + * 2000-2002 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_ASH_H__ +#define __ASM_ASH_H__ +#include + +#ifndef __ASSEMBLY__ +/* + * Data structure defining board information maintained by the boot + * ROM on IBM's "Ash" evaluation board. An effort has been made to + * keep the field names consistent with the 8xx 'bd_t' board info + * structures. + */ + +typedef struct board_info { + unsigned char bi_s_version[4]; /* Version of this structure */ + unsigned char bi_r_version[30]; /* Version of the IBM ROM */ + unsigned int bi_memsize; /* DRAM installed, in bytes */ + unsigned char bi_enetaddr[4][6]; /* Local Ethernet MAC address */ + unsigned char bi_pci_enetaddr[6]; + unsigned int bi_intfreq; /* Processor speed, in Hz */ + unsigned int bi_busfreq; /* PLB Bus speed, in Hz */ + unsigned int bi_pci_busfreq; /* PCI speed in Hz */ +} bd_t; + +/* Some 4xx parts use a different timebase frequency from the internal clock. +*/ +#define bi_tbfreq bi_intfreq + +/* Memory map for the IBM "Ash" NP405H evaluation board. + */ + +extern void *ash_rtc_base; +#define ASH_RTC_PADDR ((uint)0xf0000000) +#define ASH_RTC_VADDR ASH_RTC_PADDR +#define ASH_RTC_SIZE ((uint)8*1024) + + +/* Early initialization address mapping for block_io. + * Standard 405GP map. + */ +#define PPC4xx_PCI_IO_PADDR ((uint)PPC405_PCI_PHY_IO_BASE) +#define PPC4xx_PCI_IO_VADDR PPC4xx_PCI_IO_PADDR +#define PPC4xx_PCI_IO_SIZE ((uint)64*1024) +#define PPC4xx_PCI_CFG_PADDR ((uint)PPC405_PCI_CONFIG_ADDR) +#define PPC4xx_PCI_CFG_VADDR PPC4xx_PCI_CFG_PADDR +#define PPC4xx_PCI_CFG_SIZE ((uint)4*1024) +#define PPC4xx_PCI_LCFG_PADDR ((uint)0xef400000) +#define PPC4xx_PCI_LCFG_VADDR PPC4xx_PCI_LCFG_PADDR +#define PPC4xx_PCI_LCFG_SIZE ((uint)4*1024) +#define PPC4xx_ONB_IO_PADDR ((uint)0xef600000) +#define PPC4xx_ONB_IO_VADDR PPC4xx_ONB_IO_PADDR +#define PPC4xx_ONB_IO_SIZE ((uint)4*1024) + +#define NR_BOARD_IRQS 32 + +#ifdef CONFIG_PPC405GP_INTERNAL_CLOCK +#define BASE_BAUD 201600 +#else +#define BASE_BAUD 691200 +#endif + +#define PPC4xx_MACHINE_NAME "IBM NP405H Ash" + +extern char pci_irq_table[][4]; + + +#endif /* !__ASSEMBLY__ */ +#endif /* __ASM_ASH_H__ */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/4xx/bubinga.c b/arch/ppc/platforms/4xx/bubinga.c new file mode 100644 index 00000000000..3678abf8631 --- /dev/null +++ b/arch/ppc/platforms/4xx/bubinga.c @@ -0,0 +1,263 @@ +/* + * Support for IBM PPC 405EP evaluation board (Bubinga). + * + * Author: SAW (IBM), derived from walnut.c. + * Maintained by MontaVista Software + * + * 2003 (c) MontaVista Softare Inc. This file is licensed under the + * terms of the GNU General Public License version 2. This program is + * licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#undef DEBUG + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +extern bd_t __res; + +void *bubinga_rtc_base; + +/* Some IRQs unique to the board + * Used by the generic 405 PCI setup functions in ppc4xx_pci.c + */ +int __init +ppc405_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {28, 28, 28, 28}, /* IDSEL 1 - PCI slot 1 */ + {29, 29, 29, 29}, /* IDSEL 2 - PCI slot 2 */ + {30, 30, 30, 30}, /* IDSEL 3 - PCI slot 3 */ + {31, 31, 31, 31}, /* IDSEL 4 - PCI slot 4 */ + }; + + const long min_idsel = 1, max_idsel = 4, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +}; + +/* The serial clock for the chip is an internal clock determined by + * different clock speeds/dividers. + * Calculate the proper input baud rate and setup the serial driver. + */ +static void __init +bubinga_early_serial_map(void) +{ + u32 uart_div; + int uart_clock; + struct uart_port port; + + /* Calculate the serial clock input frequency + * + * The base baud is the PLL OUTA (provided in the board info + * structure) divided by the external UART Divisor, divided + * by 16. + */ + uart_div = (mfdcr(DCRN_CPC0_UCR_BASE) & DCRN_CPC0_UCR_U0DIV); + uart_clock = __res.bi_pllouta_freq / uart_div; + + /* Setup serial port access */ + memset(&port, 0, sizeof(port)); + port.membase = (void*)ACTING_UART0_IO_BASE; + port.irq = ACTING_UART0_INT; + port.uartclk = uart_clock; + port.regshift = 0; + port.iotype = SERIAL_IO_MEM; + port.flags = ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST; + port.line = 0; + + if (early_serial_setup(&port) != 0) { + printk("Early serial init of port 0 failed\n"); + } + + port.membase = (void*)ACTING_UART1_IO_BASE; + port.irq = ACTING_UART1_INT; + port.line = 1; + + if (early_serial_setup(&port) != 0) { + printk("Early serial init of port 1 failed\n"); + } +} + +void __init +bios_fixup(struct pci_controller *hose, struct pcil0_regs *pcip) +{ + + unsigned int bar_response, bar; + /* + * Expected PCI mapping: + * + * PLB addr PCI memory addr + * --------------------- --------------------- + * 0000'0000 - 7fff'ffff <--- 0000'0000 - 7fff'ffff + * 8000'0000 - Bfff'ffff ---> 8000'0000 - Bfff'ffff + * + * PLB addr PCI io addr + * --------------------- --------------------- + * e800'0000 - e800'ffff ---> 0000'0000 - 0001'0000 + * + * The following code is simplified by assuming that the bootrom + * has been well behaved in following this mapping. + */ + +#ifdef DEBUG + int i; + + printk("ioremap PCLIO_BASE = 0x%x\n", pcip); + printk("PCI bridge regs before fixup \n"); + for (i = 0; i <= 3; i++) { + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].ma))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].la))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pcila))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pciha))); + } + printk(" ptm1ms\t0x%x\n", in_le32(&(pcip->ptm1ms))); + printk(" ptm1la\t0x%x\n", in_le32(&(pcip->ptm1la))); + printk(" ptm2ms\t0x%x\n", in_le32(&(pcip->ptm2ms))); + printk(" ptm2la\t0x%x\n", in_le32(&(pcip->ptm2la))); + +#endif + + /* added for IBM boot rom version 1.15 bios bar changes -AK */ + + /* Disable region first */ + out_le32((void *) &(pcip->pmm[0].ma), 0x00000000); + /* PLB starting addr, PCI: 0x80000000 */ + out_le32((void *) &(pcip->pmm[0].la), 0x80000000); + /* PCI start addr, 0x80000000 */ + out_le32((void *) &(pcip->pmm[0].pcila), PPC405_PCI_MEM_BASE); + /* 512MB range of PLB to PCI */ + out_le32((void *) &(pcip->pmm[0].pciha), 0x00000000); + /* Enable no pre-fetch, enable region */ + out_le32((void *) &(pcip->pmm[0].ma), ((0xffffffff - + (PPC405_PCI_UPPER_MEM - + PPC405_PCI_MEM_BASE)) | 0x01)); + + /* Disable region one */ + out_le32((void *) &(pcip->pmm[1].ma), 0x00000000); + out_le32((void *) &(pcip->pmm[1].la), 0x00000000); + out_le32((void *) &(pcip->pmm[1].pcila), 0x00000000); + out_le32((void *) &(pcip->pmm[1].pciha), 0x00000000); + out_le32((void *) &(pcip->pmm[1].ma), 0x00000000); + out_le32((void *) &(pcip->ptm1ms), 0x00000001); + + /* Disable region two */ + out_le32((void *) &(pcip->pmm[2].ma), 0x00000000); + out_le32((void *) &(pcip->pmm[2].la), 0x00000000); + out_le32((void *) &(pcip->pmm[2].pcila), 0x00000000); + out_le32((void *) &(pcip->pmm[2].pciha), 0x00000000); + out_le32((void *) &(pcip->pmm[2].ma), 0x00000000); + out_le32((void *) &(pcip->ptm2ms), 0x00000000); + out_le32((void *) &(pcip->ptm2la), 0x00000000); + + /* Zero config bars */ + for (bar = PCI_BASE_ADDRESS_1; bar <= PCI_BASE_ADDRESS_2; bar += 4) { + early_write_config_dword(hose, hose->first_busno, + PCI_FUNC(hose->first_busno), bar, + 0x00000000); + early_read_config_dword(hose, hose->first_busno, + PCI_FUNC(hose->first_busno), bar, + &bar_response); + DBG("BUS %d, device %d, Function %d bar 0x%8.8x is 0x%8.8x\n", + hose->first_busno, PCI_SLOT(hose->first_busno), + PCI_FUNC(hose->first_busno), bar, bar_response); + } + /* end work arround */ + +#ifdef DEBUG + printk("PCI bridge regs after fixup \n"); + for (i = 0; i <= 3; i++) { + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].ma))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].la))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pcila))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pciha))); + } + printk(" ptm1ms\t0x%x\n", in_le32(&(pcip->ptm1ms))); + printk(" ptm1la\t0x%x\n", in_le32(&(pcip->ptm1la))); + printk(" ptm2ms\t0x%x\n", in_le32(&(pcip->ptm2ms))); + printk(" ptm2la\t0x%x\n", in_le32(&(pcip->ptm2la))); + +#endif +} + +void __init +bubinga_setup_arch(void) +{ + ppc4xx_setup_arch(); + + ibm_ocp_set_emac(0, 1); + + bubinga_early_serial_map(); + + /* RTC step for the evb405ep */ + bubinga_rtc_base = (void *) BUBINGA_RTC_VADDR; + TODC_INIT(TODC_TYPE_DS1743, bubinga_rtc_base, bubinga_rtc_base, + bubinga_rtc_base, 8); + /* Identify the system */ + printk("IBM Bubinga port (MontaVista Software, Inc. )\n"); +} + +void __init +bubinga_map_io(void) +{ + ppc4xx_map_io(); + io_block_mapping(BUBINGA_RTC_VADDR, + BUBINGA_RTC_PADDR, BUBINGA_RTC_SIZE, _PAGE_IO); +} + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + ppc4xx_init(r3, r4, r5, r6, r7); + + ppc_md.setup_arch = bubinga_setup_arch; + ppc_md.setup_io_mappings = bubinga_map_io; + +#ifdef CONFIG_GEN_RTC + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; +#endif +#ifdef CONFIG_KGDB + ppc_md.early_serial_map = bubinga_early_serial_map; +#endif +} + diff --git a/arch/ppc/platforms/4xx/bubinga.h b/arch/ppc/platforms/4xx/bubinga.h new file mode 100644 index 00000000000..b1df856f8e2 --- /dev/null +++ b/arch/ppc/platforms/4xx/bubinga.h @@ -0,0 +1,69 @@ +/* + * Support for IBM PPC 405EP evaluation board (Bubinga). + * + * Author: SAW (IBM), derived from walnut.h. + * Maintained by MontaVista Software + * + * 2003 (c) MontaVista Softare Inc. This file is licensed under the + * terms of the GNU General Public License version 2. This program is + * licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifdef __KERNEL__ +#ifndef __BUBINGA_H__ +#define __BUBINGA_H__ + +/* 405EP */ +#include + +#ifndef __ASSEMBLY__ +/* + * Data structure defining board information maintained by the boot + * ROM on IBM's evaluation board. An effort has been made to + * keep the field names consistent with the 8xx 'bd_t' board info + * structures. + */ + +typedef struct board_info { + unsigned char bi_s_version[4]; /* Version of this structure */ + unsigned char bi_r_version[30]; /* Version of the IBM ROM */ + unsigned int bi_memsize; /* DRAM installed, in bytes */ + unsigned char bi_enetaddr[2][6]; /* Local Ethernet MAC address */ unsigned char bi_pci_enetaddr[6]; /* PCI Ethernet MAC address */ + unsigned int bi_intfreq; /* Processor speed, in Hz */ + unsigned int bi_busfreq; /* PLB Bus speed, in Hz */ + unsigned int bi_pci_busfreq; /* PCI Bus speed, in Hz */ + unsigned int bi_opb_busfreq; /* OPB Bus speed, in Hz */ + unsigned int bi_pllouta_freq; /* PLL OUTA speed, in Hz */ +} bd_t; + +/* Some 4xx parts use a different timebase frequency from the internal clock. +*/ +#define bi_tbfreq bi_intfreq + + +/* Memory map for the Bubinga board. + * Generic 4xx plus RTC. + */ + +extern void *bubinga_rtc_base; +#define BUBINGA_RTC_PADDR ((uint)0xf0000000) +#define BUBINGA_RTC_VADDR BUBINGA_RTC_PADDR +#define BUBINGA_RTC_SIZE ((uint)8*1024) + +/* The UART clock is based off an internal clock - + * define BASE_BAUD based on the internal clock and divider(s). + * Since BASE_BAUD must be a constant, we will initialize it + * using clock/divider values which OpenBIOS initializes + * for typical configurations at various CPU speeds. + * The base baud is calculated as (FWDA / EXT UART DIV / 16) + */ +#define BASE_BAUD 0 + +#define BUBINGA_FPGA_BASE 0xF0300000 + +#define PPC4xx_MACHINE_NAME "IBM Bubinga" + +#endif /* !__ASSEMBLY__ */ +#endif /* __BUBINGA_H__ */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/4xx/cpci405.c b/arch/ppc/platforms/4xx/cpci405.c new file mode 100644 index 00000000000..ff966773a0b --- /dev/null +++ b/arch/ppc/platforms/4xx/cpci405.c @@ -0,0 +1,84 @@ +/* + * arch/ppc/platforms/cpci405.c + * + * Board setup routines for the esd CPCI-405 cPCI Board. + * + * Author: Stefan Roese + * stefan.roese@esd-electronics.com + * + * Copyright 2001 esd electronic system design - hannover germany + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +void *cpci405_nvram; + +/* + * Some IRQs unique to CPCI-405. + */ +int __init +ppc405_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {28, 28, 28, 28}, /* IDSEL 15 - cPCI slot 8 */ + {29, 29, 29, 29}, /* IDSEL 16 - cPCI slot 7 */ + {30, 30, 30, 30}, /* IDSEL 17 - cPCI slot 6 */ + {27, 27, 27, 27}, /* IDSEL 18 - cPCI slot 5 */ + {28, 28, 28, 28}, /* IDSEL 19 - cPCI slot 4 */ + {29, 29, 29, 29}, /* IDSEL 20 - cPCI slot 3 */ + {30, 30, 30, 30}, /* IDSEL 21 - cPCI slot 2 */ + }; + const long min_idsel = 15, max_idsel = 21, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +}; + +void __init +cpci405_setup_arch(void) +{ + ppc4xx_setup_arch(); + + ibm_ocp_set_emac(0, 0); + + TODC_INIT(TODC_TYPE_MK48T35, cpci405_nvram, cpci405_nvram, cpci405_nvram, 8); +} + +void __init +cpci405_map_io(void) +{ + ppc4xx_map_io(); + cpci405_nvram = ioremap(CPCI405_NVRAM_PADDR, CPCI405_NVRAM_SIZE); +} + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + ppc4xx_init(r3, r4, r5, r6, r7); + + ppc_md.setup_arch = cpci405_setup_arch; + ppc_md.setup_io_mappings = cpci405_map_io; + + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; +} diff --git a/arch/ppc/platforms/4xx/cpci405.h b/arch/ppc/platforms/4xx/cpci405.h new file mode 100644 index 00000000000..e27f7cb650d --- /dev/null +++ b/arch/ppc/platforms/4xx/cpci405.h @@ -0,0 +1,37 @@ +/* + * CPCI-405 board specific definitions + * + * Copyright (c) 2001 Stefan Roese (stefan.roese@esd-electronics.com) + */ + +#ifdef __KERNEL__ +#ifndef __ASM_CPCI405_H__ +#define __ASM_CPCI405_H__ + +#include + +/* We have a 405GP core */ +#include + +#include + +#ifndef __ASSEMBLY__ +/* Some 4xx parts use a different timebase frequency from the internal clock. +*/ +#define bi_tbfreq bi_intfreq + +/* Map for the NVRAM space */ +#define CPCI405_NVRAM_PADDR ((uint)0xf0200000) +#define CPCI405_NVRAM_SIZE ((uint)32*1024) + +#ifdef CONFIG_PPC405GP_INTERNAL_CLOCK +#define BASE_BAUD 201600 +#else +#define BASE_BAUD 691200 +#endif + +#define PPC4xx_MACHINE_NAME "esd CPCI-405" + +#endif /* !__ASSEMBLY__ */ +#endif /* __ASM_CPCI405_H__ */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/4xx/ebony.c b/arch/ppc/platforms/4xx/ebony.c new file mode 100644 index 00000000000..f63bca83e75 --- /dev/null +++ b/arch/ppc/platforms/4xx/ebony.c @@ -0,0 +1,356 @@ +/* + * arch/ppc/platforms/4xx/ebony.c + * + * Ebony board specific routines + * + * Matt Porter + * Copyright 2002-2005 MontaVista Software Inc. + * + * Eugene Surovegin or + * Copyright (c) 2003, 2004 Zultys Technologies + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * This is a horrible kludge, we eventually need to abstract this + * generic PHY stuff, so the standard phy mode defines can be + * easily used from arch code. + */ +#include "../../../../drivers/net/ibm_emac/ibm_emac_phy.h" + +bd_t __res; + +static struct ibm44x_clocks clocks __initdata; + +/* + * Ebony external IRQ triggering/polarity settings + */ +unsigned char ppc4xx_uic_ext_irq_cfg[] __initdata = { + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ0: PCI slot 0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ1: PCI slot 1 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ2: PCI slot 2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ3: PCI slot 3 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* IRQ4: IRDA */ + (IRQ_SENSE_EDGE | IRQ_POLARITY_NEGATIVE), /* IRQ5: SMI pushbutton */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ6: PHYs */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* IRQ7: AUX */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ8: EXT */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ9: EXT */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ10: EXT */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ11: EXT */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* IRQ12: EXT */ +}; + +static void __init +ebony_calibrate_decr(void) +{ + unsigned int freq; + + /* + * Determine system clock speed + * + * If we are on Rev. B silicon, then use + * default external system clock. If we are + * on Rev. C silicon then errata forces us to + * use the internal clock. + */ + switch (PVR_REV(mfspr(SPRN_PVR))) { + case PVR_REV(PVR_440GP_RB): + freq = EBONY_440GP_RB_SYSCLK; + break; + case PVR_REV(PVR_440GP_RC1): + default: + freq = EBONY_440GP_RC_SYSCLK; + break; + } + + ibm44x_calibrate_decr(freq); +} + +static int +ebony_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "vendor\t\t: IBM\n"); + seq_printf(m, "machine\t\t: Ebony\n"); + + return 0; +} + +static inline int +ebony_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + { 23, 23, 23, 23 }, /* IDSEL 1 - PCI Slot 0 */ + { 24, 24, 24, 24 }, /* IDSEL 2 - PCI Slot 1 */ + { 25, 25, 25, 25 }, /* IDSEL 3 - PCI Slot 2 */ + { 26, 26, 26, 26 }, /* IDSEL 4 - PCI Slot 3 */ + }; + + const long min_idsel = 1, max_idsel = 4, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +} + +#define PCIX_WRITEL(value, offset) \ + (writel(value, pcix_reg_base + offset)) + +/* + * FIXME: This is only here to "make it work". This will move + * to a ibm_pcix.c which will contain a generic IBM PCIX bridge + * configuration library. -Matt + */ +static void __init +ebony_setup_pcix(void) +{ + void *pcix_reg_base; + + pcix_reg_base = ioremap64(PCIX0_REG_BASE, PCIX_REG_SIZE); + + /* Disable all windows */ + PCIX_WRITEL(0, PCIX0_POM0SA); + PCIX_WRITEL(0, PCIX0_POM1SA); + PCIX_WRITEL(0, PCIX0_POM2SA); + PCIX_WRITEL(0, PCIX0_PIM0SA); + PCIX_WRITEL(0, PCIX0_PIM1SA); + PCIX_WRITEL(0, PCIX0_PIM2SA); + + /* Setup 2GB PLB->PCI outbound mem window (3_8000_0000->0_8000_0000) */ + PCIX_WRITEL(0x00000003, PCIX0_POM0LAH); + PCIX_WRITEL(0x80000000, PCIX0_POM0LAL); + PCIX_WRITEL(0x00000000, PCIX0_POM0PCIAH); + PCIX_WRITEL(0x80000000, PCIX0_POM0PCIAL); + PCIX_WRITEL(0x80000001, PCIX0_POM0SA); + + /* Setup 2GB PCI->PLB inbound memory window at 0, enable MSIs */ + PCIX_WRITEL(0x00000000, PCIX0_PIM0LAH); + PCIX_WRITEL(0x00000000, PCIX0_PIM0LAL); + PCIX_WRITEL(0x80000007, PCIX0_PIM0SA); + + eieio(); +} + +static void __init +ebony_setup_hose(void) +{ + struct pci_controller *hose; + + /* Configure windows on the PCI-X host bridge */ + ebony_setup_pcix(); + + hose = pcibios_alloc_controller(); + + if (!hose) + return; + + hose->first_busno = 0; + hose->last_busno = 0xff; + + hose->pci_mem_offset = EBONY_PCI_MEM_OFFSET; + + pci_init_resource(&hose->io_resource, + EBONY_PCI_LOWER_IO, + EBONY_PCI_UPPER_IO, + IORESOURCE_IO, + "PCI host bridge"); + + pci_init_resource(&hose->mem_resources[0], + EBONY_PCI_LOWER_MEM, + EBONY_PCI_UPPER_MEM, + IORESOURCE_MEM, + "PCI host bridge"); + + hose->io_space.start = EBONY_PCI_LOWER_IO; + hose->io_space.end = EBONY_PCI_UPPER_IO; + hose->mem_space.start = EBONY_PCI_LOWER_MEM; + hose->mem_space.end = EBONY_PCI_UPPER_MEM; + isa_io_base = + (unsigned long)ioremap64(EBONY_PCI_IO_BASE, EBONY_PCI_IO_SIZE); + hose->io_base_virt = (void *)isa_io_base; + + setup_indirect_pci(hose, + EBONY_PCI_CFGA_PLB32, + EBONY_PCI_CFGD_PLB32); + hose->set_cfg_type = 1; + + hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); + + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = ebony_map_irq; +} + +TODC_ALLOC(); + +static void __init +ebony_early_serial_map(void) +{ + struct uart_port port; + + /* Setup ioremapped serial port access */ + memset(&port, 0, sizeof(port)); + port.membase = ioremap64(PPC440GP_UART0_ADDR, 8); + port.irq = 0; + port.uartclk = clocks.uart0; + port.regshift = 0; + port.iotype = SERIAL_IO_MEM; + port.flags = ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST; + port.line = 0; + + if (early_serial_setup(&port) != 0) { + printk("Early serial init of port 0 failed\n"); + } + +#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) + /* Configure debug serial access */ + gen550_init(0, &port); +#endif + + port.membase = ioremap64(PPC440GP_UART1_ADDR, 8); + port.irq = 1; + port.uartclk = clocks.uart1; + port.line = 1; + + if (early_serial_setup(&port) != 0) { + printk("Early serial init of port 1 failed\n"); + } + +#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) + /* Configure debug serial access */ + gen550_init(1, &port); +#endif +} + +static void __init +ebony_setup_arch(void) +{ + struct ocp_def *def; + struct ocp_func_emac_data *emacdata; + + /* Set mac_addr for each EMAC */ + def = ocp_get_one_device(OCP_VENDOR_IBM, OCP_FUNC_EMAC, 0); + emacdata = def->additions; + emacdata->phy_map = 0x00000001; /* Skip 0x00 */ + emacdata->phy_mode = PHY_MODE_RMII; + memcpy(emacdata->mac_addr, __res.bi_enetaddr, 6); + + def = ocp_get_one_device(OCP_VENDOR_IBM, OCP_FUNC_EMAC, 1); + emacdata = def->additions; + emacdata->phy_map = 0x00000001; /* Skip 0x00 */ + emacdata->phy_mode = PHY_MODE_RMII; + memcpy(emacdata->mac_addr, __res.bi_enet1addr, 6); + + /* + * Determine various clocks. + * To be completely correct we should get SysClk + * from FPGA, because it can be changed by on-board switches + * --ebs + */ + ibm440gp_get_clocks(&clocks, 33333333, 6 * 1843200); + ocp_sys_info.opb_bus_freq = clocks.opb; + + /* Setup TODC access */ + TODC_INIT(TODC_TYPE_DS1743, + 0, + 0, + ioremap64(EBONY_RTC_ADDR, EBONY_RTC_SIZE), + 8); + + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_jiffy = 50000000/HZ; + + /* Setup PCI host bridge */ + ebony_setup_hose(); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = Root_NFS; +#else + ROOT_DEV = Root_HDA1; +#endif + + ebony_early_serial_map(); + + /* Identify the system */ + printk("IBM Ebony port (MontaVista Software, Inc. (source@mvista.com))\n"); +} + +void __init platform_init(unsigned long r3, unsigned long r4, + unsigned long r5, unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + + /* + * If we were passed in a board information, copy it into the + * residual data area. + */ + if (r3) + __res = *(bd_t *)(r3 + KERNELBASE); + + ibm44x_platform_init(); + + ppc_md.setup_arch = ebony_setup_arch; + ppc_md.show_cpuinfo = ebony_show_cpuinfo; + ppc_md.get_irq = NULL; /* Set in ppc4xx_pic_init() */ + + ppc_md.calibrate_decr = ebony_calibrate_decr; + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; +#ifdef CONFIG_KGDB + ppc_md.early_serial_map = ebony_early_serial_map; +#endif +} + diff --git a/arch/ppc/platforms/4xx/ebony.h b/arch/ppc/platforms/4xx/ebony.h new file mode 100644 index 00000000000..47c391c9174 --- /dev/null +++ b/arch/ppc/platforms/4xx/ebony.h @@ -0,0 +1,91 @@ +/* + * arch/ppc/platforms/ebony.h + * + * Ebony board definitions + * + * Matt Porter + * + * Copyright 2002 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_EBONY_H__ +#define __ASM_EBONY_H__ + +#include +#include + +/* F/W TLB mapping used in bootloader glue to reset EMAC */ +#define PPC44x_EMAC0_MR0 0xE0000800 + +/* Where to find the MAC info */ +#define EBONY_OPENBIOS_MAC_BASE 0xfffffe0c +#define EBONY_OPENBIOS_MAC_OFFSET 0x0c + +/* Default clock rates for Rev. B and Rev. C silicon */ +#define EBONY_440GP_RB_SYSCLK 33000000 +#define EBONY_440GP_RC_SYSCLK 400000000 + +/* RTC/NVRAM location */ +#define EBONY_RTC_ADDR 0x0000000148000000ULL +#define EBONY_RTC_SIZE 0x2000 + +/* Flash */ +#define EBONY_FPGA_ADDR 0x0000000148300000ULL +#define EBONY_BOOT_SMALL_FLASH(x) (x & 0x20) +#define EBONY_ONBRD_FLASH_EN(x) (x & 0x02) +#define EBONY_FLASH_SEL(x) (x & 0x01) +#define EBONY_SMALL_FLASH_LOW1 0x00000001ff800000ULL +#define EBONY_SMALL_FLASH_LOW2 0x00000001ff880000ULL +#define EBONY_SMALL_FLASH_HIGH1 0x00000001fff00000ULL +#define EBONY_SMALL_FLASH_HIGH2 0x00000001fff80000ULL +#define EBONY_SMALL_FLASH_SIZE 0x80000 +#define EBONY_LARGE_FLASH_LOW 0x00000001ff800000ULL +#define EBONY_LARGE_FLASH_HIGH 0x00000001ffc00000ULL +#define EBONY_LARGE_FLASH_SIZE 0x400000 + +#define EBONY_SMALL_FLASH_BASE 0x00000001fff80000ULL +#define EBONY_LARGE_FLASH_BASE 0x00000001ff800000ULL + +/* + * Serial port defines + */ + +/* OpenBIOS defined UART mappings, used before early_serial_setup */ +#define UART0_IO_BASE 0xE0000200 +#define UART1_IO_BASE 0xE0000300 + +/* external Epson SG-615P */ +#define BASE_BAUD 691200 + +#define STD_UART_OP(num) \ + { 0, BASE_BAUD, 0, UART##num##_INT, \ + (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST), \ + iomem_base: UART##num##_IO_BASE, \ + io_type: SERIAL_IO_MEM}, + +#define SERIAL_PORT_DFNS \ + STD_UART_OP(0) \ + STD_UART_OP(1) + +/* PCI support */ +#define EBONY_PCI_LOWER_IO 0x00000000 +#define EBONY_PCI_UPPER_IO 0x0000ffff +#define EBONY_PCI_LOWER_MEM 0x80002000 +#define EBONY_PCI_UPPER_MEM 0xffffefff + +#define EBONY_PCI_CFGREGS_BASE 0x000000020ec00000 +#define EBONY_PCI_CFGA_PLB32 0x0ec00000 +#define EBONY_PCI_CFGD_PLB32 0x0ec00004 + +#define EBONY_PCI_IO_BASE 0x0000000208000000ULL +#define EBONY_PCI_IO_SIZE 0x00010000 +#define EBONY_PCI_MEM_OFFSET 0x00000000 + +#endif /* __ASM_EBONY_H__ */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/4xx/ep405.c b/arch/ppc/platforms/4xx/ep405.c new file mode 100644 index 00000000000..26a07cdb30e --- /dev/null +++ b/arch/ppc/platforms/4xx/ep405.c @@ -0,0 +1,197 @@ +/* + * arch/ppc/platforms/4xx/ep405.c + * + * Embedded Planet 405GP board + * http://www.embeddedplanet.com + * + * Author: Matthew Locke + * + * 2001 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +u8 *ep405_bcsr; +u8 *ep405_nvram; + +static struct { + u8 cpld_xirq_select; + int pci_idsel; + int irq; +} ep405_devtable[] = { +#ifdef CONFIG_EP405PC + {0x07, 0x0E, 25}, /* EP405PC: USB */ +#endif +}; + +int __init +ppc405_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + int i; + + /* AFAICT this is only called a few times during PCI setup, so + performance is not critical */ + for (i = 0; i < ARRAY_SIZE(ep405_devtable); i++) { + if (idsel == ep405_devtable[i].pci_idsel) + return ep405_devtable[i].irq; + } + return -1; +}; + +void __init +ep405_setup_arch(void) +{ + ppc4xx_setup_arch(); + + ibm_ocp_set_emac(0, 0); + + if (__res.bi_nvramsize == 512*1024) { + /* FIXME: we should properly handle NVRTCs of different sizes */ + TODC_INIT(TODC_TYPE_DS1557, ep405_nvram, ep405_nvram, ep405_nvram, 8); + } +} + +void __init +bios_fixup(struct pci_controller *hose, struct pcil0_regs *pcip) +{ + unsigned int bar_response, bar; + /* + * Expected PCI mapping: + * + * PLB addr PCI memory addr + * --------------------- --------------------- + * 0000'0000 - 7fff'ffff <--- 0000'0000 - 7fff'ffff + * 8000'0000 - Bfff'ffff ---> 8000'0000 - Bfff'ffff + * + * PLB addr PCI io addr + * --------------------- --------------------- + * e800'0000 - e800'ffff ---> 0000'0000 - 0001'0000 + * + */ + + /* Disable region zero first */ + out_le32((void *) &(pcip->pmm[0].ma), 0x00000000); + /* PLB starting addr, PCI: 0x80000000 */ + out_le32((void *) &(pcip->pmm[0].la), 0x80000000); + /* PCI start addr, 0x80000000 */ + out_le32((void *) &(pcip->pmm[0].pcila), PPC405_PCI_MEM_BASE); + /* 512MB range of PLB to PCI */ + out_le32((void *) &(pcip->pmm[0].pciha), 0x00000000); + /* Enable no pre-fetch, enable region */ + out_le32((void *) &(pcip->pmm[0].ma), ((0xffffffff - + (PPC405_PCI_UPPER_MEM - + PPC405_PCI_MEM_BASE)) | 0x01)); + + /* Disable region one */ + out_le32((void *) &(pcip->pmm[1].ma), 0x00000000); + out_le32((void *) &(pcip->pmm[1].la), 0x00000000); + out_le32((void *) &(pcip->pmm[1].pcila), 0x00000000); + out_le32((void *) &(pcip->pmm[1].pciha), 0x00000000); + out_le32((void *) &(pcip->pmm[1].ma), 0x00000000); + out_le32((void *) &(pcip->ptm1ms), 0x00000000); + + /* Disable region two */ + out_le32((void *) &(pcip->pmm[2].ma), 0x00000000); + out_le32((void *) &(pcip->pmm[2].la), 0x00000000); + out_le32((void *) &(pcip->pmm[2].pcila), 0x00000000); + out_le32((void *) &(pcip->pmm[2].pciha), 0x00000000); + out_le32((void *) &(pcip->pmm[2].ma), 0x00000000); + out_le32((void *) &(pcip->ptm2ms), 0x00000000); + + /* Configure PTM (PCI->PLB) region 1 */ + out_le32((void *) &(pcip->ptm1la), 0x00000000); /* PLB base address */ + /* Disable PTM region 2 */ + out_le32((void *) &(pcip->ptm2ms), 0x00000000); + + /* Zero config bars */ + for (bar = PCI_BASE_ADDRESS_1; bar <= PCI_BASE_ADDRESS_2; bar += 4) { + early_write_config_dword(hose, hose->first_busno, + PCI_FUNC(hose->first_busno), bar, + 0x00000000); + early_read_config_dword(hose, hose->first_busno, + PCI_FUNC(hose->first_busno), bar, + &bar_response); + DBG("BUS %d, device %d, Function %d bar 0x%8.8x is 0x%8.8x\n", + hose->first_busno, PCI_SLOT(hose->first_busno), + PCI_FUNC(hose->first_busno), bar, bar_response); + } + /* end work arround */ +} + +void __init +ep405_map_io(void) +{ + bd_t *bip = &__res; + + ppc4xx_map_io(); + + ep405_bcsr = ioremap(EP405_BCSR_PADDR, EP405_BCSR_SIZE); + + if (bip->bi_nvramsize > 0) { + ep405_nvram = ioremap(EP405_NVRAM_PADDR, bip->bi_nvramsize); + } +} + +void __init +ep405_init_IRQ(void) +{ + int i; + + ppc4xx_init_IRQ(); + + /* Workaround for a bug in the firmware it incorrectly sets + the IRQ polarities for XIRQ0 and XIRQ1 */ + mtdcr(DCRN_UIC_PR(DCRN_UIC0_BASE), 0xffffff80); /* set the polarity */ + mtdcr(DCRN_UIC_SR(DCRN_UIC0_BASE), 0x00000060); /* clear bogus interrupts */ + + /* Activate the XIRQs from the CPLD */ + writeb(0xf0, ep405_bcsr+10); + + /* Set up IRQ routing */ + for (i = 0; i < ARRAY_SIZE(ep405_devtable); i++) { + if ( (ep405_devtable[i].irq >= 25) + && (ep405_devtable[i].irq) <= 31) { + writeb(ep405_devtable[i].cpld_xirq_select, ep405_bcsr+5); + writeb(ep405_devtable[i].irq - 25, ep405_bcsr+6); + } + } +} + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + ppc4xx_init(r3, r4, r5, r6, r7); + + ppc_md.setup_arch = ep405_setup_arch; + ppc_md.setup_io_mappings = ep405_map_io; + ppc_md.init_IRQ = ep405_init_IRQ; + + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; + + if (__res.bi_nvramsize == 512*1024) { + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + } else { + printk("EP405: NVRTC size is not 512k (not a DS1557). Not sure what to do with it\n"); + } +} diff --git a/arch/ppc/platforms/4xx/ep405.h b/arch/ppc/platforms/4xx/ep405.h new file mode 100644 index 00000000000..ea3eb21338f --- /dev/null +++ b/arch/ppc/platforms/4xx/ep405.h @@ -0,0 +1,54 @@ +/* + * arch/ppc/platforms/4xx/ep405.h + * + * Embedded Planet 405GP board + * http://www.embeddedplanet.com + * + * Author: Matthew Locke + * + * 2000 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_EP405_H__ +#define __ASM_EP405_H__ + +/* We have a 405GP core */ +#include + +#ifndef __ASSEMBLY__ + +#include + +typedef struct board_info { + unsigned int bi_memsize; /* DRAM installed, in bytes */ + unsigned char bi_enetaddr[6]; /* Local Ethernet MAC address */ + unsigned int bi_intfreq; /* Processor speed, in Hz */ + unsigned int bi_busfreq; /* PLB Bus speed, in Hz */ + unsigned int bi_pci_busfreq; /* PCI Bus speed, in Hz */ + unsigned int bi_nvramsize; /* Size of the NVRAM/RTC */ +} bd_t; + +/* Some 4xx parts use a different timebase frequency from the internal clock. +*/ +#define bi_tbfreq bi_intfreq + +extern u8 *ep405_bcsr; +extern u8 *ep405_nvram; + +/* Map for the BCSR and NVRAM space */ +#define EP405_BCSR_PADDR ((uint)0xf4000000) +#define EP405_BCSR_SIZE ((uint)16) +#define EP405_NVRAM_PADDR ((uint)0xf4200000) + +/* serial defines */ +#define BASE_BAUD 399193 + +#define PPC4xx_MACHINE_NAME "Embedded Planet 405GP" + +#endif /* !__ASSEMBLY__ */ +#endif /* __ASM_EP405_H__ */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/4xx/ibm405ep.c b/arch/ppc/platforms/4xx/ibm405ep.c new file mode 100644 index 00000000000..6d44567f4dd --- /dev/null +++ b/arch/ppc/platforms/4xx/ibm405ep.c @@ -0,0 +1,143 @@ +/* + * arch/ppc/platforms/ibm405ep.c + * + * Support for IBM PPC 405EP processors. + * + * Author: SAW (IBM), derived from ibmnp405l.c. + * Maintained by MontaVista Software + * + * 2003 (c) MontaVista Softare Inc. This file is licensed under the + * terms of the GNU General Public License version 2. This program is + * licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +static struct ocp_func_mal_data ibm405ep_mal0_def = { + .num_tx_chans = 4, /* Number of TX channels */ + .num_rx_chans = 2, /* Number of RX channels */ + .txeob_irq = 11, /* TX End Of Buffer IRQ */ + .rxeob_irq = 12, /* RX End Of Buffer IRQ */ + .txde_irq = 13, /* TX Descriptor Error IRQ */ + .rxde_irq = 14, /* RX Descriptor Error IRQ */ + .serr_irq = 10, /* MAL System Error IRQ */ +}; +OCP_SYSFS_MAL_DATA() + +static struct ocp_func_emac_data ibm405ep_emac0_def = { + .rgmii_idx = -1, /* No RGMII */ + .rgmii_mux = -1, /* No RGMII */ + .zmii_idx = -1, /* ZMII device index */ + .zmii_mux = 0, /* ZMII input of this EMAC */ + .mal_idx = 0, /* MAL device index */ + .mal_rx_chan = 0, /* MAL rx channel number */ + .mal_tx_chan = 0, /* MAL tx channel number */ + .wol_irq = 9, /* WOL interrupt number */ + .mdio_idx = 0, /* MDIO via EMAC0 */ + .tah_idx = -1, /* No TAH */ +}; + +static struct ocp_func_emac_data ibm405ep_emac1_def = { + .rgmii_idx = -1, /* No RGMII */ + .rgmii_mux = -1, /* No RGMII */ + .zmii_idx = -1, /* ZMII device index */ + .zmii_mux = 0, /* ZMII input of this EMAC */ + .mal_idx = 0, /* MAL device index */ + .mal_rx_chan = 1, /* MAL rx channel number */ + .mal_tx_chan = 2, /* MAL tx channel number */ + .wol_irq = 9, /* WOL interrupt number */ + .mdio_idx = 0, /* MDIO via EMAC0 */ + .tah_idx = -1, /* No TAH */ +}; +OCP_SYSFS_EMAC_DATA() + +static struct ocp_func_iic_data ibm405ep_iic0_def = { + .fast_mode = 0, /* Use standad mode (100Khz) */ +}; +OCP_SYSFS_IIC_DATA() + +struct ocp_def core_ocp[] = { + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_OPB, + .index = 0, + .paddr = 0xEF600000, + .irq = OCP_IRQ_NA, + .pm = OCP_CPM_NA, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_16550, + .index = 0, + .paddr = UART0_IO_BASE, + .irq = UART0_INT, + .pm = IBM_CPM_UART0 + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_16550, + .index = 1, + .paddr = UART1_IO_BASE, + .irq = UART1_INT, + .pm = IBM_CPM_UART1 + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_IIC, + .paddr = 0xEF600500, + .irq = 2, + .pm = IBM_CPM_IIC0, + .additions = &ibm405ep_iic0_def, + .show = &ocp_show_iic_data + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_GPIO, + .paddr = 0xEF600700, + .irq = OCP_IRQ_NA, + .pm = IBM_CPM_GPIO0 + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_MAL, + .paddr = OCP_PADDR_NA, + .irq = OCP_IRQ_NA, + .pm = OCP_CPM_NA, + .additions = &ibm405ep_mal0_def, + .show = &ocp_show_mal_data + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_EMAC, + .index = 0, + .paddr = EMAC0_BASE, + .irq = 15, + .pm = OCP_CPM_NA, + .additions = &ibm405ep_emac0_def, + .show = &ocp_show_emac_data + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_EMAC, + .index = 1, + .paddr = 0xEF600900, + .irq = 17, + .pm = OCP_CPM_NA, + .additions = &ibm405ep_emac1_def, + .show = &ocp_show_emac_data + }, + { .vendor = OCP_VENDOR_INVALID + } +}; + +/* Polarity and triggering settings for internal interrupt sources */ +struct ppc4xx_uic_settings ppc4xx_core_uic_cfg[] __initdata = { + { .polarity = 0xffff7f80, + .triggering = 0x00000000, + .ext_irq_mask = 0x0000007f, /* IRQ0 - IRQ6 */ + } +}; diff --git a/arch/ppc/platforms/4xx/ibm405ep.h b/arch/ppc/platforms/4xx/ibm405ep.h new file mode 100644 index 00000000000..e051e3fe8c6 --- /dev/null +++ b/arch/ppc/platforms/4xx/ibm405ep.h @@ -0,0 +1,148 @@ +/* + * arch/ppc/platforms/4xx/ibm405ep.h + * + * IBM PPC 405EP processor defines. + * + * Author: SAW (IBM), derived from ibm405gp.h. + * Maintained by MontaVista Software + * + * 2003 (c) MontaVista Softare Inc. This file is licensed under the + * terms of the GNU General Public License version 2. This program is + * licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_IBM405EP_H__ +#define __ASM_IBM405EP_H__ + +#include + +/* ibm405.h at bottom of this file */ + +/* PCI + * PCI Bridge config reg definitions + * see 17-19 of manual + */ + +#define PPC405_PCI_CONFIG_ADDR 0xeec00000 +#define PPC405_PCI_CONFIG_DATA 0xeec00004 + +#define PPC405_PCI_PHY_MEM_BASE 0x80000000 /* hose_a->pci_mem_offset */ + /* setbat */ +#define PPC405_PCI_MEM_BASE PPC405_PCI_PHY_MEM_BASE /* setbat */ +#define PPC405_PCI_PHY_IO_BASE 0xe8000000 /* setbat */ +#define PPC405_PCI_IO_BASE PPC405_PCI_PHY_IO_BASE /* setbat */ + +#define PPC405_PCI_LOWER_MEM 0x80000000 /* hose_a->mem_space.start */ +#define PPC405_PCI_UPPER_MEM 0xBfffffff /* hose_a->mem_space.end */ +#define PPC405_PCI_LOWER_IO 0x00000000 /* hose_a->io_space.start */ +#define PPC405_PCI_UPPER_IO 0x0000ffff /* hose_a->io_space.end */ + +#define PPC405_ISA_IO_BASE PPC405_PCI_IO_BASE + +#define PPC4xx_PCI_IO_PADDR ((uint)PPC405_PCI_PHY_IO_BASE) +#define PPC4xx_PCI_IO_VADDR PPC4xx_PCI_IO_PADDR +#define PPC4xx_PCI_IO_SIZE ((uint)64*1024) +#define PPC4xx_PCI_CFG_PADDR ((uint)PPC405_PCI_CONFIG_ADDR) +#define PPC4xx_PCI_CFG_VADDR PPC4xx_PCI_CFG_PADDR +#define PPC4xx_PCI_CFG_SIZE ((uint)4*1024) +#define PPC4xx_PCI_LCFG_PADDR ((uint)0xef400000) +#define PPC4xx_PCI_LCFG_VADDR PPC4xx_PCI_LCFG_PADDR +#define PPC4xx_PCI_LCFG_SIZE ((uint)4*1024) +#define PPC4xx_ONB_IO_PADDR ((uint)0xef600000) +#define PPC4xx_ONB_IO_VADDR PPC4xx_ONB_IO_PADDR +#define PPC4xx_ONB_IO_SIZE ((uint)4*1024) + +/* serial port defines */ +#define RS_TABLE_SIZE 2 + +#define UART0_INT 0 +#define UART1_INT 1 + +#define PCIL0_BASE 0xEF400000 +#define UART0_IO_BASE 0xEF600300 +#define UART1_IO_BASE 0xEF600400 +#define EMAC0_BASE 0xEF600800 + +#define BD_EMAC_ADDR(e,i) bi_enetaddr[e][i] + +#if defined(CONFIG_UART0_TTYS0) +#define ACTING_UART0_IO_BASE UART0_IO_BASE +#define ACTING_UART1_IO_BASE UART1_IO_BASE +#define ACTING_UART0_INT UART0_INT +#define ACTING_UART1_INT UART1_INT +#else +#define ACTING_UART0_IO_BASE UART1_IO_BASE +#define ACTING_UART1_IO_BASE UART0_IO_BASE +#define ACTING_UART0_INT UART1_INT +#define ACTING_UART1_INT UART0_INT +#endif + +#define STD_UART_OP(num) \ + { 0, BASE_BAUD, 0, ACTING_UART##num##_INT, \ + (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST), \ + iomem_base: (u8 *)ACTING_UART##num##_IO_BASE, \ + io_type: SERIAL_IO_MEM}, + +#define SERIAL_DEBUG_IO_BASE ACTING_UART0_IO_BASE +#define SERIAL_PORT_DFNS \ + STD_UART_OP(0) \ + STD_UART_OP(1) + +/* DCR defines */ +#define DCRN_CPMSR_BASE 0x0BA +#define DCRN_CPMFR_BASE 0x0B9 + +#define DCRN_CPC0_PLLMR0_BASE 0x0F0 +#define DCRN_CPC0_BOOT_BASE 0x0F1 +#define DCRN_CPC0_CR1_BASE 0x0F2 +#define DCRN_CPC0_EPRCSR_BASE 0x0F3 +#define DCRN_CPC0_PLLMR1_BASE 0x0F4 +#define DCRN_CPC0_UCR_BASE 0x0F5 +#define DCRN_CPC0_UCR_U0DIV 0x07F +#define DCRN_CPC0_SRR_BASE 0x0F6 +#define DCRN_CPC0_JTAGID_BASE 0x0F7 +#define DCRN_CPC0_SPARE_BASE 0x0F8 +#define DCRN_CPC0_PCI_BASE 0x0F9 + + +#define IBM_CPM_GPT 0x80000000 /* GPT interface */ +#define IBM_CPM_PCI 0x40000000 /* PCI bridge */ +#define IBM_CPM_UIC 0x00010000 /* Universal Int Controller */ +#define IBM_CPM_CPU 0x00008000 /* processor core */ +#define IBM_CPM_EBC 0x00002000 /* EBC controller */ +#define IBM_CPM_SDRAM0 0x00004000 /* SDRAM memory controller */ +#define IBM_CPM_GPIO0 0x00001000 /* General Purpose IO */ +#define IBM_CPM_TMRCLK 0x00000400 /* CPU timers */ +#define IBM_CPM_PLB 0x00000100 /* PLB bus arbiter */ +#define IBM_CPM_OPB 0x00000080 /* PLB to OPB bridge */ +#define IBM_CPM_DMA 0x00000040 /* DMA controller */ +#define IBM_CPM_IIC0 0x00000010 /* IIC interface */ +#define IBM_CPM_UART1 0x00000002 /* serial port 0 */ +#define IBM_CPM_UART0 0x00000001 /* serial port 1 */ +#define DFLT_IBM4xx_PM ~(IBM_CPM_PCI | IBM_CPM_CPU | IBM_CPM_DMA \ + | IBM_CPM_OPB | IBM_CPM_EBC \ + | IBM_CPM_SDRAM0 | IBM_CPM_PLB \ + | IBM_CPM_UIC | IBM_CPM_TMRCLK) +#define DCRN_DMA0_BASE 0x100 +#define DCRN_DMA1_BASE 0x108 +#define DCRN_DMA2_BASE 0x110 +#define DCRN_DMA3_BASE 0x118 +#define DCRNCAP_DMA_SG 1 /* have DMA scatter/gather capability */ +#define DCRN_DMASR_BASE 0x120 +#define DCRN_EBC_BASE 0x012 +#define DCRN_DCP0_BASE 0x014 +#define DCRN_MAL_BASE 0x180 +#define DCRN_OCM0_BASE 0x018 +#define DCRN_PLB0_BASE 0x084 +#define DCRN_PLLMR_BASE 0x0B0 +#define DCRN_POB0_BASE 0x0A0 +#define DCRN_SDRAM0_BASE 0x010 +#define DCRN_UIC0_BASE 0x0C0 +#define UIC0 DCRN_UIC0_BASE + +#include + +#endif /* __ASM_IBM405EP_H__ */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/4xx/ibm405gp.c b/arch/ppc/platforms/4xx/ibm405gp.c new file mode 100644 index 00000000000..dfd7ef3ba5f --- /dev/null +++ b/arch/ppc/platforms/4xx/ibm405gp.c @@ -0,0 +1,120 @@ +/* + * + * Copyright 2000-2001 MontaVista Software Inc. + * Original author: Armin Kuster akuster@mvista.com + * + * Module name: ibm405gp.c + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct ocp_func_emac_data ibm405gp_emac0_def = { + .rgmii_idx = -1, /* No RGMII */ + .rgmii_mux = -1, /* No RGMII */ + .zmii_idx = -1, /* ZMII device index */ + .zmii_mux = 0, /* ZMII input of this EMAC */ + .mal_idx = 0, /* MAL device index */ + .mal_rx_chan = 0, /* MAL rx channel number */ + .mal_tx_chan = 0, /* MAL tx channel number */ + .wol_irq = 9, /* WOL interrupt number */ + .mdio_idx = -1, /* No shared MDIO */ + .tah_idx = -1, /* No TAH */ +}; +OCP_SYSFS_EMAC_DATA() + +static struct ocp_func_mal_data ibm405gp_mal0_def = { + .num_tx_chans = 1, /* Number of TX channels */ + .num_rx_chans = 1, /* Number of RX channels */ + .txeob_irq = 11, /* TX End Of Buffer IRQ */ + .rxeob_irq = 12, /* RX End Of Buffer IRQ */ + .txde_irq = 13, /* TX Descriptor Error IRQ */ + .rxde_irq = 14, /* RX Descriptor Error IRQ */ + .serr_irq = 10, /* MAL System Error IRQ */ +}; +OCP_SYSFS_MAL_DATA() + +static struct ocp_func_iic_data ibm405gp_iic0_def = { + .fast_mode = 0, /* Use standad mode (100Khz) */ +}; +OCP_SYSFS_IIC_DATA() + +struct ocp_def core_ocp[] = { + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_OPB, + .index = 0, + .paddr = 0xEF600000, + .irq = OCP_IRQ_NA, + .pm = OCP_CPM_NA, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_16550, + .index = 0, + .paddr = UART0_IO_BASE, + .irq = UART0_INT, + .pm = IBM_CPM_UART0 + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_16550, + .index = 1, + .paddr = UART1_IO_BASE, + .irq = UART1_INT, + .pm = IBM_CPM_UART1 + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_IIC, + .paddr = 0xEF600500, + .irq = 2, + .pm = IBM_CPM_IIC0, + .additions = &ibm405gp_iic0_def, + .show = &ocp_show_iic_data, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_GPIO, + .paddr = 0xEF600700, + .irq = OCP_IRQ_NA, + .pm = IBM_CPM_GPIO0 + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_MAL, + .paddr = OCP_PADDR_NA, + .irq = OCP_IRQ_NA, + .pm = OCP_CPM_NA, + .additions = &ibm405gp_mal0_def, + .show = &ocp_show_mal_data, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_EMAC, + .index = 0, + .paddr = EMAC0_BASE, + .irq = 15, + .pm = IBM_CPM_EMAC0, + .additions = &ibm405gp_emac0_def, + .show = &ocp_show_emac_data, + }, + { .vendor = OCP_VENDOR_INVALID + } +}; + +/* Polarity and triggering settings for internal interrupt sources */ +struct ppc4xx_uic_settings ppc4xx_core_uic_cfg[] __initdata = { + { .polarity = 0xffffff80, + .triggering = 0x10000000, + .ext_irq_mask = 0x0000007f, /* IRQ0 - IRQ6 */ + } +}; diff --git a/arch/ppc/platforms/4xx/ibm405gp.h b/arch/ppc/platforms/4xx/ibm405gp.h new file mode 100644 index 00000000000..b2b642e81af --- /dev/null +++ b/arch/ppc/platforms/4xx/ibm405gp.h @@ -0,0 +1,151 @@ +/* + * arch/ppc/platforms/4xx/ibm405gp.h + * + * Author: Armin Kuster akuster@mvista.com + * + * 2001 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_IBM405GP_H__ +#define __ASM_IBM405GP_H__ + +#include + +/* ibm405.h at bottom of this file */ + +/* PCI + * PCI Bridge config reg definitions + * see 17-19 of manual + */ + +#define PPC405_PCI_CONFIG_ADDR 0xeec00000 +#define PPC405_PCI_CONFIG_DATA 0xeec00004 + +#define PPC405_PCI_PHY_MEM_BASE 0x80000000 /* hose_a->pci_mem_offset */ + /* setbat */ +#define PPC405_PCI_MEM_BASE PPC405_PCI_PHY_MEM_BASE /* setbat */ +#define PPC405_PCI_PHY_IO_BASE 0xe8000000 /* setbat */ +#define PPC405_PCI_IO_BASE PPC405_PCI_PHY_IO_BASE /* setbat */ + +#define PPC405_PCI_LOWER_MEM 0x80000000 /* hose_a->mem_space.start */ +#define PPC405_PCI_UPPER_MEM 0xBfffffff /* hose_a->mem_space.end */ +#define PPC405_PCI_LOWER_IO 0x00000000 /* hose_a->io_space.start */ +#define PPC405_PCI_UPPER_IO 0x0000ffff /* hose_a->io_space.end */ + +#define PPC405_ISA_IO_BASE PPC405_PCI_IO_BASE + +#define PPC4xx_PCI_IO_PADDR ((uint)PPC405_PCI_PHY_IO_BASE) +#define PPC4xx_PCI_IO_VADDR PPC4xx_PCI_IO_PADDR +#define PPC4xx_PCI_IO_SIZE ((uint)64*1024) +#define PPC4xx_PCI_CFG_PADDR ((uint)PPC405_PCI_CONFIG_ADDR) +#define PPC4xx_PCI_CFG_VADDR PPC4xx_PCI_CFG_PADDR +#define PPC4xx_PCI_CFG_SIZE ((uint)4*1024) +#define PPC4xx_PCI_LCFG_PADDR ((uint)0xef400000) +#define PPC4xx_PCI_LCFG_VADDR PPC4xx_PCI_LCFG_PADDR +#define PPC4xx_PCI_LCFG_SIZE ((uint)4*1024) +#define PPC4xx_ONB_IO_PADDR ((uint)0xef600000) +#define PPC4xx_ONB_IO_VADDR PPC4xx_ONB_IO_PADDR +#define PPC4xx_ONB_IO_SIZE ((uint)4*1024) + +/* serial port defines */ +#define RS_TABLE_SIZE 2 + +#define UART0_INT 0 +#define UART1_INT 1 + +#define PCIL0_BASE 0xEF400000 +#define UART0_IO_BASE 0xEF600300 +#define UART1_IO_BASE 0xEF600400 +#define EMAC0_BASE 0xEF600800 + +#define BD_EMAC_ADDR(e,i) bi_enetaddr[i] + +#define STD_UART_OP(num) \ + { 0, BASE_BAUD, 0, UART##num##_INT, \ + (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST), \ + iomem_base: (u8 *)UART##num##_IO_BASE, \ + io_type: SERIAL_IO_MEM}, + +#if defined(CONFIG_UART0_TTYS0) +#define SERIAL_DEBUG_IO_BASE UART0_IO_BASE +#define SERIAL_PORT_DFNS \ + STD_UART_OP(0) \ + STD_UART_OP(1) +#endif + +#if defined(CONFIG_UART0_TTYS1) +#define SERIAL_DEBUG_IO_BASE UART1_IO_BASE +#define SERIAL_PORT_DFNS \ + STD_UART_OP(1) \ + STD_UART_OP(0) +#endif + +/* DCR defines */ +#define DCRN_CHCR_BASE 0x0B1 +#define DCRN_CHPSR_BASE 0x0B4 +#define DCRN_CPMSR_BASE 0x0B8 +#define DCRN_CPMFR_BASE 0x0BA + +#define CHR0_U0EC 0x00000080 /* Select external clock for UART0 */ +#define CHR0_U1EC 0x00000040 /* Select external clock for UART1 */ +#define CHR0_UDIV 0x0000003E /* UART internal clock divisor */ +#define CHR1_CETE 0x00800000 /* CPU external timer enable */ + +#define DCRN_CHPSR_BASE 0x0B4 +#define PSR_PLL_FWD_MASK 0xC0000000 +#define PSR_PLL_FDBACK_MASK 0x30000000 +#define PSR_PLL_TUNING_MASK 0x0E000000 +#define PSR_PLB_CPU_MASK 0x01800000 +#define PSR_OPB_PLB_MASK 0x00600000 +#define PSR_PCI_PLB_MASK 0x00180000 +#define PSR_EB_PLB_MASK 0x00060000 +#define PSR_ROM_WIDTH_MASK 0x00018000 +#define PSR_ROM_LOC 0x00004000 +#define PSR_PCI_ASYNC_EN 0x00001000 +#define PSR_PCI_ARBIT_EN 0x00000400 + +#define IBM_CPM_IIC0 0x80000000 /* IIC interface */ +#define IBM_CPM_PCI 0x40000000 /* PCI bridge */ +#define IBM_CPM_CPU 0x20000000 /* processor core */ +#define IBM_CPM_DMA 0x10000000 /* DMA controller */ +#define IBM_CPM_OPB 0x08000000 /* PLB to OPB bridge */ +#define IBM_CPM_DCP 0x04000000 /* CodePack */ +#define IBM_CPM_EBC 0x02000000 /* ROM/SRAM peripheral controller */ +#define IBM_CPM_SDRAM0 0x01000000 /* SDRAM memory controller */ +#define IBM_CPM_PLB 0x00800000 /* PLB bus arbiter */ +#define IBM_CPM_GPIO0 0x00400000 /* General Purpose IO (??) */ +#define IBM_CPM_UART0 0x00200000 /* serial port 0 */ +#define IBM_CPM_UART1 0x00100000 /* serial port 1 */ +#define IBM_CPM_UIC 0x00080000 /* Universal Interrupt Controller */ +#define IBM_CPM_TMRCLK 0x00040000 /* CPU timers */ +#define IBM_CPM_EMAC0 0x00020000 /* on-chip ethernet MM unit */ +#define DFLT_IBM4xx_PM ~(IBM_CPM_PCI | IBM_CPM_CPU | IBM_CPM_DMA \ + | IBM_CPM_OPB | IBM_CPM_EBC \ + | IBM_CPM_SDRAM0 | IBM_CPM_PLB \ + | IBM_CPM_UIC | IBM_CPM_TMRCLK) + +#define DCRN_DMA0_BASE 0x100 +#define DCRN_DMA1_BASE 0x108 +#define DCRN_DMA2_BASE 0x110 +#define DCRN_DMA3_BASE 0x118 +#define DCRNCAP_DMA_SG 1 /* have DMA scatter/gather capability */ +#define DCRN_DMASR_BASE 0x120 +#define DCRN_EBC_BASE 0x012 +#define DCRN_DCP0_BASE 0x014 +#define DCRN_MAL_BASE 0x180 +#define DCRN_OCM0_BASE 0x018 +#define DCRN_PLB0_BASE 0x084 +#define DCRN_PLLMR_BASE 0x0B0 +#define DCRN_POB0_BASE 0x0A0 +#define DCRN_SDRAM0_BASE 0x010 +#define DCRN_UIC0_BASE 0x0C0 +#define UIC0 DCRN_UIC0_BASE + +#include + +#endif /* __ASM_IBM405GP_H__ */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/4xx/ibm405gpr.c b/arch/ppc/platforms/4xx/ibm405gpr.c new file mode 100644 index 00000000000..01c8ccbc721 --- /dev/null +++ b/arch/ppc/platforms/4xx/ibm405gpr.c @@ -0,0 +1,117 @@ +/* + * arch/ppc/platforms/4xx/ibm405gpr.c + * + * Author: Armin Kuster + * + * 2002 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct ocp_func_emac_data ibm405gpr_emac0_def = { + .rgmii_idx = -1, /* No RGMII */ + .rgmii_mux = -1, /* No RGMII */ + .zmii_idx = -1, /* ZMII device index */ + .zmii_mux = 0, /* ZMII input of this EMAC */ + .mal_idx = 0, /* MAL device index */ + .mal_rx_chan = 0, /* MAL rx channel number */ + .mal_tx_chan = 0, /* MAL tx channel number */ + .wol_irq = 9, /* WOL interrupt number */ + .mdio_idx = -1, /* No shared MDIO */ + .tah_idx = -1, /* No TAH */ +}; +OCP_SYSFS_EMAC_DATA() + +static struct ocp_func_mal_data ibm405gpr_mal0_def = { + .num_tx_chans = 1, /* Number of TX channels */ + .num_rx_chans = 1, /* Number of RX channels */ + .txeob_irq = 11, /* TX End Of Buffer IRQ */ + .rxeob_irq = 12, /* RX End Of Buffer IRQ */ + .txde_irq = 13, /* TX Descriptor Error IRQ */ + .rxde_irq = 14, /* RX Descriptor Error IRQ */ + .serr_irq = 10, /* MAL System Error IRQ */ +}; +OCP_SYSFS_MAL_DATA() + +static struct ocp_func_iic_data ibm405gpr_iic0_def = { + .fast_mode = 0, /* Use standad mode (100Khz) */ +}; + +OCP_SYSFS_IIC_DATA() + +struct ocp_def core_ocp[] = { + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_OPB, + .index = 0, + .paddr = 0xEF600000, + .irq = OCP_IRQ_NA, + .pm = OCP_CPM_NA, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_16550, + .index = 0, + .paddr = UART0_IO_BASE, + .irq = UART0_INT, + .pm = IBM_CPM_UART0 + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_16550, + .index = 1, + .paddr = UART1_IO_BASE, + .irq = UART1_INT, + .pm = IBM_CPM_UART1 + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_IIC, + .paddr = 0xEF600500, + .irq = 2, + .pm = IBM_CPM_IIC0, + .additions = &ibm405gpr_iic0_def, + .show = &ocp_show_iic_data, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_GPIO, + .paddr = 0xEF600700, + .irq = OCP_IRQ_NA, + .pm = IBM_CPM_GPIO0 + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_MAL, + .paddr = OCP_PADDR_NA, + .irq = OCP_IRQ_NA, + .pm = OCP_CPM_NA, + .additions = &ibm405gpr_mal0_def, + .show = &ocp_show_mal_data, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_EMAC, + .index = 0, + .paddr = EMAC0_BASE, + .irq = 15, + .pm = IBM_CPM_EMAC0, + .additions = &ibm405gpr_emac0_def, + .show = &ocp_show_emac_data, + }, + { .vendor = OCP_VENDOR_INVALID + } +}; + +/* Polarity and triggering settings for internal interrupt sources */ +struct ppc4xx_uic_settings ppc4xx_core_uic_cfg[] __initdata = { + { .polarity = 0xffffe000, + .triggering = 0x10000000, + .ext_irq_mask = 0x00001fff, /* IRQ7 - IRQ12, IRQ0 - IRQ6 */ + } +}; diff --git a/arch/ppc/platforms/4xx/ibm405gpr.h b/arch/ppc/platforms/4xx/ibm405gpr.h new file mode 100644 index 00000000000..45412fb4368 --- /dev/null +++ b/arch/ppc/platforms/4xx/ibm405gpr.h @@ -0,0 +1,151 @@ +/* + * arch/ppc/platforms/4xx/ibm405gpr.h + * + * Author: Armin Kuster + * + * 2002 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_IBM405GPR_H__ +#define __ASM_IBM405GPR_H__ + +#include + +/* ibm405.h at bottom of this file */ + +/* PCI + * PCI Bridge config reg definitions + * see 17-19 of manual + */ + +#define PPC405_PCI_CONFIG_ADDR 0xeec00000 +#define PPC405_PCI_CONFIG_DATA 0xeec00004 + +#define PPC405_PCI_PHY_MEM_BASE 0x80000000 /* hose_a->pci_mem_offset */ + /* setbat */ +#define PPC405_PCI_MEM_BASE PPC405_PCI_PHY_MEM_BASE /* setbat */ +#define PPC405_PCI_PHY_IO_BASE 0xe8000000 /* setbat */ +#define PPC405_PCI_IO_BASE PPC405_PCI_PHY_IO_BASE /* setbat */ + +#define PPC405_PCI_LOWER_MEM 0x80000000 /* hose_a->mem_space.start */ +#define PPC405_PCI_UPPER_MEM 0xBfffffff /* hose_a->mem_space.end */ +#define PPC405_PCI_LOWER_IO 0x00000000 /* hose_a->io_space.start */ +#define PPC405_PCI_UPPER_IO 0x0000ffff /* hose_a->io_space.end */ + +#define PPC405_ISA_IO_BASE PPC405_PCI_IO_BASE + +#define PPC4xx_PCI_IO_PADDR ((uint)PPC405_PCI_PHY_IO_BASE) +#define PPC4xx_PCI_IO_VADDR PPC4xx_PCI_IO_PADDR +#define PPC4xx_PCI_IO_SIZE ((uint)64*1024) +#define PPC4xx_PCI_CFG_PADDR ((uint)PPC405_PCI_CONFIG_ADDR) +#define PPC4xx_PCI_CFG_VADDR PPC4xx_PCI_CFG_PADDR +#define PPC4xx_PCI_CFG_SIZE ((uint)4*1024) +#define PPC4xx_PCI_LCFG_PADDR ((uint)0xef400000) +#define PPC4xx_PCI_LCFG_VADDR PPC4xx_PCI_LCFG_PADDR +#define PPC4xx_PCI_LCFG_SIZE ((uint)4*1024) +#define PPC4xx_ONB_IO_PADDR ((uint)0xef600000) +#define PPC4xx_ONB_IO_VADDR PPC4xx_ONB_IO_PADDR +#define PPC4xx_ONB_IO_SIZE ((uint)4*1024) + +/* serial port defines */ +#define RS_TABLE_SIZE 2 + +#define UART0_INT 0 +#define UART1_INT 1 + +#define PCIL0_BASE 0xEF400000 +#define UART0_IO_BASE 0xEF600300 +#define UART1_IO_BASE 0xEF600400 +#define EMAC0_BASE 0xEF600800 + +#define BD_EMAC_ADDR(e,i) bi_enetaddr[i] + +#define STD_UART_OP(num) \ + { 0, BASE_BAUD, 0, UART##num##_INT, \ + (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST), \ + iomem_base: (u8 *)UART##num##_IO_BASE, \ + io_type: SERIAL_IO_MEM}, + +#if defined(CONFIG_UART0_TTYS0) +#define SERIAL_DEBUG_IO_BASE UART0_IO_BASE +#define SERIAL_PORT_DFNS \ + STD_UART_OP(0) \ + STD_UART_OP(1) +#endif + +#if defined(CONFIG_UART0_TTYS1) +#define SERIAL_DEBUG_IO_BASE UART1_IO_BASE +#define SERIAL_PORT_DFNS \ + STD_UART_OP(1) \ + STD_UART_OP(0) +#endif + +/* DCR defines */ +#define DCRN_CHCR_BASE 0x0B1 +#define DCRN_CHPSR_BASE 0x0B4 +#define DCRN_CPMSR_BASE 0x0B8 +#define DCRN_CPMFR_BASE 0x0BA + +#define CHR0_U0EC 0x00000080 /* Select external clock for UART0 */ +#define CHR0_U1EC 0x00000040 /* Select external clock for UART1 */ +#define CHR0_UDIV 0x0000003E /* UART internal clock divisor */ +#define CHR1_CETE 0x00800000 /* CPU external timer enable */ + +#define DCRN_CHPSR_BASE 0x0B4 +#define PSR_PLL_FWD_MASK 0xC0000000 +#define PSR_PLL_FDBACK_MASK 0x30000000 +#define PSR_PLL_TUNING_MASK 0x0E000000 +#define PSR_PLB_CPU_MASK 0x01800000 +#define PSR_OPB_PLB_MASK 0x00600000 +#define PSR_PCI_PLB_MASK 0x00180000 +#define PSR_EB_PLB_MASK 0x00060000 +#define PSR_ROM_WIDTH_MASK 0x00018000 +#define PSR_ROM_LOC 0x00004000 +#define PSR_PCI_ASYNC_EN 0x00001000 +#define PSR_PCI_ARBIT_EN 0x00000400 + +#define IBM_CPM_IIC0 0x80000000 /* IIC interface */ +#define IBM_CPM_PCI 0x40000000 /* PCI bridge */ +#define IBM_CPM_CPU 0x20000000 /* processor core */ +#define IBM_CPM_DMA 0x10000000 /* DMA controller */ +#define IBM_CPM_OPB 0x08000000 /* PLB to OPB bridge */ +#define IBM_CPM_DCP 0x04000000 /* CodePack */ +#define IBM_CPM_EBC 0x02000000 /* ROM/SRAM peripheral controller */ +#define IBM_CPM_SDRAM0 0x01000000 /* SDRAM memory controller */ +#define IBM_CPM_PLB 0x00800000 /* PLB bus arbiter */ +#define IBM_CPM_GPIO0 0x00400000 /* General Purpose IO (??) */ +#define IBM_CPM_UART0 0x00200000 /* serial port 0 */ +#define IBM_CPM_UART1 0x00100000 /* serial port 1 */ +#define IBM_CPM_UIC 0x00080000 /* Universal Interrupt Controller */ +#define IBM_CPM_TMRCLK 0x00040000 /* CPU timers */ +#define IBM_CPM_EMAC0 0x00020000 /* on-chip ethernet MM unit */ +#define DFLT_IBM4xx_PM ~(IBM_CPM_PCI | IBM_CPM_CPU | IBM_CPM_DMA \ + | IBM_CPM_OPB | IBM_CPM_EBC \ + | IBM_CPM_SDRAM0 | IBM_CPM_PLB \ + | IBM_CPM_UIC | IBM_CPM_TMRCLK) + +#define DCRN_DMA0_BASE 0x100 +#define DCRN_DMA1_BASE 0x108 +#define DCRN_DMA2_BASE 0x110 +#define DCRN_DMA3_BASE 0x118 +#define DCRNCAP_DMA_SG 1 /* have DMA scatter/gather capability */ +#define DCRN_DMASR_BASE 0x120 +#define DCRN_EBC_BASE 0x012 +#define DCRN_DCP0_BASE 0x014 +#define DCRN_MAL_BASE 0x180 +#define DCRN_OCM0_BASE 0x018 +#define DCRN_PLB0_BASE 0x084 +#define DCRN_PLLMR_BASE 0x0B0 +#define DCRN_POB0_BASE 0x0A0 +#define DCRN_SDRAM0_BASE 0x010 +#define DCRN_UIC0_BASE 0x0C0 +#define UIC0 DCRN_UIC0_BASE + +#include + +#endif /* __ASM_IBM405GPR_H__ */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/4xx/ibm440gp.c b/arch/ppc/platforms/4xx/ibm440gp.c new file mode 100644 index 00000000000..27615ef8309 --- /dev/null +++ b/arch/ppc/platforms/4xx/ibm440gp.c @@ -0,0 +1,164 @@ +/* + * arch/ppc/platforms/4xx/ibm440gp.c + * + * PPC440GP I/O descriptions + * + * Matt Porter + * Copyright 2002-2004 MontaVista Software Inc. + * + * Eugene Surovegin or + * Copyright (c) 2003, 2004 Zultys Technologies + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ +#include +#include +#include +#include +#include + +static struct ocp_func_emac_data ibm440gp_emac0_def = { + .rgmii_idx = -1, /* No RGMII */ + .rgmii_mux = -1, /* No RGMII */ + .zmii_idx = 0, /* ZMII device index */ + .zmii_mux = 0, /* ZMII input of this EMAC */ + .mal_idx = 0, /* MAL device index */ + .mal_rx_chan = 0, /* MAL rx channel number */ + .mal_tx_chan = 0, /* MAL tx channel number */ + .wol_irq = 61, /* WOL interrupt number */ + .mdio_idx = -1, /* No shared MDIO */ + .tah_idx = -1, /* No TAH */ +}; + +static struct ocp_func_emac_data ibm440gp_emac1_def = { + .rgmii_idx = -1, /* No RGMII */ + .rgmii_mux = -1, /* No RGMII */ + .zmii_idx = 0, /* ZMII device index */ + .zmii_mux = 1, /* ZMII input of this EMAC */ + .mal_idx = 0, /* MAL device index */ + .mal_rx_chan = 1, /* MAL rx channel number */ + .mal_tx_chan = 2, /* MAL tx channel number */ + .wol_irq = 63, /* WOL interrupt number */ + .mdio_idx = -1, /* No shared MDIO */ + .tah_idx = -1, /* No TAH */ +}; +OCP_SYSFS_EMAC_DATA() + +static struct ocp_func_mal_data ibm440gp_mal0_def = { + .num_tx_chans = 4, /* Number of TX channels */ + .num_rx_chans = 2, /* Number of RX channels */ + .txeob_irq = 10, /* TX End Of Buffer IRQ */ + .rxeob_irq = 11, /* RX End Of Buffer IRQ */ + .txde_irq = 33, /* TX Descriptor Error IRQ */ + .rxde_irq = 34, /* RX Descriptor Error IRQ */ + .serr_irq = 32, /* MAL System Error IRQ */ +}; +OCP_SYSFS_MAL_DATA() + +static struct ocp_func_iic_data ibm440gp_iic0_def = { + .fast_mode = 0, /* Use standad mode (100Khz) */ +}; + +static struct ocp_func_iic_data ibm440gp_iic1_def = { + .fast_mode = 0, /* Use standad mode (100Khz) */ +}; +OCP_SYSFS_IIC_DATA() + +struct ocp_def core_ocp[] = { + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_OPB, + .index = 0, + .paddr = 0x0000000140000000ULL, + .irq = OCP_IRQ_NA, + .pm = OCP_CPM_NA, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_16550, + .index = 0, + .paddr = PPC440GP_UART0_ADDR, + .irq = UART0_INT, + .pm = IBM_CPM_UART0, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_16550, + .index = 1, + .paddr = PPC440GP_UART1_ADDR, + .irq = UART1_INT, + .pm = IBM_CPM_UART1, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_IIC, + .index = 0, + .paddr = 0x0000000140000400ULL, + .irq = 2, + .pm = IBM_CPM_IIC0, + .additions = &ibm440gp_iic0_def, + .show = &ocp_show_iic_data + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_IIC, + .index = 1, + .paddr = 0x0000000140000500ULL, + .irq = 3, + .pm = IBM_CPM_IIC1, + .additions = &ibm440gp_iic1_def, + .show = &ocp_show_iic_data + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_GPIO, + .index = 0, + .paddr = 0x0000000140000700ULL, + .irq = OCP_IRQ_NA, + .pm = IBM_CPM_GPIO0, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_MAL, + .paddr = OCP_PADDR_NA, + .irq = OCP_IRQ_NA, + .pm = OCP_CPM_NA, + .additions = &ibm440gp_mal0_def, + .show = &ocp_show_mal_data, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_EMAC, + .index = 0, + .paddr = 0x0000000140000800ULL, + .irq = 60, + .pm = OCP_CPM_NA, + .additions = &ibm440gp_emac0_def, + .show = &ocp_show_emac_data, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_EMAC, + .index = 1, + .paddr = 0x0000000140000900ULL, + .irq = 62, + .pm = OCP_CPM_NA, + .additions = &ibm440gp_emac1_def, + .show = &ocp_show_emac_data, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_ZMII, + .paddr = 0x0000000140000780ULL, + .irq = OCP_IRQ_NA, + .pm = OCP_CPM_NA, + }, + { .vendor = OCP_VENDOR_INVALID + } +}; + +/* Polarity and triggering settings for internal interrupt sources */ +struct ppc4xx_uic_settings ppc4xx_core_uic_cfg[] __initdata = { + { .polarity = 0xfffffe03, + .triggering = 0x01c00000, + .ext_irq_mask = 0x000001fc, /* IRQ0 - IRQ6 */ + }, + { .polarity = 0xffffc0ff, + .triggering = 0x00ff8000, + .ext_irq_mask = 0x00003f00, /* IRQ7 - IRQ12 */ + }, +}; diff --git a/arch/ppc/platforms/4xx/ibm440gp.h b/arch/ppc/platforms/4xx/ibm440gp.h new file mode 100644 index 00000000000..ae1efc03b29 --- /dev/null +++ b/arch/ppc/platforms/4xx/ibm440gp.h @@ -0,0 +1,66 @@ +/* + * arch/ppc/platforms/4xx/ibm440gp.h + * + * PPC440GP definitions + * + * Roland Dreier + * + * Copyright 2002 Roland Dreier + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This file contains code that was originally in the files ibm44x.h + * and ebony.h, which were written by Matt Porter of MontaVista Software Inc. + */ + +#ifdef __KERNEL__ +#ifndef __PPC_PLATFORMS_IBM440GP_H +#define __PPC_PLATFORMS_IBM440GP_H + +#include + +/* UART */ +#define PPC440GP_UART0_ADDR 0x0000000140000200ULL +#define PPC440GP_UART1_ADDR 0x0000000140000300ULL +#define UART0_INT 0 +#define UART1_INT 1 + +/* Clock and Power Management */ +#define IBM_CPM_IIC0 0x80000000 /* IIC interface */ +#define IBM_CPM_IIC1 0x40000000 /* IIC interface */ +#define IBM_CPM_PCI 0x20000000 /* PCI bridge */ +#define IBM_CPM_CPU 0x02000000 /* processor core */ +#define IBM_CPM_DMA 0x01000000 /* DMA controller */ +#define IBM_CPM_BGO 0x00800000 /* PLB to OPB bus arbiter */ +#define IBM_CPM_BGI 0x00400000 /* OPB to PLB bridge */ +#define IBM_CPM_EBC 0x00200000 /* External Bux Controller */ +#define IBM_CPM_EBM 0x00100000 /* Ext Bus Master Interface */ +#define IBM_CPM_DMC 0x00080000 /* SDRAM peripheral controller */ +#define IBM_CPM_PLB 0x00040000 /* PLB bus arbiter */ +#define IBM_CPM_SRAM 0x00020000 /* SRAM memory controller */ +#define IBM_CPM_PPM 0x00002000 /* PLB Performance Monitor */ +#define IBM_CPM_UIC1 0x00001000 /* Universal Interrupt Controller */ +#define IBM_CPM_GPIO0 0x00000800 /* General Purpose IO (??) */ +#define IBM_CPM_GPT 0x00000400 /* General Purpose Timers */ +#define IBM_CPM_UART0 0x00000200 /* serial port 0 */ +#define IBM_CPM_UART1 0x00000100 /* serial port 1 */ +#define IBM_CPM_UIC0 0x00000080 /* Universal Interrupt Controller */ +#define IBM_CPM_TMRCLK 0x00000040 /* CPU timers */ + +#define DFLT_IBM4xx_PM ~(IBM_CPM_UIC | IBM_CPM_UIC1 | IBM_CPM_CPU \ + | IBM_CPM_EBC | IBM_CPM_SRAM | IBM_CPM_BGO \ + | IBM_CPM_EBM | IBM_CPM_PLB | IBM_CPM_OPB \ + | IBM_CPM_TMRCLK | IBM_CPM_DMA | IBM_CPM_PCI) +/* + * Serial port defines + */ +#define RS_TABLE_SIZE 2 + +#include +#include + +#endif /* __PPC_PLATFORMS_IBM440GP_H */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/4xx/ibm440gx.c b/arch/ppc/platforms/4xx/ibm440gx.c new file mode 100644 index 00000000000..1f38f42835b --- /dev/null +++ b/arch/ppc/platforms/4xx/ibm440gx.c @@ -0,0 +1,234 @@ +/* + * arch/ppc/platforms/4xx/ibm440gx.c + * + * PPC440GX I/O descriptions + * + * Matt Porter + * Copyright 2002-2004 MontaVista Software Inc. + * + * Eugene Surovegin or + * Copyright (c) 2003, 2004 Zultys Technologies + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ +#include +#include +#include +#include +#include + +static struct ocp_func_emac_data ibm440gx_emac0_def = { + .rgmii_idx = -1, /* No RGMII */ + .rgmii_mux = -1, /* No RGMII */ + .zmii_idx = 0, /* ZMII device index */ + .zmii_mux = 0, /* ZMII input of this EMAC */ + .mal_idx = 0, /* MAL device index */ + .mal_rx_chan = 0, /* MAL rx channel number */ + .mal_tx_chan = 0, /* MAL tx channel number */ + .wol_irq = 61, /* WOL interrupt number */ + .mdio_idx = -1, /* No shared MDIO */ + .tah_idx = -1, /* No TAH */ +}; + +static struct ocp_func_emac_data ibm440gx_emac1_def = { + .rgmii_idx = -1, /* No RGMII */ + .rgmii_mux = -1, /* No RGMII */ + .zmii_idx = 0, /* ZMII device index */ + .zmii_mux = 1, /* ZMII input of this EMAC */ + .mal_idx = 0, /* MAL device index */ + .mal_rx_chan = 1, /* MAL rx channel number */ + .mal_tx_chan = 1, /* MAL tx channel number */ + .wol_irq = 63, /* WOL interrupt number */ + .mdio_idx = -1, /* No shared MDIO */ + .tah_idx = -1, /* No TAH */ +}; + +static struct ocp_func_emac_data ibm440gx_emac2_def = { + .rgmii_idx = 0, /* RGMII device index */ + .rgmii_mux = 0, /* RGMII input of this EMAC */ + .zmii_idx = 0, /* ZMII device index */ + .zmii_mux = 2, /* ZMII input of this EMAC */ + .mal_idx = 0, /* MAL device index */ + .mal_rx_chan = 2, /* MAL rx channel number */ + .mal_tx_chan = 2, /* MAL tx channel number */ + .wol_irq = 65, /* WOL interrupt number */ + .mdio_idx = -1, /* No shared MDIO */ + .tah_idx = 0, /* TAH device index */ + .jumbo = 1, /* Jumbo frames supported */ +}; + +static struct ocp_func_emac_data ibm440gx_emac3_def = { + .rgmii_idx = 0, /* RGMII device index */ + .rgmii_mux = 1, /* RGMII input of this EMAC */ + .zmii_idx = 0, /* ZMII device index */ + .zmii_mux = 3, /* ZMII input of this EMAC */ + .mal_idx = 0, /* MAL device index */ + .mal_rx_chan = 3, /* MAL rx channel number */ + .mal_tx_chan = 3, /* MAL tx channel number */ + .wol_irq = 67, /* WOL interrupt number */ + .mdio_idx = -1, /* No shared MDIO */ + .tah_idx = 1, /* TAH device index */ + .jumbo = 1, /* Jumbo frames supported */ +}; +OCP_SYSFS_EMAC_DATA() + +static struct ocp_func_mal_data ibm440gx_mal0_def = { + .num_tx_chans = 4, /* Number of TX channels */ + .num_rx_chans = 4, /* Number of RX channels */ + .txeob_irq = 10, /* TX End Of Buffer IRQ */ + .rxeob_irq = 11, /* RX End Of Buffer IRQ */ + .txde_irq = 33, /* TX Descriptor Error IRQ */ + .rxde_irq = 34, /* RX Descriptor Error IRQ */ + .serr_irq = 32, /* MAL System Error IRQ */ +}; +OCP_SYSFS_MAL_DATA() + +static struct ocp_func_iic_data ibm440gx_iic0_def = { + .fast_mode = 0, /* Use standad mode (100Khz) */ +}; + +static struct ocp_func_iic_data ibm440gx_iic1_def = { + .fast_mode = 0, /* Use standad mode (100Khz) */ +}; +OCP_SYSFS_IIC_DATA() + +struct ocp_def core_ocp[] = { + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_OPB, + .index = 0, + .paddr = 0x0000000140000000ULL, + .irq = OCP_IRQ_NA, + .pm = OCP_CPM_NA, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_16550, + .index = 0, + .paddr = PPC440GX_UART0_ADDR, + .irq = UART0_INT, + .pm = IBM_CPM_UART0, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_16550, + .index = 1, + .paddr = PPC440GX_UART1_ADDR, + .irq = UART1_INT, + .pm = IBM_CPM_UART1, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_IIC, + .index = 0, + .paddr = 0x0000000140000400ULL, + .irq = 2, + .pm = IBM_CPM_IIC0, + .additions = &ibm440gx_iic0_def, + .show = &ocp_show_iic_data + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_IIC, + .index = 1, + .paddr = 0x0000000140000500ULL, + .irq = 3, + .pm = IBM_CPM_IIC1, + .additions = &ibm440gx_iic1_def, + .show = &ocp_show_iic_data + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_GPIO, + .index = 0, + .paddr = 0x0000000140000700ULL, + .irq = OCP_IRQ_NA, + .pm = IBM_CPM_GPIO0, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_MAL, + .paddr = OCP_PADDR_NA, + .irq = OCP_IRQ_NA, + .pm = OCP_CPM_NA, + .additions = &ibm440gx_mal0_def, + .show = &ocp_show_mal_data, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_EMAC, + .index = 0, + .paddr = 0x0000000140000800ULL, + .irq = 60, + .pm = OCP_CPM_NA, + .additions = &ibm440gx_emac0_def, + .show = &ocp_show_emac_data, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_EMAC, + .index = 1, + .paddr = 0x0000000140000900ULL, + .irq = 62, + .pm = OCP_CPM_NA, + .additions = &ibm440gx_emac1_def, + .show = &ocp_show_emac_data, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_EMAC, + .index = 2, + .paddr = 0x0000000140000C00ULL, + .irq = 64, + .pm = OCP_CPM_NA, + .additions = &ibm440gx_emac2_def, + .show = &ocp_show_emac_data, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_EMAC, + .index = 3, + .paddr = 0x0000000140000E00ULL, + .irq = 66, + .pm = OCP_CPM_NA, + .additions = &ibm440gx_emac3_def, + .show = &ocp_show_emac_data, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_RGMII, + .paddr = 0x0000000140000790ULL, + .irq = OCP_IRQ_NA, + .pm = OCP_CPM_NA, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_ZMII, + .paddr = 0x0000000140000780ULL, + .irq = OCP_IRQ_NA, + .pm = OCP_CPM_NA, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_TAH, + .index = 0, + .paddr = 0x0000000140000b50ULL, + .irq = 68, + .pm = OCP_CPM_NA, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_TAH, + .index = 1, + .paddr = 0x0000000140000d50ULL, + .irq = 69, + .pm = OCP_CPM_NA, + }, + { .vendor = OCP_VENDOR_INVALID + } +}; + +/* Polarity and triggering settings for internal interrupt sources */ +struct ppc4xx_uic_settings ppc4xx_core_uic_cfg[] __initdata = { + { .polarity = 0xfffffe03, + .triggering = 0x01c00000, + .ext_irq_mask = 0x000001fc, /* IRQ0 - IRQ6 */ + }, + { .polarity = 0xffffc0ff, + .triggering = 0x00ff8000, + .ext_irq_mask = 0x00003f00, /* IRQ7 - IRQ12 */ + }, + { .polarity = 0xffff83ff, + .triggering = 0x000f83c0, + .ext_irq_mask = 0x00007c00, /* IRQ13 - IRQ17 */ + }, +}; diff --git a/arch/ppc/platforms/4xx/ibm440gx.h b/arch/ppc/platforms/4xx/ibm440gx.h new file mode 100644 index 00000000000..0b59d8dcd03 --- /dev/null +++ b/arch/ppc/platforms/4xx/ibm440gx.h @@ -0,0 +1,74 @@ +/* + * arch/ppc/platforms/ibm440gx.h + * + * PPC440GX definitions + * + * Matt Porter + * + * Copyright 2002 Roland Dreier + * Copyright 2003 MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifdef __KERNEL__ +#ifndef __PPC_PLATFORMS_IBM440GX_H +#define __PPC_PLATFORMS_IBM440GX_H + +#include + +#include + +/* UART */ +#define PPC440GX_UART0_ADDR 0x0000000140000200ULL +#define PPC440GX_UART1_ADDR 0x0000000140000300ULL +#define UART0_INT 0 +#define UART1_INT 1 + +/* Clock and Power Management */ +#define IBM_CPM_IIC0 0x80000000 /* IIC interface */ +#define IBM_CPM_IIC1 0x40000000 /* IIC interface */ +#define IBM_CPM_PCI 0x20000000 /* PCI bridge */ +#define IBM_CPM_RGMII 0x10000000 /* RGMII */ +#define IBM_CPM_TAHOE0 0x08000000 /* TAHOE 0 */ +#define IBM_CPM_TAHOE1 0x04000000 /* TAHOE 1 */ +#define IBM_CPM_CPU 0x02000000 /* processor core */ +#define IBM_CPM_DMA 0x01000000 /* DMA controller */ +#define IBM_CPM_BGO 0x00800000 /* PLB to OPB bus arbiter */ +#define IBM_CPM_BGI 0x00400000 /* OPB to PLB bridge */ +#define IBM_CPM_EBC 0x00200000 /* External Bux Controller */ +#define IBM_CPM_EBM 0x00100000 /* Ext Bus Master Interface */ +#define IBM_CPM_DMC 0x00080000 /* SDRAM peripheral controller */ +#define IBM_CPM_PLB 0x00040000 /* PLB bus arbiter */ +#define IBM_CPM_SRAM 0x00020000 /* SRAM memory controller */ +#define IBM_CPM_PPM 0x00002000 /* PLB Performance Monitor */ +#define IBM_CPM_UIC1 0x00001000 /* Universal Interrupt Controller */ +#define IBM_CPM_GPIO0 0x00000800 /* General Purpose IO (??) */ +#define IBM_CPM_GPT 0x00000400 /* General Purpose Timers */ +#define IBM_CPM_UART0 0x00000200 /* serial port 0 */ +#define IBM_CPM_UART1 0x00000100 /* serial port 1 */ +#define IBM_CPM_UIC0 0x00000080 /* Universal Interrupt Controller */ +#define IBM_CPM_TMRCLK 0x00000040 /* CPU timers */ +#define IBM_CPM_EMAC0 0x00000020 /* EMAC 0 */ +#define IBM_CPM_EMAC1 0x00000010 /* EMAC 1 */ +#define IBM_CPM_EMAC2 0x00000008 /* EMAC 2 */ +#define IBM_CPM_EMAC3 0x00000004 /* EMAC 3 */ + +#define DFLT_IBM4xx_PM ~(IBM_CPM_UIC | IBM_CPM_UIC1 | IBM_CPM_CPU \ + | IBM_CPM_EBC | IBM_CPM_SRAM | IBM_CPM_BGO \ + | IBM_CPM_EBM | IBM_CPM_PLB | IBM_CPM_OPB \ + | IBM_CPM_TMRCLK | IBM_CPM_DMA | IBM_CPM_PCI \ + | IBM_CPM_TAHOE0 | IBM_CPM_TAHOE1 \ + | IBM_CPM_EMAC0 | IBM_CPM_EMAC1 \ + | IBM_CPM_EMAC2 | IBM_CPM_EMAC3 ) +/* + * Serial port defines + */ +#define RS_TABLE_SIZE 2 + +#endif /* __PPC_PLATFORMS_IBM440GX_H */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/4xx/ibm440sp.c b/arch/ppc/platforms/4xx/ibm440sp.c new file mode 100644 index 00000000000..a203efb47ab --- /dev/null +++ b/arch/ppc/platforms/4xx/ibm440sp.c @@ -0,0 +1,131 @@ +/* + * arch/ppc/platforms/4xx/ibm440sp.c + * + * PPC440SP I/O descriptions + * + * Matt Porter + * Copyright 2002-2005 MontaVista Software Inc. + * + * Eugene Surovegin or + * Copyright (c) 2003, 2004 Zultys Technologies + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ +#include +#include +#include +#include + +static struct ocp_func_emac_data ibm440sp_emac0_def = { + .rgmii_idx = -1, /* No RGMII */ + .rgmii_mux = -1, /* No RGMII */ + .zmii_idx = -1, /* No ZMII */ + .zmii_mux = -1, /* No ZMII */ + .mal_idx = 0, /* MAL device index */ + .mal_rx_chan = 0, /* MAL rx channel number */ + .mal_tx_chan = 0, /* MAL tx channel number */ + .wol_irq = 61, /* WOL interrupt number */ + .mdio_idx = -1, /* No shared MDIO */ + .tah_idx = -1, /* No TAH */ + .jumbo = 1, /* Jumbo frames supported */ +}; +OCP_SYSFS_EMAC_DATA() + +static struct ocp_func_mal_data ibm440sp_mal0_def = { + .num_tx_chans = 4, /* Number of TX channels */ + .num_rx_chans = 4, /* Number of RX channels */ + .txeob_irq = 38, /* TX End Of Buffer IRQ */ + .rxeob_irq = 39, /* RX End Of Buffer IRQ */ + .txde_irq = 34, /* TX Descriptor Error IRQ */ + .rxde_irq = 35, /* RX Descriptor Error IRQ */ + .serr_irq = 33, /* MAL System Error IRQ */ +}; +OCP_SYSFS_MAL_DATA() + +static struct ocp_func_iic_data ibm440sp_iic0_def = { + .fast_mode = 0, /* Use standad mode (100Khz) */ +}; + +static struct ocp_func_iic_data ibm440sp_iic1_def = { + .fast_mode = 0, /* Use standad mode (100Khz) */ +}; +OCP_SYSFS_IIC_DATA() + +struct ocp_def core_ocp[] = { + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_OPB, + .index = 0, + .paddr = 0x0000000140000000ULL, + .irq = OCP_IRQ_NA, + .pm = OCP_CPM_NA, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_16550, + .index = 0, + .paddr = PPC440SP_UART0_ADDR, + .irq = UART0_INT, + .pm = IBM_CPM_UART0, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_16550, + .index = 1, + .paddr = PPC440SP_UART1_ADDR, + .irq = UART1_INT, + .pm = IBM_CPM_UART1, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_16550, + .index = 2, + .paddr = PPC440SP_UART2_ADDR, + .irq = UART2_INT, + .pm = IBM_CPM_UART2, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_IIC, + .index = 0, + .paddr = 0x00000001f0000400ULL, + .irq = 2, + .pm = IBM_CPM_IIC0, + .additions = &ibm440sp_iic0_def, + .show = &ocp_show_iic_data + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_IIC, + .index = 1, + .paddr = 0x00000001f0000500ULL, + .irq = 3, + .pm = IBM_CPM_IIC1, + .additions = &ibm440sp_iic1_def, + .show = &ocp_show_iic_data + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_GPIO, + .index = 0, + .paddr = 0x00000001f0000700ULL, + .irq = OCP_IRQ_NA, + .pm = IBM_CPM_GPIO0, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_MAL, + .paddr = OCP_PADDR_NA, + .irq = OCP_IRQ_NA, + .pm = OCP_CPM_NA, + .additions = &ibm440sp_mal0_def, + .show = &ocp_show_mal_data, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_EMAC, + .index = 0, + .paddr = 0x00000001f0000800ULL, + .irq = 60, + .pm = OCP_CPM_NA, + .additions = &ibm440sp_emac0_def, + .show = &ocp_show_emac_data, + }, + { .vendor = OCP_VENDOR_INVALID + } +}; diff --git a/arch/ppc/platforms/4xx/ibm440sp.h b/arch/ppc/platforms/4xx/ibm440sp.h new file mode 100644 index 00000000000..c71e46a18b9 --- /dev/null +++ b/arch/ppc/platforms/4xx/ibm440sp.h @@ -0,0 +1,64 @@ +/* + * arch/ppc/platforms/4xx/ibm440sp.h + * + * PPC440SP definitions + * + * Matt Porter + * + * Copyright 2004-2005 MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifdef __KERNEL__ +#ifndef __PPC_PLATFORMS_IBM440SP_H +#define __PPC_PLATFORMS_IBM440SP_H + +#include + +#include + +/* UART */ +#define PPC440SP_UART0_ADDR 0x00000001f0000200ULL +#define PPC440SP_UART1_ADDR 0x00000001f0000300ULL +#define PPC440SP_UART2_ADDR 0x00000001f0000600ULL +#define UART0_INT 0 +#define UART1_INT 1 +#define UART2_INT 2 + +/* Clock and Power Management */ +#define IBM_CPM_IIC0 0x80000000 /* IIC interface */ +#define IBM_CPM_IIC1 0x40000000 /* IIC interface */ +#define IBM_CPM_PCI 0x20000000 /* PCI bridge */ +#define IBM_CPM_CPU 0x02000000 /* processor core */ +#define IBM_CPM_DMA 0x01000000 /* DMA controller */ +#define IBM_CPM_BGO 0x00800000 /* PLB to OPB bus arbiter */ +#define IBM_CPM_BGI 0x00400000 /* OPB to PLB bridge */ +#define IBM_CPM_EBC 0x00200000 /* External Bux Controller */ +#define IBM_CPM_EBM 0x00100000 /* Ext Bus Master Interface */ +#define IBM_CPM_DMC 0x00080000 /* SDRAM peripheral controller */ +#define IBM_CPM_PLB 0x00040000 /* PLB bus arbiter */ +#define IBM_CPM_SRAM 0x00020000 /* SRAM memory controller */ +#define IBM_CPM_PPM 0x00002000 /* PLB Performance Monitor */ +#define IBM_CPM_UIC1 0x00001000 /* Universal Interrupt Controller */ +#define IBM_CPM_GPIO0 0x00000800 /* General Purpose IO (??) */ +#define IBM_CPM_GPT 0x00000400 /* General Purpose Timers */ +#define IBM_CPM_UART0 0x00000200 /* serial port 0 */ +#define IBM_CPM_UART1 0x00000100 /* serial port 1 */ +#define IBM_CPM_UART2 0x00000100 /* serial port 1 */ +#define IBM_CPM_UIC0 0x00000080 /* Universal Interrupt Controller */ +#define IBM_CPM_TMRCLK 0x00000040 /* CPU timers */ +#define IBM_CPM_EMAC0 0x00000020 /* EMAC 0 */ + +#define DFLT_IBM4xx_PM ~(IBM_CPM_UIC | IBM_CPM_UIC1 | IBM_CPM_CPU \ + | IBM_CPM_EBC | IBM_CPM_SRAM | IBM_CPM_BGO \ + | IBM_CPM_EBM | IBM_CPM_PLB | IBM_CPM_OPB \ + | IBM_CPM_TMRCLK | IBM_CPM_DMA | IBM_CPM_PCI \ + | IBM_CPM_TAHOE0 | IBM_CPM_TAHOE1 \ + | IBM_CPM_EMAC0 | IBM_CPM_EMAC1 \ + | IBM_CPM_EMAC2 | IBM_CPM_EMAC3 ) +#endif /* __PPC_PLATFORMS_IBM440SP_H */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/4xx/ibmnp405h.c b/arch/ppc/platforms/4xx/ibmnp405h.c new file mode 100644 index 00000000000..ecdc5be6ae2 --- /dev/null +++ b/arch/ppc/platforms/4xx/ibmnp405h.c @@ -0,0 +1,172 @@ +/* + * arch/ppc/platforms/4xx/ibmnp405h.c + * + * Author: Armin Kuster + * + * 2000-2002 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include + +static struct ocp_func_emac_data ibmnp405h_emac0_def = { + .rgmii_idx = -1, /* No RGMII */ + .rgmii_mux = -1, /* No RGMII */ + .zmii_idx = 0, /* ZMII device index */ + .zmii_mux = 0, /* ZMII input of this EMAC */ + .mal_idx = 0, /* MAL device index */ + .mal_rx_chan = 0, /* MAL rx channel number */ + .mal_tx_chan = 0, /* MAL tx channel number */ + .wol_irq = 41, /* WOL interrupt number */ + .mdio_idx = -1, /* No shared MDIO */ + .tah_idx = -1, /* No TAH */ +}; + +static struct ocp_func_emac_data ibmnp405h_emac1_def = { + .rgmii_idx = -1, /* No RGMII */ + .rgmii_mux = -1, /* No RGMII */ + .zmii_idx = 0, /* ZMII device index */ + .zmii_mux = 1, /* ZMII input of this EMAC */ + .mal_idx = 0, /* MAL device index */ + .mal_rx_chan = 1, /* MAL rx channel number */ + .mal_tx_chan = 1, /* MAL tx channel number */ + .wol_irq = 41, /* WOL interrupt number */ + .mdio_idx = -1, /* No shared MDIO */ + .tah_idx = -1, /* No TAH */ +}; +static struct ocp_func_emac_data ibmnp405h_emac2_def = { + .rgmii_idx = -1, /* No RGMII */ + .rgmii_mux = -1, /* No RGMII */ + .zmii_idx = 0, /* ZMII device index */ + .zmii_mux = 2, /* ZMII input of this EMAC */ + .mal_idx = 0, /* MAL device index */ + .mal_rx_chan = 2, /* MAL rx channel number */ + .mal_tx_chan = 2, /* MAL tx channel number */ + .wol_irq = 41, /* WOL interrupt number */ + .mdio_idx = -1, /* No shared MDIO */ + .tah_idx = -1, /* No TAH */ +}; +static struct ocp_func_emac_data ibmnp405h_emac3_def = { + .rgmii_idx = -1, /* No RGMII */ + .rgmii_mux = -1, /* No RGMII */ + .zmii_idx = 0, /* ZMII device index */ + .zmii_mux = 3, /* ZMII input of this EMAC */ + .mal_idx = 0, /* MAL device index */ + .mal_rx_chan = 3, /* MAL rx channel number */ + .mal_tx_chan = 3, /* MAL tx channel number */ + .wol_irq = 41, /* WOL interrupt number */ + .mdio_idx = -1, /* No shared MDIO */ + .tah_idx = -1, /* No TAH */ +}; +OCP_SYSFS_EMAC_DATA() + +static struct ocp_func_mal_data ibmnp405h_mal0_def = { + .num_tx_chans = 8, /* Number of TX channels */ + .num_rx_chans = 4, /* Number of RX channels */ + .txeob_irq = 17, /* TX End Of Buffer IRQ */ + .rxeob_irq = 18, /* RX End Of Buffer IRQ */ + .txde_irq = 46, /* TX Descriptor Error IRQ */ + .rxde_irq = 47, /* RX Descriptor Error IRQ */ + .serr_irq = 45, /* MAL System Error IRQ */ +}; +OCP_SYSFS_MAL_DATA() + +static struct ocp_func_iic_data ibmnp405h_iic0_def = { + .fast_mode = 0, /* Use standad mode (100Khz) */ +}; +OCP_SYSFS_IIC_DATA() + +struct ocp_def core_ocp[] = { + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_OPB, + .index = 0, + .paddr = 0xEF600000, + .irq = OCP_IRQ_NA, + .pm = OCP_CPM_NA, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_16550, + .index = 0, + .paddr = UART0_IO_BASE, + .irq = UART0_INT, + .pm = IBM_CPM_UART0 + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_16550, + .index = 1, + .paddr = UART1_IO_BASE, + .irq = UART1_INT, + .pm = IBM_CPM_UART1 + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_IIC, + .paddr = 0xEF600500, + .irq = 2, + .pm = IBM_CPM_IIC0, + .additions = &ibmnp405h_iic0_def, + .show = &ocp_show_iic_data + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_GPIO, + .paddr = 0xEF600700, + .irq = OCP_IRQ_NA, + .pm = IBM_CPM_GPIO0 + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_MAL, + .paddr = OCP_PADDR_NA, + .irq = OCP_IRQ_NA, + .pm = OCP_CPM_NA, + .additions = &ibmnp405h_mal0_def, + .show = &ocp_show_mal_data, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_EMAC, + .index = 0, + .paddr = EMAC0_BASE, + .irq = 37, + .pm = IBM_CPM_EMAC0, + .additions = &ibmnp405h_emac0_def, + .show = &ocp_show_emac_data, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_EMAC, + .index = 1, + .paddr = 0xEF600900, + .irq = 38, + .pm = IBM_CPM_EMAC1, + .additions = &ibmnp405h_emac1_def, + .show = &ocp_show_emac_data, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_EMAC, + .index = 2, + .paddr = 0xEF600a00, + .irq = 39, + .pm = IBM_CPM_EMAC2, + .additions = &ibmnp405h_emac2_def, + .show = &ocp_show_emac_data, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_EMAC, + .index = 3, + .paddr = 0xEF600b00, + .irq = 40, + .pm = IBM_CPM_EMAC3, + .additions = &ibmnp405h_emac3_def, + .show = &ocp_show_emac_data, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_ZMII, + .paddr = 0xEF600C10, + .irq = OCP_IRQ_NA, + .pm = OCP_CPM_NA, + }, + { .vendor = OCP_VENDOR_INVALID + } +}; diff --git a/arch/ppc/platforms/4xx/ibmnp405h.h b/arch/ppc/platforms/4xx/ibmnp405h.h new file mode 100644 index 00000000000..e2c2b06128c --- /dev/null +++ b/arch/ppc/platforms/4xx/ibmnp405h.h @@ -0,0 +1,157 @@ +/* + * arch/ppc/platforms/4xx/ibmnp405h.h + * + * Author: Armin Kuster + * + * 2002 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_IBMNP405H_H__ +#define __ASM_IBMNP405H_H__ + +#include + +/* ibm405.h at bottom of this file */ + +#define PPC405_PCI_CONFIG_ADDR 0xeec00000 +#define PPC405_PCI_CONFIG_DATA 0xeec00004 +#define PPC405_PCI_PHY_MEM_BASE 0x80000000 /* hose_a->pci_mem_offset */ + /* setbat */ +#define PPC405_PCI_MEM_BASE PPC405_PCI_PHY_MEM_BASE /* setbat */ +#define PPC405_PCI_PHY_IO_BASE 0xe8000000 /* setbat */ +#define PPC405_PCI_IO_BASE PPC405_PCI_PHY_IO_BASE /* setbat */ + +#define PPC405_PCI_LOWER_MEM 0x00000000 /* hose_a->mem_space.start */ +#define PPC405_PCI_UPPER_MEM 0xBfffffff /* hose_a->mem_space.end */ +#define PPC405_PCI_LOWER_IO 0x00000000 /* hose_a->io_space.start */ +#define PPC405_PCI_UPPER_IO 0x0000ffff /* hose_a->io_space.end */ + +#define PPC405_ISA_IO_BASE PPC405_PCI_IO_BASE + +#define PPC4xx_PCI_IO_ADDR ((uint)PPC405_PCI_PHY_IO_BASE) +#define PPC4xx_PCI_IO_SIZE ((uint)64*1024) +#define PPC4xx_PCI_CFG_ADDR ((uint)PPC405_PCI_CONFIG_ADDR) +#define PPC4xx_PCI_CFG_SIZE ((uint)4*1024) +#define PPC4xx_PCI_LCFG_ADDR ((uint)0xef400000) +#define PPC4xx_PCI_LCFG_SIZE ((uint)4*1024) +#define PPC4xx_ONB_IO_ADDR ((uint)0xef600000) +#define PPC4xx_ONB_IO_SIZE ((uint)4*1024) + +/* serial port defines */ +#define RS_TABLE_SIZE 4 + +#define UART0_INT 0 +#define UART1_INT 1 +#define PCIL0_BASE 0xEF400000 +#define UART0_IO_BASE 0xEF600300 +#define UART1_IO_BASE 0xEF600400 +#define OPB0_BASE 0xEF600600 +#define EMAC0_BASE 0xEF600800 + +#define BD_EMAC_ADDR(e,i) bi_enetaddr[e][i] + +#define STD_UART_OP(num) \ + { 0, BASE_BAUD, 0, UART##num##_INT, \ + (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST), \ + iomem_base:(u8 *) UART##num##_IO_BASE, \ + io_type: SERIAL_IO_MEM}, + +#if defined(CONFIG_UART0_TTYS0) +#define SERIAL_DEBUG_IO_BASE UART0_IO_BASE +#define SERIAL_PORT_DFNS \ + STD_UART_OP(0) \ + STD_UART_OP(1) +#endif + +#if defined(CONFIG_UART0_TTYS1) +#define SERIAL_DEBUG_IO_BASE UART0_IO_BASE +#define SERIAL_PORT_DFNS \ + STD_UART_OP(1) \ + STD_UART_OP(0) +#endif + +/* DCR defines */ +/* ------------------------------------------------------------------------- */ + +#define DCRN_CHCR_BASE 0x0F1 +#define DCRN_CHPSR_BASE 0x0B4 +#define DCRN_CPMSR_BASE 0x0BA +#define DCRN_CPMFR_BASE 0x0B9 +#define DCRN_CPMER_BASE 0x0B8 + +/* CPM Clocking & Power Mangement defines */ +#define IBM_CPM_PCI 0x40000000 /* PCI */ +#define IBM_CPM_EMAC2 0x20000000 /* EMAC 2 MII */ +#define IBM_CPM_EMAC3 0x04000000 /* EMAC 3 MII */ +#define IBM_CPM_EMAC0 0x00800000 /* EMAC 0 MII */ +#define IBM_CPM_EMAC1 0x00100000 /* EMAC 1 MII */ +#define IBM_CPM_EMMII 0 /* Shift value for MII */ +#define IBM_CPM_EMRX 1 /* Shift value for recv */ +#define IBM_CPM_EMTX 2 /* Shift value for MAC */ +#define IBM_CPM_UIC1 0x00020000 /* Universal Interrupt Controller */ +#define IBM_CPM_UIC0 0x00010000 /* Universal Interrupt Controller */ +#define IBM_CPM_CPU 0x00008000 /* processor core */ +#define IBM_CPM_EBC 0x00004000 /* ROM/SRAM peripheral controller */ +#define IBM_CPM_SDRAM0 0x00002000 /* SDRAM memory controller */ +#define IBM_CPM_GPIO0 0x00001000 /* General Purpose IO (??) */ +#define IBM_CPM_HDLC 0x00000800 /* HDCL */ +#define IBM_CPM_TMRCLK 0x00000400 /* CPU timers */ +#define IBM_CPM_PLB 0x00000100 /* PLB bus arbiter */ +#define IBM_CPM_OPB 0x00000080 /* PLB to OPB bridge */ +#define IBM_CPM_DMA 0x00000040 /* DMA controller */ +#define IBM_CPM_IIC0 0x00000010 /* IIC interface */ +#define IBM_CPM_UART0 0x00000002 /* serial port 0 */ +#define IBM_CPM_UART1 0x00000001 /* serial port 1 */ +/* this is the default setting for devices put to sleep when booting */ + +#define DFLT_IBM4xx_PM ~(IBM_CPM_UIC0 | IBM_CPM_UIC1 | IBM_CPM_CPU \ + | IBM_CPM_EBC | IBM_CPM_SDRAM0 | IBM_CPM_PLB \ + | IBM_CPM_OPB | IBM_CPM_TMRCLK | IBM_CPM_DMA \ + | IBM_CPM_EMAC0 | IBM_CPM_EMAC1 | IBM_CPM_EMAC2 \ + | IBM_CPM_EMAC3 | IBM_CPM_PCI) + +#define DCRN_DMA0_BASE 0x100 +#define DCRN_DMA1_BASE 0x108 +#define DCRN_DMA2_BASE 0x110 +#define DCRN_DMA3_BASE 0x118 +#define DCRNCAP_DMA_SG 1 /* have DMA scatter/gather capability */ +#define DCRN_DMASR_BASE 0x120 +#define DCRN_EBC_BASE 0x012 +#define DCRN_DCP0_BASE 0x014 +#define DCRN_MAL_BASE 0x180 +#define DCRN_OCM0_BASE 0x018 +#define DCRN_PLB0_BASE 0x084 +#define DCRN_PLLMR_BASE 0x0B0 +#define DCRN_POB0_BASE 0x0A0 +#define DCRN_SDRAM0_BASE 0x010 +#define DCRN_UIC0_BASE 0x0C0 +#define DCRN_UIC1_BASE 0x0D0 +#define DCRN_CPC0_EPRCSR 0x0F3 + +#define UIC0_UIC1NC 0x00000002 + +#define CHR1_CETE 0x00000004 /* CPU external timer enable */ +#define UIC0 DCRN_UIC0_BASE +#define UIC1 DCRN_UIC1_BASE + +#undef NR_UICS +#define NR_UICS 2 + +/* EMAC DCRN's FIXME: armin */ +#define DCRN_MALRXCTP2R(base) ((base) + 0x42) /* Channel Rx 2 Channel Table Pointer */ +#define DCRN_MALRXCTP3R(base) ((base) + 0x43) /* Channel Rx 3 Channel Table Pointer */ +#define DCRN_MALTXCTP4R(base) ((base) + 0x24) /* Channel Tx 4 Channel Table Pointer */ +#define DCRN_MALTXCTP5R(base) ((base) + 0x25) /* Channel Tx 5 Channel Table Pointer */ +#define DCRN_MALTXCTP6R(base) ((base) + 0x26) /* Channel Tx 6 Channel Table Pointer */ +#define DCRN_MALTXCTP7R(base) ((base) + 0x27) /* Channel Tx 7 Channel Table Pointer */ +#define DCRN_MALRCBS2(base) ((base) + 0x62) /* Channel Rx 2 Channel Buffer Size */ +#define DCRN_MALRCBS3(base) ((base) + 0x63) /* Channel Rx 3 Channel Buffer Size */ + +#include + +#endif /* __ASM_IBMNP405H_H__ */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/4xx/ibmstb4.c b/arch/ppc/platforms/4xx/ibmstb4.c new file mode 100644 index 00000000000..874d16bab73 --- /dev/null +++ b/arch/ppc/platforms/4xx/ibmstb4.c @@ -0,0 +1,83 @@ +/* + * arch/ppc/platforms/4xx/ibmstb4.c + * + * Author: Armin Kuster + * + * 2000-2001 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include + +static struct ocp_func_iic_data ibmstb4_iic0_def = { + .fast_mode = 0, /* Use standad mode (100Khz) */ +}; + +static struct ocp_func_iic_data ibmstb4_iic1_def = { + .fast_mode = 0, /* Use standad mode (100Khz) */ +}; +OCP_SYSFS_IIC_DATA() + +struct ocp_def core_ocp[] __initdata = { + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_16550, + .index = 0, + .paddr = UART0_IO_BASE, + .irq = UART0_INT, + .pm = IBM_CPM_UART0, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_16550, + .index = 1, + .paddr = UART1_IO_BASE, + .irq = UART1_INT, + .pm = IBM_CPM_UART1, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_16550, + .index = 2, + .paddr = UART2_IO_BASE, + .irq = UART2_INT, + .pm = IBM_CPM_UART2, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_IIC, + .paddr = IIC0_BASE, + .irq = IIC0_IRQ, + .pm = IBM_CPM_IIC0, + .additions = &ibmstb4_iic0_def, + .show = &ocp_show_iic_data + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_IIC, + .paddr = IIC1_BASE, + .irq = IIC1_IRQ, + .pm = IBM_CPM_IIC1, + .additions = &ibmstb4_iic1_def, + .show = &ocp_show_iic_data + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_GPIO, + .paddr = GPIO0_BASE, + .irq = OCP_IRQ_NA, + .pm = IBM_CPM_GPIO0, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_IDE, + .paddr = IDE0_BASE, + .irq = IDE0_IRQ, + .pm = OCP_CPM_NA, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_USB, + .paddr = USB0_BASE, + .irq = USB0_IRQ, + .pm = OCP_CPM_NA, + }, + { .vendor = OCP_VENDOR_INVALID, + } +}; diff --git a/arch/ppc/platforms/4xx/ibmstb4.h b/arch/ppc/platforms/4xx/ibmstb4.h new file mode 100644 index 00000000000..bcb4b1ee71f --- /dev/null +++ b/arch/ppc/platforms/4xx/ibmstb4.h @@ -0,0 +1,238 @@ +/* + * arch/ppc/platforms/4xx/ibmstb4.h + * + * Author: Armin Kuster + * + * 2001 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_IBMSTB4_H__ +#define __ASM_IBMSTB4_H__ + +#include + +/* serial port defines */ +#define STB04xxx_IO_BASE ((uint)0xe0000000) +#define PPC4xx_PCI_IO_ADDR STB04xxx_IO_BASE +#define PPC4xx_ONB_IO_PADDR STB04xxx_IO_BASE +#define PPC4xx_ONB_IO_VADDR ((uint)0xe0000000) +#define PPC4xx_ONB_IO_SIZE ((uint)14*64*1024) + +/* + * map STB04xxx internal i/o address (0x400x00xx) to an address + * which is below the 2GB limit... + * + * 4000 000x uart1 -> 0xe000 000x + * 4001 00xx ppu + * 4002 00xx smart card + * 4003 000x iic + * 4004 000x uart0 + * 4005 0xxx timer + * 4006 00xx gpio + * 4007 00xx smart card + * 400b 000x iic + * 400c 000x scp + * 400d 000x modem + * 400e 000x uart2 +*/ +#define STB04xxx_MAP_IO_ADDR(a) (((uint)(a)) + (STB04xxx_IO_BASE - 0x40000000)) + +#define RS_TABLE_SIZE 3 +#define UART0_INT 20 + +#ifdef __BOOTER__ +#define UART0_IO_BASE 0x40040000 +#else +#define UART0_IO_BASE 0xe0040000 +#endif + +#define UART1_INT 21 + +#ifdef __BOOTER__ +#define UART1_IO_BASE 0x40000000 +#else +#define UART1_IO_BASE 0xe0000000 +#endif + +#define UART2_INT 31 +#ifdef __BOOTER__ +#define UART2_IO_BASE 0x400e0000 +#else +#define UART2_IO_BASE 0xe00e0000 +#endif + +#define IDE0_BASE 0x400F0000 +#define IDE0_SIZE 0x200 +#define IDE0_IRQ 25 +#define IIC0_BASE 0x40030000 +#define IIC1_BASE 0x400b0000 +#define OPB0_BASE 0x40000000 +#define GPIO0_BASE 0x40060000 + +#define USB0_IRQ 18 +#define USB0_BASE STB04xxx_MAP_IO_ADDR(0x40010000) +#define USB0_EXTENT 4096 + +#define IIC_NUMS 2 +#define UART_NUMS 3 +#define IIC0_IRQ 9 +#define IIC1_IRQ 10 +#define IIC_OWN 0x55 +#define IIC_CLOCK 50 + +#define BD_EMAC_ADDR(e,i) bi_enetaddr[i] + +#define STD_UART_OP(num) \ + { 0, BASE_BAUD, 0, UART##num##_INT, \ + (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST), \ + iomem_base: (u8 *)UART##num##_IO_BASE, \ + io_type: SERIAL_IO_MEM}, + +#if defined(CONFIG_UART0_TTYS0) +#define SERIAL_DEBUG_IO_BASE UART0_IO_BASE +#define SERIAL_PORT_DFNS \ + STD_UART_OP(0) \ + STD_UART_OP(1) \ + STD_UART_OP(2) +#endif + +#if defined(CONFIG_UART0_TTYS1) +#define SERIAL_DEBUG_IO_BASE UART2_IO_BASE +#define SERIAL_PORT_DFNS \ + STD_UART_OP(1) \ + STD_UART_OP(0) \ + STD_UART_OP(2) +#endif + +#if defined(CONFIG_UART0_TTYS2) +#define SERIAL_DEBUG_IO_BASE UART2_IO_BASE +#define SERIAL_PORT_DFNS \ + STD_UART_OP(2) \ + STD_UART_OP(0) \ + STD_UART_OP(1) +#endif + +#define DCRN_BE_BASE 0x090 +#define DCRN_DMA0_BASE 0x0C0 +#define DCRN_DMA1_BASE 0x0C8 +#define DCRN_DMA2_BASE 0x0D0 +#define DCRN_DMA3_BASE 0x0D8 +#define DCRNCAP_DMA_CC 1 /* have DMA chained count capability */ +#define DCRN_DMASR_BASE 0x0E0 +#define DCRN_PLB0_BASE 0x054 +#define DCRN_PLB1_BASE 0x064 +#define DCRN_POB0_BASE 0x0B0 +#define DCRN_SCCR_BASE 0x120 +#define DCRN_UIC0_BASE 0x040 +#define DCRN_BE_BASE 0x090 +#define DCRN_DMA0_BASE 0x0C0 +#define DCRN_DMA1_BASE 0x0C8 +#define DCRN_DMA2_BASE 0x0D0 +#define DCRN_DMA3_BASE 0x0D8 +#define DCRN_CIC_BASE 0x030 +#define DCRN_DMASR_BASE 0x0E0 +#define DCRN_EBIMC_BASE 0x070 +#define DCRN_DCRX_BASE 0x020 +#define DCRN_CPMFR_BASE 0x102 +#define DCRN_SCCR_BASE 0x120 +#define UIC0 DCRN_UIC0_BASE + +#define IBM_CPM_IIC0 0x80000000 /* IIC 0 interface */ +#define IBM_CPM_USB0 0x40000000 /* IEEE-1284 */ +#define IBM_CPM_IIC1 0x20000000 /* IIC 1 interface */ +#define IBM_CPM_CPU 0x10000000 /* PPC405B3 clock control */ +#define IBM_CPM_AUD 0x08000000 /* Audio Decoder */ +#define IBM_CPM_EBIU 0x04000000 /* External Bus Interface Unit */ +#define IBM_CPM_SDRAM1 0x02000000 /* SDRAM 1 memory controller */ +#define IBM_CPM_DMA 0x01000000 /* DMA controller */ +#define IBM_CPM_DMA1 0x00800000 /* reserved */ +#define IBM_CPM_XPT1 0x00400000 /* reserved */ +#define IBM_CPM_XPT2 0x00200000 /* reserved */ +#define IBM_CPM_UART1 0x00100000 /* Serial 1 / Infrared */ +#define IBM_CPM_UART0 0x00080000 /* Serial 0 / 16550 */ +#define IBM_CPM_EPI 0x00040000 /* DCR Extension */ +#define IBM_CPM_SC0 0x00020000 /* Smart Card 0 */ +#define IBM_CPM_VID 0x00010000 /* reserved */ +#define IBM_CPM_SC1 0x00008000 /* Smart Card 1 */ +#define IBM_CPM_USBSDRA 0x00004000 /* SDRAM 0 memory controller */ +#define IBM_CPM_XPT0 0x00002000 /* Transport - 54 Mhz */ +#define IBM_CPM_CBS 0x00001000 /* Cross Bar Switch */ +#define IBM_CPM_GPT 0x00000800 /* GPTPWM */ +#define IBM_CPM_GPIO0 0x00000400 /* General Purpose IO 0 */ +#define IBM_CPM_DENC 0x00000200 /* Digital video Encoder */ +#define IBM_CPM_TMRCLK 0x00000100 /* CPU timers */ +#define IBM_CPM_XPT27 0x00000080 /* Transport - 27 Mhz */ +#define IBM_CPM_UIC 0x00000040 /* Universal Interrupt Controller */ +#define IBM_CPM_SSP 0x00000010 /* Modem Serial Interface (SSP) */ +#define IBM_CPM_UART2 0x00000008 /* Serial Control Port */ +#define IBM_CPM_DDIO 0x00000004 /* Descrambler */ +#define IBM_CPM_VID2 0x00000002 /* Video Decoder clock domain 2 */ + +#define DFLT_IBM4xx_PM ~(IBM_CPM_CPU | IBM_CPM_EBIU | IBM_CPM_SDRAM1 \ + | IBM_CPM_DMA | IBM_CPM_DMA1 | IBM_CPM_CBS \ + | IBM_CPM_USBSDRA | IBM_CPM_XPT0 | IBM_CPM_TMRCLK \ + | IBM_CPM_XPT27 | IBM_CPM_UIC ) + +#define DCRN_BEAR (DCRN_BE_BASE + 0x0) /* Bus Error Address Register */ +#define DCRN_BESR (DCRN_BE_BASE + 0x1) /* Bus Error Syndrome Register */ +/* DCRN_BESR */ +#define BESR_DSES 0x80000000 /* Data-Side Error Status */ +#define BESR_DMES 0x40000000 /* DMA Error Status */ +#define BESR_RWS 0x20000000 /* Read/Write Status */ +#define BESR_ETMASK 0x1C000000 /* Error Type */ +#define ET_PROT 0 +#define ET_PARITY 1 +#define ET_NCFG 2 +#define ET_BUSERR 4 +#define ET_BUSTO 6 + +#define CHR1_CETE 0x00800000 /* CPU external timer enable */ +#define CHR1_PCIPW 0x00008000 /* PCI Int enable/Peripheral Write enable */ + +#define DCRN_CICCR (DCRN_CIC_BASE + 0x0) /* CIC Control Register */ +#define DCRN_DMAS1 (DCRN_CIC_BASE + 0x1) /* DMA Select1 Register */ +#define DCRN_DMAS2 (DCRN_CIC_BASE + 0x2) /* DMA Select2 Register */ +#define DCRN_CICVCR (DCRN_CIC_BASE + 0x3) /* CIC Video COntro Register */ +#define DCRN_CICSEL3 (DCRN_CIC_BASE + 0x5) /* CIC Select 3 Register */ +#define DCRN_SGPO (DCRN_CIC_BASE + 0x6) /* CIC GPIO Output Register */ +#define DCRN_SGPOD (DCRN_CIC_BASE + 0x7) /* CIC GPIO OD Register */ +#define DCRN_SGPTC (DCRN_CIC_BASE + 0x8) /* CIC GPIO Tristate Ctrl Reg */ +#define DCRN_SGPI (DCRN_CIC_BASE + 0x9) /* CIC GPIO Input Reg */ + +#define DCRN_DCRXICR (DCRN_DCRX_BASE + 0x0) /* Internal Control Register */ +#define DCRN_DCRXISR (DCRN_DCRX_BASE + 0x1) /* Internal Status Register */ +#define DCRN_DCRXECR (DCRN_DCRX_BASE + 0x2) /* External Control Register */ +#define DCRN_DCRXESR (DCRN_DCRX_BASE + 0x3) /* External Status Register */ +#define DCRN_DCRXTAR (DCRN_DCRX_BASE + 0x4) /* Target Address Register */ +#define DCRN_DCRXTDR (DCRN_DCRX_BASE + 0x5) /* Target Data Register */ +#define DCRN_DCRXIGR (DCRN_DCRX_BASE + 0x6) /* Interrupt Generation Register */ +#define DCRN_DCRXBCR (DCRN_DCRX_BASE + 0x7) /* Line Buffer Control Register */ + +#define DCRN_BRCRH0 (DCRN_EBIMC_BASE + 0x0) /* Bus Region Config High 0 */ +#define DCRN_BRCRH1 (DCRN_EBIMC_BASE + 0x1) /* Bus Region Config High 1 */ +#define DCRN_BRCRH2 (DCRN_EBIMC_BASE + 0x2) /* Bus Region Config High 2 */ +#define DCRN_BRCRH3 (DCRN_EBIMC_BASE + 0x3) /* Bus Region Config High 3 */ +#define DCRN_BRCRH4 (DCRN_EBIMC_BASE + 0x4) /* Bus Region Config High 4 */ +#define DCRN_BRCRH5 (DCRN_EBIMC_BASE + 0x5) /* Bus Region Config High 5 */ +#define DCRN_BRCRH6 (DCRN_EBIMC_BASE + 0x6) /* Bus Region Config High 6 */ +#define DCRN_BRCRH7 (DCRN_EBIMC_BASE + 0x7) /* Bus Region Config High 7 */ +#define DCRN_BRCR0 (DCRN_EBIMC_BASE + 0x10) /* BRC 0 */ +#define DCRN_BRCR1 (DCRN_EBIMC_BASE + 0x11) /* BRC 1 */ +#define DCRN_BRCR2 (DCRN_EBIMC_BASE + 0x12) /* BRC 2 */ +#define DCRN_BRCR3 (DCRN_EBIMC_BASE + 0x13) /* BRC 3 */ +#define DCRN_BRCR4 (DCRN_EBIMC_BASE + 0x14) /* BRC 4 */ +#define DCRN_BRCR5 (DCRN_EBIMC_BASE + 0x15) /* BRC 5 */ +#define DCRN_BRCR6 (DCRN_EBIMC_BASE + 0x16) /* BRC 6 */ +#define DCRN_BRCR7 (DCRN_EBIMC_BASE + 0x17) /* BRC 7 */ +#define DCRN_BEAR0 (DCRN_EBIMC_BASE + 0x20) /* Bus Error Address Register */ +#define DCRN_BESR0 (DCRN_EBIMC_BASE + 0x21) /* Bus Error Status Register */ +#define DCRN_BIUCR (DCRN_EBIMC_BASE + 0x2A) /* Bus Interfac Unit Ctrl Reg */ + +#include + +#endif /* __ASM_IBMSTB4_H__ */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/4xx/ibmstbx25.c b/arch/ppc/platforms/4xx/ibmstbx25.c new file mode 100644 index 00000000000..b895b9cca57 --- /dev/null +++ b/arch/ppc/platforms/4xx/ibmstbx25.c @@ -0,0 +1,68 @@ +/* + * arch/ppc/platforms/4xx/ibmstbx25.c + * + * Author: Armin Kuster + * + * 2000-2002 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include + +static struct ocp_func_iic_data ibmstbx25_iic0_def = { + .fast_mode = 0, /* Use standad mode (100Khz) */ +}; +OCP_SYSFS_IIC_DATA() + +struct ocp_def core_ocp[] __initdata = { + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_16550, + .index = 0, + .paddr = UART0_IO_BASE, + .irq = UART0_INT, + .pm = IBM_CPM_UART0, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_16550, + .index = 1, + .paddr = UART1_IO_BASE, + .irq = UART1_INT, + .pm = IBM_CPM_UART1, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_16550, + .index = 2, + .paddr = UART2_IO_BASE, + .irq = UART2_INT, + .pm = IBM_CPM_UART2, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_IIC, + .paddr = IIC0_BASE, + .irq = IIC0_IRQ, + .pm = IBM_CPM_IIC0, + .additions = &ibmstbx25_iic0_def, + .show = &ocp_show_iic_data + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_GPIO, + .paddr = GPIO0_BASE, + .irq = OCP_IRQ_NA, + .pm = IBM_CPM_GPIO0, + }, + { .vendor = OCP_VENDOR_INVALID + } +}; + +/* Polarity and triggering settings for internal interrupt sources */ +struct ppc4xx_uic_settings ppc4xx_core_uic_cfg[] __initdata = { + { .polarity = 0xffff8f80, + .triggering = 0x00000000, + .ext_irq_mask = 0x0000707f, /* IRQ7 - IRQ9, IRQ0 - IRQ6 */ + } +}; diff --git a/arch/ppc/platforms/4xx/ibmstbx25.h b/arch/ppc/platforms/4xx/ibmstbx25.h new file mode 100644 index 00000000000..9a2efc366e9 --- /dev/null +++ b/arch/ppc/platforms/4xx/ibmstbx25.h @@ -0,0 +1,261 @@ +/* + * arch/ppc/platforms/4xx/ibmstbx25.h + * + * Author: Armin Kuster + * + * 2002 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_IBMSTBX25_H__ +#define __ASM_IBMSTBX25_H__ + +#include + +/* serial port defines */ +#define STBx25xx_IO_BASE ((uint)0xe0000000) +#define PPC4xx_ONB_IO_PADDR STBx25xx_IO_BASE +#define PPC4xx_ONB_IO_VADDR ((uint)0xe0000000) +#define PPC4xx_ONB_IO_SIZE ((uint)14*64*1024) + +/* + * map STBxxxx internal i/o address (0x400x00xx) to an address + * which is below the 2GB limit... + * + * 4000 000x uart1 -> 0xe000 000x + * 4001 00xx uart2 + * 4002 00xx smart card + * 4003 000x iic + * 4004 000x uart0 + * 4005 0xxx timer + * 4006 00xx gpio + * 4007 00xx smart card + * 400b 000x iic + * 400c 000x scp + * 400d 000x modem + * 400e 000x uart2 +*/ +#define STBx25xx_MAP_IO_ADDR(a) (((uint)(a)) + (STBx25xx_IO_BASE - 0x40000000)) + +#define RS_TABLE_SIZE 3 + +#define OPB_BASE_START 0x40000000 +#define EBIU_BASE_START 0xF0100000 +#define DCR_BASE_START 0x0000 + +#ifdef __BOOTER__ +#define UART1_IO_BASE 0x40000000 +#define UART2_IO_BASE 0x40010000 +#else +#define UART1_IO_BASE 0xe0000000 +#define UART2_IO_BASE 0xe0010000 +#endif +#define SC0_BASE 0x40020000 /* smart card #0 */ +#define IIC0_BASE 0x40030000 +#ifdef __BOOTER__ +#define UART0_IO_BASE 0x40040000 +#else +#define UART0_IO_BASE 0xe0040000 +#endif +#define SCC0_BASE 0x40040000 /* Serial 0 controller IrdA */ +#define GPT0_BASE 0x40050000 /* General purpose timers */ +#define GPIO0_BASE 0x40060000 +#define SC1_BASE 0x40070000 /* smart card #1 */ +#define SCP0_BASE 0x400C0000 /* Serial Controller Port */ +#define SSP0_BASE 0x400D0000 /* Sync serial port */ + +#define IDE0_BASE 0xf0100000 +#define REDWOOD_IDE_CTRL 0xf1100000 + +#define RTCFPC_IRQ 0 +#define XPORT_IRQ 1 +#define AUD_IRQ 2 +#define AID_IRQ 3 +#define DMA0 4 +#define DMA1_IRQ 5 +#define DMA2_IRQ 6 +#define DMA3_IRQ 7 +#define SC0_IRQ 8 +#define IIC0_IRQ 9 +#define IIR0_IRQ 10 +#define GPT0_IRQ 11 +#define GPT1_IRQ 12 +#define SCP0_IRQ 13 +#define SSP0_IRQ 14 +#define GPT2_IRQ 15 /* count down timer */ +#define SC1_IRQ 16 +/* IRQ 17 - 19 external */ +#define UART0_INT 20 +#define UART1_INT 21 +#define UART2_INT 22 +#define XPTDMA_IRQ 23 +#define DCRIDE_IRQ 24 +/* IRQ 25 - 30 external */ +#define IDE0_IRQ 26 + +#define IIC_NUMS 1 +#define UART_NUMS 3 +#define IIC_OWN 0x55 +#define IIC_CLOCK 50 + +#define BD_EMAC_ADDR(e,i) bi_enetaddr[i] + +#define STD_UART_OP(num) \ + { 0, BASE_BAUD, 0, UART##num##_INT, \ + (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST), \ + iomem_base: (u8 *)UART##num##_IO_BASE, \ + io_type: SERIAL_IO_MEM}, + +#if defined(CONFIG_UART0_TTYS0) +#define SERIAL_DEBUG_IO_BASE UART0_IO_BASE +#define SERIAL_PORT_DFNS \ + STD_UART_OP(0) \ + STD_UART_OP(1) \ + STD_UART_OP(2) +#endif + +#if defined(CONFIG_UART0_TTYS1) +#define SERIAL_DEBUG_IO_BASE UART2_IO_BASE +#define SERIAL_PORT_DFNS \ + STD_UART_OP(1) \ + STD_UART_OP(0) \ + STD_UART_OP(2) +#endif + +#if defined(CONFIG_UART0_TTYS2) +#define SERIAL_DEBUG_IO_BASE UART2_IO_BASE +#define SERIAL_PORT_DFNS \ + STD_UART_OP(2) \ + STD_UART_OP(0) \ + STD_UART_OP(1) +#endif + +#define DCRN_BE_BASE 0x090 +#define DCRN_DMA0_BASE 0x0C0 +#define DCRN_DMA1_BASE 0x0C8 +#define DCRN_DMA2_BASE 0x0D0 +#define DCRN_DMA3_BASE 0x0D8 +#define DCRNCAP_DMA_CC 1 /* have DMA chained count capability */ +#define DCRN_DMASR_BASE 0x0E0 +#define DCRN_PLB0_BASE 0x054 +#define DCRN_PLB1_BASE 0x064 +#define DCRN_POB0_BASE 0x0B0 +#define DCRN_SCCR_BASE 0x120 +#define DCRN_UIC0_BASE 0x040 +#define DCRN_BE_BASE 0x090 +#define DCRN_DMA0_BASE 0x0C0 +#define DCRN_DMA1_BASE 0x0C8 +#define DCRN_DMA2_BASE 0x0D0 +#define DCRN_DMA3_BASE 0x0D8 +#define DCRN_CIC_BASE 0x030 +#define DCRN_DMASR_BASE 0x0E0 +#define DCRN_EBIMC_BASE 0x070 +#define DCRN_DCRX_BASE 0x020 +#define DCRN_CPMFR_BASE 0x102 +#define DCRN_SCCR_BASE 0x120 +#define DCRN_RTCFP_BASE 0x310 + +#define UIC0 DCRN_UIC0_BASE + +#define IBM_CPM_IIC0 0x80000000 /* IIC 0 interface */ +#define IBM_CPM_CPU 0x10000000 /* PPC405B3 clock control */ +#define IBM_CPM_AUD 0x08000000 /* Audio Decoder */ +#define IBM_CPM_EBIU 0x04000000 /* External Bus Interface Unit */ +#define IBM_CPM_IRR 0x02000000 /* Infrared receiver */ +#define IBM_CPM_DMA 0x01000000 /* DMA controller */ +#define IBM_CPM_UART2 0x00200000 /* Serial Control Port */ +#define IBM_CPM_UART1 0x00100000 /* Serial 1 / Infrared */ +#define IBM_CPM_UART0 0x00080000 /* Serial 0 / 16550 */ +#define IBM_PM_DCRIDE 0x00040000 /* DCR timeout & IDE line Mode clock */ +#define IBM_CPM_SC0 0x00020000 /* Smart Card 0 */ +#define IBM_CPM_VID 0x00010000 /* reserved */ +#define IBM_CPM_SC1 0x00008000 /* Smart Card 0 */ +#define IBM_CPM_XPT0 0x00002000 /* Transport - 54 Mhz */ +#define IBM_CPM_CBS 0x00001000 /* Cross Bar Switch */ +#define IBM_CPM_GPT 0x00000800 /* GPTPWM */ +#define IBM_CPM_GPIO0 0x00000400 /* General Purpose IO 0 */ +#define IBM_CPM_DENC 0x00000200 /* Digital video Encoder */ +#define IBM_CPM_C405T 0x00000100 /* CPU timers */ +#define IBM_CPM_XPT27 0x00000080 /* Transport - 27 Mhz */ +#define IBM_CPM_UIC 0x00000040 /* Universal Interrupt Controller */ +#define IBM_CPM_RTCFPC 0x00000020 /* Realtime clock and front panel */ +#define IBM_CPM_SSP 0x00000010 /* Modem Serial Interface (SSP) */ +#define IBM_CPM_VID2 0x00000002 /* Video Decoder clock domain 2 */ +#define DFLT_IBM4xx_PM ~(IBM_CPM_CPU | IBM_CPM_EBIU | IBM_CPM_DMA \ + | IBM_CPM_CBS | IBM_CPM_XPT0 | IBM_CPM_C405T \ + | IBM_CPM_XPT27 | IBM_CPM_UIC) + +#define DCRN_BEAR (DCRN_BE_BASE + 0x0) /* Bus Error Address Register */ +#define DCRN_BESR (DCRN_BE_BASE + 0x1) /* Bus Error Syndrome Register */ +/* DCRN_BESR */ +#define BESR_DSES 0x80000000 /* Data-Side Error Status */ +#define BESR_DMES 0x40000000 /* DMA Error Status */ +#define BESR_RWS 0x20000000 /* Read/Write Status */ +#define BESR_ETMASK 0x1C000000 /* Error Type */ +#define ET_PROT 0 +#define ET_PARITY 1 +#define ET_NCFG 2 +#define ET_BUSERR 4 +#define ET_BUSTO 6 + +#define CHR1_CETE 0x00800000 /* CPU external timer enable */ +#define CHR1_PCIPW 0x00008000 /* PCI Int enable/Peripheral Write enable */ + +#define DCRN_CICCR (DCRN_CIC_BASE + 0x0) /* CIC Control Register */ +#define DCRN_DMAS1 (DCRN_CIC_BASE + 0x1) /* DMA Select1 Register */ +#define DCRN_DMAS2 (DCRN_CIC_BASE + 0x2) /* DMA Select2 Register */ +#define DCRN_CICVCR (DCRN_CIC_BASE + 0x3) /* CIC Video COntro Register */ +#define DCRN_CICSEL3 (DCRN_CIC_BASE + 0x5) /* CIC Select 3 Register */ +#define DCRN_SGPO (DCRN_CIC_BASE + 0x6) /* CIC GPIO Output Register */ +#define DCRN_SGPOD (DCRN_CIC_BASE + 0x7) /* CIC GPIO OD Register */ +#define DCRN_SGPTC (DCRN_CIC_BASE + 0x8) /* CIC GPIO Tristate Ctrl Reg */ +#define DCRN_SGPI (DCRN_CIC_BASE + 0x9) /* CIC GPIO Input Reg */ + +#define DCRN_DCRXICR (DCRN_DCRX_BASE + 0x0) /* Internal Control Register */ +#define DCRN_DCRXISR (DCRN_DCRX_BASE + 0x1) /* Internal Status Register */ +#define DCRN_DCRXECR (DCRN_DCRX_BASE + 0x2) /* External Control Register */ +#define DCRN_DCRXESR (DCRN_DCRX_BASE + 0x3) /* External Status Register */ +#define DCRN_DCRXTAR (DCRN_DCRX_BASE + 0x4) /* Target Address Register */ +#define DCRN_DCRXTDR (DCRN_DCRX_BASE + 0x5) /* Target Data Register */ +#define DCRN_DCRXIGR (DCRN_DCRX_BASE + 0x6) /* Interrupt Generation Register */ +#define DCRN_DCRXBCR (DCRN_DCRX_BASE + 0x7) /* Line Buffer Control Register */ + +#define DCRN_BRCRH0 (DCRN_EBIMC_BASE + 0x0) /* Bus Region Config High 0 */ +#define DCRN_BRCRH1 (DCRN_EBIMC_BASE + 0x1) /* Bus Region Config High 1 */ +#define DCRN_BRCRH2 (DCRN_EBIMC_BASE + 0x2) /* Bus Region Config High 2 */ +#define DCRN_BRCRH3 (DCRN_EBIMC_BASE + 0x3) /* Bus Region Config High 3 */ +#define DCRN_BRCRH4 (DCRN_EBIMC_BASE + 0x4) /* Bus Region Config High 4 */ +#define DCRN_BRCRH5 (DCRN_EBIMC_BASE + 0x5) /* Bus Region Config High 5 */ +#define DCRN_BRCRH6 (DCRN_EBIMC_BASE + 0x6) /* Bus Region Config High 6 */ +#define DCRN_BRCRH7 (DCRN_EBIMC_BASE + 0x7) /* Bus Region Config High 7 */ +#define DCRN_BRCR0 (DCRN_EBIMC_BASE + 0x10) /* BRC 0 */ +#define DCRN_BRCR1 (DCRN_EBIMC_BASE + 0x11) /* BRC 1 */ +#define DCRN_BRCR2 (DCRN_EBIMC_BASE + 0x12) /* BRC 2 */ +#define DCRN_BRCR3 (DCRN_EBIMC_BASE + 0x13) /* BRC 3 */ +#define DCRN_BRCR4 (DCRN_EBIMC_BASE + 0x14) /* BRC 4 */ +#define DCRN_BRCR5 (DCRN_EBIMC_BASE + 0x15) /* BRC 5 */ +#define DCRN_BRCR6 (DCRN_EBIMC_BASE + 0x16) /* BRC 6 */ +#define DCRN_BRCR7 (DCRN_EBIMC_BASE + 0x17) /* BRC 7 */ +#define DCRN_BEAR0 (DCRN_EBIMC_BASE + 0x20) /* Bus Error Address Register */ +#define DCRN_BESR0 (DCRN_EBIMC_BASE + 0x21) /* Bus Error Status Register */ +#define DCRN_BIUCR (DCRN_EBIMC_BASE + 0x2A) /* Bus Interfac Unit Ctrl Reg */ + +#define DCRN_RTC_FPC0_CNTL (DCRN_RTCFP_BASE + 0x00) /* RTC cntl */ +#define DCRN_RTC_FPC0_INT (DCRN_RTCFP_BASE + 0x01) /* RTC Interrupt */ +#define DCRN_RTC_FPC0_TIME (DCRN_RTCFP_BASE + 0x02) /* RTC time reg */ +#define DCRN_RTC_FPC0_ALRM (DCRN_RTCFP_BASE + 0x03) /* RTC Alarm reg */ +#define DCRN_RTC_FPC0_D1 (DCRN_RTCFP_BASE + 0x04) /* LED Data 1 */ +#define DCRN_RTC_FPC0_D2 (DCRN_RTCFP_BASE + 0x05) /* LED Data 2 */ +#define DCRN_RTC_FPC0_D3 (DCRN_RTCFP_BASE + 0x06) /* LED Data 3 */ +#define DCRN_RTC_FPC0_D4 (DCRN_RTCFP_BASE + 0x07) /* LED Data 4 */ +#define DCRN_RTC_FPC0_D5 (DCRN_RTCFP_BASE + 0x08) /* LED Data 5 */ +#define DCRN_RTC_FPC0_FCNTL (DCRN_RTCFP_BASE + 0x09) /* LED control */ +#define DCRN_RTC_FPC0_BRT (DCRN_RTCFP_BASE + 0x0A) /* Brightness cntl */ + +#include + +#endif /* __ASM_IBMSTBX25_H__ */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/4xx/luan.c b/arch/ppc/platforms/4xx/luan.c new file mode 100644 index 00000000000..1df2339f1f6 --- /dev/null +++ b/arch/ppc/platforms/4xx/luan.c @@ -0,0 +1,387 @@ +/* + * arch/ppc/platforms/4xx/luan.c + * + * Luan board specific routines + * + * Matt Porter + * + * Copyright 2004-2005 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * This is a horrible kludge, we eventually need to abstract this + * generic PHY stuff, so the standard phy mode defines can be + * easily used from arch code. + */ +#include "../../../../drivers/net/ibm_emac/ibm_emac_phy.h" + +bd_t __res; + +static struct ibm44x_clocks clocks __initdata; + +static void __init +luan_calibrate_decr(void) +{ + unsigned int freq; + + if (mfspr(SPRN_CCR1) & CCR1_TCS) + freq = LUAN_TMR_CLK; + else + freq = clocks.cpu; + + ibm44x_calibrate_decr(freq); +} + +static int +luan_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "vendor\t\t: IBM\n"); + seq_printf(m, "machine\t\t: PPC440SP EVB (Luan)\n"); + + return 0; +} + +static inline int +luan_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + struct pci_controller *hose = pci_bus_to_hose(dev->bus->number); + + /* PCIX0 in adapter mode, no host interrupt routing */ + + /* PCIX1 */ + if (hose->index == 0) { + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + { 49, 49, 49, 49 }, /* IDSEL 1 - PCIX1 Slot 0 */ + { 49, 49, 49, 49 }, /* IDSEL 2 - PCIX1 Slot 1 */ + { 49, 49, 49, 49 }, /* IDSEL 3 - PCIX1 Slot 2 */ + { 49, 49, 49, 49 }, /* IDSEL 4 - PCIX1 Slot 3 */ + }; + const long min_idsel = 1, max_idsel = 4, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; + /* PCIX2 */ + } else if (hose->index == 1) { + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + { 50, 50, 50, 50 }, /* IDSEL 1 - PCIX2 Slot 0 */ + { 50, 50, 50, 50 }, /* IDSEL 2 - PCIX2 Slot 1 */ + { 50, 50, 50, 50 }, /* IDSEL 3 - PCIX2 Slot 2 */ + { 50, 50, 50, 50 }, /* IDSEL 4 - PCIX2 Slot 3 */ + }; + const long min_idsel = 1, max_idsel = 4, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; + } + return -1; +} + +static void __init luan_set_emacdata(void) +{ + struct ocp_def *def; + struct ocp_func_emac_data *emacdata; + + /* Set phy_map, phy_mode, and mac_addr for the EMAC */ + def = ocp_get_one_device(OCP_VENDOR_IBM, OCP_FUNC_EMAC, 0); + emacdata = def->additions; + emacdata->phy_map = 0x00000001; /* Skip 0x00 */ + emacdata->phy_mode = PHY_MODE_GMII; + memcpy(emacdata->mac_addr, __res.bi_enetaddr, 6); +} + +#define PCIX_READW(offset) \ + (readw((void *)((u32)pcix_reg_base+offset))) + +#define PCIX_WRITEW(value, offset) \ + (writew(value, (void *)((u32)pcix_reg_base+offset))) + +#define PCIX_WRITEL(value, offset) \ + (writel(value, (void *)((u32)pcix_reg_base+offset))) + +static void __init +luan_setup_pcix(void) +{ + int i; + void *pcix_reg_base; + + for (i=0;i<3;i++) { + pcix_reg_base = ioremap64(PCIX0_REG_BASE + i*PCIX_REG_OFFSET, PCIX_REG_SIZE); + + /* Enable PCIX0 I/O, Mem, and Busmaster cycles */ + PCIX_WRITEW(PCIX_READW(PCIX0_COMMAND) | PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER, PCIX0_COMMAND); + + /* Disable all windows */ + PCIX_WRITEL(0, PCIX0_POM0SA); + PCIX_WRITEL(0, PCIX0_POM1SA); + PCIX_WRITEL(0, PCIX0_POM2SA); + PCIX_WRITEL(0, PCIX0_PIM0SA); + PCIX_WRITEL(0, PCIX0_PIM0SAH); + PCIX_WRITEL(0, PCIX0_PIM1SA); + PCIX_WRITEL(0, PCIX0_PIM2SA); + PCIX_WRITEL(0, PCIX0_PIM2SAH); + + /* + * Setup 512MB PLB->PCI outbound mem window + * (a_n000_0000->0_n000_0000) + * */ + PCIX_WRITEL(0x0000000a, PCIX0_POM0LAH); + PCIX_WRITEL(0x80000000 | i*LUAN_PCIX_MEM_SIZE, PCIX0_POM0LAL); + PCIX_WRITEL(0x00000000, PCIX0_POM0PCIAH); + PCIX_WRITEL(0x80000000 | i*LUAN_PCIX_MEM_SIZE, PCIX0_POM0PCIAL); + PCIX_WRITEL(0xe0000001, PCIX0_POM0SA); + + /* Setup 2GB PCI->PLB inbound memory window at 0, enable MSIs */ + PCIX_WRITEL(0x00000000, PCIX0_PIM0LAH); + PCIX_WRITEL(0x00000000, PCIX0_PIM0LAL); + PCIX_WRITEL(0xe0000007, PCIX0_PIM0SA); + PCIX_WRITEL(0xffffffff, PCIX0_PIM0SAH); + + iounmap(pcix_reg_base); + } + + eieio(); +} + +static void __init +luan_setup_hose(struct pci_controller *hose, + int lower_mem, + int upper_mem, + int cfga, + int cfgd, + u64 pcix_io_base) +{ + char name[20]; + + sprintf(name, "PCIX%d host bridge", hose->index); + + hose->pci_mem_offset = LUAN_PCIX_MEM_OFFSET; + + pci_init_resource(&hose->io_resource, + LUAN_PCIX_LOWER_IO, + LUAN_PCIX_UPPER_IO, + IORESOURCE_IO, + name); + + pci_init_resource(&hose->mem_resources[0], + lower_mem, + upper_mem, + IORESOURCE_MEM, + name); + + hose->io_space.start = LUAN_PCIX_LOWER_IO; + hose->io_space.end = LUAN_PCIX_UPPER_IO; + hose->mem_space.start = lower_mem; + hose->mem_space.end = upper_mem; + isa_io_base = + (unsigned long)ioremap64(pcix_io_base, PCIX_IO_SIZE); + hose->io_base_virt = (void *)isa_io_base; + + setup_indirect_pci(hose, cfga, cfgd); + hose->set_cfg_type = 1; +} + +static void __init +luan_setup_hoses(void) +{ + struct pci_controller *hose1, *hose2; + + /* Configure windows on the PCI-X host bridge */ + luan_setup_pcix(); + + /* Allocate hoses for PCIX1 and PCIX2 */ + hose1 = pcibios_alloc_controller(); + hose2 = pcibios_alloc_controller(); + if (!hose1 || !hose2) + return; + + /* Setup PCIX1 */ + hose1->first_busno = 0; + hose1->last_busno = 0xff; + + luan_setup_hose(hose1, + LUAN_PCIX1_LOWER_MEM, + LUAN_PCIX1_UPPER_MEM, + PCIX1_CFGA, + PCIX1_CFGD, + PCIX1_IO_BASE); + + hose1->last_busno = pciauto_bus_scan(hose1, hose1->first_busno); + + /* Setup PCIX2 */ + hose2->first_busno = hose1->last_busno + 1; + hose2->last_busno = 0xff; + + luan_setup_hose(hose2, + LUAN_PCIX2_LOWER_MEM, + LUAN_PCIX2_UPPER_MEM, + PCIX2_CFGA, + PCIX2_CFGD, + PCIX2_IO_BASE); + + hose2->last_busno = pciauto_bus_scan(hose2, hose2->first_busno); + + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = luan_map_irq; +} + +TODC_ALLOC(); + +static void __init +luan_early_serial_map(void) +{ + struct uart_port port; + + /* Setup ioremapped serial port access */ + memset(&port, 0, sizeof(port)); + port.membase = ioremap64(PPC440SP_UART0_ADDR, 8); + port.irq = UART0_INT; + port.uartclk = clocks.uart0; + port.regshift = 0; + port.iotype = SERIAL_IO_MEM; + port.flags = ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST; + port.line = 0; + + if (early_serial_setup(&port) != 0) { + printk("Early serial init of port 0 failed\n"); + } + + port.membase = ioremap64(PPC440SP_UART1_ADDR, 8); + port.irq = UART1_INT; + port.uartclk = clocks.uart1; + port.line = 1; + + if (early_serial_setup(&port) != 0) { + printk("Early serial init of port 1 failed\n"); + } + + port.membase = ioremap64(PPC440SP_UART2_ADDR, 8); + port.irq = UART2_INT; + port.uartclk = BASE_BAUD; + port.line = 2; + + if (early_serial_setup(&port) != 0) { + printk("Early serial init of port 2 failed\n"); + } +} + +static void __init +luan_setup_arch(void) +{ + luan_set_emacdata(); + +#if !defined(CONFIG_BDI_SWITCH) + /* + * The Abatron BDI JTAG debugger does not tolerate others + * mucking with the debug registers. + */ + mtspr(SPRN_DBCR0, (DBCR0_TDE | DBCR0_IDM)); +#endif + + /* + * Determine various clocks. + * To be completely correct we should get SysClk + * from FPGA, because it can be changed by on-board switches + * --ebs + */ + /* 440GX and 440SP clocking is the same -mdp */ + ibm440gx_get_clocks(&clocks, 33333333, 6 * 1843200); + ocp_sys_info.opb_bus_freq = clocks.opb; + + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_jiffy = 50000000/HZ; + + /* Setup PCIXn host bridges */ + luan_setup_hoses(); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = Root_NFS; +#else + ROOT_DEV = Root_HDA1; +#endif + + luan_early_serial_map(); + + /* Identify the system */ + printk("Luan port (MontaVista Software, Inc. )\n"); +} + +void __init platform_init(unsigned long r3, unsigned long r4, + unsigned long r5, unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + + /* + * If we were passed in a board information, copy it into the + * residual data area. + */ + if (r3) + __res = *(bd_t *)(r3 + KERNELBASE); + + ibm44x_platform_init(); + + ppc_md.setup_arch = luan_setup_arch; + ppc_md.show_cpuinfo = luan_show_cpuinfo; + ppc_md.find_end_of_memory = ibm440sp_find_end_of_memory; + ppc_md.get_irq = NULL; /* Set in ppc4xx_pic_init() */ + + ppc_md.calibrate_decr = luan_calibrate_decr; +#ifdef CONFIG_KGDB + ppc_md.early_serial_map = luan_early_serial_map; +#endif +} diff --git a/arch/ppc/platforms/4xx/luan.h b/arch/ppc/platforms/4xx/luan.h new file mode 100644 index 00000000000..09b444c8781 --- /dev/null +++ b/arch/ppc/platforms/4xx/luan.h @@ -0,0 +1,80 @@ +/* + * arch/ppc/platforms/4xx/luan.h + * + * Luan board definitions + * + * Matt Porter + * + * Copyright 2004-2005 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifdef __KERNEL__ +#ifndef __ASM_LUAN_H__ +#define __ASM_LUAN_H__ + +#include +#include + +/* F/W TLB mapping used in bootloader glue to reset EMAC */ +#define PPC44x_EMAC0_MR0 0xa0000800 + +/* Location of MAC addresses in PIBS image */ +#define PIBS_FLASH_BASE 0xffe00000 +#define PIBS_MAC_BASE (PIBS_FLASH_BASE+0x1b0400) + +/* External timer clock frequency */ +#define LUAN_TMR_CLK 25000000 + +/* Flash */ +#define LUAN_FPGA_REG_0 0x0000000148300000ULL +#define LUAN_BOOT_LARGE_FLASH(x) (x & 0x40) +#define LUAN_SMALL_FLASH_LOW 0x00000001ff900000ULL +#define LUAN_SMALL_FLASH_HIGH 0x00000001ffe00000ULL +#define LUAN_SMALL_FLASH_SIZE 0x100000 +#define LUAN_LARGE_FLASH_LOW 0x00000001ff800000ULL +#define LUAN_LARGE_FLASH_HIGH 0x00000001ffc00000ULL +#define LUAN_LARGE_FLASH_SIZE 0x400000 + +/* + * Serial port defines + */ +#define RS_TABLE_SIZE 3 + +/* PIBS defined UART mappings, used before early_serial_setup */ +#define UART0_IO_BASE 0xa0000200 +#define UART1_IO_BASE 0xa0000300 +#define UART2_IO_BASE 0xa0000600 + +#define BASE_BAUD 11059200 +#define STD_UART_OP(num) \ + { 0, BASE_BAUD, 0, UART##num##_INT, \ + (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST), \ + iomem_base: UART##num##_IO_BASE, \ + io_type: SERIAL_IO_MEM}, + +#define SERIAL_PORT_DFNS \ + STD_UART_OP(0) \ + STD_UART_OP(1) \ + STD_UART_OP(2) + +/* PCI support */ +#define LUAN_PCIX_LOWER_IO 0x00000000 +#define LUAN_PCIX_UPPER_IO 0x0000ffff +#define LUAN_PCIX0_LOWER_MEM 0x80000000 +#define LUAN_PCIX0_UPPER_MEM 0x9fffffff +#define LUAN_PCIX1_LOWER_MEM 0xa0000000 +#define LUAN_PCIX1_UPPER_MEM 0xbfffffff +#define LUAN_PCIX2_LOWER_MEM 0xc0000000 +#define LUAN_PCIX2_UPPER_MEM 0xdfffffff + +#define LUAN_PCIX_MEM_SIZE 0x20000000 +#define LUAN_PCIX_MEM_OFFSET 0x00000000 + +#endif /* __ASM_LUAN_H__ */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/4xx/oak.c b/arch/ppc/platforms/4xx/oak.c new file mode 100644 index 00000000000..fa25ee1fa73 --- /dev/null +++ b/arch/ppc/platforms/4xx/oak.c @@ -0,0 +1,255 @@ +/* + * + * Copyright (c) 1999-2000 Grant Erickson + * + * Module name: oak.c + * + * Description: + * Architecture- / platform-specific boot-time initialization code for + * the IBM PowerPC 403GCX "Oak" evaluation board. Adapted from original + * code by Gary Thomas, Cort Dougan , and Dan Malek + * . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "oak.h" + +/* Function Prototypes */ + +extern void abort(void); + +/* Global Variables */ + +unsigned char __res[sizeof(bd_t)]; + + +/* + * void __init oak_init() + * + * Description: + * This routine... + * + * Input(s): + * r3 - Optional pointer to a board information structure. + * r4 - Optional pointer to the physical starting address of the init RAM + * disk. + * r5 - Optional pointer to the physical ending address of the init RAM + * disk. + * r6 - Optional pointer to the physical starting address of any kernel + * command-line parameters. + * r7 - Optional pointer to the physical ending address of any kernel + * command-line parameters. + * + * Output(s): + * N/A + * + * Returns: + * N/A + * + */ +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + + /* + * If we were passed in a board information, copy it into the + * residual data area. + */ + if (r3) { + memcpy((void *)__res, (void *)(r3 + KERNELBASE), sizeof(bd_t)); + } + +#if defined(CONFIG_BLK_DEV_INITRD) + /* + * If the init RAM disk has been configured in, and there's a valid + * starting address for it, set it up. + */ + if (r4) { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + /* Copy the kernel command line arguments to a safe place. */ + + if (r6) { + *(char *)(r7 + KERNELBASE) = 0; + strcpy(cmd_line, (char *)(r6 + KERNELBASE)); + } + + /* Initialize machine-dependency vectors */ + + ppc_md.setup_arch = oak_setup_arch; + ppc_md.show_percpuinfo = oak_show_percpuinfo; + ppc_md.irq_canonicalize = NULL; + ppc_md.init_IRQ = ppc4xx_pic_init; + ppc_md.get_irq = NULL; /* Set in ppc4xx_pic_init() */ + ppc_md.init = NULL; + + ppc_md.restart = oak_restart; + ppc_md.power_off = oak_power_off; + ppc_md.halt = oak_halt; + + ppc_md.time_init = oak_time_init; + ppc_md.set_rtc_time = oak_set_rtc_time; + ppc_md.get_rtc_time = oak_get_rtc_time; + ppc_md.calibrate_decr = oak_calibrate_decr; +} + +/* + * Document me. + */ +void __init +oak_setup_arch(void) +{ + /* XXX - Implement me */ +} + +/* + * int oak_show_percpuinfo() + * + * Description: + * This routine pretty-prints the platform's internal CPU and bus clock + * frequencies into the buffer for usage in /proc/cpuinfo. + * + * Input(s): + * *buffer - Buffer into which CPU and bus clock frequencies are to be + * printed. + * + * Output(s): + * *buffer - Buffer with the CPU and bus clock frequencies. + * + * Returns: + * The number of bytes copied into 'buffer' if OK, otherwise zero or less + * on error. + */ +int +oak_show_percpuinfo(struct seq_file *m, int i) +{ + bd_t *bp = (bd_t *)__res; + + seq_printf(m, "clock\t\t: %dMHz\n" + "bus clock\t\t: %dMHz\n", + bp->bi_intfreq / 1000000, + bp->bi_busfreq / 1000000); + + return 0; +} + +/* + * Document me. + */ +void +oak_restart(char *cmd) +{ + abort(); +} + +/* + * Document me. + */ +void +oak_power_off(void) +{ + oak_restart(NULL); +} + +/* + * Document me. + */ +void +oak_halt(void) +{ + oak_restart(NULL); +} + +/* + * Document me. + */ +long __init +oak_time_init(void) +{ + /* XXX - Implement me */ + return 0; +} + +/* + * Document me. + */ +int __init +oak_set_rtc_time(unsigned long time) +{ + /* XXX - Implement me */ + + return (0); +} + +/* + * Document me. + */ +unsigned long __init +oak_get_rtc_time(void) +{ + /* XXX - Implement me */ + + return (0); +} + +/* + * void __init oak_calibrate_decr() + * + * Description: + * This routine retrieves the internal processor frequency from the board + * information structure, sets up the kernel timer decrementer based on + * that value, enables the 403 programmable interval timer (PIT) and sets + * it up for auto-reload. + * + * Input(s): + * N/A + * + * Output(s): + * N/A + * + * Returns: + * N/A + * + */ +void __init +oak_calibrate_decr(void) +{ + unsigned int freq; + bd_t *bip = (bd_t *)__res; + + freq = bip->bi_intfreq; + + decrementer_count = freq / HZ; + count_period_num = 1; + count_period_den = freq; + + /* Enable the PIT and set auto-reload of its value */ + + mtspr(SPRN_TCR, TCR_PIE | TCR_ARE); + + /* Clear any pending timer interrupts */ + + mtspr(SPRN_TSR, TSR_ENW | TSR_WIS | TSR_PIS | TSR_FIS); +} diff --git a/arch/ppc/platforms/4xx/oak.h b/arch/ppc/platforms/4xx/oak.h new file mode 100644 index 00000000000..1b86a4c66b0 --- /dev/null +++ b/arch/ppc/platforms/4xx/oak.h @@ -0,0 +1,96 @@ +/* + * + * Copyright (c) 1999 Grant Erickson + * + * Module name: oak.h + * + * Description: + * Macros, definitions, and data structures specific to the IBM PowerPC + * 403G{A,B,C,CX} "Oak" evaluation board. Anything specific to the pro- + * cessor itself is defined elsewhere. + * + */ + +#ifdef __KERNEL__ +#ifndef __ASM_OAK_H__ +#define __ASM_OAK_H__ + +/* We have an IBM 403G{A,B,C,CX} core */ +#include + +#define _IO_BASE 0 +#define _ISA_MEM_BASE 0 +#define PCI_DRAM_OFFSET 0 + +/* Memory map for the "Oak" evaluation board */ + +#define PPC403SPU_IO_BASE 0x40000000 /* 403 On-chip serial port */ +#define PPC403SPU_IO_SIZE 0x00000008 +#define OAKSERIAL_IO_BASE 0x7E000000 /* NS16550DV serial port */ +#define OAKSERIAL_IO_SIZE 0x00000008 +#define OAKNET_IO_BASE 0xF4000000 /* NS83902AV Ethernet */ +#define OAKNET_IO_SIZE 0x00000040 +#define OAKPROM_IO_BASE 0xFFFE0000 /* AMD 29F010 Flash ROM */ +#define OAKPROM_IO_SIZE 0x00020000 + + +/* Interrupt assignments fixed by the hardware implementation */ + +/* This is annoying kbuild-2.4 problem. -- Tom */ + +#define PPC403SPU_RX_INT 4 /* AIC_INT4 */ +#define PPC403SPU_TX_INT 5 /* AIC_INT5 */ +#define OAKNET_INT 27 /* AIC_INT27 */ +#define OAKSERIAL_INT 28 /* AIC_INT28 */ + +#ifndef __ASSEMBLY__ +/* + * Data structure defining board information maintained by the boot + * ROM on IBM's "Oak" evaluation board. An effort has been made to + * keep the field names consistent with the 8xx 'bd_t' board info + * structures. + */ + +typedef struct board_info { + unsigned char bi_s_version[4]; /* Version of this structure */ + unsigned char bi_r_version[30]; /* Version of the IBM ROM */ + unsigned int bi_memsize; /* DRAM installed, in bytes */ + unsigned char bi_enetaddr[6]; /* Ethernet MAC address */ + unsigned int bi_intfreq; /* Processor speed, in Hz */ + unsigned int bi_busfreq; /* Bus speed, in Hz */ +} bd_t; + +#ifdef __cplusplus +extern "C" { +#endif + +extern void oak_init(unsigned long r3, + unsigned long ird_start, + unsigned long ird_end, + unsigned long cline_start, + unsigned long cline_end); +extern void oak_setup_arch(void); +extern int oak_setup_residual(char *buffer); +extern void oak_init_IRQ(void); +extern int oak_get_irq(struct pt_regs *regs); +extern void oak_restart(char *cmd); +extern void oak_power_off(void); +extern void oak_halt(void); +extern void oak_time_init(void); +extern int oak_set_rtc_time(unsigned long now); +extern unsigned long oak_get_rtc_time(void); +extern void oak_calibrate_decr(void); + +#ifdef __cplusplus +} +#endif + +/* Some 4xx parts use a different timebase frequency from the internal clock. +*/ +#define bi_tbfreq bi_intfreq + +#define PPC4xx_MACHINE_NAME "IBM Oak" + +#endif /* !__ASSEMBLY__ */ +#endif /* __ASM_OAK_H__ */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/4xx/oak_setup.h b/arch/ppc/platforms/4xx/oak_setup.h new file mode 100644 index 00000000000..8648bd084df --- /dev/null +++ b/arch/ppc/platforms/4xx/oak_setup.h @@ -0,0 +1,50 @@ +/* + * + * Copyright (c) 1999-2000 Grant Erickson + * + * Module name: oak_setup.h + * + * Description: + * Architecture- / platform-specific boot-time initialization code for + * the IBM PowerPC 403GCX "Oak" evaluation board. Adapted from original + * code by Gary Thomas, Cort Dougan , and Dan Malek + * . + * + */ + +#ifndef __OAK_SETUP_H__ +#define __OAK_SETUP_H__ + +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +extern unsigned char __res[sizeof(bd_t)]; + +extern void oak_init(unsigned long r3, + unsigned long ird_start, + unsigned long ird_end, + unsigned long cline_start, + unsigned long cline_end); +extern void oak_setup_arch(void); +extern int oak_setup_residual(char *buffer); +extern void oak_init_IRQ(void); +extern int oak_get_irq(struct pt_regs *regs); +extern void oak_restart(char *cmd); +extern void oak_power_off(void); +extern void oak_halt(void); +extern void oak_time_init(void); +extern int oak_set_rtc_time(unsigned long now); +extern unsigned long oak_get_rtc_time(void); +extern void oak_calibrate_decr(void); + + +#ifdef __cplusplus +} +#endif + +#endif /* __OAK_SETUP_H__ */ diff --git a/arch/ppc/platforms/4xx/ocotea.c b/arch/ppc/platforms/4xx/ocotea.c new file mode 100644 index 00000000000..28de707434f --- /dev/null +++ b/arch/ppc/platforms/4xx/ocotea.c @@ -0,0 +1,367 @@ +/* + * arch/ppc/platforms/4xx/ocotea.c + * + * Ocotea board specific routines + * + * Matt Porter + * + * Copyright 2003-2005 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * This is a horrible kludge, we eventually need to abstract this + * generic PHY stuff, so the standard phy mode defines can be + * easily used from arch code. + */ +#include "../../../../drivers/net/ibm_emac/ibm_emac_phy.h" + +bd_t __res; + +static struct ibm44x_clocks clocks __initdata; + +static void __init +ocotea_calibrate_decr(void) +{ + unsigned int freq; + + if (mfspr(SPRN_CCR1) & CCR1_TCS) + freq = OCOTEA_TMR_CLK; + else + freq = clocks.cpu; + + ibm44x_calibrate_decr(freq); +} + +static int +ocotea_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "vendor\t\t: IBM\n"); + seq_printf(m, "machine\t\t: PPC440GX EVB (Ocotea)\n"); + ibm440gx_show_cpuinfo(m); + return 0; +} + +static inline int +ocotea_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + { 23, 23, 23, 23 }, /* IDSEL 1 - PCI Slot 0 */ + { 24, 24, 24, 24 }, /* IDSEL 2 - PCI Slot 1 */ + { 25, 25, 25, 25 }, /* IDSEL 3 - PCI Slot 2 */ + { 26, 26, 26, 26 }, /* IDSEL 4 - PCI Slot 3 */ + }; + + const long min_idsel = 1, max_idsel = 4, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +} + +static void __init ocotea_set_emacdata(void) +{ + struct ocp_def *def; + struct ocp_func_emac_data *emacdata; + int i; + + /* + * Note: Current rev. board only operates in Group 4a + * mode, so we always set EMAC0-1 for SMII and EMAC2-3 + * for RGMII (though these could run in RTBI just the same). + * + * The FPGA reg 3 information isn't even suitable for + * determining the phy_mode, so if the board becomes + * usable in !4a, it will be necessary to parse an environment + * variable from the firmware or similar to properly configure + * the phy_map/phy_mode. + */ + /* Set phy_map, phy_mode, and mac_addr for each EMAC */ + for (i=0; i<4; i++) { + def = ocp_get_one_device(OCP_VENDOR_IBM, OCP_FUNC_EMAC, i); + emacdata = def->additions; + if (i < 2) { + emacdata->phy_map = 0x00000001; /* Skip 0x00 */ + emacdata->phy_mode = PHY_MODE_SMII; + } + else { + emacdata->phy_map = 0x0000ffff; /* Skip 0x00-0x0f */ + emacdata->phy_mode = PHY_MODE_RGMII; + } + if (i == 0) + memcpy(emacdata->mac_addr, __res.bi_enetaddr, 6); + else if (i == 1) + memcpy(emacdata->mac_addr, __res.bi_enet1addr, 6); + else if (i == 2) + memcpy(emacdata->mac_addr, __res.bi_enet2addr, 6); + else if (i == 3) + memcpy(emacdata->mac_addr, __res.bi_enet3addr, 6); + } +} + +#define PCIX_READW(offset) \ + (readw(pcix_reg_base+offset)) + +#define PCIX_WRITEW(value, offset) \ + (writew(value, pcix_reg_base+offset)) + +#define PCIX_WRITEL(value, offset) \ + (writel(value, pcix_reg_base+offset)) + +/* + * FIXME: This is only here to "make it work". This will move + * to a ibm_pcix.c which will contain a generic IBM PCIX bridge + * configuration library. -Matt + */ +static void __init +ocotea_setup_pcix(void) +{ + void *pcix_reg_base; + + pcix_reg_base = ioremap64(PCIX0_REG_BASE, PCIX_REG_SIZE); + + /* Enable PCIX0 I/O, Mem, and Busmaster cycles */ + PCIX_WRITEW(PCIX_READW(PCIX0_COMMAND) | PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER, PCIX0_COMMAND); + + /* Disable all windows */ + PCIX_WRITEL(0, PCIX0_POM0SA); + PCIX_WRITEL(0, PCIX0_POM1SA); + PCIX_WRITEL(0, PCIX0_POM2SA); + PCIX_WRITEL(0, PCIX0_PIM0SA); + PCIX_WRITEL(0, PCIX0_PIM0SAH); + PCIX_WRITEL(0, PCIX0_PIM1SA); + PCIX_WRITEL(0, PCIX0_PIM2SA); + PCIX_WRITEL(0, PCIX0_PIM2SAH); + + /* Setup 2GB PLB->PCI outbound mem window (3_8000_0000->0_8000_0000) */ + PCIX_WRITEL(0x00000003, PCIX0_POM0LAH); + PCIX_WRITEL(0x80000000, PCIX0_POM0LAL); + PCIX_WRITEL(0x00000000, PCIX0_POM0PCIAH); + PCIX_WRITEL(0x80000000, PCIX0_POM0PCIAL); + PCIX_WRITEL(0x80000001, PCIX0_POM0SA); + + /* Setup 2GB PCI->PLB inbound memory window at 0, enable MSIs */ + PCIX_WRITEL(0x00000000, PCIX0_PIM0LAH); + PCIX_WRITEL(0x00000000, PCIX0_PIM0LAL); + PCIX_WRITEL(0xe0000007, PCIX0_PIM0SA); + + eieio(); +} + +static void __init +ocotea_setup_hose(void) +{ + struct pci_controller *hose; + + /* Configure windows on the PCI-X host bridge */ + ocotea_setup_pcix(); + + hose = pcibios_alloc_controller(); + + if (!hose) + return; + + hose->first_busno = 0; + hose->last_busno = 0xff; + + hose->pci_mem_offset = OCOTEA_PCI_MEM_OFFSET; + + pci_init_resource(&hose->io_resource, + OCOTEA_PCI_LOWER_IO, + OCOTEA_PCI_UPPER_IO, + IORESOURCE_IO, + "PCI host bridge"); + + pci_init_resource(&hose->mem_resources[0], + OCOTEA_PCI_LOWER_MEM, + OCOTEA_PCI_UPPER_MEM, + IORESOURCE_MEM, + "PCI host bridge"); + + hose->io_space.start = OCOTEA_PCI_LOWER_IO; + hose->io_space.end = OCOTEA_PCI_UPPER_IO; + hose->mem_space.start = OCOTEA_PCI_LOWER_MEM; + hose->mem_space.end = OCOTEA_PCI_UPPER_MEM; + isa_io_base = + (unsigned long)ioremap64(OCOTEA_PCI_IO_BASE, OCOTEA_PCI_IO_SIZE); + hose->io_base_virt = (void *)isa_io_base; + + setup_indirect_pci(hose, + OCOTEA_PCI_CFGA_PLB32, + OCOTEA_PCI_CFGD_PLB32); + hose->set_cfg_type = 1; + + hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); + + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = ocotea_map_irq; +} + + +TODC_ALLOC(); + +static void __init +ocotea_early_serial_map(void) +{ + struct uart_port port; + + /* Setup ioremapped serial port access */ + memset(&port, 0, sizeof(port)); + port.membase = ioremap64(PPC440GX_UART0_ADDR, 8); + port.irq = UART0_INT; + port.uartclk = clocks.uart0; + port.regshift = 0; + port.iotype = SERIAL_IO_MEM; + port.flags = ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST; + port.line = 0; + + if (early_serial_setup(&port) != 0) { + printk("Early serial init of port 0 failed\n"); + } + +#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) + /* Configure debug serial access */ + gen550_init(0, &port); +#endif + + port.membase = ioremap64(PPC440GX_UART1_ADDR, 8); + port.irq = UART1_INT; + port.uartclk = clocks.uart1; + port.line = 1; + + if (early_serial_setup(&port) != 0) { + printk("Early serial init of port 1 failed\n"); + } + +#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) + /* Configure debug serial access */ + gen550_init(1, &port); +#endif +} + +static void __init +ocotea_setup_arch(void) +{ + ocotea_set_emacdata(); + + ibm440gx_tah_enable(); + + /* Setup TODC access */ + TODC_INIT(TODC_TYPE_DS1743, + 0, + 0, + ioremap64(OCOTEA_RTC_ADDR, OCOTEA_RTC_SIZE), + 8); + + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_jiffy = 50000000/HZ; + + /* Setup PCI host bridge */ + ocotea_setup_hose(); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = Root_NFS; +#else + ROOT_DEV = Root_HDA1; +#endif + + ocotea_early_serial_map(); + + /* Identify the system */ + printk("IBM Ocotea port (MontaVista Software, Inc. )\n"); +} + +static void __init ocotea_init(void) +{ + ibm440gx_l2c_setup(&clocks); +} + +void __init platform_init(unsigned long r3, unsigned long r4, + unsigned long r5, unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + + /* + * If we were passed in a board information, copy it into the + * residual data area. + */ + if (r3) + __res = *(bd_t *)(r3 + KERNELBASE); + + /* + * Determine various clocks. + * To be completely correct we should get SysClk + * from FPGA, because it can be changed by on-board switches + * --ebs + */ + ibm440gx_get_clocks(&clocks, 33333333, 6 * 1843200); + ocp_sys_info.opb_bus_freq = clocks.opb; + + ibm44x_platform_init(); + + ppc_md.setup_arch = ocotea_setup_arch; + ppc_md.show_cpuinfo = ocotea_show_cpuinfo; + ppc_md.get_irq = NULL; /* Set in ppc4xx_pic_init() */ + + ppc_md.calibrate_decr = ocotea_calibrate_decr; + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; +#ifdef CONFIG_KGDB + ppc_md.early_serial_map = ocotea_early_serial_map; +#endif + ppc_md.init = ocotea_init; +} diff --git a/arch/ppc/platforms/4xx/ocotea.h b/arch/ppc/platforms/4xx/ocotea.h new file mode 100644 index 00000000000..202dc825119 --- /dev/null +++ b/arch/ppc/platforms/4xx/ocotea.h @@ -0,0 +1,88 @@ +/* + * arch/ppc/platforms/ocotea.h + * + * Ocotea board definitions + * + * Matt Porter + * + * Copyright 2003-2005 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifdef __KERNEL__ +#ifndef __ASM_OCOTEA_H__ +#define __ASM_OCOTEA_H__ + +#include +#include + +/* F/W TLB mapping used in bootloader glue to reset EMAC */ +#define PPC44x_EMAC0_MR0 0xe0000800 + +/* Location of MAC addresses in PIBS image */ +#define PIBS_FLASH_BASE 0xfff00000 +#define PIBS_MAC_BASE (PIBS_FLASH_BASE+0xb0500) +#define PIBS_MAC_SIZE 0x200 +#define PIBS_MAC_OFFSET 0x100 + +/* External timer clock frequency */ +#define OCOTEA_TMR_CLK 25000000 + +/* RTC/NVRAM location */ +#define OCOTEA_RTC_ADDR 0x0000000148000000ULL +#define OCOTEA_RTC_SIZE 0x2000 + +/* Flash */ +#define OCOTEA_FPGA_REG_0 0x0000000148300000ULL +#define OCOTEA_BOOT_LARGE_FLASH(x) (x & 0x40) +#define OCOTEA_SMALL_FLASH_LOW 0x00000001ff900000ULL +#define OCOTEA_SMALL_FLASH_HIGH 0x00000001fff00000ULL +#define OCOTEA_SMALL_FLASH_SIZE 0x100000 +#define OCOTEA_LARGE_FLASH_LOW 0x00000001ff800000ULL +#define OCOTEA_LARGE_FLASH_HIGH 0x00000001ffc00000ULL +#define OCOTEA_LARGE_FLASH_SIZE 0x400000 + +/* FPGA_REG_3 (Ethernet Groups) */ +#define OCOTEA_FPGA_REG_3 0x0000000148300003ULL + +/* + * Serial port defines + */ +#define RS_TABLE_SIZE 2 + +/* OpenBIOS defined UART mappings, used before early_serial_setup */ +#define UART0_IO_BASE 0xE0000200 +#define UART1_IO_BASE 0xE0000300 + +#define BASE_BAUD 11059200/16 +#define STD_UART_OP(num) \ + { 0, BASE_BAUD, 0, UART##num##_INT, \ + (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST), \ + iomem_base: UART##num##_IO_BASE, \ + io_type: SERIAL_IO_MEM}, + +#define SERIAL_PORT_DFNS \ + STD_UART_OP(0) \ + STD_UART_OP(1) + +/* PCI support */ +#define OCOTEA_PCI_LOWER_IO 0x00000000 +#define OCOTEA_PCI_UPPER_IO 0x0000ffff +#define OCOTEA_PCI_LOWER_MEM 0x80000000 +#define OCOTEA_PCI_UPPER_MEM 0xffffefff + +#define OCOTEA_PCI_CFGREGS_BASE 0x000000020ec00000ULL +#define OCOTEA_PCI_CFGA_PLB32 0x0ec00000 +#define OCOTEA_PCI_CFGD_PLB32 0x0ec00004 + +#define OCOTEA_PCI_IO_BASE 0x0000000208000000ULL +#define OCOTEA_PCI_IO_SIZE 0x00010000 +#define OCOTEA_PCI_MEM_OFFSET 0x00000000 + +#endif /* __ASM_OCOTEA_H__ */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/4xx/redwood5.c b/arch/ppc/platforms/4xx/redwood5.c new file mode 100644 index 00000000000..2f5e410afbc --- /dev/null +++ b/arch/ppc/platforms/4xx/redwood5.c @@ -0,0 +1,110 @@ +/* + * arch/ppc/platforms/4xx/redwood5.c + * + * Support for the IBM redwood5 eval board file + * + * Author: Armin Kuster + * + * 2000-2001 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include +#include + +static struct resource smc91x_resources[] = { + [0] = { + .start = SMC91111_BASE_ADDR, + .end = SMC91111_BASE_ADDR + SMC91111_REG_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = SMC91111_IRQ, + .end = SMC91111_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device smc91x_device = { + .name = "smc91x", + .id = 0, + .num_resources = ARRAY_SIZE(smc91x_resources), + .resource = smc91x_resources, +}; + +static struct platform_device *redwood5_devs[] __initdata = { + &smc91x_device, +}; + +static int __init +redwood5_platform_add_devices(void) +{ + return platform_add_devices(redwood5_devs, ARRAY_SIZE(redwood5_devs)); +} + +void __init +redwood5_setup_arch(void) +{ + ppc4xx_setup_arch(); + +#ifdef CONFIG_DEBUG_BRINGUP + printk("\n"); + printk("machine\t: %s\n", PPC4xx_MACHINE_NAME); + printk("\n"); + printk("bi_s_version\t %s\n", bip->bi_s_version); + printk("bi_r_version\t %s\n", bip->bi_r_version); + printk("bi_memsize\t 0x%8.8x\t %dMBytes\n", bip->bi_memsize,bip->bi_memsize/(1024*1000)); + printk("bi_enetaddr %d\t %2.2x%2.2x%2.2x-%2.2x%2.2x%2.2x\n", 0, + bip->bi_enetaddr[0], bip->bi_enetaddr[1], + bip->bi_enetaddr[2], bip->bi_enetaddr[3], + bip->bi_enetaddr[4], bip->bi_enetaddr[5]); + + printk("bi_intfreq\t 0x%8.8x\t clock:\t %dMhz\n", + bip->bi_intfreq, bip->bi_intfreq/ 1000000); + + printk("bi_busfreq\t 0x%8.8x\t plb bus clock:\t %dMHz\n", + bip->bi_busfreq, bip->bi_busfreq / 1000000 ); + printk("bi_tbfreq\t 0x%8.8x\t TB freq:\t %dMHz\n", + bip->bi_tbfreq, bip->bi_tbfreq/1000000); + + printk("\n"); +#endif + device_initcall(redwood5_platform_add_devices); +} + +void __init +redwood5_map_io(void) +{ + int i; + + ppc4xx_map_io(); + for (i = 0; i < 16; i++) { + unsigned long v, p; + + /* 0x400x0000 -> 0xe00x0000 */ + p = 0x40000000 | (i << 16); + v = STB04xxx_IO_BASE | (i << 16); + + io_block_mapping(v, p, PAGE_SIZE, + _PAGE_NO_CACHE | pgprot_val(PAGE_KERNEL) | _PAGE_GUARDED); + } + + +} + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + ppc4xx_init(r3, r4, r5, r6, r7); + + ppc_md.setup_arch = redwood5_setup_arch; + ppc_md.setup_io_mappings = redwood5_map_io; +} diff --git a/arch/ppc/platforms/4xx/redwood5.h b/arch/ppc/platforms/4xx/redwood5.h new file mode 100644 index 00000000000..264e34fb3fb --- /dev/null +++ b/arch/ppc/platforms/4xx/redwood5.h @@ -0,0 +1,54 @@ +/* + * arch/ppc/platforms/4xx/redwood5.h + * + * Macros, definitions, and data structures specific to the IBM PowerPC + * STB03xxx "Redwood" evaluation board. + * + * Author: Armin Kuster + * + * 2001 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_REDWOOD5_H__ +#define __ASM_REDWOOD5_H__ + +/* Redwood5 has an STB04xxx core */ +#include + +#ifndef __ASSEMBLY__ +typedef struct board_info { + unsigned char bi_s_version[4]; /* Version of this structure */ + unsigned char bi_r_version[30]; /* Version of the IBM ROM */ + unsigned int bi_memsize; /* DRAM installed, in bytes */ + unsigned int bi_dummy; /* field shouldn't exist */ + unsigned char bi_enetaddr[6]; /* Ethernet MAC address */ + unsigned int bi_intfreq; /* Processor speed, in Hz */ + unsigned int bi_busfreq; /* Bus speed, in Hz */ + unsigned int bi_tbfreq; /* Software timebase freq */ +} bd_t; +#endif /* !__ASSEMBLY__ */ + + +#define SMC91111_BASE_ADDR 0xf2000300 +#define SMC91111_REG_SIZE 16 +#define SMC91111_IRQ 28 + +#ifdef MAX_HWIFS +#undef MAX_HWIFS +#endif +#define MAX_HWIFS 1 + +#define _IO_BASE 0 +#define _ISA_MEM_BASE 0 +#define PCI_DRAM_OFFSET 0 + +#define BASE_BAUD (378000000 / 18 / 16) + +#define PPC4xx_MACHINE_NAME "IBM Redwood5" + +#endif /* __ASM_REDWOOD5_H__ */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/4xx/redwood6.c b/arch/ppc/platforms/4xx/redwood6.c new file mode 100644 index 00000000000..8b1012994df --- /dev/null +++ b/arch/ppc/platforms/4xx/redwood6.c @@ -0,0 +1,159 @@ +/* + * arch/ppc/platforms/4xx/redwood6.c + * + * Author: Armin Kuster + * + * 2002 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Define external IRQ senses and polarities. + */ +unsigned char ppc4xx_uic_ext_irq_cfg[] __initdata = { + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Ext Int 7 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Ext Int 8 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Ext Int 9 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Ext Int 0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Ext Int 1 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Ext Int 2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Ext Int 3 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Ext Int 4 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Ext Int 5 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Ext Int 6 */ +}; + +static struct resource smc91x_resources[] = { + [0] = { + .start = SMC91111_BASE_ADDR, + .end = SMC91111_BASE_ADDR + SMC91111_REG_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = SMC91111_IRQ, + .end = SMC91111_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device smc91x_device = { + .name = "smc91x", + .id = 0, + .num_resources = ARRAY_SIZE(smc91x_resources), + .resource = smc91x_resources, +}; + +static struct platform_device *redwood6_devs[] __initdata = { + &smc91x_device, +}; + +static int __init +redwood6_platform_add_devices(void) +{ + return platform_add_devices(redwood6_devs, ARRAY_SIZE(redwood6_devs)); +} + + +void __init +redwood6_setup_arch(void) +{ +#ifdef CONFIG_IDE + void *xilinx, *xilinx_1, *xilinx_2; + unsigned short us_reg5; +#endif + + ppc4xx_setup_arch(); + +#ifdef CONFIG_IDE + xilinx = (unsigned long) ioremap(IDE_XLINUX_MUX_BASE, 0x10); + /* init xilinx control registers - enable ide mux, clear reset bit */ + if (!xilinx) { + printk(KERN_CRIT + "redwood6_setup_arch() xilinxi ioremap failed\n"); + return; + } + xilinx_1 = xilinx + 0xa; + xilinx_2 = xilinx + 0xe; + + us_reg5 = readb(xilinx_1); + writeb(0x01d1, xilinx_1); + writeb(0x0008, xilinx_2); + + udelay(10 * 1000); + + writeb(0x01d1, xilinx_1); + writeb(0x0008, xilinx_2); +#endif + +#ifdef DEBUG_BRINGUP + bd_t *bip = (bd_t *) __res; + printk("\n"); + printk("machine\t: %s\n", PPC4xx_MACHINE_NAME); + printk("\n"); + printk("bi_s_version\t %s\n", bip->bi_s_version); + printk("bi_r_version\t %s\n", bip->bi_r_version); + printk("bi_memsize\t 0x%8.8x\t %dMBytes\n", bip->bi_memsize, + bip->bi_memsize / (1024 * 1000)); + printk("bi_enetaddr %d\t %2.2x%2.2x%2.2x-%2.2x%2.2x%2.2x\n", 0, + bip->bi_enetaddr[0], bip->bi_enetaddr[1], bip->bi_enetaddr[2], + bip->bi_enetaddr[3], bip->bi_enetaddr[4], bip->bi_enetaddr[5]); + + printk("bi_intfreq\t 0x%8.8x\t clock:\t %dMhz\n", + bip->bi_intfreq, bip->bi_intfreq / 1000000); + + printk("bi_busfreq\t 0x%8.8x\t plb bus clock:\t %dMHz\n", + bip->bi_busfreq, bip->bi_busfreq / 1000000); + printk("bi_tbfreq\t 0x%8.8x\t TB freq:\t %dMHz\n", + bip->bi_tbfreq, bip->bi_tbfreq / 1000000); + + printk("\n"); +#endif + + /* Identify the system */ + printk(KERN_INFO "IBM Redwood6 (STBx25XX) Platform\n"); + printk(KERN_INFO + "Port by MontaVista Software, Inc. (source@mvista.com)\n"); + + device_initcall(redwood6_platform_add_devices); +} + +void __init +redwood6_map_io(void) +{ + int i; + + ppc4xx_map_io(); + for (i = 0; i < 16; i++) { + unsigned long v, p; + + /* 0x400x0000 -> 0xe00x0000 */ + p = 0x40000000 | (i << 16); + v = STBx25xx_IO_BASE | (i << 16); + + io_block_mapping(v, p, PAGE_SIZE, + _PAGE_NO_CACHE | pgprot_val(PAGE_KERNEL) | + _PAGE_GUARDED); + } +} + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + ppc4xx_init(r3, r4, r5, r6, r7); + + ppc_md.setup_arch = redwood6_setup_arch; + ppc_md.setup_io_mappings = redwood6_map_io; +} diff --git a/arch/ppc/platforms/4xx/redwood6.h b/arch/ppc/platforms/4xx/redwood6.h new file mode 100644 index 00000000000..1814b9f5fc3 --- /dev/null +++ b/arch/ppc/platforms/4xx/redwood6.h @@ -0,0 +1,55 @@ +/* + * arch/ppc/platforms/4xx/redwood6.h + * + * Macros, definitions, and data structures specific to the IBM PowerPC + * STBx25xx "Redwood6" evaluation board. + * + * Author: Armin Kuster + * + * 2002 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_REDWOOD5_H__ +#define __ASM_REDWOOD5_H__ + +/* Redwood6 has an STBx25xx core */ +#include + +#ifndef __ASSEMBLY__ +typedef struct board_info { + unsigned char bi_s_version[4]; /* Version of this structure */ + unsigned char bi_r_version[30]; /* Version of the IBM ROM */ + unsigned int bi_memsize; /* DRAM installed, in bytes */ + unsigned int bi_dummy; /* field shouldn't exist */ + unsigned char bi_enetaddr[6]; /* Ethernet MAC address */ + unsigned int bi_intfreq; /* Processor speed, in Hz */ + unsigned int bi_busfreq; /* Bus speed, in Hz */ + unsigned int bi_tbfreq; /* Software timebase freq */ +} bd_t; +#endif /* !__ASSEMBLY__ */ + +#define SMC91111_BASE_ADDR 0xf2030300 +#define SMC91111_REG_SIZE 16 +#define SMC91111_IRQ 27 +#define IDE_XLINUX_MUX_BASE 0xf2040000 +#define IDE_DMA_ADDR 0xfce00000 + +#ifdef MAX_HWIFS +#undef MAX_HWIFS +#endif +#define MAX_HWIFS 1 + +#define _IO_BASE 0 +#define _ISA_MEM_BASE 0 +#define PCI_DRAM_OFFSET 0 + +#define BASE_BAUD (378000000 / 18 / 16) + +#define PPC4xx_MACHINE_NAME "IBM Redwood6" + +#endif /* __ASM_REDWOOD5_H__ */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/4xx/sycamore.c b/arch/ppc/platforms/4xx/sycamore.c new file mode 100644 index 00000000000..d8019eec470 --- /dev/null +++ b/arch/ppc/platforms/4xx/sycamore.c @@ -0,0 +1,278 @@ +/* + * arch/ppc/platforms/4xx/sycamore.c + * + * Architecture- / platform-specific boot-time initialization code for + * IBM PowerPC 4xx based boards. + * + * Author: Armin Kuster + * + * 2000-2002 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +void *kb_cs; +void *kb_data; +void *sycamore_rtc_base; + +/* + * Define external IRQ senses and polarities. + */ +unsigned char ppc4xx_uic_ext_irq_cfg[] __initdata = { + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Ext Int 7 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Ext Int 8 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Ext Int 9 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Ext Int 10 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Ext Int 11 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Ext Int 12 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Ext Int 0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Ext Int 1 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Ext Int 2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Ext Int 3 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Ext Int 4 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Ext Int 5 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Ext Int 6 */ +}; + + +/* Some IRQs unique to Sycamore. + * Used by the generic 405 PCI setup functions in ppc4xx_pci.c + */ +int __init +ppc405_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {28, 28, 28, 28}, /* IDSEL 1 - PCI slot 1 */ + {29, 29, 29, 29}, /* IDSEL 2 - PCI slot 2 */ + {30, 30, 30, 30}, /* IDSEL 3 - PCI slot 3 */ + {31, 31, 31, 31}, /* IDSEL 4 - PCI slot 4 */ + }; + + const long min_idsel = 1, max_idsel = 4, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +}; + +void __init +sycamore_setup_arch(void) +{ +#define SYCAMORE_PS2_BASE 0xF0100000 +#define SYCAMORE_FPGA_BASE 0xF0300000 + + void *fpga_brdc; + unsigned char fpga_brdc_data; + void *fpga_enable; + void *fpga_polarity; + void *fpga_status; + void *fpga_trigger; + + ppc4xx_setup_arch(); + + ibm_ocp_set_emac(0, 1); + + kb_data = ioremap(SYCAMORE_PS2_BASE, 8); + if (!kb_data) { + printk(KERN_CRIT + "sycamore_setup_arch() kb_data ioremap failed\n"); + return; + } + + kb_cs = kb_data + 1; + + fpga_status = ioremap(SYCAMORE_FPGA_BASE, 8); + if (!fpga_status) { + printk(KERN_CRIT + "sycamore_setup_arch() fpga_status ioremap failed\n"); + return; + } + + fpga_enable = fpga_status + 1; + fpga_polarity = fpga_status + 2; + fpga_trigger = fpga_status + 3; + fpga_brdc = fpga_status + 4; + + /* split the keyboard and mouse interrupts */ + fpga_brdc_data = readb(fpga_brdc); + fpga_brdc_data |= 0x80; + writeb(fpga_brdc_data, fpga_brdc); + + writeb(0x3, fpga_enable); + + writeb(0x3, fpga_polarity); + + writeb(0x3, fpga_trigger); + + /* RTC step for the sycamore */ + sycamore_rtc_base = (void *) SYCAMORE_RTC_VADDR; + TODC_INIT(TODC_TYPE_DS1743, sycamore_rtc_base, sycamore_rtc_base, + sycamore_rtc_base, 8); + + /* Identify the system */ + printk(KERN_INFO "IBM Sycamore (IBM405GPr) Platform\n"); + printk(KERN_INFO + "Port by MontaVista Software, Inc. (source@mvista.com)\n"); +} + +void __init +bios_fixup(struct pci_controller *hose, struct pcil0_regs *pcip) +{ +#ifdef CONFIG_PCI + unsigned int bar_response, bar; + /* + * Expected PCI mapping: + * + * PLB addr PCI memory addr + * --------------------- --------------------- + * 0000'0000 - 7fff'ffff <--- 0000'0000 - 7fff'ffff + * 8000'0000 - Bfff'ffff ---> 8000'0000 - Bfff'ffff + * + * PLB addr PCI io addr + * --------------------- --------------------- + * e800'0000 - e800'ffff ---> 0000'0000 - 0001'0000 + * + * The following code is simplified by assuming that the bootrom + * has been well behaved in following this mapping. + */ + +#ifdef DEBUG + int i; + + printk("ioremap PCLIO_BASE = 0x%x\n", pcip); + printk("PCI bridge regs before fixup \n"); + for (i = 0; i <= 3; i++) { + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].ma))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].la))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pcila))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pciha))); + } + printk(" ptm1ms\t0x%x\n", in_le32(&(pcip->ptm1ms))); + printk(" ptm1la\t0x%x\n", in_le32(&(pcip->ptm1la))); + printk(" ptm2ms\t0x%x\n", in_le32(&(pcip->ptm2ms))); + printk(" ptm2la\t0x%x\n", in_le32(&(pcip->ptm2la))); + +#endif + + /* added for IBM boot rom version 1.15 bios bar changes -AK */ + + /* Disable region first */ + out_le32((void *) &(pcip->pmm[0].ma), 0x00000000); + /* PLB starting addr, PCI: 0x80000000 */ + out_le32((void *) &(pcip->pmm[0].la), 0x80000000); + /* PCI start addr, 0x80000000 */ + out_le32((void *) &(pcip->pmm[0].pcila), PPC405_PCI_MEM_BASE); + /* 512MB range of PLB to PCI */ + out_le32((void *) &(pcip->pmm[0].pciha), 0x00000000); + /* Enable no pre-fetch, enable region */ + out_le32((void *) &(pcip->pmm[0].ma), ((0xffffffff - + (PPC405_PCI_UPPER_MEM - + PPC405_PCI_MEM_BASE)) | 0x01)); + + /* Enable inbound region one - 1GB size */ + out_le32((void *) &(pcip->ptm1ms), 0xc0000001); + + /* Disable outbound region one */ + out_le32((void *) &(pcip->pmm[1].ma), 0x00000000); + out_le32((void *) &(pcip->pmm[1].la), 0x00000000); + out_le32((void *) &(pcip->pmm[1].pcila), 0x00000000); + out_le32((void *) &(pcip->pmm[1].pciha), 0x00000000); + out_le32((void *) &(pcip->pmm[1].ma), 0x00000000); + + /* Disable inbound region two */ + out_le32((void *) &(pcip->ptm2ms), 0x00000000); + + /* Disable outbound region two */ + out_le32((void *) &(pcip->pmm[2].ma), 0x00000000); + out_le32((void *) &(pcip->pmm[2].la), 0x00000000); + out_le32((void *) &(pcip->pmm[2].pcila), 0x00000000); + out_le32((void *) &(pcip->pmm[2].pciha), 0x00000000); + out_le32((void *) &(pcip->pmm[2].ma), 0x00000000); + + /* Zero config bars */ + for (bar = PCI_BASE_ADDRESS_1; bar <= PCI_BASE_ADDRESS_2; bar += 4) { + early_write_config_dword(hose, hose->first_busno, + PCI_FUNC(hose->first_busno), bar, + 0x00000000); + early_read_config_dword(hose, hose->first_busno, + PCI_FUNC(hose->first_busno), bar, + &bar_response); + DBG("BUS %d, device %d, Function %d bar 0x%8.8x is 0x%8.8x\n", + hose->first_busno, PCI_SLOT(hose->first_busno), + PCI_FUNC(hose->first_busno), bar, bar_response); + } + /* end work arround */ + +#ifdef DEBUG + printk("PCI bridge regs after fixup \n"); + for (i = 0; i <= 3; i++) { + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].ma))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].la))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pcila))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pciha))); + } + printk(" ptm1ms\t0x%x\n", in_le32(&(pcip->ptm1ms))); + printk(" ptm1la\t0x%x\n", in_le32(&(pcip->ptm1la))); + printk(" ptm2ms\t0x%x\n", in_le32(&(pcip->ptm2ms))); + printk(" ptm2la\t0x%x\n", in_le32(&(pcip->ptm2la))); + +#endif +#endif + +} + +void __init +sycamore_map_io(void) +{ + ppc4xx_map_io(); + io_block_mapping(SYCAMORE_RTC_VADDR, + SYCAMORE_RTC_PADDR, SYCAMORE_RTC_SIZE, _PAGE_IO); +} + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + ppc4xx_init(r3, r4, r5, r6, r7); + + ppc_md.setup_arch = sycamore_setup_arch; + ppc_md.setup_io_mappings = sycamore_map_io; + +#ifdef CONFIG_GEN_RTC + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; +#endif +} diff --git a/arch/ppc/platforms/4xx/sycamore.h b/arch/ppc/platforms/4xx/sycamore.h new file mode 100644 index 00000000000..3e7b4e2c8c5 --- /dev/null +++ b/arch/ppc/platforms/4xx/sycamore.h @@ -0,0 +1,67 @@ +/* + * arch/ppc/platforms/4xx/sycamore.h + * + * Macros, definitions, and data structures specific to the IBM PowerPC + * 405GPr "Sycamore" evaluation board. + * + * Author: Armin Kuster + * + * 2000 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_SYCAMORE_H__ +#define __ASM_SYCAMORE_H__ + +#include + +#ifndef __ASSEMBLY__ +/* + * Data structure defining board information maintained by the boot + * ROM on IBM's "Sycamore" evaluation board. An effort has been made to + * keep the field names consistent with the 8xx 'bd_t' board info + * structures. + */ + +typedef struct board_info { + unsigned char bi_s_version[4]; /* Version of this structure */ + unsigned char bi_r_version[30]; /* Version of the IBM ROM */ + unsigned int bi_memsize; /* DRAM installed, in bytes */ + unsigned char bi_enetaddr[6]; /* Local Ethernet MAC address */ + unsigned char bi_pci_enetaddr[6]; /* PCI Ethernet MAC address */ + unsigned int bi_intfreq; /* Processor speed, in Hz */ + unsigned int bi_busfreq; /* PLB Bus speed, in Hz */ + unsigned int bi_pci_busfreq; /* PCI Bus speed, in Hz */ +} bd_t; + +/* Some 4xx parts use a different timebase frequency from the internal clock. +*/ +#define bi_tbfreq bi_intfreq + + +/* Memory map for the IBM "Sycamore" 405GP evaluation board. + * Generic 4xx plus RTC. + */ + +extern void *sycamore_rtc_base; +#define SYCAMORE_RTC_PADDR ((uint)0xf0000000) +#define SYCAMORE_RTC_VADDR SYCAMORE_RTC_PADDR +#define SYCAMORE_RTC_SIZE ((uint)8*1024) + +#ifdef CONFIG_PPC405GP_INTERNAL_CLOCK +#define BASE_BAUD 201600 +#else +#define BASE_BAUD 691200 +#endif + +#define SYCAMORE_PS2_BASE 0xF0100000 +#define SYCAMORE_FPGA_BASE 0xF0300000 + +#define PPC4xx_MACHINE_NAME "IBM Sycamore" + +#endif /* !__ASSEMBLY__ */ +#endif /* __ASM_SYCAMORE_H__ */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/4xx/virtex-ii_pro.c b/arch/ppc/platforms/4xx/virtex-ii_pro.c new file mode 100644 index 00000000000..097cc9d5aca --- /dev/null +++ b/arch/ppc/platforms/4xx/virtex-ii_pro.c @@ -0,0 +1,60 @@ +/* + * arch/ppc/platforms/4xx/virtex-ii_pro.c + * + * Author: MontaVista Software, Inc. + * source@mvista.com + * + * 2002-2004 (c) MontaVista Software, Inc. This file is licensed under the + * terms of the GNU General Public License version 2. This program is licensed + * "as is" without any warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include "virtex-ii_pro.h" + +/* Have OCP take care of the serial ports. */ +struct ocp_def core_ocp[] = { +#ifdef XPAR_UARTNS550_0_BASEADDR + { .vendor = OCP_VENDOR_XILINX, + .function = OCP_FUNC_16550, + .index = 0, + .paddr = XPAR_UARTNS550_0_BASEADDR, + .irq = XPAR_INTC_0_UARTNS550_0_VEC_ID, + .pm = OCP_CPM_NA + }, +#ifdef XPAR_UARTNS550_1_BASEADDR + { .vendor = OCP_VENDOR_XILINX, + .function = OCP_FUNC_16550, + .index = 1, + .paddr = XPAR_UARTNS550_1_BASEADDR, + .irq = XPAR_INTC_0_UARTNS550_1_VEC_ID, + .pm = OCP_CPM_NA + }, +#ifdef XPAR_UARTNS550_2_BASEADDR + { .vendor = OCP_VENDOR_XILINX, + .function = OCP_FUNC_16550, + .index = 2, + .paddr = XPAR_UARTNS550_2_BASEADDR, + .irq = XPAR_INTC_0_UARTNS550_2_VEC_ID, + .pm = OCP_CPM_NA + }, +#ifdef XPAR_UARTNS550_3_BASEADDR + { .vendor = OCP_VENDOR_XILINX, + .function = OCP_FUNC_16550, + .index = 3, + .paddr = XPAR_UARTNS550_3_BASEADDR, + .irq = XPAR_INTC_0_UARTNS550_3_VEC_ID, + .pm = OCP_CPM_NA + }, +#ifdef XPAR_UARTNS550_4_BASEADDR +#error Edit this file to add more devices. +#endif /* 4 */ +#endif /* 3 */ +#endif /* 2 */ +#endif /* 1 */ +#endif /* 0 */ + { .vendor = OCP_VENDOR_INVALID + } +}; diff --git a/arch/ppc/platforms/4xx/virtex-ii_pro.h b/arch/ppc/platforms/4xx/virtex-ii_pro.h new file mode 100644 index 00000000000..9014c488733 --- /dev/null +++ b/arch/ppc/platforms/4xx/virtex-ii_pro.h @@ -0,0 +1,99 @@ +/* + * arch/ppc/platforms/4xx/virtex-ii_pro.h + * + * Include file that defines the Xilinx Virtex-II Pro processor + * + * Author: MontaVista Software, Inc. + * source@mvista.com + * + * 2002-2004 (c) MontaVista Software, Inc. This file is licensed under the + * terms of the GNU General Public License version 2. This program is licensed + * "as is" without any warranty of any kind, whether express or implied. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_VIRTEXIIPRO_H__ +#define __ASM_VIRTEXIIPRO_H__ + +#include +#include + +/* serial defines */ + +#define RS_TABLE_SIZE 4 /* change this and add more devices below + if you have more then 4 16x50 UARTs */ + +#define BASE_BAUD (XPAR_UARTNS550_0_CLOCK_FREQ_HZ/16) + +/* The serial ports in the Virtex-II Pro have each I/O byte in the + * LSByte of a word. This means that iomem_reg_shift needs to be 2 to + * change the byte offsets into word offsets. In addition the base + * addresses need to have 3 added to them to get to the LSByte. + */ +#define STD_UART_OP(num) \ + { 0, BASE_BAUD, 0, XPAR_INTC_0_UARTNS550_##num##_VEC_ID, \ + ASYNC_BOOT_AUTOCONF, \ + .iomem_base = (u8 *)XPAR_UARTNS550_##num##_BASEADDR + 3, \ + .iomem_reg_shift = 2, \ + .io_type = SERIAL_IO_MEM}, + +#if defined(XPAR_INTC_0_UARTNS550_0_VEC_ID) +#define ML300_UART0 STD_UART_OP(0) +#else +#define ML300_UART0 +#endif + +#if defined(XPAR_INTC_0_UARTNS550_1_VEC_ID) +#define ML300_UART1 STD_UART_OP(1) +#else +#define ML300_UART1 +#endif + +#if defined(XPAR_INTC_0_UARTNS550_2_VEC_ID) +#define ML300_UART2 STD_UART_OP(2) +#else +#define ML300_UART2 +#endif + +#if defined(XPAR_INTC_0_UARTNS550_3_VEC_ID) +#define ML300_UART3 STD_UART_OP(3) +#else +#define ML300_UART3 +#endif + +#if defined(XPAR_INTC_0_UARTNS550_4_VEC_ID) +#error Edit this file to add more devices. +#elif defined(XPAR_INTC_0_UARTNS550_3_VEC_ID) +#define NR_SER_PORTS 4 +#elif defined(XPAR_INTC_0_UARTNS550_2_VEC_ID) +#define NR_SER_PORTS 3 +#elif defined(XPAR_INTC_0_UARTNS550_1_VEC_ID) +#define NR_SER_PORTS 2 +#elif defined(XPAR_INTC_0_UARTNS550_0_VEC_ID) +#define NR_SER_PORTS 1 +#else +#define NR_SER_PORTS 0 +#endif + +#if defined(CONFIG_UART0_TTYS0) +#define SERIAL_PORT_DFNS \ + ML300_UART0 \ + ML300_UART1 \ + ML300_UART2 \ + ML300_UART3 +#endif + +#if defined(CONFIG_UART0_TTYS1) +#define SERIAL_PORT_DFNS \ + ML300_UART1 \ + ML300_UART0 \ + ML300_UART2 \ + ML300_UART3 +#endif + +#define DCRN_CPMFR_BASE 0 + +#include + +#endif /* __ASM_VIRTEXIIPRO_H__ */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/4xx/walnut.c b/arch/ppc/platforms/4xx/walnut.c new file mode 100644 index 00000000000..a33eda4b748 --- /dev/null +++ b/arch/ppc/platforms/4xx/walnut.c @@ -0,0 +1,249 @@ +/* + * arch/ppc/platforms/4xx/walnut.c + * + * Architecture- / platform-specific boot-time initialization code for + * IBM PowerPC 4xx based boards. Adapted from original + * code by Gary Thomas, Cort Dougan , and Dan Malek + * . + * + * Copyright(c) 1999-2000 Grant Erickson + * + * 2002 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +void *kb_cs; +void *kb_data; +void *walnut_rtc_base; + +/* Some IRQs unique to Walnut. + * Used by the generic 405 PCI setup functions in ppc4xx_pci.c + */ +int __init +ppc405_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {28, 28, 28, 28}, /* IDSEL 1 - PCI slot 1 */ + {29, 29, 29, 29}, /* IDSEL 2 - PCI slot 2 */ + {30, 30, 30, 30}, /* IDSEL 3 - PCI slot 3 */ + {31, 31, 31, 31}, /* IDSEL 4 - PCI slot 4 */ + }; + + const long min_idsel = 1, max_idsel = 4, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +}; + +void __init +walnut_setup_arch(void) +{ + + void *fpga_brdc; + unsigned char fpga_brdc_data; + void *fpga_enable; + void *fpga_polarity; + void *fpga_status; + void *fpga_trigger; + + ppc4xx_setup_arch(); + + ibm_ocp_set_emac(0, 0); + + kb_data = ioremap(WALNUT_PS2_BASE, 8); + if (!kb_data) { + printk(KERN_CRIT + "walnut_setup_arch() kb_data ioremap failed\n"); + return; + } + + kb_cs = kb_data + 1; + + fpga_status = ioremap(WALNUT_FPGA_BASE, 8); + if (!fpga_status) { + printk(KERN_CRIT + "walnut_setup_arch() fpga_status ioremap failed\n"); + return; + } + + fpga_enable = fpga_status + 1; + fpga_polarity = fpga_status + 2; + fpga_trigger = fpga_status + 3; + fpga_brdc = fpga_status + 4; + + /* split the keyboard and mouse interrupts */ + fpga_brdc_data = readb(fpga_brdc); + fpga_brdc_data |= 0x80; + writeb(fpga_brdc_data, fpga_brdc); + + writeb(0x3, fpga_enable); + + writeb(0x3, fpga_polarity); + + writeb(0x3, fpga_trigger); + + /* RTC step for the walnut */ + walnut_rtc_base = (void *) WALNUT_RTC_VADDR; + TODC_INIT(TODC_TYPE_DS1743, walnut_rtc_base, walnut_rtc_base, + walnut_rtc_base, 8); + /* Identify the system */ + printk("IBM Walnut port (C) 2000-2002 MontaVista Software, Inc. (source@mvista.com)\n"); +} + +void __init +bios_fixup(struct pci_controller *hose, struct pcil0_regs *pcip) +{ +#ifdef CONFIG_PCI + unsigned int bar_response, bar; + /* + * Expected PCI mapping: + * + * PLB addr PCI memory addr + * --------------------- --------------------- + * 0000'0000 - 7fff'ffff <--- 0000'0000 - 7fff'ffff + * 8000'0000 - Bfff'ffff ---> 8000'0000 - Bfff'ffff + * + * PLB addr PCI io addr + * --------------------- --------------------- + * e800'0000 - e800'ffff ---> 0000'0000 - 0001'0000 + * + * The following code is simplified by assuming that the bootrom + * has been well behaved in following this mapping. + */ + +#ifdef DEBUG + int i; + + printk("ioremap PCLIO_BASE = 0x%x\n", pcip); + printk("PCI bridge regs before fixup \n"); + for (i = 0; i <= 3; i++) { + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].ma))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].la))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pcila))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pciha))); + } + printk(" ptm1ms\t0x%x\n", in_le32(&(pcip->ptm1ms))); + printk(" ptm1la\t0x%x\n", in_le32(&(pcip->ptm1la))); + printk(" ptm2ms\t0x%x\n", in_le32(&(pcip->ptm2ms))); + printk(" ptm2la\t0x%x\n", in_le32(&(pcip->ptm2la))); + +#endif + + /* added for IBM boot rom version 1.15 bios bar changes -AK */ + + /* Disable region first */ + out_le32((void *) &(pcip->pmm[0].ma), 0x00000000); + /* PLB starting addr, PCI: 0x80000000 */ + out_le32((void *) &(pcip->pmm[0].la), 0x80000000); + /* PCI start addr, 0x80000000 */ + out_le32((void *) &(pcip->pmm[0].pcila), PPC405_PCI_MEM_BASE); + /* 512MB range of PLB to PCI */ + out_le32((void *) &(pcip->pmm[0].pciha), 0x00000000); + /* Enable no pre-fetch, enable region */ + out_le32((void *) &(pcip->pmm[0].ma), ((0xffffffff - + (PPC405_PCI_UPPER_MEM - + PPC405_PCI_MEM_BASE)) | 0x01)); + + /* Disable region one */ + out_le32((void *) &(pcip->pmm[1].ma), 0x00000000); + out_le32((void *) &(pcip->pmm[1].la), 0x00000000); + out_le32((void *) &(pcip->pmm[1].pcila), 0x00000000); + out_le32((void *) &(pcip->pmm[1].pciha), 0x00000000); + out_le32((void *) &(pcip->pmm[1].ma), 0x00000000); + out_le32((void *) &(pcip->ptm1ms), 0x00000000); + + /* Disable region two */ + out_le32((void *) &(pcip->pmm[2].ma), 0x00000000); + out_le32((void *) &(pcip->pmm[2].la), 0x00000000); + out_le32((void *) &(pcip->pmm[2].pcila), 0x00000000); + out_le32((void *) &(pcip->pmm[2].pciha), 0x00000000); + out_le32((void *) &(pcip->pmm[2].ma), 0x00000000); + out_le32((void *) &(pcip->ptm2ms), 0x00000000); + + /* Zero config bars */ + for (bar = PCI_BASE_ADDRESS_1; bar <= PCI_BASE_ADDRESS_2; bar += 4) { + early_write_config_dword(hose, hose->first_busno, + PCI_FUNC(hose->first_busno), bar, + 0x00000000); + early_read_config_dword(hose, hose->first_busno, + PCI_FUNC(hose->first_busno), bar, + &bar_response); + DBG("BUS %d, device %d, Function %d bar 0x%8.8x is 0x%8.8x\n", + hose->first_busno, PCI_SLOT(hose->first_busno), + PCI_FUNC(hose->first_busno), bar, bar_response); + } + /* end work arround */ + +#ifdef DEBUG + printk("PCI bridge regs after fixup \n"); + for (i = 0; i <= 3; i++) { + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].ma))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].la))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pcila))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pciha))); + } + printk(" ptm1ms\t0x%x\n", in_le32(&(pcip->ptm1ms))); + printk(" ptm1la\t0x%x\n", in_le32(&(pcip->ptm1la))); + printk(" ptm2ms\t0x%x\n", in_le32(&(pcip->ptm2ms))); + printk(" ptm2la\t0x%x\n", in_le32(&(pcip->ptm2la))); + +#endif +#endif +} + +void __init +walnut_map_io(void) +{ + ppc4xx_map_io(); + io_block_mapping(WALNUT_RTC_VADDR, + WALNUT_RTC_PADDR, WALNUT_RTC_SIZE, _PAGE_IO); +} + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + ppc4xx_init(r3, r4, r5, r6, r7); + + ppc_md.setup_arch = walnut_setup_arch; + ppc_md.setup_io_mappings = walnut_map_io; + +#ifdef CONFIG_GEN_RTC + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; +#endif +} diff --git a/arch/ppc/platforms/4xx/walnut.h b/arch/ppc/platforms/4xx/walnut.h new file mode 100644 index 00000000000..04cfbf3696b --- /dev/null +++ b/arch/ppc/platforms/4xx/walnut.h @@ -0,0 +1,72 @@ +/* + * arch/ppc/platforms/4xx/walnut.h + * + * Macros, definitions, and data structures specific to the IBM PowerPC + * 405GP "Walnut" evaluation board. + * + * Authors: Grant Erickson , Frank Rowand + * , Debbie Chu or + * source@mvista.com + * + * Copyright (c) 1999 Grant Erickson + * + * 2000 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_WALNUT_H__ +#define __ASM_WALNUT_H__ + +/* We have a 405GP core */ +#include + +#ifndef __ASSEMBLY__ +/* + * Data structure defining board information maintained by the boot + * ROM on IBM's "Walnut" evaluation board. An effort has been made to + * keep the field names consistent with the 8xx 'bd_t' board info + * structures. + */ + +typedef struct board_info { + unsigned char bi_s_version[4]; /* Version of this structure */ + unsigned char bi_r_version[30]; /* Version of the IBM ROM */ + unsigned int bi_memsize; /* DRAM installed, in bytes */ + unsigned char bi_enetaddr[6]; /* Local Ethernet MAC address */ + unsigned char bi_pci_enetaddr[6]; /* PCI Ethernet MAC address */ + unsigned int bi_intfreq; /* Processor speed, in Hz */ + unsigned int bi_busfreq; /* PLB Bus speed, in Hz */ + unsigned int bi_pci_busfreq; /* PCI Bus speed, in Hz */ +} bd_t; + +/* Some 4xx parts use a different timebase frequency from the internal clock. +*/ +#define bi_tbfreq bi_intfreq + + +/* Memory map for the IBM "Walnut" 405GP evaluation board. + * Generic 4xx plus RTC. + */ + +extern void *walnut_rtc_base; +#define WALNUT_RTC_PADDR ((uint)0xf0000000) +#define WALNUT_RTC_VADDR WALNUT_RTC_PADDR +#define WALNUT_RTC_SIZE ((uint)8*1024) + +#ifdef CONFIG_PPC405GP_INTERNAL_CLOCK +#define BASE_BAUD 201600 +#else +#define BASE_BAUD 691200 +#endif + +#define WALNUT_PS2_BASE 0xF0100000 +#define WALNUT_FPGA_BASE 0xF0300000 + +#define PPC4xx_MACHINE_NAME "IBM Walnut" + +#endif /* !__ASSEMBLY__ */ +#endif /* __ASM_WALNUT_H__ */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/4xx/xilinx_ml300.c b/arch/ppc/platforms/4xx/xilinx_ml300.c new file mode 100644 index 00000000000..0b1b77d986b --- /dev/null +++ b/arch/ppc/platforms/4xx/xilinx_ml300.c @@ -0,0 +1,146 @@ +/* + * arch/ppc/platforms/4xx/xilinx_ml300.c + * + * Xilinx ML300 evaluation board initialization + * + * Author: MontaVista Software, Inc. + * source@mvista.com + * + * 2002-2004 (c) MontaVista Software, Inc. This file is licensed under the + * terms of the GNU General Public License version 2. This program is licensed + * "as is" without any warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* for NR_SER_PORTS */ + +/* + * As an overview of how the following functions (platform_init, + * ml300_map_io, ml300_setup_arch and ml300_init_IRQ) fit into the + * kernel startup procedure, here's a call tree: + * + * start_here arch/ppc/kernel/head_4xx.S + * early_init arch/ppc/kernel/setup.c + * machine_init arch/ppc/kernel/setup.c + * platform_init this file + * ppc4xx_init arch/ppc/syslib/ppc4xx_setup.c + * parse_bootinfo + * find_bootinfo + * "setup some default ppc_md pointers" + * MMU_init arch/ppc/mm/init.c + * *ppc_md.setup_io_mappings == ml300_map_io this file + * ppc4xx_map_io arch/ppc/syslib/ppc4xx_setup.c + * start_kernel init/main.c + * setup_arch arch/ppc/kernel/setup.c + * #if defined(CONFIG_KGDB) + * *ppc_md.kgdb_map_scc() == gen550_kgdb_map_scc + * #endif + * *ppc_md.setup_arch == ml300_setup_arch this file + * ppc4xx_setup_arch arch/ppc/syslib/ppc4xx_setup.c + * ppc4xx_find_bridges arch/ppc/syslib/ppc405_pci.c + * init_IRQ arch/ppc/kernel/irq.c + * *ppc_md.init_IRQ == ml300_init_IRQ this file + * ppc4xx_init_IRQ arch/ppc/syslib/ppc4xx_setup.c + * ppc4xx_pic_init arch/ppc/syslib/xilinx_pic.c + */ + +#if defined(XPAR_POWER_0_POWERDOWN_BASEADDR) + +static volatile unsigned *powerdown_base = + (volatile unsigned *) XPAR_POWER_0_POWERDOWN_BASEADDR; + +static void +xilinx_power_off(void) +{ + local_irq_disable(); + out_be32(powerdown_base, XPAR_POWER_0_POWERDOWN_VALUE); + while (1) ; +} +#endif + +void __init +ml300_map_io(void) +{ + ppc4xx_map_io(); + +#if defined(XPAR_POWER_0_POWERDOWN_BASEADDR) + powerdown_base = ioremap((unsigned long) powerdown_base, + XPAR_POWER_0_POWERDOWN_HIGHADDR - + XPAR_POWER_0_POWERDOWN_BASEADDR + 1); +#endif +} + +static void __init +ml300_early_serial_map(void) +{ +#ifdef CONFIG_SERIAL_8250 + struct serial_state old_ports[] = { SERIAL_PORT_DFNS }; + struct uart_port port; + int i; + + /* Setup ioremapped serial port access */ + for (i = 0; i < ARRAY_SIZE(old_ports); i++ ) { + memset(&port, 0, sizeof(port)); + port.membase = ioremap((phys_addr_t)(old_ports[i].iomem_base), 16); + port.irq = old_ports[i].irq; + port.uartclk = old_ports[i].baud_base * 16; + port.regshift = old_ports[i].iomem_reg_shift; + port.iotype = SERIAL_IO_MEM; + port.flags = ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST; + port.line = i; + + if (early_serial_setup(&port) != 0) { + printk("Early serial init of port %d failed\n", i); + } + } +#endif /* CONFIG_SERIAL_8250 */ +} + +void __init +ml300_setup_arch(void) +{ + ppc4xx_setup_arch(); /* calls ppc4xx_find_bridges() */ + + ml300_early_serial_map(); + + /* Identify the system */ + printk(KERN_INFO "Xilinx Virtex-II Pro port\n"); + printk(KERN_INFO "Port by MontaVista Software, Inc. (source@mvista.com)\n"); +} + +/* Called after board_setup_irq from ppc4xx_init_IRQ(). */ +void __init +ml300_init_irq(void) +{ + ppc4xx_init_IRQ(); +} + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + ppc4xx_init(r3, r4, r5, r6, r7); + + ppc_md.setup_arch = ml300_setup_arch; + ppc_md.setup_io_mappings = ml300_map_io; + ppc_md.init_IRQ = ml300_init_irq; + +#if defined(XPAR_POWER_0_POWERDOWN_BASEADDR) + ppc_md.power_off = xilinx_power_off; +#endif + +#ifdef CONFIG_KGDB + ppc_md.early_serial_map = ml300_early_serial_map; +#endif +} + diff --git a/arch/ppc/platforms/4xx/xilinx_ml300.h b/arch/ppc/platforms/4xx/xilinx_ml300.h new file mode 100644 index 00000000000..f8c58841233 --- /dev/null +++ b/arch/ppc/platforms/4xx/xilinx_ml300.h @@ -0,0 +1,47 @@ +/* + * arch/ppc/platforms/4xx/xilinx_ml300.h + * + * Include file that defines the Xilinx ML300 evaluation board + * + * Author: MontaVista Software, Inc. + * source@mvista.com + * + * 2002-2004 (c) MontaVista Software, Inc. This file is licensed under the + * terms of the GNU General Public License version 2. This program is licensed + * "as is" without any warranty of any kind, whether express or implied. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_XILINX_ML300_H__ +#define __ASM_XILINX_ML300_H__ + +/* ML300 has a Xilinx Virtex-II Pro processor */ +#include + +#ifndef __ASSEMBLY__ + +#include + +typedef struct board_info { + unsigned int bi_memsize; /* DRAM installed, in bytes */ + unsigned char bi_enetaddr[6]; /* Local Ethernet MAC address */ + unsigned int bi_intfreq; /* Processor speed, in Hz */ + unsigned int bi_busfreq; /* PLB Bus speed, in Hz */ + unsigned int bi_pci_busfreq; /* PCI Bus speed, in Hz */ +} bd_t; + +/* Some 4xx parts use a different timebase frequency from the internal clock. +*/ +#define bi_tbfreq bi_intfreq + +#endif /* !__ASSEMBLY__ */ + +/* We don't need anything mapped. Size of zero will accomplish that. */ +#define PPC4xx_ONB_IO_PADDR 0u +#define PPC4xx_ONB_IO_VADDR 0u +#define PPC4xx_ONB_IO_SIZE 0u + +#define PPC4xx_MACHINE_NAME "Xilinx ML300" + +#endif /* __ASM_XILINX_ML300_H__ */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/4xx/xparameters/xparameters_ml300.h b/arch/ppc/platforms/4xx/xparameters/xparameters_ml300.h new file mode 100644 index 00000000000..97e3f4d4bd5 --- /dev/null +++ b/arch/ppc/platforms/4xx/xparameters/xparameters_ml300.h @@ -0,0 +1,310 @@ +/******************************************************************* +* +* Author: Xilinx, Inc. +* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2004 Xilinx Inc. +* All rights reserved. +* +* +* You should have received a copy of the GNU General Public License along +* with this program; if not, write to the Free Software Foundation, Inc., +* 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Description: Driver parameters +* +*******************************************************************/ + +#define XPAR_XPCI_NUM_INSTANCES 1 +#define XPAR_XPCI_CLOCK_HZ 33333333 +#define XPAR_OPB_PCI_REF_0_DEVICE_ID 0 +#define XPAR_OPB_PCI_REF_0_BASEADDR 0x20000000 +#define XPAR_OPB_PCI_REF_0_HIGHADDR 0x3FFFFFFF +#define XPAR_OPB_PCI_REF_0_CONFIG_ADDR 0x3C000000 +#define XPAR_OPB_PCI_REF_0_CONFIG_DATA 0x3C000004 +#define XPAR_OPB_PCI_REF_0_LCONFIG_ADDR 0x3E000000 +#define XPAR_OPB_PCI_REF_0_MEM_BASEADDR 0x20000000 +#define XPAR_OPB_PCI_REF_0_MEM_HIGHADDR 0x37FFFFFF +#define XPAR_OPB_PCI_REF_0_IO_BASEADDR 0x38000000 +#define XPAR_OPB_PCI_REF_0_IO_HIGHADDR 0x3BFFFFFF + +/******************************************************************/ + +#define XPAR_XEMAC_NUM_INSTANCES 1 +#define XPAR_OPB_ETHERNET_0_BASEADDR 0x60000000 +#define XPAR_OPB_ETHERNET_0_HIGHADDR 0x60003FFF +#define XPAR_OPB_ETHERNET_0_DEVICE_ID 0 +#define XPAR_OPB_ETHERNET_0_ERR_COUNT_EXIST 1 +#define XPAR_OPB_ETHERNET_0_DMA_PRESENT 1 +#define XPAR_OPB_ETHERNET_0_MII_EXIST 1 + +/******************************************************************/ + +#define XPAR_MY_OPB_GPIO_0_DEVICE_ID_0 0 +#define XPAR_MY_OPB_GPIO_0_BASEADDR_0 0x90000000 +#define XPAR_MY_OPB_GPIO_0_HIGHADDR_0 (0x90000000+0x7) +#define XPAR_MY_OPB_GPIO_0_DEVICE_ID_1 1 +#define XPAR_MY_OPB_GPIO_0_BASEADDR_1 (0x90000000+0x8) +#define XPAR_MY_OPB_GPIO_0_HIGHADDR_1 (0x90000000+0x1F) +#define XPAR_XGPIO_NUM_INSTANCES 2 + +/******************************************************************/ + +#define XPAR_XIIC_NUM_INSTANCES 1 +#define XPAR_OPB_IIC_0_BASEADDR 0xA8000000 +#define XPAR_OPB_IIC_0_HIGHADDR 0xA80001FF +#define XPAR_OPB_IIC_0_DEVICE_ID 0 +#define XPAR_OPB_IIC_0_TEN_BIT_ADR 0 + +/******************************************************************/ + +#define XPAR_XUARTNS550_NUM_INSTANCES 2 +#define XPAR_XUARTNS550_CLOCK_HZ 100000000 +#define XPAR_OPB_UART16550_0_BASEADDR 0xA0000000 +#define XPAR_OPB_UART16550_0_HIGHADDR 0xA0001FFF +#define XPAR_OPB_UART16550_0_DEVICE_ID 0 +#define XPAR_OPB_UART16550_1_BASEADDR 0xA0010000 +#define XPAR_OPB_UART16550_1_HIGHADDR 0xA0011FFF +#define XPAR_OPB_UART16550_1_DEVICE_ID 1 + +/******************************************************************/ + +#define XPAR_XSPI_NUM_INSTANCES 1 +#define XPAR_OPB_SPI_0_BASEADDR 0xA4000000 +#define XPAR_OPB_SPI_0_HIGHADDR 0xA400007F +#define XPAR_OPB_SPI_0_DEVICE_ID 0 +#define XPAR_OPB_SPI_0_FIFO_EXIST 1 +#define XPAR_OPB_SPI_0_SPI_SLAVE_ONLY 0 +#define XPAR_OPB_SPI_0_NUM_SS_BITS 1 + +/******************************************************************/ + +#define XPAR_XPS2_NUM_INSTANCES 2 +#define XPAR_OPB_PS2_DUAL_REF_0_DEVICE_ID_0 0 +#define XPAR_OPB_PS2_DUAL_REF_0_BASEADDR_0 0xA9000000 +#define XPAR_OPB_PS2_DUAL_REF_0_HIGHADDR_0 (0xA9000000+0x3F) +#define XPAR_OPB_PS2_DUAL_REF_0_DEVICE_ID_1 1 +#define XPAR_OPB_PS2_DUAL_REF_0_BASEADDR_1 (0xA9000000+0x1000) +#define XPAR_OPB_PS2_DUAL_REF_0_HIGHADDR_1 (0xA9000000+0x103F) + +/******************************************************************/ + +#define XPAR_XTOUCHSCREEN_NUM_INSTANCES 1 +#define XPAR_OPB_TSD_REF_0_BASEADDR 0xAA000000 +#define XPAR_OPB_TSD_REF_0_HIGHADDR 0xAA000007 +#define XPAR_OPB_TSD_REF_0_DEVICE_ID 0 + +/******************************************************************/ + +#define XPAR_OPB_AC97_CONTROLLER_REF_0_BASEADDR 0xA6000000 +#define XPAR_OPB_AC97_CONTROLLER_REF_0_HIGHADDR 0xA60000FF +#define XPAR_OPB_PAR_PORT_REF_0_BASEADDR 0x90010000 +#define XPAR_OPB_PAR_PORT_REF_0_HIGHADDR 0x900100FF +#define XPAR_PLB_DDR_0_BASEADDR 0x00000000 +#define XPAR_PLB_DDR_0_HIGHADDR 0x0FFFFFFF + +/******************************************************************/ + +#define XPAR_XINTC_HAS_IPR 1 +#define XPAR_INTC_MAX_NUM_INTR_INPUTS 18 +#define XPAR_XINTC_USE_DCR 0 +#define XPAR_XINTC_NUM_INSTANCES 1 +#define XPAR_DCR_INTC_0_BASEADDR 0xD0000FC0 +#define XPAR_DCR_INTC_0_HIGHADDR 0xD0000FDF +#define XPAR_DCR_INTC_0_DEVICE_ID 0 +#define XPAR_DCR_INTC_0_KIND_OF_INTR 0x00038000 + +/******************************************************************/ + +#define XPAR_DCR_INTC_0_MISC_LOGIC_0_PHY_MII_INT_INTR 0 +#define XPAR_DCR_INTC_0_OPB_ETHERNET_0_IP2INTC_IRPT_INTR 1 +#define XPAR_DCR_INTC_0_MISC_LOGIC_0_IIC_TEMP_CRIT_INTR 2 +#define XPAR_DCR_INTC_0_MISC_LOGIC_0_IIC_IRQ_INTR 3 +#define XPAR_DCR_INTC_0_OPB_IIC_0_IP2INTC_IRPT_INTR 4 +#define XPAR_DCR_INTC_0_OPB_SYSACE_0_SYSACE_IRQ_INTR 5 +#define XPAR_DCR_INTC_0_OPB_UART16550_0_IP2INTC_IRPT_INTR 6 +#define XPAR_DCR_INTC_0_OPB_UART16550_1_IP2INTC_IRPT_INTR 7 +#define XPAR_DCR_INTC_0_OPB_PS2_DUAL_REF_0_SYS_INTR1_INTR 8 +#define XPAR_DCR_INTC_0_OPB_PS2_DUAL_REF_0_SYS_INTR2_INTR 9 +#define XPAR_DCR_INTC_0_OPB_SPI_0_IP2INTC_IRPT_INTR 10 +#define XPAR_DCR_INTC_0_OPB_TSD_REF_0_INTR_INTR 11 +#define XPAR_DCR_INTC_0_OPB_AC97_CONTROLLER_REF_0_PLAYBACK_INTERRUPT_INTR 12 +#define XPAR_DCR_INTC_0_OPB_AC97_CONTROLLER_REF_0_RECORD_INTERRUPT_INTR 13 +#define XPAR_DCR_INTC_0_OPB_PCI_REF_0_INTR_OUT_INTR 14 +#define XPAR_DCR_INTC_0_PLB2OPB_BRIDGE_0_BUS_ERROR_DET_INTR 15 +#define XPAR_DCR_INTC_0_PLB_V34_0_BUS_ERROR_DET_INTR 16 +#define XPAR_DCR_INTC_0_OPB2PLB_BRIDGE_0_BUS_ERROR_DET_INTR 17 + +/******************************************************************/ + +#define XPAR_XTFT_NUM_INSTANCES 1 +#define XPAR_PLB_TFT_CNTLR_REF_0_DCR_BASEADDR 0xD0000200 +#define XPAR_PLB_TFT_CNTLR_REF_0_DCR_HIGHADDR 0xD0000207 +#define XPAR_PLB_TFT_CNTLR_REF_0_DEVICE_ID 0 + +/******************************************************************/ + +#define XPAR_XSYSACE_MEM_WIDTH 8 +#define XPAR_XSYSACE_NUM_INSTANCES 1 +#define XPAR_OPB_SYSACE_0_BASEADDR 0xCF000000 +#define XPAR_OPB_SYSACE_0_HIGHADDR 0xCF0001FF +#define XPAR_OPB_SYSACE_0_DEVICE_ID 0 +#define XPAR_OPB_SYSACE_0_MEM_WIDTH 8 + +/******************************************************************/ + +#define XPAR_CPU_PPC405_CORE_CLOCK_FREQ_HZ 300000000 + +/******************************************************************/ + +/******************************************************************/ + +/* Linux Redefines */ + +/******************************************************************/ + +#define XPAR_UARTNS550_0_BASEADDR (XPAR_OPB_UART16550_0_BASEADDR+0x1000) +#define XPAR_UARTNS550_0_HIGHADDR XPAR_OPB_UART16550_0_HIGHADDR +#define XPAR_UARTNS550_0_CLOCK_FREQ_HZ XPAR_XUARTNS550_CLOCK_HZ +#define XPAR_UARTNS550_0_DEVICE_ID XPAR_OPB_UART16550_0_DEVICE_ID +#define XPAR_UARTNS550_1_BASEADDR (XPAR_OPB_UART16550_1_BASEADDR+0x1000) +#define XPAR_UARTNS550_1_HIGHADDR XPAR_OPB_UART16550_1_HIGHADDR +#define XPAR_UARTNS550_1_CLOCK_FREQ_HZ XPAR_XUARTNS550_CLOCK_HZ +#define XPAR_UARTNS550_1_DEVICE_ID XPAR_OPB_UART16550_1_DEVICE_ID + +/******************************************************************/ + +#define XPAR_GPIO_0_BASEADDR XPAR_MY_OPB_GPIO_0_BASEADDR_0 +#define XPAR_GPIO_0_HIGHADDR XPAR_MY_OPB_GPIO_0_HIGHADDR_0 +#define XPAR_GPIO_0_DEVICE_ID XPAR_MY_OPB_GPIO_0_DEVICE_ID_0 +#define XPAR_GPIO_1_BASEADDR XPAR_MY_OPB_GPIO_0_BASEADDR_1 +#define XPAR_GPIO_1_HIGHADDR XPAR_MY_OPB_GPIO_0_HIGHADDR_1 +#define XPAR_GPIO_1_DEVICE_ID XPAR_MY_OPB_GPIO_0_DEVICE_ID_1 + +/******************************************************************/ + +#define XPAR_IIC_0_BASEADDR XPAR_OPB_IIC_0_BASEADDR +#define XPAR_IIC_0_HIGHADDR XPAR_OPB_IIC_0_HIGHADDR +#define XPAR_IIC_0_TEN_BIT_ADR XPAR_OPB_IIC_0_TEN_BIT_ADR +#define XPAR_IIC_0_DEVICE_ID XPAR_OPB_IIC_0_DEVICE_ID + +/******************************************************************/ + +#define XPAR_SYSACE_0_BASEADDR XPAR_OPB_SYSACE_0_BASEADDR +#define XPAR_SYSACE_0_HIGHADDR XPAR_OPB_SYSACE_0_HIGHADDR +#define XPAR_SYSACE_0_DEVICE_ID XPAR_OPB_SYSACE_0_DEVICE_ID + +/******************************************************************/ + +#define XPAR_INTC_0_BASEADDR XPAR_DCR_INTC_0_BASEADDR +#define XPAR_INTC_0_HIGHADDR XPAR_DCR_INTC_0_HIGHADDR +#define XPAR_INTC_0_KIND_OF_INTR XPAR_DCR_INTC_0_KIND_OF_INTR +#define XPAR_INTC_0_DEVICE_ID XPAR_DCR_INTC_0_DEVICE_ID + +/******************************************************************/ + +#define XPAR_INTC_0_EMAC_0_VEC_ID XPAR_DCR_INTC_0_OPB_ETHERNET_0_IP2INTC_IRPT_INTR +#define XPAR_INTC_0_IIC_0_VEC_ID XPAR_DCR_INTC_0_OPB_IIC_0_IP2INTC_IRPT_INTR +#define XPAR_INTC_0_SYSACE_0_VEC_ID XPAR_DCR_INTC_0_OPB_SYSACE_0_SYSACE_IRQ_INTR +#define XPAR_INTC_0_UARTNS550_0_VEC_ID XPAR_DCR_INTC_0_OPB_UART16550_0_IP2INTC_IRPT_INTR +#define XPAR_INTC_0_UARTNS550_1_VEC_ID XPAR_DCR_INTC_0_OPB_UART16550_1_IP2INTC_IRPT_INTR +#define XPAR_INTC_0_PS2_0_VEC_ID XPAR_DCR_INTC_0_OPB_PS2_DUAL_REF_0_SYS_INTR1_INTR +#define XPAR_INTC_0_PS2_1_VEC_ID XPAR_DCR_INTC_0_OPB_PS2_DUAL_REF_0_SYS_INTR2_INTR +#define XPAR_INTC_0_SPI_0_VEC_ID XPAR_DCR_INTC_0_OPB_SPI_0_IP2INTC_IRPT_INTR +#define XPAR_INTC_0_TOUCHSCREEN_0_VEC_ID XPAR_DCR_INTC_0_OPB_TSD_REF_0_INTR_INTR +#define XPAR_INTC_0_PCI_0_VEC_ID_A XPAR_DCR_INTC_0_OPB_PCI_REF_0_INTR_OUT_INTR +#define XPAR_INTC_0_PCI_0_VEC_ID_B XPAR_DCR_INTC_0_OPB_PCI_REF_0_INTR_OUT_INTR +#define XPAR_INTC_0_PCI_0_VEC_ID_C XPAR_DCR_INTC_0_OPB_PCI_REF_0_INTR_OUT_INTR +#define XPAR_INTC_0_PCI_0_VEC_ID_D XPAR_DCR_INTC_0_OPB_PCI_REF_0_INTR_OUT_INTR + +/******************************************************************/ + +#define XPAR_EMAC_0_BASEADDR XPAR_OPB_ETHERNET_0_BASEADDR +#define XPAR_EMAC_0_HIGHADDR XPAR_OPB_ETHERNET_0_HIGHADDR +#define XPAR_EMAC_0_DMA_PRESENT XPAR_OPB_ETHERNET_0_DMA_PRESENT +#define XPAR_EMAC_0_MII_EXIST XPAR_OPB_ETHERNET_0_MII_EXIST +#define XPAR_EMAC_0_ERR_COUNT_EXIST XPAR_OPB_ETHERNET_0_ERR_COUNT_EXIST +#define XPAR_EMAC_0_DEVICE_ID XPAR_OPB_ETHERNET_0_DEVICE_ID + +/******************************************************************/ + +#define XPAR_SPI_0_BASEADDR XPAR_OPB_SPI_0_BASEADDR +#define XPAR_SPI_0_HIGHADDR XPAR_OPB_SPI_0_HIGHADDR +#define XPAR_SPI_0_DEVICE_ID XPAR_OPB_SPI_0_DEVICE_ID + +/******************************************************************/ + +#define XPAR_TOUCHSCREEN_0_BASEADDR XPAR_OPB_TSD_REF_0_BASEADDR +#define XPAR_TOUCHSCREEN_0_HIGHADDR XPAR_OPB_TSD_REF_0_HIGHADDR +#define XPAR_TOUCHSCREEN_0_DEVICE_ID XPAR_OPB_TSD_REF_0_DEVICE_ID + +/******************************************************************/ + +#define XPAR_TFT_0_BASEADDR XPAR_PLB_TFT_CNTLR_REF_0_DCR_BASEADDR + +/******************************************************************/ + +#define XPAR_PCI_0_BASEADDR XPAR_OPB_PCI_REF_0_BASEADDR +#define XPAR_PCI_0_HIGHADDR XPAR_OPB_PCI_REF_0_HIGHADDR +#define XPAR_PCI_0_CONFIG_ADDR XPAR_OPB_PCI_REF_0_CONFIG_ADDR +#define XPAR_PCI_0_CONFIG_DATA XPAR_OPB_PCI_REF_0_CONFIG_DATA +#define XPAR_PCI_0_LCONFIG_ADDR XPAR_OPB_PCI_REF_0_LCONFIG_ADDR +#define XPAR_PCI_0_MEM_BASEADDR XPAR_OPB_PCI_REF_0_MEM_BASEADDR +#define XPAR_PCI_0_MEM_HIGHADDR XPAR_OPB_PCI_REF_0_MEM_HIGHADDR +#define XPAR_PCI_0_IO_BASEADDR XPAR_OPB_PCI_REF_0_IO_BASEADDR +#define XPAR_PCI_0_IO_HIGHADDR XPAR_OPB_PCI_REF_0_IO_HIGHADDR +#define XPAR_PCI_0_CLOCK_FREQ_HZ XPAR_XPCI_CLOCK_HZ +#define XPAR_PCI_0_DEVICE_ID XPAR_OPB_PCI_REF_0_DEVICE_ID + +/******************************************************************/ + +#define XPAR_PS2_0_BASEADDR XPAR_OPB_PS2_DUAL_REF_0_BASEADDR_0 +#define XPAR_PS2_0_HIGHADDR XPAR_OPB_PS2_DUAL_REF_0_HIGHADDR_0 +#define XPAR_PS2_0_DEVICE_ID XPAR_OPB_PS2_DUAL_REF_0_DEVICE_ID_0 +#define XPAR_PS2_1_BASEADDR XPAR_OPB_PS2_DUAL_REF_0_BASEADDR_1 +#define XPAR_PS2_1_HIGHADDR XPAR_OPB_PS2_DUAL_REF_0_HIGHADDR_1 +#define XPAR_PS2_1_DEVICE_ID XPAR_OPB_PS2_DUAL_REF_0_DEVICE_ID_1 + +/******************************************************************/ + +#define XPAR_PLB_CLOCK_FREQ_HZ 100000000 +#define XPAR_CORE_CLOCK_FREQ_HZ XPAR_CPU_PPC405_CORE_CLOCK_FREQ_HZ +#define XPAR_DDR_0_SIZE 0x08000000 + +/******************************************************************/ + +#define XPAR_PERSISTENT_0_IIC_0_BASEADDR 0x00000400 +#define XPAR_PERSISTENT_0_IIC_0_HIGHADDR 0x000007FF +#define XPAR_PERSISTENT_0_IIC_0_EEPROMADDR 0xA0 + +/******************************************************************/ + +#define XPAR_POWER_0_POWERDOWN_BASEADDR 0x90000004 +#define XPAR_POWER_0_POWERDOWN_HIGHADDR 0x90000007 +#define XPAR_POWER_0_POWERDOWN_VALUE 0xFF + +/******************************************************************/ diff --git a/arch/ppc/platforms/83xx/Makefile b/arch/ppc/platforms/83xx/Makefile new file mode 100644 index 00000000000..eb55341d6a1 --- /dev/null +++ b/arch/ppc/platforms/83xx/Makefile @@ -0,0 +1,4 @@ +# +# Makefile for the PowerPC 83xx linux kernel. +# +obj-$(CONFIG_MPC834x_SYS) += mpc834x_sys.o diff --git a/arch/ppc/platforms/83xx/mpc834x_sys.c b/arch/ppc/platforms/83xx/mpc834x_sys.c new file mode 100644 index 00000000000..b3b0f51979d --- /dev/null +++ b/arch/ppc/platforms/83xx/mpc834x_sys.c @@ -0,0 +1,289 @@ +/* + * arch/ppc/platforms/83xx/mpc834x_sys.c + * + * MPC834x SYS board specific routines + * + * Maintainer: Kumar Gala + * + * Copyright 2005 Freescale Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for linux/serial_core.h */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifndef CONFIG_PCI +unsigned long isa_io_base = 0; +unsigned long isa_mem_base = 0; +#endif + +extern unsigned long total_memory; /* in mm/init */ + +unsigned char __res[sizeof (bd_t)]; + +#ifdef CONFIG_PCI +#error "PCI is not supported" +/* NEED mpc83xx_map_irq & mpc83xx_exclude_device + see platforms/85xx/mpc85xx_ads_common.c */ +#endif /* CONFIG_PCI */ + +/* ************************************************************************ + * + * Setup the architecture + * + */ +static void __init +mpc834x_sys_setup_arch(void) +{ + bd_t *binfo = (bd_t *) __res; + unsigned int freq; + struct gianfar_platform_data *pdata; + + /* get the core frequency */ + freq = binfo->bi_intfreq; + + /* Set loops_per_jiffy to a half-way reasonable value, + for use until calibrate_delay gets called. */ + loops_per_jiffy = freq / HZ; + +#ifdef CONFIG_PCI + /* setup PCI host bridges */ + mpc83xx_sys_setup_hose(); +#endif + mpc83xx_early_serial_map(); + + /* setup the board related information for the enet controllers */ + pdata = (struct gianfar_platform_data *) ppc_sys_get_pdata(MPC83xx_TSEC1); + pdata->board_flags = FSL_GIANFAR_BRD_HAS_PHY_INTR; + pdata->interruptPHY = MPC83xx_IRQ_EXT1; + pdata->phyid = 0; + /* fixup phy address */ + pdata->phy_reg_addr += binfo->bi_immr_base; + memcpy(pdata->mac_addr, binfo->bi_enetaddr, 6); + + pdata = (struct gianfar_platform_data *) ppc_sys_get_pdata(MPC83xx_TSEC2); + pdata->board_flags = FSL_GIANFAR_BRD_HAS_PHY_INTR; + pdata->interruptPHY = MPC83xx_IRQ_EXT2; + pdata->phyid = 1; + /* fixup phy address */ + pdata->phy_reg_addr += binfo->bi_immr_base; + memcpy(pdata->mac_addr, binfo->bi_enet1addr, 6); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = Root_NFS; +#else + ROOT_DEV = Root_HDA1; +#endif +} + +static void __init +mpc834x_sys_map_io(void) +{ + /* we steal the lowest ioremap addr for virt space */ + io_block_mapping(VIRT_IMMRBAR, immrbar, 1024*1024, _PAGE_IO); + io_block_mapping(BCSR_VIRT_ADDR, BCSR_PHYS_ADDR, BCSR_SIZE, _PAGE_IO); +} + +int +mpc834x_sys_show_cpuinfo(struct seq_file *m) +{ + uint pvid, svid, phid1; + bd_t *binfo = (bd_t *) __res; + unsigned int freq; + + /* get the core frequency */ + freq = binfo->bi_intfreq; + + pvid = mfspr(SPRN_PVR); + svid = mfspr(SPRN_SVR); + + seq_printf(m, "Vendor\t\t: Freescale Inc.\n"); + seq_printf(m, "Machine\t\t: mpc%s sys\n", cur_ppc_sys_spec->ppc_sys_name); + seq_printf(m, "core clock\t: %d MHz\n" + "bus clock\t: %d MHz\n", + (int)(binfo->bi_intfreq / 1000000), + (int)(binfo->bi_busfreq / 1000000)); + seq_printf(m, "PVR\t\t: 0x%x\n", pvid); + seq_printf(m, "SVR\t\t: 0x%x\n", svid); + + /* Display cpu Pll setting */ + phid1 = mfspr(SPRN_HID1); + seq_printf(m, "PLL setting\t: 0x%x\n", ((phid1 >> 24) & 0x3f)); + + /* Display the amount of memory */ + seq_printf(m, "Memory\t\t: %d MB\n", (int)(binfo->bi_memsize / (1024 * 1024))); + + return 0; +} + + +void __init +mpc834x_sys_init_IRQ(void) +{ + bd_t *binfo = (bd_t *) __res; + + u8 senses[8] = { + 0, /* EXT 0 */ + IRQ_SENSE_LEVEL, /* EXT 1 */ + IRQ_SENSE_LEVEL, /* EXT 2 */ + 0, /* EXT 3 */ + 0, /* EXT 4 */ + 0, /* EXT 5 */ + 0, /* EXT 6 */ + 0, /* EXT 7 */ + }; + + ipic_init(binfo->bi_immr_base + 0x00700, 0, MPC83xx_IPIC_IRQ_OFFSET, senses, 8); + + /* Initialize the default interrupt mapping priorities, + * in case the boot rom changed something on us. + */ + ipic_set_default_priority(); +} + +static __inline__ void +mpc834x_sys_set_bat(void) +{ + /* we steal the lowest ioremap addr for virt space */ + mb(); + mtspr(SPRN_DBAT1U, VIRT_IMMRBAR | 0x1e); + mtspr(SPRN_DBAT1L, immrbar | 0x2a); + mb(); +} + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + bd_t *binfo = (bd_t *) __res; + + /* parse_bootinfo must always be called first */ + parse_bootinfo(find_bootinfo()); + + /* + * If we were passed in a board information, copy it into the + * residual data area. + */ + if (r3) { + memcpy((void *) __res, (void *) (r3 + KERNELBASE), + sizeof (bd_t)); + } + +#if defined(CONFIG_BLK_DEV_INITRD) + /* + * If the init RAM disk has been configured in, and there's a valid + * starting address for it, set it up. + */ + if (r4) { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + /* Copy the kernel command line arguments to a safe place. */ + if (r6) { + *(char *) (r7 + KERNELBASE) = 0; + strcpy(cmd_line, (char *) (r6 + KERNELBASE)); + } + + immrbar = binfo->bi_immr_base; + + mpc834x_sys_set_bat(); + +#if defined(CONFIG_SERIAL_8250) && defined(CONFIG_SERIAL_TEXT_DEBUG) + { + struct uart_port p; + + memset(&p, 0, sizeof (p)); + p.iotype = SERIAL_IO_MEM; + p.membase = (unsigned char __iomem *)(VIRT_IMMRBAR + 0x4500); + p.uartclk = binfo->bi_busfreq; + + gen550_init(0, &p); + + memset(&p, 0, sizeof (p)); + p.iotype = SERIAL_IO_MEM; + p.membase = (unsigned char __iomem *)(VIRT_IMMRBAR + 0x4600); + p.uartclk = binfo->bi_busfreq; + + gen550_init(1, &p); + } +#endif + + identify_ppc_sys_by_id(mfspr(SPRN_SVR)); + + /* setup the PowerPC module struct */ + ppc_md.setup_arch = mpc834x_sys_setup_arch; + ppc_md.show_cpuinfo = mpc834x_sys_show_cpuinfo; + + ppc_md.init_IRQ = mpc834x_sys_init_IRQ; + ppc_md.get_irq = ipic_get_irq; + + ppc_md.restart = mpc83xx_restart; + ppc_md.power_off = mpc83xx_power_off; + ppc_md.halt = mpc83xx_halt; + + ppc_md.find_end_of_memory = mpc83xx_find_end_of_memory; + ppc_md.setup_io_mappings = mpc834x_sys_map_io; + + ppc_md.time_init = mpc83xx_time_init; + ppc_md.set_rtc_time = NULL; + ppc_md.get_rtc_time = NULL; + ppc_md.calibrate_decr = mpc83xx_calibrate_decr; + + ppc_md.early_serial_map = mpc83xx_early_serial_map; +#if defined(CONFIG_SERIAL_8250) && defined(CONFIG_SERIAL_TEXT_DEBUG) + ppc_md.progress = gen550_progress; +#endif /* CONFIG_SERIAL_8250 && CONFIG_SERIAL_TEXT_DEBUG */ + + if (ppc_md.progress) + ppc_md.progress("mpc834x_sys_init(): exit", 0); + + return; +} diff --git a/arch/ppc/platforms/83xx/mpc834x_sys.h b/arch/ppc/platforms/83xx/mpc834x_sys.h new file mode 100644 index 00000000000..f4d055ae19c --- /dev/null +++ b/arch/ppc/platforms/83xx/mpc834x_sys.h @@ -0,0 +1,51 @@ +/* + * arch/ppc/platforms/83xx/mpc834x_sys.h + * + * MPC834X SYS common board definitions + * + * Maintainer: Kumar Gala + * + * Copyright 2005 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef __MACH_MPC83XX_SYS_H__ +#define __MACH_MPC83XX_SYS_H__ + +#include +#include +#include +#include +#include + +#define VIRT_IMMRBAR ((uint)0xfe000000) + +#define BCSR_PHYS_ADDR ((uint)0xf8000000) +#define BCSR_VIRT_ADDR ((uint)0xfe100000) +#define BCSR_SIZE ((uint)(32 * 1024)) + +#ifdef CONFIG_PCI +/* PCI interrupt controller */ +#define PIRQA MPC83xx_IRQ_IRQ4 +#define PIRQB MPC83xx_IRQ_IRQ5 +#define PIRQC MPC83xx_IRQ_IRQ6 +#define PIRQD MPC83xx_IRQ_IRQ7 + +#define MPC834x_SYS_PCI1_LOWER_IO 0x00000000 +#define MPC834x_SYS_PCI1_UPPER_IO 0x00ffffff + +#define MPC834x_SYS_PCI1_LOWER_MEM 0x80000000 +#define MPC834x_SYS_PCI1_UPPER_MEM 0x9fffffff + +#define MPC834x_SYS_PCI1_IO_BASE 0xe2000000 +#define MPC834x_SYS_PCI1_MEM_OFFSET 0x00000000 + +#define MPC834x_SYS_PCI1_IO_SIZE 0x01000000 +#endif /* CONFIG_PCI */ + +#endif /* __MACH_MPC83XX_SYS_H__ */ diff --git a/arch/ppc/platforms/85xx/Kconfig b/arch/ppc/platforms/85xx/Kconfig new file mode 100644 index 00000000000..ff92e38e7da --- /dev/null +++ b/arch/ppc/platforms/85xx/Kconfig @@ -0,0 +1,76 @@ +config 85xx + bool + depends on E500 + default y + +config PPC_INDIRECT_PCI_BE + bool + depends on 85xx + default y + +menu "Freescale 85xx options" + depends on E500 + +choice + prompt "Machine Type" + depends on 85xx + default MPC8540_ADS + +config MPC8540_ADS + bool "Freescale MPC8540 ADS" + help + This option enables support for the MPC 8540 ADS evaluation board. + +config MPC8555_CDS + bool "Freescale MPC8555 CDS" + help + This option enablese support for the MPC8555 CDS evaluation board. + +config MPC8560_ADS + bool "Freescale MPC8560 ADS" + help + This option enables support for the MPC 8560 ADS evaluation board. + +config SBC8560 + bool "WindRiver PowerQUICC III SBC8560" + help + This option enables support for the WindRiver PowerQUICC III + SBC8560 board. + +config STX_GP3 + bool "Silicon Turnkey Express GP3" + help + This option enables support for the Silicon Turnkey Express GP3 + board. + +endchoice + +# It's often necessary to know the specific 85xx processor type. +# Fortunately, it is implied (so far) from the board type, so we +# don't need to ask more redundant questions. +config MPC8540 + bool + depends on MPC8540_ADS + default y + +config MPC8555 + bool + depends on MPC8555_CDS + default y + +config MPC8560 + bool + depends on SBC8560 || MPC8560_ADS || STX_GP3 + default y + +config 85xx_PCI2 + bool "Supprt for 2nd PCI host controller" + depends on MPC8555_CDS + default y + +config PPC_GEN550 + bool + depends on MPC8540 || SBC8560 || MPC8555 + default y + +endmenu diff --git a/arch/ppc/platforms/85xx/Makefile b/arch/ppc/platforms/85xx/Makefile new file mode 100644 index 00000000000..854fbd298ba --- /dev/null +++ b/arch/ppc/platforms/85xx/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for the PowerPC 85xx linux kernel. +# +obj-$(CONFIG_MPC8540_ADS) += mpc85xx_ads_common.o mpc8540_ads.o +obj-$(CONFIG_MPC8555_CDS) += mpc85xx_cds_common.o +obj-$(CONFIG_MPC8560_ADS) += mpc85xx_ads_common.o mpc8560_ads.o +obj-$(CONFIG_SBC8560) += sbc85xx.o sbc8560.o +obj-$(CONFIG_STX_GP3) += stx_gp3.o diff --git a/arch/ppc/platforms/85xx/mpc8540_ads.c b/arch/ppc/platforms/85xx/mpc8540_ads.c new file mode 100644 index 00000000000..4d857d6d633 --- /dev/null +++ b/arch/ppc/platforms/85xx/mpc8540_ads.c @@ -0,0 +1,218 @@ +/* + * arch/ppc/platforms/85xx/mpc8540_ads.c + * + * MPC8540ADS board specific routines + * + * Maintainer: Kumar Gala + * + * Copyright 2004 Freescale Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for linux/serial_core.h */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* ************************************************************************ + * + * Setup the architecture + * + */ +static void __init +mpc8540ads_setup_arch(void) +{ + bd_t *binfo = (bd_t *) __res; + unsigned int freq; + struct gianfar_platform_data *pdata; + + /* get the core frequency */ + freq = binfo->bi_intfreq; + + if (ppc_md.progress) + ppc_md.progress("mpc8540ads_setup_arch()", 0); + + /* Set loops_per_jiffy to a half-way reasonable value, + for use until calibrate_delay gets called. */ + loops_per_jiffy = freq / HZ; + +#ifdef CONFIG_PCI + /* setup PCI host bridges */ + mpc85xx_setup_hose(); +#endif + +#ifdef CONFIG_SERIAL_8250 + mpc85xx_early_serial_map(); +#endif + +#ifdef CONFIG_SERIAL_TEXT_DEBUG + /* Invalidate the entry we stole earlier the serial ports + * should be properly mapped */ + invalidate_tlbcam_entry(NUM_TLBCAMS - 1); +#endif + + /* setup the board related information for the enet controllers */ + pdata = (struct gianfar_platform_data *) ppc_sys_get_pdata(MPC85xx_TSEC1); + pdata->board_flags = FSL_GIANFAR_BRD_HAS_PHY_INTR; + pdata->interruptPHY = MPC85xx_IRQ_EXT5; + pdata->phyid = 0; + /* fixup phy address */ + pdata->phy_reg_addr += binfo->bi_immr_base; + memcpy(pdata->mac_addr, binfo->bi_enetaddr, 6); + + pdata = (struct gianfar_platform_data *) ppc_sys_get_pdata(MPC85xx_TSEC2); + pdata->board_flags = FSL_GIANFAR_BRD_HAS_PHY_INTR; + pdata->interruptPHY = MPC85xx_IRQ_EXT5; + pdata->phyid = 1; + /* fixup phy address */ + pdata->phy_reg_addr += binfo->bi_immr_base; + memcpy(pdata->mac_addr, binfo->bi_enet1addr, 6); + + pdata = (struct gianfar_platform_data *) ppc_sys_get_pdata(MPC85xx_FEC); + pdata->board_flags = 0; + pdata->interruptPHY = MPC85xx_IRQ_EXT5; + pdata->phyid = 3; + /* fixup phy address */ + pdata->phy_reg_addr += binfo->bi_immr_base; + memcpy(pdata->mac_addr, binfo->bi_enet2addr, 6); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = Root_NFS; +#else + ROOT_DEV = Root_HDA1; +#endif +} + +/* ************************************************************************ */ +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + /* parse_bootinfo must always be called first */ + parse_bootinfo(find_bootinfo()); + + /* + * If we were passed in a board information, copy it into the + * residual data area. + */ + if (r3) { + memcpy((void *) __res, (void *) (r3 + KERNELBASE), + sizeof (bd_t)); + } +#ifdef CONFIG_SERIAL_TEXT_DEBUG + { + bd_t *binfo = (bd_t *) __res; + struct uart_port p; + + /* Use the last TLB entry to map CCSRBAR to allow access to DUART regs */ + settlbcam(NUM_TLBCAMS - 1, binfo->bi_immr_base, + binfo->bi_immr_base, MPC85xx_CCSRBAR_SIZE, _PAGE_IO, 0); + + memset(&p, 0, sizeof (p)); + p.iotype = SERIAL_IO_MEM; + p.membase = (void *) binfo->bi_immr_base + MPC85xx_UART0_OFFSET; + p.uartclk = binfo->bi_busfreq; + + gen550_init(0, &p); + + memset(&p, 0, sizeof (p)); + p.iotype = SERIAL_IO_MEM; + p.membase = (void *) binfo->bi_immr_base + MPC85xx_UART1_OFFSET; + p.uartclk = binfo->bi_busfreq; + + gen550_init(1, &p); + } +#endif + +#if defined(CONFIG_BLK_DEV_INITRD) + /* + * If the init RAM disk has been configured in, and there's a valid + * starting address for it, set it up. + */ + if (r4) { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + /* Copy the kernel command line arguments to a safe place. */ + + if (r6) { + *(char *) (r7 + KERNELBASE) = 0; + strcpy(cmd_line, (char *) (r6 + KERNELBASE)); + } + + identify_ppc_sys_by_id(mfspr(SPRN_SVR)); + + /* setup the PowerPC module struct */ + ppc_md.setup_arch = mpc8540ads_setup_arch; + ppc_md.show_cpuinfo = mpc85xx_ads_show_cpuinfo; + + ppc_md.init_IRQ = mpc85xx_ads_init_IRQ; + ppc_md.get_irq = openpic_get_irq; + + ppc_md.restart = mpc85xx_restart; + ppc_md.power_off = mpc85xx_power_off; + ppc_md.halt = mpc85xx_halt; + + ppc_md.find_end_of_memory = mpc85xx_find_end_of_memory; + + ppc_md.time_init = NULL; + ppc_md.set_rtc_time = NULL; + ppc_md.get_rtc_time = NULL; + ppc_md.calibrate_decr = mpc85xx_calibrate_decr; + +#if defined(CONFIG_SERIAL_8250) && defined(CONFIG_SERIAL_TEXT_DEBUG) + ppc_md.progress = gen550_progress; +#endif /* CONFIG_SERIAL_8250 && CONFIG_SERIAL_TEXT_DEBUG */ + + if (ppc_md.progress) + ppc_md.progress("mpc8540ads_init(): exit", 0); + + return; +} diff --git a/arch/ppc/platforms/85xx/mpc8540_ads.h b/arch/ppc/platforms/85xx/mpc8540_ads.h new file mode 100644 index 00000000000..3d05d7c4a93 --- /dev/null +++ b/arch/ppc/platforms/85xx/mpc8540_ads.h @@ -0,0 +1,25 @@ +/* + * arch/ppc/platforms/85xx/mpc8540_ads.h + * + * MPC8540ADS board definitions + * + * Maintainer: Kumar Gala + * + * Copyright 2004 Freescale Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef __MACH_MPC8540ADS_H__ +#define __MACH_MPC8540ADS_H__ + +#include +#include +#include +#include + +#endif /* __MACH_MPC8540ADS_H__ */ diff --git a/arch/ppc/platforms/85xx/mpc8555_cds.h b/arch/ppc/platforms/85xx/mpc8555_cds.h new file mode 100644 index 00000000000..e0e75568bc5 --- /dev/null +++ b/arch/ppc/platforms/85xx/mpc8555_cds.h @@ -0,0 +1,26 @@ +/* + * arch/ppc/platforms/mpc8555_cds.h + * + * MPC8555CDS board definitions + * + * Maintainer: Kumar Gala + * + * Copyright 2004 Freescale Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef __MACH_MPC8555CDS_H__ +#define __MACH_MPC8555CDS_H__ + +#include +#include +#include + +#define CPM_MAP_ADDR (CCSRBAR + MPC85xx_CPM_OFFSET) + +#endif /* __MACH_MPC8555CDS_H__ */ diff --git a/arch/ppc/platforms/85xx/mpc8560_ads.c b/arch/ppc/platforms/85xx/mpc8560_ads.c new file mode 100644 index 00000000000..761b8c7b25d --- /dev/null +++ b/arch/ppc/platforms/85xx/mpc8560_ads.c @@ -0,0 +1,210 @@ +/* + * arch/ppc/platforms/85xx/mpc8560_ads.c + * + * MPC8560ADS board specific routines + * + * Maintainer: Kumar Gala + * + * Copyright 2004 Freescale Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for linux/serial_core.h */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +extern void cpm2_reset(void); + +/* ************************************************************************ + * + * Setup the architecture + * + */ + +static void __init +mpc8560ads_setup_arch(void) +{ + bd_t *binfo = (bd_t *) __res; + unsigned int freq; + struct gianfar_platform_data *pdata; + + cpm2_reset(); + + /* get the core frequency */ + freq = binfo->bi_intfreq; + + if (ppc_md.progress) + ppc_md.progress("mpc8560ads_setup_arch()", 0); + + /* Set loops_per_jiffy to a half-way reasonable value, + for use until calibrate_delay gets called. */ + loops_per_jiffy = freq / HZ; + +#ifdef CONFIG_PCI + /* setup PCI host bridges */ + mpc85xx_setup_hose(); +#endif + + /* setup the board related information for the enet controllers */ + pdata = (struct gianfar_platform_data *) ppc_sys_get_pdata(MPC85xx_TSEC1); + pdata->board_flags = FSL_GIANFAR_BRD_HAS_PHY_INTR; + pdata->interruptPHY = MPC85xx_IRQ_EXT5; + pdata->phyid = 0; + /* fixup phy address */ + pdata->phy_reg_addr += binfo->bi_immr_base; + memcpy(pdata->mac_addr, binfo->bi_enetaddr, 6); + + pdata = (struct gianfar_platform_data *) ppc_sys_get_pdata(MPC85xx_TSEC2); + pdata->board_flags = FSL_GIANFAR_BRD_HAS_PHY_INTR; + pdata->interruptPHY = MPC85xx_IRQ_EXT5; + pdata->phyid = 1; + /* fixup phy address */ + pdata->phy_reg_addr += binfo->bi_immr_base; + memcpy(pdata->mac_addr, binfo->bi_enet1addr, 6); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = Root_NFS; +#else + ROOT_DEV = Root_HDA1; +#endif +} + +static irqreturn_t cpm2_cascade(int irq, void *dev_id, struct pt_regs *regs) +{ + while ((irq = cpm2_get_irq(regs)) >= 0) + __do_IRQ(irq, regs); + return IRQ_HANDLED; +} + +static struct irqaction cpm2_irqaction = { + .handler = cpm2_cascade, + .flags = SA_INTERRUPT, + .mask = CPU_MASK_NONE, + .name = "cpm2_cascade", +}; + +static void __init +mpc8560_ads_init_IRQ(void) +{ + /* Setup OpenPIC */ + mpc85xx_ads_init_IRQ(); + + /* Setup CPM2 PIC */ + cpm2_init_IRQ(); + + setup_irq(MPC85xx_IRQ_CPM, &cpm2_irqaction); + + return; +} + + + +/* ************************************************************************ */ +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + /* parse_bootinfo must always be called first */ + parse_bootinfo(find_bootinfo()); + + /* + * If we were passed in a board information, copy it into the + * residual data area. + */ + if (r3) { + memcpy((void *) __res, (void *) (r3 + KERNELBASE), + sizeof (bd_t)); + + } +#if defined(CONFIG_BLK_DEV_INITRD) + /* + * If the init RAM disk has been configured in, and there's a valid + * starting address for it, set it up. + */ + if (r4) { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + /* Copy the kernel command line arguments to a safe place. */ + + if (r6) { + *(char *) (r7 + KERNELBASE) = 0; + strcpy(cmd_line, (char *) (r6 + KERNELBASE)); + } + + identify_ppc_sys_by_id(mfspr(SPRN_SVR)); + + /* setup the PowerPC module struct */ + ppc_md.setup_arch = mpc8560ads_setup_arch; + ppc_md.show_cpuinfo = mpc85xx_ads_show_cpuinfo; + + ppc_md.init_IRQ = mpc8560_ads_init_IRQ; + ppc_md.get_irq = openpic_get_irq; + + ppc_md.restart = mpc85xx_restart; + ppc_md.power_off = mpc85xx_power_off; + ppc_md.halt = mpc85xx_halt; + + ppc_md.find_end_of_memory = mpc85xx_find_end_of_memory; + + ppc_md.time_init = NULL; + ppc_md.set_rtc_time = NULL; + ppc_md.get_rtc_time = NULL; + ppc_md.calibrate_decr = mpc85xx_calibrate_decr; + + if (ppc_md.progress) + ppc_md.progress("mpc8560ads_init(): exit", 0); + + return; +} diff --git a/arch/ppc/platforms/85xx/mpc8560_ads.h b/arch/ppc/platforms/85xx/mpc8560_ads.h new file mode 100644 index 00000000000..7df885d73e9 --- /dev/null +++ b/arch/ppc/platforms/85xx/mpc8560_ads.h @@ -0,0 +1,27 @@ +/* + * arch/ppc/platforms/mpc8560_ads.h + * + * MPC8540ADS board definitions + * + * Maintainer: Kumar Gala + * + * Copyright 2004 Freescale Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef __MACH_MPC8560ADS_H +#define __MACH_MPC8560ADS_H + +#include +#include +#include + +#define CPM_MAP_ADDR (CCSRBAR + MPC85xx_CPM_OFFSET) +#define PHY_INTERRUPT MPC85xx_IRQ_EXT7 + +#endif /* __MACH_MPC8560ADS_H */ diff --git a/arch/ppc/platforms/85xx/mpc85xx_ads_common.c b/arch/ppc/platforms/85xx/mpc85xx_ads_common.c new file mode 100644 index 00000000000..ba9f9f562c4 --- /dev/null +++ b/arch/ppc/platforms/85xx/mpc85xx_ads_common.c @@ -0,0 +1,225 @@ +/* + * arch/ppc/platforms/85xx/mpc85xx_ads_common.c + * + * MPC85xx ADS board common routines + * + * Maintainer: Kumar Gala + * + * Copyright 2004 Freescale Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#ifndef CONFIG_PCI +unsigned long isa_io_base = 0; +unsigned long isa_mem_base = 0; +#endif + +extern unsigned long total_memory; /* in mm/init */ + +unsigned char __res[sizeof (bd_t)]; + +/* Internal interrupts are all Level Sensitive, and Positive Polarity */ + +static u_char mpc85xx_ads_openpic_initsenses[] __initdata = { + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 0: L2 Cache */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 1: ECM */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 2: DDR DRAM */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 3: LBIU */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 4: DMA 0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 5: DMA 1 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 6: DMA 2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 7: DMA 3 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 8: PCI/PCI-X */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 9: RIO Inbound Port Write Error */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 10: RIO Doorbell Inbound */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 11: RIO Outbound Message */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 12: RIO Inbound Message */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 13: TSEC 0 Transmit */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 14: TSEC 0 Receive */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 15: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 16: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 17: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 18: TSEC 0 Receive/Transmit Error */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 19: TSEC 1 Transmit */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 20: TSEC 1 Receive */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 21: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 22: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 23: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 24: TSEC 1 Receive/Transmit Error */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 25: Fast Ethernet */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 26: DUART */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 27: I2C */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 28: Performance Monitor */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 29: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 30: CPM */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 31: Unused */ + 0x0, /* External 0: */ +#if defined(CONFIG_PCI) + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 1: PCI slot 0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 2: PCI slot 1 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 3: PCI slot 2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 4: PCI slot 3 */ +#else + 0x0, /* External 1: */ + 0x0, /* External 2: */ + 0x0, /* External 3: */ + 0x0, /* External 4: */ +#endif + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 5: PHY */ + 0x0, /* External 6: */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 7: PHY */ + 0x0, /* External 8: */ + 0x0, /* External 9: */ + 0x0, /* External 10: */ + 0x0, /* External 11: */ +}; + +/* ************************************************************************ */ +int +mpc85xx_ads_show_cpuinfo(struct seq_file *m) +{ + uint pvid, svid, phid1; + uint memsize = total_memory; + bd_t *binfo = (bd_t *) __res; + unsigned int freq; + + /* get the core frequency */ + freq = binfo->bi_intfreq; + + pvid = mfspr(SPRN_PVR); + svid = mfspr(SPRN_SVR); + + seq_printf(m, "Vendor\t\t: Freescale Semiconductor\n"); + seq_printf(m, "Machine\t\t: mpc%sads\n", cur_ppc_sys_spec->ppc_sys_name); + seq_printf(m, "clock\t\t: %dMHz\n", freq / 1000000); + seq_printf(m, "PVR\t\t: 0x%x\n", pvid); + seq_printf(m, "SVR\t\t: 0x%x\n", svid); + + /* Display cpu Pll setting */ + phid1 = mfspr(SPRN_HID1); + seq_printf(m, "PLL setting\t: 0x%x\n", ((phid1 >> 24) & 0x3f)); + + /* Display the amount of memory */ + seq_printf(m, "Memory\t\t: %d MB\n", memsize / (1024 * 1024)); + + return 0; +} + +void __init +mpc85xx_ads_init_IRQ(void) +{ + bd_t *binfo = (bd_t *) __res; + /* Determine the Physical Address of the OpenPIC regs */ + phys_addr_t OpenPIC_PAddr = + binfo->bi_immr_base + MPC85xx_OPENPIC_OFFSET; + OpenPIC_Addr = ioremap(OpenPIC_PAddr, MPC85xx_OPENPIC_SIZE); + OpenPIC_InitSenses = mpc85xx_ads_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof (mpc85xx_ads_openpic_initsenses); + + /* Skip reserved space and internal sources */ + openpic_set_sources(0, 32, OpenPIC_Addr + 0x10200); + /* Map PIC IRQs 0-11 */ + openpic_set_sources(32, 12, OpenPIC_Addr + 0x10000); + + /* we let openpic interrupts starting from an offset, to + * leave space for cascading interrupts underneath. + */ + openpic_init(MPC85xx_OPENPIC_IRQ_OFFSET); + + return; +} + +#ifdef CONFIG_PCI +/* + * interrupt routing + */ + +int +mpc85xx_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * This is little evil, but works around the fact + * that revA boards have IDSEL starting at 18 + * and others boards (older) start at 12 + * + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {PIRQA, PIRQB, PIRQC, PIRQD}, /* IDSEL 2 */ + {PIRQD, PIRQA, PIRQB, PIRQC}, + {PIRQC, PIRQD, PIRQA, PIRQB}, + {PIRQB, PIRQC, PIRQD, PIRQA}, /* IDSEL 5 */ + {0, 0, 0, 0}, /* -- */ + {0, 0, 0, 0}, /* -- */ + {0, 0, 0, 0}, /* -- */ + {0, 0, 0, 0}, /* -- */ + {0, 0, 0, 0}, /* -- */ + {0, 0, 0, 0}, /* -- */ + {PIRQA, PIRQB, PIRQC, PIRQD}, /* IDSEL 12 */ + {PIRQD, PIRQA, PIRQB, PIRQC}, + {PIRQC, PIRQD, PIRQA, PIRQB}, + {PIRQB, PIRQC, PIRQD, PIRQA}, /* IDSEL 15 */ + {0, 0, 0, 0}, /* -- */ + {0, 0, 0, 0}, /* -- */ + {PIRQA, PIRQB, PIRQC, PIRQD}, /* IDSEL 18 */ + {PIRQD, PIRQA, PIRQB, PIRQC}, + {PIRQC, PIRQD, PIRQA, PIRQB}, + {PIRQB, PIRQC, PIRQD, PIRQA}, /* IDSEL 21 */ + }; + + const long min_idsel = 2, max_idsel = 21, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +} + +int +mpc85xx_exclude_device(u_char bus, u_char devfn) +{ + if (bus == 0 && PCI_SLOT(devfn) == 0) + return PCIBIOS_DEVICE_NOT_FOUND; + else + return PCIBIOS_SUCCESSFUL; +} + +#endif /* CONFIG_PCI */ diff --git a/arch/ppc/platforms/85xx/mpc85xx_ads_common.h b/arch/ppc/platforms/85xx/mpc85xx_ads_common.h new file mode 100644 index 00000000000..3875e839cff --- /dev/null +++ b/arch/ppc/platforms/85xx/mpc85xx_ads_common.h @@ -0,0 +1,50 @@ +/* + * arch/ppc/platforms/85xx/mpc85xx_ads_common.h + * + * MPC85XX ADS common board definitions + * + * Maintainer: Kumar Gala + * + * Copyright 2004 Freescale Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef __MACH_MPC85XX_ADS_H__ +#define __MACH_MPC85XX_ADS_H__ + +#include +#include +#include +#include + +#define BOARD_CCSRBAR ((uint)0xe0000000) +#define BCSR_ADDR ((uint)0xf8000000) +#define BCSR_SIZE ((uint)(32 * 1024)) + +extern int mpc85xx_ads_show_cpuinfo(struct seq_file *m); +extern void mpc85xx_ads_init_IRQ(void) __init; +extern void mpc85xx_ads_map_io(void) __init; + +/* PCI interrupt controller */ +#define PIRQA MPC85xx_IRQ_EXT1 +#define PIRQB MPC85xx_IRQ_EXT2 +#define PIRQC MPC85xx_IRQ_EXT3 +#define PIRQD MPC85xx_IRQ_EXT4 + +#define MPC85XX_PCI1_LOWER_IO 0x00000000 +#define MPC85XX_PCI1_UPPER_IO 0x00ffffff + +#define MPC85XX_PCI1_LOWER_MEM 0x80000000 +#define MPC85XX_PCI1_UPPER_MEM 0x9fffffff + +#define MPC85XX_PCI1_IO_BASE 0xe2000000 +#define MPC85XX_PCI1_MEM_OFFSET 0x00000000 + +#define MPC85XX_PCI1_IO_SIZE 0x01000000 + +#endif /* __MACH_MPC85XX_ADS_H__ */ diff --git a/arch/ppc/platforms/85xx/mpc85xx_cds_common.c b/arch/ppc/platforms/85xx/mpc85xx_cds_common.c new file mode 100644 index 00000000000..6c020d67ad7 --- /dev/null +++ b/arch/ppc/platforms/85xx/mpc85xx_cds_common.c @@ -0,0 +1,467 @@ +/* + * arch/ppc/platform/85xx/mpc85xx_cds_common.c + * + * MPC85xx CDS board specific routines + * + * Maintainer: Kumar Gala + * + * Copyright 2004 Freescale Semiconductor, Inc + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +#ifndef CONFIG_PCI +unsigned long isa_io_base = 0; +unsigned long isa_mem_base = 0; +#endif + +extern unsigned long total_memory; /* in mm/init */ + +unsigned char __res[sizeof (bd_t)]; + +static int cds_pci_slot = 2; +static volatile u8 * cadmus; + +/* Internal interrupts are all Level Sensitive, and Positive Polarity */ + +static u_char mpc85xx_cds_openpic_initsenses[] __initdata = { + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 0: L2 Cache */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 1: ECM */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 2: DDR DRAM */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 3: LBIU */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 4: DMA 0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 5: DMA 1 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 6: DMA 2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 7: DMA 3 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 8: PCI/PCI-X */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 9: RIO Inbound Port Write Error */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 10: RIO Doorbell Inbound */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 11: RIO Outbound Message */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 12: RIO Inbound Message */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 13: TSEC 0 Transmit */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 14: TSEC 0 Receive */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 15: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 16: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 17: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 18: TSEC 0 Receive/Transmit Error */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 19: TSEC 1 Transmit */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 20: TSEC 1 Receive */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 21: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 22: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 23: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 24: TSEC 1 Receive/Transmit Error */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 25: Fast Ethernet */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 26: DUART */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 27: I2C */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 28: Performance Monitor */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 29: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 30: CPM */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 31: Unused */ +#if defined(CONFIG_PCI) + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 0: PCI1 slot */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 1: PCI1 slot */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 2: PCI1 slot */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 3: PCI1 slot */ +#else + 0x0, /* External 0: */ + 0x0, /* External 1: */ + 0x0, /* External 2: */ + 0x0, /* External 3: */ +#endif + 0x0, /* External 4: */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 5: PHY */ + 0x0, /* External 6: */ + 0x0, /* External 7: */ + 0x0, /* External 8: */ + 0x0, /* External 9: */ + 0x0, /* External 10: */ +#if defined(CONFIG_85xx_PCI2) && defined(CONFIG_PCI) + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 11: PCI2 slot 0 */ +#else + 0x0, /* External 11: */ +#endif +}; + +/* ************************************************************************ */ +int +mpc85xx_cds_show_cpuinfo(struct seq_file *m) +{ + uint pvid, svid, phid1; + uint memsize = total_memory; + bd_t *binfo = (bd_t *) __res; + unsigned int freq; + + /* get the core frequency */ + freq = binfo->bi_intfreq; + + pvid = mfspr(SPRN_PVR); + svid = mfspr(SPRN_SVR); + + seq_printf(m, "Vendor\t\t: Freescale Semiconductor\n"); + seq_printf(m, "Machine\t\t: CDS - MPC%s (%x)\n", cur_ppc_sys_spec->ppc_sys_name, cadmus[CM_VER]); + seq_printf(m, "clock\t\t: %dMHz\n", freq / 1000000); + seq_printf(m, "PVR\t\t: 0x%x\n", pvid); + seq_printf(m, "SVR\t\t: 0x%x\n", svid); + + /* Display cpu Pll setting */ + phid1 = mfspr(SPRN_HID1); + seq_printf(m, "PLL setting\t: 0x%x\n", ((phid1 >> 24) & 0x3f)); + + /* Display the amount of memory */ + seq_printf(m, "Memory\t\t: %d MB\n", memsize / (1024 * 1024)); + + return 0; +} + +#ifdef CONFIG_CPM2 +static void cpm2_cascade(int irq, void *dev_id, struct pt_regs *regs) +{ + while((irq = cpm2_get_irq(regs)) >= 0) + __do_IRQ(irq, regs); +} + +static struct irqaction cpm2_irqaction = { + .handler = cpm2_cascade, + .flags = SA_INTERRUPT, + .mask = CPU_MASK_NONE, + .name = "cpm2_cascade", +}; +#endif /* CONFIG_CPM2 */ + +void __init +mpc85xx_cds_init_IRQ(void) +{ + bd_t *binfo = (bd_t *) __res; + + /* Determine the Physical Address of the OpenPIC regs */ + phys_addr_t OpenPIC_PAddr = binfo->bi_immr_base + MPC85xx_OPENPIC_OFFSET; + OpenPIC_Addr = ioremap(OpenPIC_PAddr, MPC85xx_OPENPIC_SIZE); + OpenPIC_InitSenses = mpc85xx_cds_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof (mpc85xx_cds_openpic_initsenses); + + /* Skip reserved space and internal sources */ + openpic_set_sources(0, 32, OpenPIC_Addr + 0x10200); + /* Map PIC IRQs 0-11 */ + openpic_set_sources(32, 12, OpenPIC_Addr + 0x10000); + + /* we let openpic interrupts starting from an offset, to + * leave space for cascading interrupts underneath. + */ + openpic_init(MPC85xx_OPENPIC_IRQ_OFFSET); + +#ifdef CONFIG_CPM2 + /* Setup CPM2 PIC */ + cpm2_init_IRQ(); + + setup_irq(MPC85xx_IRQ_CPM, &cpm2_irqaction); +#endif + + return; +} + +#ifdef CONFIG_PCI +/* + * interrupt routing + */ +int +mpc85xx_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + struct pci_controller *hose = pci_bus_to_hose(dev->bus->number); + + if (!hose->index) + { + /* Handle PCI1 interrupts */ + char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + + /* Note IRQ assignment for slots is based on which slot the elysium is + * in -- in this setup elysium is in slot #2 (this PIRQA as first + * interrupt on slot */ + { + { 0, 1, 2, 3 }, /* 16 - PMC */ + { 3, 0, 0, 0 }, /* 17 P2P (Tsi320) */ + { 0, 1, 2, 3 }, /* 18 - Slot 1 */ + { 1, 2, 3, 0 }, /* 19 - Slot 2 */ + { 2, 3, 0, 1 }, /* 20 - Slot 3 */ + { 3, 0, 1, 2 }, /* 21 - Slot 4 */ + }; + + const long min_idsel = 16, max_idsel = 21, irqs_per_slot = 4; + int i, j; + + for (i = 0; i < 6; i++) + for (j = 0; j < 4; j++) + pci_irq_table[i][j] = + ((pci_irq_table[i][j] + 5 - + cds_pci_slot) & 0x3) + PIRQ0A; + + return PCI_IRQ_TABLE_LOOKUP; + } else { + /* Handle PCI2 interrupts (if we have one) */ + char pci_irq_table[][4] = + { + /* + * We only have one slot and one interrupt + * going to PIRQA - PIRQD */ + { PIRQ1A, PIRQ1A, PIRQ1A, PIRQ1A }, /* 21 - slot 0 */ + }; + + const long min_idsel = 21, max_idsel = 21, irqs_per_slot = 4; + + return PCI_IRQ_TABLE_LOOKUP; + } +} + +#define ARCADIA_HOST_BRIDGE_IDSEL 17 +#define ARCADIA_2ND_BRIDGE_IDSEL 3 + +extern int mpc85xx_pci1_last_busno; + +int +mpc85xx_exclude_device(u_char bus, u_char devfn) +{ + if (bus == 0 && PCI_SLOT(devfn) == 0) + return PCIBIOS_DEVICE_NOT_FOUND; +#ifdef CONFIG_85xx_PCI2 + if (mpc85xx_pci1_last_busno) + if (bus == (mpc85xx_pci1_last_busno + 1) && PCI_SLOT(devfn) == 0) + return PCIBIOS_DEVICE_NOT_FOUND; +#endif + /* We explicitly do not go past the Tundra 320 Bridge */ + if (bus == 1) + return PCIBIOS_DEVICE_NOT_FOUND; + if ((bus == 0) && (PCI_SLOT(devfn) == ARCADIA_2ND_BRIDGE_IDSEL)) + return PCIBIOS_DEVICE_NOT_FOUND; + else + return PCIBIOS_SUCCESSFUL; +} +#endif /* CONFIG_PCI */ + +TODC_ALLOC(); + +/* ************************************************************************ + * + * Setup the architecture + * + */ +static void __init +mpc85xx_cds_setup_arch(void) +{ + bd_t *binfo = (bd_t *) __res; + unsigned int freq; + struct gianfar_platform_data *pdata; + + /* get the core frequency */ + freq = binfo->bi_intfreq; + + printk("mpc85xx_cds_setup_arch\n"); + +#ifdef CONFIG_CPM2 + cpm2_reset(); +#endif + + cadmus = ioremap(CADMUS_BASE, CADMUS_SIZE); + cds_pci_slot = ((cadmus[CM_CSR] >> 6) & 0x3) + 1; + printk("CDS Version = %x in PCI slot %d\n", cadmus[CM_VER], cds_pci_slot); + + /* Setup TODC access */ + TODC_INIT(TODC_TYPE_DS1743, + 0, + 0, + ioremap(CDS_RTC_ADDR, CDS_RTC_SIZE), + 8); + + /* Set loops_per_jiffy to a half-way reasonable value, + for use until calibrate_delay gets called. */ + loops_per_jiffy = freq / HZ; + +#ifdef CONFIG_PCI + /* setup PCI host bridges */ + mpc85xx_setup_hose(); +#endif + +#ifdef CONFIG_SERIAL_8250 + mpc85xx_early_serial_map(); +#endif + +#ifdef CONFIG_SERIAL_TEXT_DEBUG + /* Invalidate the entry we stole earlier the serial ports + * should be properly mapped */ + invalidate_tlbcam_entry(NUM_TLBCAMS - 1); +#endif + + /* setup the board related information for the enet controllers */ + pdata = (struct gianfar_platform_data *) ppc_sys_get_pdata(MPC85xx_TSEC1); + pdata->board_flags = FSL_GIANFAR_BRD_HAS_PHY_INTR; + pdata->interruptPHY = MPC85xx_IRQ_EXT5; + pdata->phyid = 0; + /* fixup phy address */ + pdata->phy_reg_addr += binfo->bi_immr_base; + memcpy(pdata->mac_addr, binfo->bi_enetaddr, 6); + + pdata = (struct gianfar_platform_data *) ppc_sys_get_pdata(MPC85xx_TSEC2); + pdata->board_flags = FSL_GIANFAR_BRD_HAS_PHY_INTR; + pdata->interruptPHY = MPC85xx_IRQ_EXT5; + pdata->phyid = 1; + /* fixup phy address */ + pdata->phy_reg_addr += binfo->bi_immr_base; + memcpy(pdata->mac_addr, binfo->bi_enet1addr, 6); + + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = Root_NFS; +#else + ROOT_DEV = Root_HDA1; +#endif +} + +/* ************************************************************************ */ +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + /* parse_bootinfo must always be called first */ + parse_bootinfo(find_bootinfo()); + + /* + * If we were passed in a board information, copy it into the + * residual data area. + */ + if (r3) { + memcpy((void *) __res, (void *) (r3 + KERNELBASE), + sizeof (bd_t)); + + } +#ifdef CONFIG_SERIAL_TEXT_DEBUG + { + bd_t *binfo = (bd_t *) __res; + struct uart_port p; + + /* Use the last TLB entry to map CCSRBAR to allow access to DUART regs */ + settlbcam(NUM_TLBCAMS - 1, binfo->bi_immr_base, + binfo->bi_immr_base, MPC85xx_CCSRBAR_SIZE, _PAGE_IO, 0); + + memset(&p, 0, sizeof (p)); + p.iotype = SERIAL_IO_MEM; + p.membase = (void *) binfo->bi_immr_base + MPC85xx_UART0_OFFSET; + p.uartclk = binfo->bi_busfreq; + + gen550_init(0, &p); + + memset(&p, 0, sizeof (p)); + p.iotype = SERIAL_IO_MEM; + p.membase = (void *) binfo->bi_immr_base + MPC85xx_UART1_OFFSET; + p.uartclk = binfo->bi_busfreq; + + gen550_init(1, &p); + } +#endif + +#if defined(CONFIG_BLK_DEV_INITRD) + /* + * If the init RAM disk has been configured in, and there's a valid + * starting address for it, set it up. + */ + if (r4) { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + /* Copy the kernel command line arguments to a safe place. */ + + if (r6) { + *(char *) (r7 + KERNELBASE) = 0; + strcpy(cmd_line, (char *) (r6 + KERNELBASE)); + } + + identify_ppc_sys_by_id(mfspr(SPRN_SVR)); + + /* setup the PowerPC module struct */ + ppc_md.setup_arch = mpc85xx_cds_setup_arch; + ppc_md.show_cpuinfo = mpc85xx_cds_show_cpuinfo; + + ppc_md.init_IRQ = mpc85xx_cds_init_IRQ; + ppc_md.get_irq = openpic_get_irq; + + ppc_md.restart = mpc85xx_restart; + ppc_md.power_off = mpc85xx_power_off; + ppc_md.halt = mpc85xx_halt; + + ppc_md.find_end_of_memory = mpc85xx_find_end_of_memory; + + ppc_md.calibrate_decr = mpc85xx_calibrate_decr; + + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; + +#if defined(CONFIG_SERIAL_8250) && defined(CONFIG_SERIAL_TEXT_DEBUG) + ppc_md.progress = gen550_progress; +#endif /* CONFIG_SERIAL_8250 && CONFIG_SERIAL_TEXT_DEBUG */ + + if (ppc_md.progress) + ppc_md.progress("mpc85xx_cds_init(): exit", 0); + + return; +} diff --git a/arch/ppc/platforms/85xx/mpc85xx_cds_common.h b/arch/ppc/platforms/85xx/mpc85xx_cds_common.h new file mode 100644 index 00000000000..7627d77504b --- /dev/null +++ b/arch/ppc/platforms/85xx/mpc85xx_cds_common.h @@ -0,0 +1,80 @@ +/* + * arch/ppc/platforms/85xx/mpc85xx_cds_common.h + * + * MPC85xx CDS board definitions + * + * Maintainer: Kumar Gala + * + * Copyright 2004 Freescale Semiconductor, Inc + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef __MACH_MPC85XX_CDS_H__ +#define __MACH_MPC85XX_CDS_H__ + +#include +#include +#include +#include +#include + +#define BOARD_CCSRBAR ((uint)0xe0000000) +#define CCSRBAR_SIZE ((uint)1024*1024) + +/* CADMUS info */ +#define CADMUS_BASE (0xf8004000) +#define CADMUS_SIZE (256) +#define CM_VER (0) +#define CM_CSR (1) +#define CM_RST (2) + +/* CDS NVRAM/RTC */ +#define CDS_RTC_ADDR (0xf8000000) +#define CDS_RTC_SIZE (8 * 1024) + +/* PCI config */ +#define PCI1_CFG_ADDR_OFFSET (0x8000) +#define PCI1_CFG_DATA_OFFSET (0x8004) + +#define PCI2_CFG_ADDR_OFFSET (0x9000) +#define PCI2_CFG_DATA_OFFSET (0x9004) + +/* PCI interrupt controller */ +#define PIRQ0A MPC85xx_IRQ_EXT0 +#define PIRQ0B MPC85xx_IRQ_EXT1 +#define PIRQ0C MPC85xx_IRQ_EXT2 +#define PIRQ0D MPC85xx_IRQ_EXT3 +#define PIRQ1A MPC85xx_IRQ_EXT11 + +/* PCI 1 memory map */ +#define MPC85XX_PCI1_LOWER_IO 0x00000000 +#define MPC85XX_PCI1_UPPER_IO 0x00ffffff + +#define MPC85XX_PCI1_LOWER_MEM 0x80000000 +#define MPC85XX_PCI1_UPPER_MEM 0x9fffffff + +#define MPC85XX_PCI1_IO_BASE 0xe2000000 +#define MPC85XX_PCI1_MEM_OFFSET 0x00000000 + +#define MPC85XX_PCI1_IO_SIZE 0x01000000 + +/* PCI 2 memory map */ +/* Note: the standard PPC fixups will cause IO space to get bumped by + * hose->io_base_virt - isa_io_base => MPC85XX_PCI1_IO_SIZE */ +#define MPC85XX_PCI2_LOWER_IO 0x00000000 +#define MPC85XX_PCI2_UPPER_IO 0x00ffffff + +#define MPC85XX_PCI2_LOWER_MEM 0xa0000000 +#define MPC85XX_PCI2_UPPER_MEM 0xbfffffff + +#define MPC85XX_PCI2_IO_BASE 0xe3000000 +#define MPC85XX_PCI2_MEM_OFFSET 0x00000000 + +#define MPC85XX_PCI2_IO_SIZE 0x01000000 + +#endif /* __MACH_MPC85XX_CDS_H__ */ diff --git a/arch/ppc/platforms/85xx/sbc8560.c b/arch/ppc/platforms/85xx/sbc8560.c new file mode 100644 index 00000000000..9ab05e590c3 --- /dev/null +++ b/arch/ppc/platforms/85xx/sbc8560.c @@ -0,0 +1,227 @@ +/* + * arch/ppc/platforms/85xx/sbc8560.c + * + * Wind River SBC8560 board specific routines + * + * Maintainer: Kumar Gala + * + * Copyright 2004 Freescale Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for linux/serial_core.h */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef CONFIG_SERIAL_8250 +static void __init +sbc8560_early_serial_map(void) +{ + struct uart_port uart_req; + + /* Setup serial port access */ + memset(&uart_req, 0, sizeof (uart_req)); + uart_req.irq = MPC85xx_IRQ_EXT9; + uart_req.flags = STD_COM_FLAGS; + uart_req.uartclk = BASE_BAUD * 16; + uart_req.iotype = SERIAL_IO_MEM; + uart_req.mapbase = UARTA_ADDR; + uart_req.membase = ioremap(uart_req.mapbase, MPC85xx_UART0_SIZE); + uart_req.type = PORT_16650; + +#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) + gen550_init(0, &uart_req); +#endif + + if (early_serial_setup(&uart_req) != 0) + printk("Early serial init of port 0 failed\n"); + + /* Assume early_serial_setup() doesn't modify uart_req */ + uart_req.line = 1; + uart_req.mapbase = UARTB_ADDR; + uart_req.membase = ioremap(uart_req.mapbase, MPC85xx_UART1_SIZE); + uart_req.irq = MPC85xx_IRQ_EXT10; + +#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) + gen550_init(1, &uart_req); +#endif + + if (early_serial_setup(&uart_req) != 0) + printk("Early serial init of port 1 failed\n"); +} +#endif + +/* ************************************************************************ + * + * Setup the architecture + * + */ +static void __init +sbc8560_setup_arch(void) +{ + bd_t *binfo = (bd_t *) __res; + unsigned int freq; + struct gianfar_platform_data *pdata; + + /* get the core frequency */ + freq = binfo->bi_intfreq; + + if (ppc_md.progress) + ppc_md.progress("sbc8560_setup_arch()", 0); + + /* Set loops_per_jiffy to a half-way reasonable value, + for use until calibrate_delay gets called. */ + loops_per_jiffy = freq / HZ; + +#ifdef CONFIG_PCI + /* setup PCI host bridges */ + mpc85xx_setup_hose(); +#endif +#ifdef CONFIG_SERIAL_8250 + sbc8560_early_serial_map(); +#endif +#ifdef CONFIG_SERIAL_TEXT_DEBUG + /* Invalidate the entry we stole earlier the serial ports + * should be properly mapped */ + invalidate_tlbcam_entry(NUM_TLBCAMS - 1); +#endif + + /* setup the board related information for the enet controllers */ + pdata = (struct gianfar_platform_data *) ppc_sys_get_pdata(MPC85xx_TSEC1); + pdata->board_flags = FSL_GIANFAR_BRD_HAS_PHY_INTR; + pdata->interruptPHY = MPC85xx_IRQ_EXT6; + pdata->phyid = 25; + /* fixup phy address */ + pdata->phy_reg_addr += binfo->bi_immr_base; + memcpy(pdata->mac_addr, binfo->bi_enetaddr, 6); + + pdata = (struct gianfar_platform_data *) ppc_sys_get_pdata(MPC85xx_TSEC2); + pdata->board_flags = FSL_GIANFAR_BRD_HAS_PHY_INTR; + pdata->interruptPHY = MPC85xx_IRQ_EXT7; + pdata->phyid = 26; + /* fixup phy address */ + pdata->phy_reg_addr += binfo->bi_immr_base; + memcpy(pdata->mac_addr, binfo->bi_enet1addr, 6); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = Root_NFS; +#else + ROOT_DEV = Root_HDA1; +#endif +} + +/* ************************************************************************ */ +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + /* parse_bootinfo must always be called first */ + parse_bootinfo(find_bootinfo()); + + /* + * If we were passed in a board information, copy it into the + * residual data area. + */ + if (r3) { + memcpy((void *) __res, (void *) (r3 + KERNELBASE), + sizeof (bd_t)); + } + +#ifdef CONFIG_SERIAL_TEXT_DEBUG + /* Use the last TLB entry to map CCSRBAR to allow access to DUART regs */ + settlbcam(NUM_TLBCAMS - 1, UARTA_ADDR, + UARTA_ADDR, 0x1000, _PAGE_IO, 0); +#endif + +#if defined(CONFIG_BLK_DEV_INITRD) + /* + * If the init RAM disk has been configured in, and there's a valid + * starting address for it, set it up. + */ + if (r4) { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + /* Copy the kernel command line arguments to a safe place. */ + + if (r6) { + *(char *) (r7 + KERNELBASE) = 0; + strcpy(cmd_line, (char *) (r6 + KERNELBASE)); + } + + identify_ppc_sys_by_id(mfspr(SPRN_SVR)); + + /* setup the PowerPC module struct */ + ppc_md.setup_arch = sbc8560_setup_arch; + ppc_md.show_cpuinfo = sbc8560_show_cpuinfo; + + ppc_md.init_IRQ = sbc8560_init_IRQ; + ppc_md.get_irq = openpic_get_irq; + + ppc_md.restart = mpc85xx_restart; + ppc_md.power_off = mpc85xx_power_off; + ppc_md.halt = mpc85xx_halt; + + ppc_md.find_end_of_memory = mpc85xx_find_end_of_memory; + + ppc_md.time_init = NULL; + ppc_md.set_rtc_time = NULL; + ppc_md.get_rtc_time = NULL; + ppc_md.calibrate_decr = mpc85xx_calibrate_decr; + +#if defined(CONFIG_SERIAL_8250) && defined(CONFIG_SERIAL_TEXT_DEBUG) + ppc_md.progress = gen550_progress; +#endif /* CONFIG_SERIAL_8250 && CONFIG_SERIAL_TEXT_DEBUG */ + + if (ppc_md.progress) + ppc_md.progress("sbc8560_init(): exit", 0); +} diff --git a/arch/ppc/platforms/85xx/sbc8560.h b/arch/ppc/platforms/85xx/sbc8560.h new file mode 100644 index 00000000000..5e1b00c77da --- /dev/null +++ b/arch/ppc/platforms/85xx/sbc8560.h @@ -0,0 +1,49 @@ +/* + * arch/ppc/platforms/85xx/sbc8560.h + * + * Wind River SBC8560 board definitions + * + * Copyright 2003 Motorola Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef __MACH_SBC8560_H__ +#define __MACH_SBC8560_H__ + +#include +#include + +#define CPM_MAP_ADDR (CCSRBAR + MPC85xx_CPM_OFFSET) + +#ifdef CONFIG_SERIAL_MANY_PORTS +#define RS_TABLE_SIZE 64 +#else +#define RS_TABLE_SIZE 2 +#endif + +/* Rate for the 1.8432 Mhz clock for the onboard serial chip */ +#define BASE_BAUD ( 1843200 / 16 ) + +#ifdef CONFIG_SERIAL_DETECT_IRQ +#define STD_COM_FLAGS (ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) +#else +#define STD_COM_FLAGS (ASYNC_SKIP_TEST) +#endif + +#define STD_SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, UARTA_ADDR, MPC85xx_IRQ_EXT9, STD_COM_FLAGS, /* ttyS0 */ \ + iomem_base: (u8 *)UARTA_ADDR, \ + io_type: SERIAL_IO_MEM }, \ + { 0, BASE_BAUD, UARTB_ADDR, MPC85xx_IRQ_EXT10, STD_COM_FLAGS, /* ttyS1 */ \ + iomem_base: (u8 *)UARTB_ADDR, \ + io_type: SERIAL_IO_MEM }, + +#define SERIAL_PORT_DFNS \ + STD_SERIAL_PORT_DFNS + +#endif /* __MACH_SBC8560_H__ */ diff --git a/arch/ppc/platforms/85xx/sbc85xx.c b/arch/ppc/platforms/85xx/sbc85xx.c new file mode 100644 index 00000000000..2d638c1c1bd --- /dev/null +++ b/arch/ppc/platforms/85xx/sbc85xx.c @@ -0,0 +1,203 @@ +/* + * arch/ppc/platform/85xx/sbc85xx.c + * + * WindRiver PowerQUICC III SBC85xx board common routines + * + * Copyright 2002, 2003 Motorola Inc. + * Copyright 2004 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +unsigned char __res[sizeof (bd_t)]; + +#ifndef CONFIG_PCI +unsigned long isa_io_base = 0; +unsigned long isa_mem_base = 0; +unsigned long pci_dram_offset = 0; +#endif + +extern unsigned long total_memory; /* in mm/init */ + +/* Internal interrupts are all Level Sensitive, and Positive Polarity */ + +static u_char sbc8560_openpic_initsenses[] __initdata = { + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 0: L2 Cache */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 1: ECM */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 2: DDR DRAM */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 3: LBIU */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 4: DMA 0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 5: DMA 1 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 6: DMA 2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 7: DMA 3 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 8: PCI/PCI-X */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 9: RIO Inbound Port Write Error */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 10: RIO Doorbell Inbound */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 11: RIO Outbound Message */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 12: RIO Inbound Message */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 13: TSEC 0 Transmit */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 14: TSEC 0 Receive */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 15: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 16: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 17: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 18: TSEC 0 Receive/Transmit Error */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 19: TSEC 1 Transmit */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 20: TSEC 1 Receive */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 21: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 22: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 23: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 24: TSEC 1 Receive/Transmit Error */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 25: Fast Ethernet */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 26: DUART */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 27: I2C */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 28: Performance Monitor */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 29: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 30: CPM */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 31: Unused */ + 0x0, /* External 0: */ + 0x0, /* External 1: */ +#if defined(CONFIG_PCI) + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 2: PCI slot 0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 3: PCI slot 1 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 4: PCI slot 2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 5: PCI slot 3 */ +#else + 0x0, /* External 2: */ + 0x0, /* External 3: */ + 0x0, /* External 4: */ + 0x0, /* External 5: */ +#endif + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 6: PHY */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 7: PHY */ + 0x0, /* External 8: */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* External 9: PHY */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* External 10: PHY */ + 0x0, /* External 11: */ +}; + +/* ************************************************************************ */ +int +sbc8560_show_cpuinfo(struct seq_file *m) +{ + uint pvid, svid, phid1; + uint memsize = total_memory; + bd_t *binfo = (bd_t *) __res; + unsigned int freq; + + /* get the core frequency */ + freq = binfo->bi_intfreq; + + pvid = mfspr(SPRN_PVR); + svid = mfspr(SPRN_SVR); + + seq_printf(m, "Vendor\t\t: Wind River\n"); + seq_printf(m, "Machine\t\t: SBC%s\n", cur_ppc_sys_spec->ppc_sys_name); + seq_printf(m, "clock\t\t: %dMHz\n", freq / 1000000); + seq_printf(m, "PVR\t\t: 0x%x\n", pvid); + seq_printf(m, "SVR\t\t: 0x%x\n", svid); + + /* Display cpu Pll setting */ + phid1 = mfspr(SPRN_HID1); + seq_printf(m, "PLL setting\t: 0x%x\n", ((phid1 >> 24) & 0x3f)); + + /* Display the amount of memory */ + seq_printf(m, "Memory\t\t: %d MB\n", memsize / (1024 * 1024)); + + return 0; +} + +void __init +sbc8560_init_IRQ(void) +{ + bd_t *binfo = (bd_t *) __res; + /* Determine the Physical Address of the OpenPIC regs */ + phys_addr_t OpenPIC_PAddr = + binfo->bi_immr_base + MPC85xx_OPENPIC_OFFSET; + OpenPIC_Addr = ioremap(OpenPIC_PAddr, MPC85xx_OPENPIC_SIZE); + OpenPIC_InitSenses = sbc8560_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof (sbc8560_openpic_initsenses); + + /* Skip reserved space and internal sources */ + openpic_set_sources(0, 32, OpenPIC_Addr + 0x10200); + /* Map PIC IRQs 0-11 */ + openpic_set_sources(32, 12, OpenPIC_Addr + 0x10000); + + /* we let openpic interrupts starting from an offset, to + * leave space for cascading interrupts underneath. + */ + openpic_init(MPC85xx_OPENPIC_IRQ_OFFSET); + + return; +} + +/* + * interrupt routing + */ + +#ifdef CONFIG_PCI +int mpc85xx_map_irq(struct pci_dev *dev, unsigned char idsel, + unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {PIRQA, PIRQB, PIRQC, PIRQD}, + {PIRQD, PIRQA, PIRQB, PIRQC}, + {PIRQC, PIRQD, PIRQA, PIRQB}, + {PIRQB, PIRQC, PIRQD, PIRQA}, + }; + + const long min_idsel = 12, max_idsel = 15, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +} + +int mpc85xx_exclude_device(u_char bus, u_char devfn) +{ + if (bus == 0 && PCI_SLOT(devfn) == 0) + return PCIBIOS_DEVICE_NOT_FOUND; + else + return PCIBIOS_SUCCESSFUL; +} +#endif /* CONFIG_PCI */ diff --git a/arch/ppc/platforms/85xx/sbc85xx.h b/arch/ppc/platforms/85xx/sbc85xx.h new file mode 100644 index 00000000000..7af93c691a6 --- /dev/null +++ b/arch/ppc/platforms/85xx/sbc85xx.h @@ -0,0 +1,55 @@ +/* + * arch/ppc/platforms/85xx/sbc85xx.h + * + * WindRiver PowerQUICC III SBC85xx common board definitions + * + * Copyright 2003 Motorola Inc. + * Copyright 2004 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef __PLATFORMS_85XX_SBC85XX_H__ +#define __PLATFORMS_85XX_SBC85XX_H__ + +#include +#include +#include +#include + +#define BOARD_CCSRBAR ((uint)0xff700000) +#define CCSRBAR_SIZE ((uint)1024*1024) + +#define BCSR_ADDR ((uint)0xfc000000) +#define BCSR_SIZE ((uint)(16 * 1024 * 1024)) + +#define UARTA_ADDR (BCSR_ADDR + 0x00700000) +#define UARTB_ADDR (BCSR_ADDR + 0x00800000) +#define RTC_DEVICE_ADDR (BCSR_ADDR + 0x00900000) +#define EEPROM_ADDR (BCSR_ADDR + 0x00b00000) + +extern int sbc8560_show_cpuinfo(struct seq_file *m); +extern void sbc8560_init_IRQ(void) __init; + +/* PCI interrupt controller */ +#define PIRQA MPC85xx_IRQ_EXT1 +#define PIRQB MPC85xx_IRQ_EXT2 +#define PIRQC MPC85xx_IRQ_EXT3 +#define PIRQD MPC85xx_IRQ_EXT4 + +#define MPC85XX_PCI1_LOWER_IO 0x00000000 +#define MPC85XX_PCI1_UPPER_IO 0x00ffffff + +#define MPC85XX_PCI1_LOWER_MEM 0x80000000 +#define MPC85XX_PCI1_UPPER_MEM 0x9fffffff + +#define MPC85XX_PCI1_IO_BASE 0xe2000000 +#define MPC85XX_PCI1_MEM_OFFSET 0x00000000 + +#define MPC85XX_PCI1_IO_SIZE 0x01000000 + +#endif /* __PLATFORMS_85XX_SBC85XX_H__ */ diff --git a/arch/ppc/platforms/85xx/stx_gp3.c b/arch/ppc/platforms/85xx/stx_gp3.c new file mode 100644 index 00000000000..bc95836e417 --- /dev/null +++ b/arch/ppc/platforms/85xx/stx_gp3.c @@ -0,0 +1,355 @@ +/* + * arch/ppc/platforms/85xx/stx_gp3.c + * + * STx GP3 board specific routines + * + * Dan Malek + * Copyright 2004 Embedded Edge, LLC + * + * Copied from mpc8560_ads.c + * Copyright 2002, 2003 Motorola Inc. + * + * Ported to 2.6, Matt Porter + * Copyright 2004-2005 MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +extern void cpm2_reset(void); + +unsigned char __res[sizeof(bd_t)]; + +#ifndef CONFIG_PCI +unsigned long isa_io_base = 0; +unsigned long isa_mem_base = 0; +unsigned long pci_dram_offset = 0; +#endif + +/* Internal interrupts are all Level Sensitive, and Positive Polarity */ +static u8 gp3_openpic_initsenses[] __initdata = { + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 0: L2 Cache */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 1: ECM */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 2: DDR DRAM */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 3: LBIU */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 4: DMA 0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 5: DMA 1 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 6: DMA 2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 7: DMA 3 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 8: PCI/PCI-X */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 9: RIO Inbound Port Write Error */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 10: RIO Doorbell Inbound */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 11: RIO Outbound Message */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 12: RIO Inbound Message */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 13: TSEC 0 Transmit */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 14: TSEC 0 Receive */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 15: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 16: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 17: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 18: TSEC 0 Receive/Transmit Error */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 19: TSEC 1 Transmit */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 20: TSEC 1 Receive */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 21: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 22: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 23: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 24: TSEC 1 Receive/Transmit Error */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 25: Fast Ethernet */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 26: DUART */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 27: I2C */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 28: Performance Monitor */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 29: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 30: CPM */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 31: Unused */ + 0x0, /* External 0: */ +#if defined(CONFIG_PCI) + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 1: PCI slot 0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 2: PCI slot 1 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 3: PCI slot 2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 4: PCI slot 3 */ +#else + 0x0, /* External 1: */ + 0x0, /* External 2: */ + 0x0, /* External 3: */ + 0x0, /* External 4: */ +#endif + 0x0, /* External 5: */ + 0x0, /* External 6: */ + 0x0, /* External 7: */ + 0x0, /* External 8: */ + 0x0, /* External 9: */ + 0x0, /* External 10: */ + 0x0, /* External 11: */ +}; + +/* + * Setup the architecture + */ +static void __init +gp3_setup_arch(void) +{ + bd_t *binfo = (bd_t *) __res; + unsigned int freq; + struct gianfar_platform_data *pdata; + + cpm2_reset(); + + /* get the core frequency */ + freq = binfo->bi_intfreq; + + if (ppc_md.progress) + ppc_md.progress("gp3_setup_arch()", 0); + + /* Set loops_per_jiffy to a half-way reasonable value, + for use until calibrate_delay gets called. */ + loops_per_jiffy = freq / HZ; + +#ifdef CONFIG_PCI + /* setup PCI host bridges */ + mpc85xx_setup_hose(); +#endif + + /* setup the board related information for the enet controllers */ + pdata = (struct gianfar_platform_data *) ppc_sys_get_pdata(MPC85xx_TSEC1); +/* pdata->board_flags = FSL_GIANFAR_BRD_HAS_PHY_INTR; */ + pdata->interruptPHY = MPC85xx_IRQ_EXT5; + pdata->phyid = 2; + pdata->phy_reg_addr += binfo->bi_immr_base; + memcpy(pdata->mac_addr, binfo->bi_enetaddr, 6); + + pdata = (struct gianfar_platform_data *) ppc_sys_get_pdata(MPC85xx_TSEC2); +/* pdata->board_flags = FSL_GIANFAR_BRD_HAS_PHY_INTR; */ + pdata->interruptPHY = MPC85xx_IRQ_EXT5; + pdata->phyid = 4; + /* fixup phy address */ + pdata->phy_reg_addr += binfo->bi_immr_base; + memcpy(pdata->mac_addr, binfo->bi_enet1addr, 6); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = Root_NFS; +#else + ROOT_DEV = Root_HDA1; +#endif + + printk ("bi_immr_base = %8.8lx\n", binfo->bi_immr_base); +} + +static irqreturn_t cpm2_cascade(int irq, void *dev_id, struct pt_regs *regs) +{ + while ((irq = cpm2_get_irq(regs)) >= 0) + __do_IRQ(irq, regs); + + return IRQ_HANDLED; +} + +static struct irqaction cpm2_irqaction = { + .handler = cpm2_cascade, + .flags = SA_INTERRUPT, + .mask = CPU_MASK_NONE, + .name = "cpm2_cascade", +}; + +static void __init +gp3_init_IRQ(void) +{ + int i; + bd_t *binfo = (bd_t *) __res; + + /* + * Setup OpenPIC + */ + + /* Determine the Physical Address of the OpenPIC regs */ + phys_addr_t OpenPIC_PAddr = + binfo->bi_immr_base + MPC85xx_OPENPIC_OFFSET; + OpenPIC_Addr = ioremap(OpenPIC_PAddr, MPC85xx_OPENPIC_SIZE); + OpenPIC_InitSenses = gp3_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof (gp3_openpic_initsenses); + + /* Skip reserved space and internal sources */ + openpic_set_sources(0, 32, OpenPIC_Addr + 0x10200); + + /* Map PIC IRQs 0-11 */ + openpic_set_sources(32, 12, OpenPIC_Addr + 0x10000); + + /* + * Let openpic interrupts starting from an offset, to + * leave space for cascading interrupts underneath. + */ + openpic_init(MPC85xx_OPENPIC_IRQ_OFFSET); + + /* Setup CPM2 PIC */ + cpm2_init_IRQ(); + + setup_irq(MPC85xx_IRQ_CPM, &cpm2_irqaction); + + return; +} + +static int +gp3_show_cpuinfo(struct seq_file *m) +{ + uint pvid, svid, phid1; + bd_t *binfo = (bd_t *) __res; + uint memsize; + unsigned int freq; + extern unsigned long total_memory; /* in mm/init */ + + /* get the core frequency */ + freq = binfo->bi_intfreq; + + pvid = mfspr(SPRN_PVR); + svid = mfspr(SPRN_SVR); + + memsize = total_memory; + + seq_printf(m, "Vendor\t\t: RPC Electronics STx \n"); + seq_printf(m, "Machine\t\t: GP3 - MPC%s\n", cur_ppc_sys_spec->ppc_sys_name); + seq_printf(m, "bus freq\t: %u.%.6u MHz\n", freq / 1000000, + freq % 1000000); + seq_printf(m, "PVR\t\t: 0x%x\n", pvid); + seq_printf(m, "SVR\t\t: 0x%x\n", svid); + + /* Display cpu Pll setting */ + phid1 = mfspr(SPRN_HID1); + seq_printf(m, "PLL setting\t: 0x%x\n", ((phid1 >> 24) & 0x3f)); + + /* Display the amount of memory */ + seq_printf(m, "Memory\t\t: %d MB\n", memsize / (1024 * 1024)); + + return 0; +} + +#ifdef CONFIG_PCI +int mpc85xx_map_irq(struct pci_dev *dev, unsigned char idsel, + unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {PIRQA, PIRQB, PIRQC, PIRQD}, + {PIRQD, PIRQA, PIRQB, PIRQC}, + {PIRQC, PIRQD, PIRQA, PIRQB}, + {PIRQB, PIRQC, PIRQD, PIRQA}, + }; + + const long min_idsel = 12, max_idsel = 15, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +} + +int mpc85xx_exclude_device(u_char bus, u_char devfn) +{ + if (bus == 0 && PCI_SLOT(devfn) == 0) + return PCIBIOS_DEVICE_NOT_FOUND; + else + return PCIBIOS_SUCCESSFUL; +} +#endif /* CONFIG_PCI */ + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + /* parse_bootinfo must always be called first */ + parse_bootinfo(find_bootinfo()); + + /* + * If we were passed in a board information, copy it into the + * residual data area. + */ + if (r3) { + memcpy((void *) __res, (void *) (r3 + KERNELBASE), + sizeof (bd_t)); + + } +#if defined(CONFIG_BLK_DEV_INITRD) + /* + * If the init RAM disk has been configured in, and there's a valid + * starting address for it, set it up. + */ + if (r4) { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + /* Copy the kernel command line arguments to a safe place. */ + + if (r6) { + *(char *) (r7 + KERNELBASE) = 0; + strcpy(cmd_line, (char *) (r6 + KERNELBASE)); + } + + identify_ppc_sys_by_id(mfspr(SPRN_SVR)); + + /* setup the PowerPC module struct */ + ppc_md.setup_arch = gp3_setup_arch; + ppc_md.show_cpuinfo = gp3_show_cpuinfo; + + ppc_md.init_IRQ = gp3_init_IRQ; + ppc_md.get_irq = openpic_get_irq; + + ppc_md.restart = mpc85xx_restart; + ppc_md.power_off = mpc85xx_power_off; + ppc_md.halt = mpc85xx_halt; + + ppc_md.find_end_of_memory = mpc85xx_find_end_of_memory; + + ppc_md.calibrate_decr = mpc85xx_calibrate_decr; + + if (ppc_md.progress) + ppc_md.progress("platform_init(): exit", 0); + + return; +} diff --git a/arch/ppc/platforms/85xx/stx_gp3.h b/arch/ppc/platforms/85xx/stx_gp3.h new file mode 100644 index 00000000000..7bcc6c35a41 --- /dev/null +++ b/arch/ppc/platforms/85xx/stx_gp3.h @@ -0,0 +1,74 @@ +/* + * arch/ppc/platforms/stx8560_gp3.h + * + * STx GP3 board definitions + * + * Dan Malek (dan@embeddededge.com) + * Copyright 2004 Embedded Edge, LLC + * + * Ported to 2.6, Matt Porter + * Copyright 2004-2005 MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef __MACH_STX_GP3_H +#define __MACH_STX_GP3_H + +#include +#include +#include +#include + +#define BOARD_CCSRBAR ((uint)0xe0000000) +#define CCSRBAR_SIZE ((uint)1024*1024) + +#define CPM_MAP_ADDR (CCSRBAR + MPC85xx_CPM_OFFSET) + +#define BCSR_ADDR ((uint)0xfc000000) +#define BCSR_SIZE ((uint)(16 * 1024)) + +#define BCSR_TSEC1_RESET 0x00000080 +#define BCSR_TSEC2_RESET 0x00000040 +#define BCSR_LED1 0x00000008 +#define BCSR_LED2 0x00000004 +#define BCSR_LED3 0x00000002 +#define BCSR_LED4 0x00000001 + +extern void mpc85xx_setup_hose(void) __init; +extern void mpc85xx_restart(char *cmd); +extern void mpc85xx_power_off(void); +extern void mpc85xx_halt(void); +extern int mpc85xx_show_cpuinfo(struct seq_file *m); +extern void mpc85xx_init_IRQ(void) __init; +extern unsigned long mpc85xx_find_end_of_memory(void) __init; +extern void mpc85xx_calibrate_decr(void) __init; + +#define PCI_CFG_ADDR_OFFSET (0x8000) +#define PCI_CFG_DATA_OFFSET (0x8004) + +/* PCI interrupt controller */ +#define PIRQA MPC85xx_IRQ_EXT1 +#define PIRQB MPC85xx_IRQ_EXT2 +#define PIRQC MPC85xx_IRQ_EXT3 +#define PIRQD MPC85xx_IRQ_EXT4 +#define PCI_MIN_IDSEL 16 +#define PCI_MAX_IDSEL 19 +#define PCI_IRQ_SLOT 4 + +#define MPC85XX_PCI1_LOWER_IO 0x00000000 +#define MPC85XX_PCI1_UPPER_IO 0x00ffffff + +#define MPC85XX_PCI1_LOWER_MEM 0x80000000 +#define MPC85XX_PCI1_UPPER_MEM 0x9fffffff + +#define MPC85XX_PCI1_IO_BASE 0xe2000000 +#define MPC85XX_PCI1_MEM_OFFSET 0x00000000 + +#define MPC85XX_PCI1_IO_SIZE 0x01000000 + +#endif /* __MACH_STX_GP3_H */ diff --git a/arch/ppc/platforms/Makefile b/arch/ppc/platforms/Makefile new file mode 100644 index 00000000000..5488a053f41 --- /dev/null +++ b/arch/ppc/platforms/Makefile @@ -0,0 +1,53 @@ +# +# Makefile for the linux kernel. +# + +# Extra CFLAGS so we don't have to do relative includes +CFLAGS_pmac_setup.o += -Iarch/$(ARCH)/mm + +obj-$(CONFIG_APUS) += apus_setup.o +ifeq ($(CONFIG_APUS),y) +obj-$(CONFIG_PCI) += apus_pci.o +endif +obj-$(CONFIG_PPC_PMAC) += pmac_pic.o pmac_setup.o pmac_time.o \ + pmac_feature.o pmac_pci.o pmac_sleep.o \ + pmac_low_i2c.o pmac_cache.o +obj-$(CONFIG_PPC_CHRP) += chrp_setup.o chrp_time.o chrp_pci.o \ + chrp_pegasos_eth.o +obj-$(CONFIG_PPC_PREP) += prep_pci.o prep_setup.o +ifeq ($(CONFIG_PPC_PMAC),y) +obj-$(CONFIG_NVRAM) += pmac_nvram.o +obj-$(CONFIG_CPU_FREQ_PMAC) += pmac_cpufreq.o +endif +obj-$(CONFIG_PMAC_BACKLIGHT) += pmac_backlight.o +obj-$(CONFIG_PREP_RESIDUAL) += residual.o +obj-$(CONFIG_ADIR) += adir_setup.o adir_pic.o adir_pci.o +obj-$(CONFIG_PQ2ADS) += pq2ads.o +obj-$(CONFIG_TQM8260) += tqm8260_setup.o +obj-$(CONFIG_CPCI690) += cpci690.o +obj-$(CONFIG_EV64260) += ev64260.o +obj-$(CONFIG_CHESTNUT) += chestnut.o +obj-$(CONFIG_GEMINI) += gemini_pci.o gemini_setup.o gemini_prom.o +obj-$(CONFIG_K2) += k2.o +obj-$(CONFIG_LOPEC) += lopec.o +obj-$(CONFIG_KATANA) += katana.o +obj-$(CONFIG_HDPU) += hdpu.o +obj-$(CONFIG_MCPN765) += mcpn765.o +obj-$(CONFIG_MENF1) += menf1_setup.o menf1_pci.o +obj-$(CONFIG_MVME5100) += mvme5100.o +obj-$(CONFIG_PAL4) += pal4_setup.o pal4_pci.o +obj-$(CONFIG_PCORE) += pcore.o +obj-$(CONFIG_POWERPMC250) += powerpmc250.o +obj-$(CONFIG_PPLUS) += pplus.o +obj-$(CONFIG_PRPMC750) += prpmc750.o +obj-$(CONFIG_PRPMC800) += prpmc800.o +obj-$(CONFIG_RADSTONE_PPC7D) += radstone_ppc7d.o +obj-$(CONFIG_SANDPOINT) += sandpoint.o +obj-$(CONFIG_SBC82xx) += sbc82xx.o +obj-$(CONFIG_SPRUCE) += spruce.o +obj-$(CONFIG_LITE5200) += lite5200.o + +ifeq ($(CONFIG_SMP),y) +obj-$(CONFIG_PPC_PMAC) += pmac_smp.o +obj-$(CONFIG_PPC_CHRP) += chrp_smp.o +endif diff --git a/arch/ppc/platforms/adir.h b/arch/ppc/platforms/adir.h new file mode 100644 index 00000000000..13a748b4695 --- /dev/null +++ b/arch/ppc/platforms/adir.h @@ -0,0 +1,95 @@ +/* + * arch/ppc/platforms/adir.h + * + * Definitions for SBS Adirondack board support + * + * By Michael Sokolov + */ + +#ifndef __PPC_PLATFORMS_ADIR_H +#define __PPC_PLATFORMS_ADIR_H + +/* + * SBS Adirondack definitions + */ + +/* PPC physical address space layout. We use the one set up by the firmware. */ +#define ADIR_PCI32_MEM_BASE 0x80000000 +#define ADIR_PCI32_MEM_SIZE 0x20000000 +#define ADIR_PCI64_MEM_BASE 0xA0000000 +#define ADIR_PCI64_MEM_SIZE 0x20000000 +#define ADIR_PCI32_IO_BASE 0xC0000000 +#define ADIR_PCI32_IO_SIZE 0x10000000 +#define ADIR_PCI64_IO_BASE 0xD0000000 +#define ADIR_PCI64_IO_SIZE 0x10000000 +#define ADIR_PCI64_PHB 0xFF400000 +#define ADIR_PCI32_PHB 0xFF500000 + +#define ADIR_PCI64_CONFIG_ADDR (ADIR_PCI64_PHB + 0x000f8000) +#define ADIR_PCI64_CONFIG_DATA (ADIR_PCI64_PHB + 0x000f8010) + +#define ADIR_PCI32_CONFIG_ADDR (ADIR_PCI32_PHB + 0x000f8000) +#define ADIR_PCI32_CONFIG_DATA (ADIR_PCI32_PHB + 0x000f8010) + +/* System memory as seen from PCI */ +#define ADIR_PCI_SYS_MEM_BASE 0x80000000 + +/* Static virtual mapping of PCI I/O */ +#define ADIR_PCI32_VIRT_IO_BASE 0xFE000000 +#define ADIR_PCI32_VIRT_IO_SIZE 0x01000000 +#define ADIR_PCI64_VIRT_IO_BASE 0xFF000000 +#define ADIR_PCI64_VIRT_IO_SIZE 0x01000000 + +/* Registers */ +#define ADIR_NVRAM_RTC_ADDR 0x74 +#define ADIR_NVRAM_RTC_DATA 0x75 + +#define ADIR_BOARD_ID_REG (ADIR_PCI32_VIRT_IO_BASE + 0x08FFF0) +#define ADIR_CPLD1REV_REG (ADIR_PCI32_VIRT_IO_BASE + 0x08FFF1) +#define ADIR_CPLD2REV_REG (ADIR_PCI32_VIRT_IO_BASE + 0x08FFF2) +#define ADIR_FLASHCTL_REG (ADIR_PCI32_VIRT_IO_BASE + 0x08FFF3) +#define ADIR_CPC710_STAT_REG (ADIR_PCI32_VIRT_IO_BASE + 0x08FFF4) +#define ADIR_CLOCK_REG (ADIR_PCI32_VIRT_IO_BASE + 0x08FFF5) +#define ADIR_GPIO_REG (ADIR_PCI32_VIRT_IO_BASE + 0x08FFF8) +#define ADIR_MISC_REG (ADIR_PCI32_VIRT_IO_BASE + 0x08FFF9) +#define ADIR_LED_REG (ADIR_PCI32_VIRT_IO_BASE + 0x08FFFA) + +#define ADIR_CLOCK_REG_PD 0x10 +#define ADIR_CLOCK_REG_SPREAD 0x08 +#define ADIR_CLOCK_REG_SEL133 0x04 +#define ADIR_CLOCK_REG_SEL1 0x02 +#define ADIR_CLOCK_REG_SEL0 0x01 + +#define ADIR_PROCA_INT_MASK (ADIR_PCI32_VIRT_IO_BASE + 0x0EFFF0) +#define ADIR_PROCB_INT_MASK (ADIR_PCI32_VIRT_IO_BASE + 0x0EFFF2) +#define ADIR_PROCA_INT_STAT (ADIR_PCI32_VIRT_IO_BASE + 0x0EFFF4) +#define ADIR_PROCB_INT_STAT (ADIR_PCI32_VIRT_IO_BASE + 0x0EFFF6) + +/* Linux IRQ numbers */ +#define ADIR_IRQ_NONE -1 +#define ADIR_IRQ_SERIAL2 3 +#define ADIR_IRQ_SERIAL1 4 +#define ADIR_IRQ_FDC 6 +#define ADIR_IRQ_PARALLEL 7 +#define ADIR_IRQ_VIA_AUDIO 10 +#define ADIR_IRQ_VIA_USB 11 +#define ADIR_IRQ_IDE0 14 +#define ADIR_IRQ_IDE1 15 +#define ADIR_IRQ_PCI0_INTA 16 +#define ADIR_IRQ_PCI0_INTB 17 +#define ADIR_IRQ_PCI0_INTC 18 +#define ADIR_IRQ_PCI0_INTD 19 +#define ADIR_IRQ_PCI1_INTA 20 +#define ADIR_IRQ_PCI1_INTB 21 +#define ADIR_IRQ_PCI1_INTC 22 +#define ADIR_IRQ_PCI1_INTD 23 +#define ADIR_IRQ_MBSCSI 24 /* motherboard SCSI */ +#define ADIR_IRQ_MBETH1 25 /* motherboard Ethernet 1 */ +#define ADIR_IRQ_MBETH0 26 /* motherboard Ethernet 0 */ +#define ADIR_IRQ_CPC710_INT1 27 +#define ADIR_IRQ_CPC710_INT2 28 +#define ADIR_IRQ_VT82C686_NMI 29 +#define ADIR_IRQ_VT82C686_INTR 30 +#define ADIR_IRQ_INTERPROC 31 + +#endif /* __PPC_PLATFORMS_ADIR_H */ diff --git a/arch/ppc/platforms/adir_pci.c b/arch/ppc/platforms/adir_pci.c new file mode 100644 index 00000000000..f94ac53e071 --- /dev/null +++ b/arch/ppc/platforms/adir_pci.c @@ -0,0 +1,247 @@ +/* + * arch/ppc/platforms/adir_pci.c + * + * PCI support for SBS Adirondack + * + * By Michael Sokolov + * based on the K2 version by Matt Porter + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include "adir.h" + +#undef DEBUG +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif /* DEBUG */ + +static inline int __init +adir_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ +#define PCIIRQ(a,b,c,d) {ADIR_IRQ_##a,ADIR_IRQ_##b,ADIR_IRQ_##c,ADIR_IRQ_##d}, + struct pci_controller *hose = pci_bus_to_hose(dev->bus->number); + /* + * The three PCI devices on the motherboard have dedicated lines to the + * CPLD interrupt controller, bypassing the standard PCI INTA-D and the + * PC interrupt controller. All other PCI devices (slots) have usual + * staggered INTA-D lines, resulting in 8 lines total (PCI0 INTA-D and + * PCI1 INTA-D). All 8 go to the CPLD interrupt controller. PCI0 INTA-D + * also go to the south bridge, so we have the option of taking them + * via the CPLD interrupt controller or via the south bridge 8259 + * 8258 thingy. PCI1 INTA-D can only be taken via the CPLD interrupt + * controller. We take all PCI interrupts via the CPLD interrupt + * controller as recommended by SBS. + * + * We also have some monkey business with the PCI devices within the + * VT82C686B south bridge itself. This chip actually has 7 functions on + * its IDSEL. Function 0 is the actual south bridge, function 1 is IDE, + * and function 4 is some special stuff. The other 4 functions are just + * regular PCI devices bundled in the chip. 2 and 3 are USB UHCIs and 5 + * and 6 are audio (not supported on the Adirondack). + * + * This is where the monkey business begins. PCI devices are supposed + * to signal normal PCI interrupts. But the 4 functions in question are + * located in the south bridge chip, which is designed with the + * assumption that it will be fielding PCI INTA-D interrupts rather + * than generating them. Here's what it does. Each of the functions in + * question routes its interrupt to one of the IRQs on the 8259 thingy. + * Which one? It looks at the Interrupt Line register in the PCI config + * space, even though the PCI spec says it's for BIOS/OS interaction + * only. + * + * How do we deal with this? We take these interrupts via 8259 IRQs as + * we have to. We return the desired IRQ numbers from this routine when + * called for the functions in question. The PCI scan code will then + * stick our return value into the Interrupt Line register in the PCI + * config space, and the interrupt will actually go there. We identify + * these functions within the south bridge IDSEL by their interrupt pin + * numbers, as the VT82C686B has 04 in the Interrupt Pin register for + * USB and 03 for audio. + */ + if (!hose->index) { + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + /* south bridge */ PCIIRQ(IDE0, NONE, VIA_AUDIO, VIA_USB) + /* Ethernet 0 */ PCIIRQ(MBETH0, MBETH0, MBETH0, MBETH0) + /* PCI0 slot 1 */ PCIIRQ(PCI0_INTB, PCI0_INTC, PCI0_INTD, PCI0_INTA) + /* PCI0 slot 2 */ PCIIRQ(PCI0_INTC, PCI0_INTD, PCI0_INTA, PCI0_INTB) + /* PCI0 slot 3 */ PCIIRQ(PCI0_INTD, PCI0_INTA, PCI0_INTB, PCI0_INTC) + }; + const long min_idsel = 3, max_idsel = 7, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; + } else { + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + /* Ethernet 1 */ PCIIRQ(MBETH1, MBETH1, MBETH1, MBETH1) + /* SCSI */ PCIIRQ(MBSCSI, MBSCSI, MBSCSI, MBSCSI) + /* PCI1 slot 1 */ PCIIRQ(PCI1_INTB, PCI1_INTC, PCI1_INTD, PCI1_INTA) + /* PCI1 slot 2 */ PCIIRQ(PCI1_INTC, PCI1_INTD, PCI1_INTA, PCI1_INTB) + /* PCI1 slot 3 */ PCIIRQ(PCI1_INTD, PCI1_INTA, PCI1_INTB, PCI1_INTC) + }; + const long min_idsel = 3, max_idsel = 7, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; + } +#undef PCIIRQ +} + +static void +adir_pcibios_fixup_resources(struct pci_dev *dev) +{ + int i; + + if ((dev->vendor == PCI_VENDOR_ID_IBM) && + (dev->device == PCI_DEVICE_ID_IBM_CPC710_PCI64)) + { + DBG("Fixup CPC710 resources\n"); + for (i=0; iresource[i].start = 0; + dev->resource[i].end = 0; + } + } +} + +/* + * CPC710 DD3 has an errata causing it to hang the system if a type 0 config + * cycle is attempted on its PCI32 interface with a device number > 21. + * CPC710's PCI bridges map device numbers 1 through 21 to AD11 through AD31. + * Per the PCI spec it MUST accept all other device numbers and do nothing, and + * software MUST scan all device numbers without assuming how IDSELs are + * mapped. However, as the CPC710 DD3's errata causes such correct scanning + * procedure to hang the system, we have no choice but to introduce this hack + * of knowingly avoiding device numbers > 21 on PCI0, + */ +static int +adir_exclude_device(u_char bus, u_char devfn) +{ + if ((bus == 0) && (PCI_SLOT(devfn) > 21)) + return PCIBIOS_DEVICE_NOT_FOUND; + else + return PCIBIOS_SUCCESSFUL; +} + +void adir_find_bridges(void) +{ + struct pci_controller *hose_a, *hose_b; + + /* Setup PCI32 hose */ + hose_a = pcibios_alloc_controller(); + if (!hose_a) + return; + + hose_a->first_busno = 0; + hose_a->last_busno = 0xff; + hose_a->pci_mem_offset = ADIR_PCI32_MEM_BASE; + hose_a->io_space.start = 0; + hose_a->io_space.end = ADIR_PCI32_VIRT_IO_SIZE - 1; + hose_a->mem_space.start = 0; + hose_a->mem_space.end = ADIR_PCI32_MEM_SIZE - 1; + hose_a->io_resource.start = 0; + hose_a->io_resource.end = ADIR_PCI32_VIRT_IO_SIZE - 1; + hose_a->io_resource.flags = IORESOURCE_IO; + hose_a->mem_resources[0].start = ADIR_PCI32_MEM_BASE; + hose_a->mem_resources[0].end = ADIR_PCI32_MEM_BASE + + ADIR_PCI32_MEM_SIZE - 1; + hose_a->mem_resources[0].flags = IORESOURCE_MEM; + hose_a->io_base_phys = ADIR_PCI32_IO_BASE; + hose_a->io_base_virt = (void *) ADIR_PCI32_VIRT_IO_BASE; + + ppc_md.pci_exclude_device = adir_exclude_device; + setup_indirect_pci(hose_a, ADIR_PCI32_CONFIG_ADDR, + ADIR_PCI32_CONFIG_DATA); + + /* Initialize PCI32 bus registers */ + early_write_config_byte(hose_a, + hose_a->first_busno, + PCI_DEVFN(0, 0), + CPC710_BUS_NUMBER, + hose_a->first_busno); + early_write_config_byte(hose_a, + hose_a->first_busno, + PCI_DEVFN(0, 0), + CPC710_SUB_BUS_NUMBER, + hose_a->last_busno); + + hose_a->last_busno = pciauto_bus_scan(hose_a, hose_a->first_busno); + + /* Write out correct max subordinate bus number for hose A */ + early_write_config_byte(hose_a, + hose_a->first_busno, + PCI_DEVFN(0, 0), + CPC710_SUB_BUS_NUMBER, + hose_a->last_busno); + + /* Setup PCI64 hose */ + hose_b = pcibios_alloc_controller(); + if (!hose_b) + return; + + hose_b->first_busno = hose_a->last_busno + 1; + hose_b->last_busno = 0xff; + hose_b->pci_mem_offset = ADIR_PCI64_MEM_BASE; + hose_b->io_space.start = 0; + hose_b->io_space.end = ADIR_PCI64_VIRT_IO_SIZE - 1; + hose_b->mem_space.start = 0; + hose_b->mem_space.end = ADIR_PCI64_MEM_SIZE - 1; + hose_b->io_resource.start = 0; + hose_b->io_resource.end = ADIR_PCI64_VIRT_IO_SIZE - 1; + hose_b->io_resource.flags = IORESOURCE_IO; + hose_b->mem_resources[0].start = ADIR_PCI64_MEM_BASE; + hose_b->mem_resources[0].end = ADIR_PCI64_MEM_BASE + + ADIR_PCI64_MEM_SIZE - 1; + hose_b->mem_resources[0].flags = IORESOURCE_MEM; + hose_b->io_base_phys = ADIR_PCI64_IO_BASE; + hose_b->io_base_virt = (void *) ADIR_PCI64_VIRT_IO_BASE; + + setup_indirect_pci(hose_b, ADIR_PCI64_CONFIG_ADDR, + ADIR_PCI64_CONFIG_DATA); + + /* Initialize PCI64 bus registers */ + early_write_config_byte(hose_b, + 0, + PCI_DEVFN(0, 0), + CPC710_SUB_BUS_NUMBER, + 0xff); + + early_write_config_byte(hose_b, + 0, + PCI_DEVFN(0, 0), + CPC710_BUS_NUMBER, + hose_b->first_busno); + + hose_b->last_busno = pciauto_bus_scan(hose_b, + hose_b->first_busno); + + /* Write out correct max subordinate bus number for hose B */ + early_write_config_byte(hose_b, + hose_b->first_busno, + PCI_DEVFN(0, 0), + CPC710_SUB_BUS_NUMBER, + hose_b->last_busno); + + ppc_md.pcibios_fixup = NULL; + ppc_md.pcibios_fixup_resources = adir_pcibios_fixup_resources; + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = adir_map_irq; +} diff --git a/arch/ppc/platforms/adir_pic.c b/arch/ppc/platforms/adir_pic.c new file mode 100644 index 00000000000..9947cba52af --- /dev/null +++ b/arch/ppc/platforms/adir_pic.c @@ -0,0 +1,130 @@ +/* + * arch/ppc/platforms/adir_pic.c + * + * Interrupt controller support for SBS Adirondack + * + * By Michael Sokolov + * based on the K2 and SCM versions by Matt Porter + */ + +#include +#include +#include +#include +#include + +#include +#include +#include "adir.h" + +static void adir_onboard_pic_enable(unsigned int irq); +static void adir_onboard_pic_disable(unsigned int irq); + +__init static void +adir_onboard_pic_init(void) +{ + volatile u_short *maskreg = (volatile u_short *) ADIR_PROCA_INT_MASK; + + /* Disable all Adirondack onboard interrupts */ + out_be16(maskreg, 0xFFFF); +} + +static int +adir_onboard_pic_get_irq(void) +{ + volatile u_short *statreg = (volatile u_short *) ADIR_PROCA_INT_STAT; + int irq; + u_short int_status, int_test; + + int_status = in_be16(statreg); + for (irq = 0, int_test = 1; irq < 16; irq++, int_test <<= 1) { + if (int_status & int_test) + break; + } + + if (irq == 16) + return -1; + + return (irq+16); +} + +static void +adir_onboard_pic_enable(unsigned int irq) +{ + volatile u_short *maskreg = (volatile u_short *) ADIR_PROCA_INT_MASK; + + /* Change irq to Adirondack onboard native value */ + irq -= 16; + + /* Enable requested irq number */ + out_be16(maskreg, in_be16(maskreg) & ~(1 << irq)); +} + +static void +adir_onboard_pic_disable(unsigned int irq) +{ + volatile u_short *maskreg = (volatile u_short *) ADIR_PROCA_INT_MASK; + + /* Change irq to Adirondack onboard native value */ + irq -= 16; + + /* Disable requested irq number */ + out_be16(maskreg, in_be16(maskreg) | (1 << irq)); +} + +static struct hw_interrupt_type adir_onboard_pic = { + " ADIR PIC ", + NULL, + NULL, + adir_onboard_pic_enable, /* unmask */ + adir_onboard_pic_disable, /* mask */ + adir_onboard_pic_disable, /* mask and ack */ + NULL, + NULL +}; + +static struct irqaction noop_action = { + .handler = no_action, + .flags = SA_INTERRUPT, + .mask = CPU_MASK_NONE, + .name = "82c59 primary cascade", +}; + +/* + * Linux interrupt values are assigned as follows: + * + * 0-15 VT82C686 8259 interrupts + * 16-31 Adirondack CPLD interrupts + */ +__init void +adir_init_IRQ(void) +{ + int i; + + /* Initialize the cascaded 8259's on the VT82C686 */ + for (i=0; i<16; i++) + irq_desc[i].handler = &i8259_pic; + i8259_init(NULL); + + /* Initialize Adirondack CPLD PIC and enable 8259 interrupt cascade */ + for (i=16; i<32; i++) + irq_desc[i].handler = &adir_onboard_pic; + adir_onboard_pic_init(); + + /* Enable 8259 interrupt cascade */ + setup_irq(ADIR_IRQ_VT82C686_INTR, &noop_action); +} + +int +adir_get_irq(struct pt_regs *regs) +{ + int irq; + + if ((irq = adir_onboard_pic_get_irq()) < 0) + return irq; + + if (irq == ADIR_IRQ_VT82C686_INTR) + irq = i8259_irq(regs); + + return irq; +} diff --git a/arch/ppc/platforms/adir_setup.c b/arch/ppc/platforms/adir_setup.c new file mode 100644 index 00000000000..6a6754ee061 --- /dev/null +++ b/arch/ppc/platforms/adir_setup.c @@ -0,0 +1,210 @@ +/* + * arch/ppc/platforms/adir_setup.c + * + * Board setup routines for SBS Adirondack + * + * By Michael Sokolov + * based on the K2 version by Matt Porter + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "adir.h" + +extern void adir_init_IRQ(void); +extern int adir_get_irq(struct pt_regs *); +extern void adir_find_bridges(void); +extern unsigned long loops_per_jiffy; + +static unsigned int cpu_750cx[16] = { + 5, 15, 14, 0, 4, 13, 0, 9, 6, 11, 8, 10, 16, 12, 7, 0 +}; + +static int +adir_get_bus_speed(void) +{ + if (!(*((u_char *) ADIR_CLOCK_REG) & ADIR_CLOCK_REG_SEL133)) + return 100000000; + else + return 133333333; +} + +static int +adir_get_cpu_speed(void) +{ + unsigned long hid1; + int cpu_speed; + + hid1 = mfspr(SPRN_HID1) >> 28; + + hid1 = cpu_750cx[hid1]; + + cpu_speed = adir_get_bus_speed()*hid1/2; + return cpu_speed; +} + +static void __init +adir_calibrate_decr(void) +{ + int freq, divisor = 4; + + /* determine processor bus speed */ + freq = adir_get_bus_speed(); + tb_ticks_per_jiffy = freq / HZ / divisor; + tb_to_us = mulhwu_scale_factor(freq/divisor, 1000000); +} + +static int +adir_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "vendor\t\t: SBS\n"); + seq_printf(m, "machine\t\t: Adirondack\n"); + seq_printf(m, "cpu speed\t: %dMhz\n", adir_get_cpu_speed()/1000000); + seq_printf(m, "bus speed\t: %dMhz\n", adir_get_bus_speed()/1000000); + seq_printf(m, "memory type\t: SDRAM\n"); + + return 0; +} + +extern char cmd_line[]; + +TODC_ALLOC(); + +static void __init +adir_setup_arch(void) +{ + unsigned int cpu; + + /* Setup TODC access */ + TODC_INIT(TODC_TYPE_MC146818, ADIR_NVRAM_RTC_ADDR, 0, + ADIR_NVRAM_RTC_DATA, 8); + + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_jiffy = 50000000/HZ; + + /* Setup PCI host bridges */ + adir_find_bridges(); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = Root_NFS; +#else + ROOT_DEV = Root_SDA1; +#endif + + /* Identify the system */ + printk("System Identification: SBS Adirondack - PowerPC 750CXe @ %d Mhz\n", adir_get_cpu_speed()/1000000); + printk("SBS Adirondack port (C) 2001 SBS Technologies, Inc.\n"); + + /* Identify the CPU manufacturer */ + cpu = mfspr(SPRN_PVR); + printk("CPU manufacturer: IBM [rev=%04x]\n", (cpu & 0xffff)); +} + +static void +adir_restart(char *cmd) +{ + local_irq_disable(); + /* SRR0 has system reset vector, SRR1 has default MSR value */ + /* rfi restores MSR from SRR1 and sets the PC to the SRR0 value */ + __asm__ __volatile__ + ("lis 3,0xfff0\n\t" + "ori 3,3,0x0100\n\t" + "mtspr 26,3\n\t" + "li 3,0\n\t" + "mtspr 27,3\n\t" + "rfi\n\t"); + for(;;); +} + +static void +adir_power_off(void) +{ + for(;;); +} + +static void +adir_halt(void) +{ + adir_restart(NULL); +} + +static unsigned long __init +adir_find_end_of_memory(void) +{ + return boot_mem_size; +} + +static void __init +adir_map_io(void) +{ + io_block_mapping(ADIR_PCI32_VIRT_IO_BASE, ADIR_PCI32_IO_BASE, + ADIR_PCI32_VIRT_IO_SIZE, _PAGE_IO); + io_block_mapping(ADIR_PCI64_VIRT_IO_BASE, ADIR_PCI64_IO_BASE, + ADIR_PCI64_VIRT_IO_SIZE, _PAGE_IO); +} + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + /* + * On the Adirondack we use bi_recs and pass the pointer to them in R3. + */ + parse_bootinfo((struct bi_record *) (r3 + KERNELBASE)); + + /* Remember, isa_io_base is virtual but isa_mem_base is physical! */ + isa_io_base = ADIR_PCI32_VIRT_IO_BASE; + isa_mem_base = ADIR_PCI32_MEM_BASE; + pci_dram_offset = ADIR_PCI_SYS_MEM_BASE; + + ppc_md.setup_arch = adir_setup_arch; + ppc_md.show_cpuinfo = adir_show_cpuinfo; + ppc_md.irq_canonicalize = NULL; + ppc_md.init_IRQ = adir_init_IRQ; + ppc_md.get_irq = adir_get_irq; + ppc_md.init = NULL; + + ppc_md.find_end_of_memory = adir_find_end_of_memory; + ppc_md.setup_io_mappings = adir_map_io; + + ppc_md.restart = adir_restart; + ppc_md.power_off = adir_power_off; + ppc_md.halt = adir_halt; + + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.nvram_read_val = todc_mc146818_read_val; + ppc_md.nvram_write_val = todc_mc146818_write_val; + ppc_md.calibrate_decr = adir_calibrate_decr; +} diff --git a/arch/ppc/platforms/apus_pci.c b/arch/ppc/platforms/apus_pci.c new file mode 100644 index 00000000000..33dad6db824 --- /dev/null +++ b/arch/ppc/platforms/apus_pci.c @@ -0,0 +1,208 @@ +/* + * Copyright (C) Michel Dänzer + * + * APUS PCI routines. + * + * Currently, only B/CVisionPPC cards (Permedia2) are supported. + * + * Thanks to Geert Uytterhoeven for the idea: + * Read values from given config space(s) for the first devices, -1 otherwise + * + */ + +#include +#ifdef CONFIG_AMIGA + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "apus_pci.h" + + +/* These definitions are mostly adapted from pm2fb.c */ + +#undef APUS_PCI_MASTER_DEBUG +#ifdef APUS_PCI_MASTER_DEBUG +#define DPRINTK(a,b...) printk(KERN_DEBUG "apus_pci: %s: " a, __FUNCTION__ , ## b) +#else +#define DPRINTK(a,b...) +#endif + +/* + * The _DEFINITIVE_ memory mapping/unmapping functions. + * This is due to the fact that they're changing soooo often... + */ +#define DEFW() wmb() +#define DEFR() rmb() +#define DEFRW() mb() + +#define DEVNO(d) ((d)>>3) +#define FNNO(d) ((d)&7) + + +extern unsigned long powerup_PCI_present; + +static struct pci_controller *apus_hose; + + +void *pci_io_base(unsigned int bus) +{ + return 0; +} + + +int +apus_pcibios_read_config(struct pci_bus *bus, int devfn, int offset, + int len, u32 *val) +{ + int fnno = FNNO(devfn); + int devno = DEVNO(devfn); + volatile unsigned char *cfg_data; + + if (bus->number > 0 || devno != 1) { + *val = ~0; + return PCIBIOS_DEVICE_NOT_FOUND; + } + /* base address + function offset + offset ^ endianness conversion */ + /* XXX the fnno<<5 bit seems wacky -- paulus */ + cfg_data = apus_hose->cfg_data + (fnno<<5) + (offset ^ (len - 1)); + switch (len) { + case 1: + *val = readb(cfg_data); + break; + case 2: + *val = readw(cfg_data); + break; + default: + *val = readl(cfg_data); + break; + } + + DPRINTK("read b: 0x%x, d: 0x%x, f: 0x%x, o: 0x%x, l: %d, v: 0x%x\n", + bus->number, devfn>>3, devfn&7, offset, len, *val); + return PCIBIOS_SUCCESSFUL; +} + +int +apus_pcibios_write_config(struct pci_bus *bus, int devfn, int offset, + int len, u32 *val) +{ + int fnno = FNNO(devfn); + int devno = DEVNO(devfn); + volatile unsigned char *cfg_data; + + if (bus->number > 0 || devno != 1) { + return PCIBIOS_DEVICE_NOT_FOUND; + } + /* base address + function offset + offset ^ endianness conversion */ + /* XXX the fnno<<5 bit seems wacky -- paulus */ + cfg_data = apus_hose->cfg_data + (fnno<<5) + (offset ^ (len - 1)); + switch (len) { + case 1: + writeb(val, cfg_data); DEFW(); + break; + case 2: + writew(val, cfg_data); DEFW(); + break; + default: + writel(val, cfg_data); DEFW(); + break; + } + + DPRINTK("write b: 0x%x, d: 0x%x, f: 0x%x, o: 0x%x, l: %d, v: 0x%x\n", + bus->number, devfn>>3, devfn&7, offset, len, val); + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops apus_pci_ops = { + apus_pcibios_read_config, + apus_pcibios_write_config +}; + +static struct resource pci_mem = { "B/CVisionPPC PCI mem", CVPPC_FB_APERTURE_ONE, CVPPC_PCI_CONFIG, IORESOURCE_MEM }; + +void __init +apus_pcibios_fixup(void) +{ +/* struct pci_dev *dev = pci_find_slot(0, 1<<3); + unsigned int reg, val, offset;*/ + + /* FIXME: interrupt? */ + /*dev->interrupt = xxx;*/ + + request_resource(&iomem_resource, &pci_mem); + printk("%s: PCI mem resource requested\n", __FUNCTION__); +} + +static void __init apus_pcibios_fixup_bus(struct pci_bus *bus) +{ + bus->resource[1] = &pci_mem; +} + + +/* + * This is from pm2fb.c again + * + * Check if PCI (B/CVisionPPC) is available, initialize it and set up + * the pcibios_* pointers + */ + + +void __init +apus_setup_pci_ptrs(void) +{ + if (!powerup_PCI_present) { + DPRINTK("no PCI bridge detected\n"); + return; + } + DPRINTK("Phase5 B/CVisionPPC PCI bridge detected.\n"); + + apus_hose = pcibios_alloc_controller(); + if (!apus_hose) { + printk("apus_pci: Can't allocate PCI controller structure\n"); + return; + } + + if (!(apus_hose->cfg_data = ioremap(CVPPC_PCI_CONFIG, 256))) { + printk("apus_pci: unable to map PCI config region\n"); + return; + } + + if (!(apus_hose->cfg_addr = ioremap(CSPPC_PCI_BRIDGE, 256))) { + printk("apus_pci: unable to map PCI bridge\n"); + return; + } + + writel(CSPPCF_BRIDGE_BIG_ENDIAN, apus_hose->cfg_addr + CSPPC_BRIDGE_ENDIAN); + DEFW(); + + writel(CVPPC_REGS_REGION, apus_hose->cfg_data+ PCI_BASE_ADDRESS_0); + DEFW(); + writel(CVPPC_FB_APERTURE_ONE, apus_hose->cfg_data + PCI_BASE_ADDRESS_1); + DEFW(); + writel(CVPPC_FB_APERTURE_TWO, apus_hose->cfg_data + PCI_BASE_ADDRESS_2); + DEFW(); + writel(CVPPC_ROM_ADDRESS, apus_hose->cfg_data + PCI_ROM_ADDRESS); + DEFW(); + + writel(0xef000000 | PCI_COMMAND_IO | PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER, apus_hose->cfg_data + PCI_COMMAND); + DEFW(); + + apus_hose->first_busno = 0; + apus_hose->last_busno = 0; + apus_hose->ops = &apus_pci_ops; + ppc_md.pcibios_fixup = apus_pcibios_fixup; + ppc_md.pcibios_fixup_bus = apus_pcibios_fixup_bus; + + return; +} + +#endif /* CONFIG_AMIGA */ diff --git a/arch/ppc/platforms/apus_pci.h b/arch/ppc/platforms/apus_pci.h new file mode 100644 index 00000000000..f15974ae018 --- /dev/null +++ b/arch/ppc/platforms/apus_pci.h @@ -0,0 +1,34 @@ +/* + * Phase5 CybervisionPPC (TVP4020) definitions for the Permedia2 framebuffer + * driver. + * + * Copyright (c) 1998-1999 Ilario Nardinocchi (nardinoc@CS.UniBO.IT) + * -------------------------------------------------------------------------- + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file README.legal in the main directory of this archive + * for more details. + */ + +#ifndef APUS_PCI_H +#define APUS_PCI_H + + +#define CSPPC_PCI_BRIDGE 0xfffe0000 +#define CSPPC_BRIDGE_ENDIAN 0x0000 +#define CSPPC_BRIDGE_INT 0x0010 + +#define CVPPC_PCI_CONFIG 0xfffc0000 +#define CVPPC_ROM_ADDRESS 0xe2000001 +#define CVPPC_REGS_REGION 0xef000000 +#define CVPPC_FB_APERTURE_ONE 0xe0000000 +#define CVPPC_FB_APERTURE_TWO 0xe1000000 +#define CVPPC_FB_SIZE 0x00800000 + +/* CVPPC_BRIDGE_ENDIAN */ +#define CSPPCF_BRIDGE_BIG_ENDIAN 0x02 + +/* CVPPC_BRIDGE_INT */ +#define CSPPCF_BRIDGE_ACTIVE_INT2 0x01 + + +#endif /* APUS_PCI_H */ diff --git a/arch/ppc/platforms/apus_setup.c b/arch/ppc/platforms/apus_setup.c new file mode 100644 index 00000000000..2f74fde98eb --- /dev/null +++ b/arch/ppc/platforms/apus_setup.c @@ -0,0 +1,815 @@ +/* + * arch/ppc/platforms/apus_setup.c + * + * Copyright (C) 1998, 1999 Jesper Skov + * + * Basically what is needed to replace functionality found in + * arch/m68k allowing Amiga drivers to work under APUS. + * Bits of code and/or ideas from arch/m68k and arch/ppc files. + * + * TODO: + * This file needs a *really* good cleanup. Restructure and optimize. + * Make sure it can be compiled for non-APUS configs. Begin to move + * Amiga specific stuff into mach/amiga. + */ + +#include +#include +#include +#include +#include +#include + +/* Needs INITSERIAL call in head.S! */ +#undef APUS_DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +unsigned long m68k_machtype; +char debug_device[6] = ""; + +extern void amiga_init_IRQ(void); + +extern void apus_setup_pci_ptrs(void); + +void (*mach_sched_init) (void (*handler)(int, void *, struct pt_regs *)) __initdata = NULL; +/* machine dependent irq functions */ +void (*mach_init_IRQ) (void) __initdata = NULL; +void (*(*mach_default_handler)[]) (int, void *, struct pt_regs *) = NULL; +void (*mach_get_model) (char *model) = NULL; +int (*mach_get_hardware_list) (char *buffer) = NULL; +int (*mach_get_irq_list) (struct seq_file *, void *) = NULL; +void (*mach_process_int) (int, struct pt_regs *) = NULL; +/* machine dependent timer functions */ +unsigned long (*mach_gettimeoffset) (void); +void (*mach_gettod) (int*, int*, int*, int*, int*, int*); +int (*mach_hwclk) (int, struct hwclk_time*) = NULL; +int (*mach_set_clock_mmss) (unsigned long) = NULL; +void (*mach_reset)( void ); +long mach_max_dma_address = 0x00ffffff; /* default set to the lower 16MB */ +#if defined(CONFIG_AMIGA_FLOPPY) +void (*mach_floppy_setup) (char *, int *) __initdata = NULL; +#endif +#ifdef CONFIG_HEARTBEAT +void (*mach_heartbeat) (int) = NULL; +extern void apus_heartbeat (void); +#endif + +extern unsigned long amiga_model; +extern unsigned decrementer_count;/* count value for 1e6/HZ microseconds */ +extern unsigned count_period_num; /* 1 decrementer count equals */ +extern unsigned count_period_den; /* count_period_num / count_period_den us */ + +int num_memory = 0; +struct mem_info memory[NUM_MEMINFO];/* memory description */ +/* FIXME: Duplicate memory data to avoid conflicts with m68k shared code. */ +int m68k_realnum_memory = 0; +struct mem_info m68k_memory[NUM_MEMINFO];/* memory description */ + +struct mem_info ramdisk; + +extern void amiga_floppy_setup(char *, int *); +extern void config_amiga(void); + +static int __60nsram = 0; + +/* for cpuinfo */ +static int __bus_speed = 0; +static int __speed_test_failed = 0; + +/********************************************** COMPILE PROTECTION */ +/* Provide some stubs that links to Amiga specific functions. + * This allows CONFIG_APUS to be removed from generic PPC files while + * preventing link errors for other PPC targets. + */ +unsigned long apus_get_rtc_time(void) +{ +#ifdef CONFIG_APUS + extern unsigned long m68k_get_rtc_time(void); + + return m68k_get_rtc_time (); +#else + return 0; +#endif +} + +int apus_set_rtc_time(unsigned long nowtime) +{ +#ifdef CONFIG_APUS + extern int m68k_set_rtc_time(unsigned long nowtime); + + return m68k_set_rtc_time (nowtime); +#else + return 0; +#endif +} + +/*********************************************************** SETUP */ +/* From arch/m68k/kernel/setup.c. */ +void __init apus_setup_arch(void) +{ +#ifdef CONFIG_APUS + extern char cmd_line[]; + int i; + char *p, *q; + + /* Let m68k-shared code know it should do the Amiga thing. */ + m68k_machtype = MACH_AMIGA; + + /* Parse the command line for arch-specific options. + * For the m68k, this is currently only "debug=xxx" to enable printing + * certain kernel messages to some machine-specific device. */ + for( p = cmd_line; p && *p; ) { + i = 0; + if (!strncmp( p, "debug=", 6 )) { + strlcpy( debug_device, p+6, sizeof(debug_device) ); + if ((q = strchr( debug_device, ' ' ))) *q = 0; + i = 1; + } else if (!strncmp( p, "60nsram", 7 )) { + APUS_WRITE (APUS_REG_WAITSTATE, + REGWAITSTATE_SETRESET + |REGWAITSTATE_PPCR + |REGWAITSTATE_PPCW); + __60nsram = 1; + i = 1; + } + + if (i) { + /* option processed, delete it */ + if ((q = strchr( p, ' ' ))) + strcpy( p, q+1 ); + else + *p = 0; + } else { + if ((p = strchr( p, ' ' ))) ++p; + } + } + + config_amiga(); + +#if 0 /* Enable for logging - also include logging.o in Makefile rule */ + { +#define LOG_SIZE 4096 + void* base; + + /* Throw away some memory - the P5 firmare stomps on top + * of CHIP memory during bootup. + */ + amiga_chip_alloc(0x1000); + + base = amiga_chip_alloc(LOG_SIZE+sizeof(klog_data_t)); + LOG_INIT(base, base+sizeof(klog_data_t), LOG_SIZE); + } +#endif +#endif +} + +int +apus_show_cpuinfo(struct seq_file *m) +{ + extern int __map_without_bats; + extern unsigned long powerup_PCI_present; + + seq_printf(m, "machine\t\t: Amiga\n"); + seq_printf(m, "bus speed\t: %d%s", __bus_speed, + (__speed_test_failed) ? " [failed]\n" : "\n"); + seq_printf(m, "using BATs\t: %s\n", + (__map_without_bats) ? "No" : "Yes"); + seq_printf(m, "ram speed\t: %dns\n", (__60nsram) ? 60 : 70); + seq_printf(m, "PCI bridge\t: %s\n", + (powerup_PCI_present) ? "Yes" : "No"); + return 0; +} + +static void get_current_tb(unsigned long long *time) +{ + __asm __volatile ("1:mftbu 4 \n\t" + " mftb 5 \n\t" + " mftbu 6 \n\t" + " cmpw 4,6 \n\t" + " bne 1b \n\t" + " stw 4,0(%0)\n\t" + " stw 5,4(%0)\n\t" + : + : "r" (time) + : "r4", "r5", "r6"); +} + + +void apus_calibrate_decr(void) +{ +#ifdef CONFIG_APUS + unsigned long freq; + + /* This algorithm for determining the bus speed was + contributed by Ralph Schmidt. */ + unsigned long long start, stop; + int bus_speed; + int speed_test_failed = 0; + + { + unsigned long loop = amiga_eclock / 10; + + get_current_tb (&start); + while (loop--) { + unsigned char tmp; + + tmp = ciaa.pra; + } + get_current_tb (&stop); + } + + bus_speed = (((unsigned long)(stop-start))*10*4) / 1000000; + if (AMI_1200 == amiga_model) + bus_speed /= 2; + + if ((bus_speed >= 47) && (bus_speed < 53)) { + bus_speed = 50; + freq = 12500000; + } else if ((bus_speed >= 57) && (bus_speed < 63)) { + bus_speed = 60; + freq = 15000000; + } else if ((bus_speed >= 63) && (bus_speed < 69)) { + bus_speed = 67; + freq = 16666667; + } else { + printk ("APUS: Unable to determine bus speed (%d). " + "Defaulting to 50MHz", bus_speed); + bus_speed = 50; + freq = 12500000; + speed_test_failed = 1; + } + + /* Ease diagnostics... */ + { + extern int __map_without_bats; + extern unsigned long powerup_PCI_present; + + printk ("APUS: BATs=%d, BUS=%dMHz", + (__map_without_bats) ? 0 : 1, + bus_speed); + if (speed_test_failed) + printk ("[FAILED - please report]"); + + printk (", RAM=%dns, PCI bridge=%d\n", + (__60nsram) ? 60 : 70, + (powerup_PCI_present) ? 1 : 0); + + /* print a bit more if asked politely... */ + if (!(ciaa.pra & 0x40)){ + extern unsigned int bat_addrs[4][3]; + int b; + for (b = 0; b < 4; ++b) { + printk ("APUS: BAT%d ", b); + printk ("%08x-%08x -> %08x\n", + bat_addrs[b][0], + bat_addrs[b][1], + bat_addrs[b][2]); + } + } + + } + + printk("time_init: decrementer frequency = %lu.%.6lu MHz\n", + freq/1000000, freq%1000000); + tb_ticks_per_jiffy = freq / HZ; + tb_to_us = mulhwu_scale_factor(freq, 1000000); + + __bus_speed = bus_speed; + __speed_test_failed = speed_test_failed; +#endif +} + +void arch_gettod(int *year, int *mon, int *day, int *hour, + int *min, int *sec) +{ +#ifdef CONFIG_APUS + if (mach_gettod) + mach_gettod(year, mon, day, hour, min, sec); + else + *year = *mon = *day = *hour = *min = *sec = 0; +#endif +} + +/* for "kbd-reset" cmdline param */ +__init +void kbd_reset_setup(char *str, int *ints) +{ +} + +/*********************************************************** FLOPPY */ +#if defined(CONFIG_AMIGA_FLOPPY) +__init +void floppy_setup(char *str, int *ints) +{ + if (mach_floppy_setup) + mach_floppy_setup (str, ints); +} +#endif + +/*********************************************************** MEMORY */ +#define KMAP_MAX 32 +unsigned long kmap_chunks[KMAP_MAX*3]; +int kmap_chunk_count = 0; + +/* From pgtable.h */ +static __inline__ pte_t *my_find_pte(struct mm_struct *mm,unsigned long va) +{ + pgd_t *dir = 0; + pmd_t *pmd = 0; + pte_t *pte = 0; + + va &= PAGE_MASK; + + dir = pgd_offset( mm, va ); + if (dir) + { + pmd = pmd_offset(dir, va & PAGE_MASK); + if (pmd && pmd_present(*pmd)) + { + pte = pte_offset(pmd, va); + } + } + return pte; +} + + +/* Again simulating an m68k/mm/kmap.c function. */ +void kernel_set_cachemode( unsigned long address, unsigned long size, + unsigned int cmode ) +{ + unsigned long mask, flags; + + switch (cmode) + { + case IOMAP_FULL_CACHING: + mask = ~(_PAGE_NO_CACHE | _PAGE_GUARDED); + flags = 0; + break; + case IOMAP_NOCACHE_SER: + mask = ~0; + flags = (_PAGE_NO_CACHE | _PAGE_GUARDED); + break; + default: + panic ("kernel_set_cachemode() doesn't support mode %d\n", + cmode); + break; + } + + size /= PAGE_SIZE; + address &= PAGE_MASK; + while (size--) + { + pte_t *pte; + + pte = my_find_pte(&init_mm, address); + if ( !pte ) + { + printk("pte NULL in kernel_set_cachemode()\n"); + return; + } + + pte_val (*pte) &= mask; + pte_val (*pte) |= flags; + flush_tlb_page(find_vma(&init_mm,address),address); + + address += PAGE_SIZE; + } +} + +unsigned long mm_ptov (unsigned long paddr) +{ + unsigned long ret; + if (paddr < 16*1024*1024) + ret = ZTWO_VADDR(paddr); + else { + int i; + + for (i = 0; i < kmap_chunk_count;){ + unsigned long phys = kmap_chunks[i++]; + unsigned long size = kmap_chunks[i++]; + unsigned long virt = kmap_chunks[i++]; + if (paddr >= phys + && paddr < (phys + size)){ + ret = virt + paddr - phys; + goto exit; + } + } + + ret = (unsigned long) __va(paddr); + } +exit: +#ifdef DEBUGPV + printk ("PTOV(%lx)=%lx\n", paddr, ret); +#endif + return ret; +} + +int mm_end_of_chunk (unsigned long addr, int len) +{ + if (memory[0].addr + memory[0].size == addr + len) + return 1; + return 0; +} + +/*********************************************************** CACHE */ + +#define L1_CACHE_BYTES 32 +#define MAX_CACHE_SIZE 8192 +void cache_push(__u32 addr, int length) +{ + addr = mm_ptov(addr); + + if (MAX_CACHE_SIZE < length) + length = MAX_CACHE_SIZE; + + while(length > 0){ + __asm ("dcbf 0,%0\n\t" + : : "r" (addr)); + addr += L1_CACHE_BYTES; + length -= L1_CACHE_BYTES; + } + /* Also flush trailing block */ + __asm ("dcbf 0,%0\n\t" + "sync \n\t" + : : "r" (addr)); +} + +void cache_clear(__u32 addr, int length) +{ + if (MAX_CACHE_SIZE < length) + length = MAX_CACHE_SIZE; + + addr = mm_ptov(addr); + + __asm ("dcbf 0,%0\n\t" + "sync \n\t" + "icbi 0,%0 \n\t" + "isync \n\t" + : : "r" (addr)); + + addr += L1_CACHE_BYTES; + length -= L1_CACHE_BYTES; + + while(length > 0){ + __asm ("dcbf 0,%0\n\t" + "sync \n\t" + "icbi 0,%0 \n\t" + "isync \n\t" + : : "r" (addr)); + addr += L1_CACHE_BYTES; + length -= L1_CACHE_BYTES; + } + + __asm ("dcbf 0,%0\n\t" + "sync \n\t" + "icbi 0,%0 \n\t" + "isync \n\t" + : : "r" (addr)); +} + +/****************************************************** from setup.c */ +void +apus_restart(char *cmd) +{ + local_irq_disable(); + + APUS_WRITE(APUS_REG_LOCK, + REGLOCK_BLACKMAGICK1|REGLOCK_BLACKMAGICK2); + APUS_WRITE(APUS_REG_LOCK, + REGLOCK_BLACKMAGICK1|REGLOCK_BLACKMAGICK3); + APUS_WRITE(APUS_REG_LOCK, + REGLOCK_BLACKMAGICK2|REGLOCK_BLACKMAGICK3); + APUS_WRITE(APUS_REG_SHADOW, REGSHADOW_SELFRESET); + APUS_WRITE(APUS_REG_RESET, REGRESET_AMIGARESET); + for(;;); +} + +void +apus_power_off(void) +{ + for (;;); +} + +void +apus_halt(void) +{ + apus_restart(NULL); +} + +/****************************************************** IRQ stuff */ + +static unsigned char last_ipl[8]; + +int apus_get_irq(struct pt_regs* regs) +{ + unsigned char ipl_emu, mask; + unsigned int level; + + APUS_READ(APUS_IPL_EMU, ipl_emu); + level = (ipl_emu >> 3) & IPLEMU_IPLMASK; + mask = IPLEMU_SETRESET|IPLEMU_DISABLEINT|level; + level ^= 7; + + /* Save previous IPL value */ + if (last_ipl[level]) + return -2; + last_ipl[level] = ipl_emu; + + /* Set to current IPL value */ + APUS_WRITE(APUS_IPL_EMU, mask); + APUS_WRITE(APUS_IPL_EMU, IPLEMU_DISABLEINT|level); + + +#ifdef __INTERRUPT_DEBUG + printk("<%d:%d>", level, ~ipl_emu & IPLEMU_IPLMASK); +#endif + return level + IRQ_AMIGA_AUTO; +} + +void apus_end_irq(unsigned int irq) +{ + unsigned char ipl_emu; + unsigned int level = irq - IRQ_AMIGA_AUTO; +#ifdef __INTERRUPT_DEBUG + printk("{%d}", ~last_ipl[level] & IPLEMU_IPLMASK); +#endif + /* Restore IPL to the previous value */ + ipl_emu = last_ipl[level] & IPLEMU_IPLMASK; + APUS_WRITE(APUS_IPL_EMU, IPLEMU_SETRESET|IPLEMU_DISABLEINT|ipl_emu); + last_ipl[level] = 0; + ipl_emu ^= 7; + APUS_WRITE(APUS_IPL_EMU, IPLEMU_DISABLEINT|ipl_emu); +} + +/****************************************************** debugging */ + +/* some serial hardware definitions */ +#define SDR_OVRUN (1<<15) +#define SDR_RBF (1<<14) +#define SDR_TBE (1<<13) +#define SDR_TSRE (1<<12) + +#define AC_SETCLR (1<<15) +#define AC_UARTBRK (1<<11) + +#define SER_DTR (1<<7) +#define SER_RTS (1<<6) +#define SER_DCD (1<<5) +#define SER_CTS (1<<4) +#define SER_DSR (1<<3) + +static __inline__ void ser_RTSon(void) +{ + ciab.pra &= ~SER_RTS; /* active low */ +} + +int __debug_ser_out( unsigned char c ) +{ + custom.serdat = c | 0x100; + mb(); + while (!(custom.serdatr & 0x2000)) + barrier(); + return 1; +} + +unsigned char __debug_ser_in( void ) +{ + unsigned char c; + + /* XXX: is that ok?? derived from amiga_ser.c... */ + while( !(custom.intreqr & IF_RBF) ) + barrier(); + c = custom.serdatr; + /* clear the interrupt, so that another character can be read */ + custom.intreq = IF_RBF; + return c; +} + +int __debug_serinit( void ) +{ + unsigned long flags; + + local_irq_save(flags); + + /* turn off Rx and Tx interrupts */ + custom.intena = IF_RBF | IF_TBE; + + /* clear any pending interrupt */ + custom.intreq = IF_RBF | IF_TBE; + + local_irq_restore(flags); + + /* + * set the appropriate directions for the modem control flags, + * and clear RTS and DTR + */ + ciab.ddra |= (SER_DTR | SER_RTS); /* outputs */ + ciab.ddra &= ~(SER_DCD | SER_CTS | SER_DSR); /* inputs */ + +#ifdef CONFIG_KGDB + /* turn Rx interrupts on for GDB */ + custom.intena = IF_SETCLR | IF_RBF; + ser_RTSon(); +#endif + + return 0; +} + +void __debug_print_hex(unsigned long x) +{ + int i; + char hexchars[] = "0123456789ABCDEF"; + + for (i = 0; i < 8; i++) { + __debug_ser_out(hexchars[(x >> 28) & 15]); + x <<= 4; + } + __debug_ser_out('\n'); + __debug_ser_out('\r'); +} + +void __debug_print_string(char* s) +{ + unsigned char c; + while((c = *s++)) + __debug_ser_out(c); + __debug_ser_out('\n'); + __debug_ser_out('\r'); +} + +static void apus_progress(char *s, unsigned short value) +{ + __debug_print_string(s); +} + +/****************************************************** init */ + +/* The number of spurious interrupts */ +volatile unsigned int num_spurious; + +extern struct irqaction amiga_sys_irqaction[AUTO_IRQS]; + + +extern void amiga_enable_irq(unsigned int irq); +extern void amiga_disable_irq(unsigned int irq); + +struct hw_interrupt_type amiga_sys_irqctrl = { + .typename = "Amiga IPL", + .end = apus_end_irq, +}; + +struct hw_interrupt_type amiga_irqctrl = { + .typename = "Amiga ", + .enable = amiga_enable_irq, + .disable = amiga_disable_irq, +}; + +#define HARDWARE_MAPPED_SIZE (512*1024) +unsigned long __init apus_find_end_of_memory(void) +{ + int shadow = 0; + unsigned long total; + + /* The memory size reported by ADOS excludes the 512KB + reserved for PPC exception registers and possibly 512KB + containing a shadow of the ADOS ROM. */ + { + unsigned long size = memory[0].size; + + /* If 2MB aligned, size was probably user + specified. We can't tell anything about shadowing + in this case so skip shadow assignment. */ + if (0 != (size & 0x1fffff)){ + /* Align to 512KB to ensure correct handling + of both memfile and system specified + sizes. */ + size = ((size+0x0007ffff) & 0xfff80000); + /* If memory is 1MB aligned, assume + shadowing. */ + shadow = !(size & 0x80000); + } + + /* Add the chunk that ADOS does not see. by aligning + the size to the nearest 2MB limit upwards. */ + memory[0].size = ((size+0x001fffff) & 0xffe00000); + } + + ppc_memstart = memory[0].addr; + ppc_memoffset = PAGE_OFFSET - PPC_MEMSTART; + total = memory[0].size; + + /* Remove the memory chunks that are controlled by special + Phase5 hardware. */ + + /* Remove the upper 512KB if it contains a shadow of + the ADOS ROM. FIXME: It might be possible to + disable this shadow HW. Check the booter + (ppc_boot.c) */ + if (shadow) + total -= HARDWARE_MAPPED_SIZE; + + /* Remove the upper 512KB where the PPC exception + vectors are mapped. */ + total -= HARDWARE_MAPPED_SIZE; + + /* Linux/APUS only handles one block of memory -- the one on + the PowerUP board. Other system memory is horrible slow in + comparison. The user can use other memory for swapping + using the z2ram device. */ + return total; +} + +static void __init +apus_map_io(void) +{ + /* Map PPC exception vectors. */ + io_block_mapping(0xfff00000, 0xfff00000, 0x00020000, _PAGE_KERNEL); + /* Map chip and ZorroII memory */ + io_block_mapping(zTwoBase, 0x00000000, 0x01000000, _PAGE_IO); +} + +__init +void apus_init_IRQ(void) +{ + struct irqaction *action; + int i; + +#ifdef CONFIG_PCI + apus_setup_pci_ptrs(); +#endif + + for ( i = 0 ; i < AMI_IRQS; i++ ) { + irq_desc[i].status = IRQ_LEVEL; + if (i < IRQ_AMIGA_AUTO) { + irq_desc[i].handler = &amiga_irqctrl; + } else { + irq_desc[i].handler = &amiga_sys_irqctrl; + action = &amiga_sys_irqaction[i-IRQ_AMIGA_AUTO]; + if (action->name) + setup_irq(i, action); + } + } + + amiga_init_IRQ(); + +} + +__init +void platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + extern int parse_bootinfo(const struct bi_record *); + extern char _end[]; + + /* Parse bootinfo. The bootinfo is located right after + the kernel bss */ + parse_bootinfo((const struct bi_record *)&_end); +#ifdef CONFIG_BLK_DEV_INITRD + /* Take care of initrd if we have one. Use data from + bootinfo to avoid the need to initialize PPC + registers when kernel is booted via a PPC reset. */ + if ( ramdisk.addr ) { + initrd_start = (unsigned long) __va(ramdisk.addr); + initrd_end = (unsigned long) + __va(ramdisk.size + ramdisk.addr); + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + ISA_DMA_THRESHOLD = 0x00ffffff; + + ppc_md.setup_arch = apus_setup_arch; + ppc_md.show_cpuinfo = apus_show_cpuinfo; + ppc_md.init_IRQ = apus_init_IRQ; + ppc_md.get_irq = apus_get_irq; + +#ifdef CONFIG_HEARTBEAT + ppc_md.heartbeat = apus_heartbeat; + ppc_md.heartbeat_count = 1; +#endif +#ifdef APUS_DEBUG + __debug_serinit(); + ppc_md.progress = apus_progress; +#endif + ppc_md.init = NULL; + + ppc_md.restart = apus_restart; + ppc_md.power_off = apus_power_off; + ppc_md.halt = apus_halt; + + ppc_md.time_init = NULL; + ppc_md.set_rtc_time = apus_set_rtc_time; + ppc_md.get_rtc_time = apus_get_rtc_time; + ppc_md.calibrate_decr = apus_calibrate_decr; + + ppc_md.find_end_of_memory = apus_find_end_of_memory; + ppc_md.setup_io_mappings = apus_map_io; +} diff --git a/arch/ppc/platforms/bseip.h b/arch/ppc/platforms/bseip.h new file mode 100644 index 00000000000..691f4a52b0a --- /dev/null +++ b/arch/ppc/platforms/bseip.h @@ -0,0 +1,38 @@ +/* + * A collection of structures, addresses, and values associated with + * the Bright Star Engineering ip-Engine board. Copied from the MBX stuff. + * + * Copyright (c) 1998 Dan Malek (dmalek@jlc.net) + */ +#ifndef __MACH_BSEIP_DEFS +#define __MACH_BSEIP_DEFS + +#ifndef __ASSEMBLY__ +/* A Board Information structure that is given to a program when + * prom starts it up. + */ +typedef struct bd_info { + unsigned int bi_memstart; /* Memory start address */ + unsigned int bi_memsize; /* Memory (end) size in bytes */ + unsigned int bi_intfreq; /* Internal Freq, in Hz */ + unsigned int bi_busfreq; /* Bus Freq, in Hz */ + unsigned char bi_enetaddr[6]; + unsigned int bi_baudrate; +} bd_t; + +extern bd_t m8xx_board_info; + +/* Memory map is configured by the PROM startup. + * All we need to get started is the IMMR. + */ +#define IMAP_ADDR ((uint)0xff000000) +#define IMAP_SIZE ((uint)(64 * 1024)) +#define PCMCIA_MEM_ADDR ((uint)0x04000000) +#define PCMCIA_MEM_SIZE ((uint)(64 * 1024)) +#endif /* !__ASSEMBLY__ */ + +/* We don't use the 8259. +*/ +#define NR_8259_INTS 0 + +#endif diff --git a/arch/ppc/platforms/ccm.h b/arch/ppc/platforms/ccm.h new file mode 100644 index 00000000000..edb87b57383 --- /dev/null +++ b/arch/ppc/platforms/ccm.h @@ -0,0 +1,28 @@ +/* + * Siemens Card Controller Module specific definitions + * + * Copyright (C) 2001-2002 Wolfgang Denk (wd@denx.de) + */ + +#ifndef __MACH_CCM_H +#define __MACH_CCM_H + +#include + +#include + +#define CCM_IMMR_BASE 0xF0000000 /* phys. addr of IMMR */ +#define CCM_IMAP_SIZE (64 * 1024) /* size of mapped area */ + +#define IMAP_ADDR CCM_IMMR_BASE /* physical base address of IMMR area */ +#define IMAP_SIZE CCM_IMAP_SIZE /* mapped size of IMMR area */ + +#define FEC_INTERRUPT 13 /* = SIU_LEVEL6 */ +#define DEC_INTERRUPT 11 /* = SIU_LEVEL5 */ +#define CPM_INTERRUPT 9 /* = SIU_LEVEL4 */ + +/* We don't use the 8259. +*/ +#define NR_8259_INTS 0 + +#endif /* __MACH_CCM_H */ diff --git a/arch/ppc/platforms/chestnut.c b/arch/ppc/platforms/chestnut.c new file mode 100644 index 00000000000..7786818bd9d --- /dev/null +++ b/arch/ppc/platforms/chestnut.c @@ -0,0 +1,580 @@ +/* + * arch/ppc/platforms/chestnut.c + * + * Board setup routines for IBM Chestnut + * + * Author: + * + * <2004> (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void __iomem *sram_base; /* Virtual addr of Internal SRAM */ +static void __iomem *cpld_base; /* Virtual addr of CPLD Regs */ + +static mv64x60_handle_t bh; + +extern void gen550_progress(char *, unsigned short); +extern void gen550_init(int, struct uart_port *); +extern void mv64360_pcibios_fixup(mv64x60_handle_t *bh); + +#define BIT(x) (1<first_busno = 0; + bh.hose_a->last_busno = 0xff; + bh.hose_a->last_busno = pciauto_bus_scan(bh.hose_a, 0); +} + +void __init +chestnut_setup_peripherals(void) +{ + mv64x60_set_32bit_window(&bh, MV64x60_CPU2BOOT_WIN, + CHESTNUT_BOOT_8BIT_BASE, CHESTNUT_BOOT_8BIT_SIZE, 0); + bh.ci->enable_window_32bit(&bh, MV64x60_CPU2BOOT_WIN); + + mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_0_WIN, + CHESTNUT_32BIT_BASE, CHESTNUT_32BIT_SIZE, 0); + bh.ci->enable_window_32bit(&bh, MV64x60_CPU2DEV_0_WIN); + + mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_1_WIN, + CHESTNUT_CPLD_BASE, CHESTNUT_CPLD_SIZE, 0); + bh.ci->enable_window_32bit(&bh, MV64x60_CPU2DEV_1_WIN); + cpld_base = ioremap(CHESTNUT_CPLD_BASE, CHESTNUT_CPLD_SIZE); + + mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_2_WIN, + CHESTNUT_UART_BASE, CHESTNUT_UART_SIZE, 0); + bh.ci->enable_window_32bit(&bh, MV64x60_CPU2DEV_2_WIN); + + mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_3_WIN, + CHESTNUT_FRAM_BASE, CHESTNUT_FRAM_SIZE, 0); + bh.ci->enable_window_32bit(&bh, MV64x60_CPU2DEV_3_WIN); + + mv64x60_set_32bit_window(&bh, MV64x60_CPU2SRAM_WIN, + CHESTNUT_INTERNAL_SRAM_BASE, MV64360_SRAM_SIZE, 0); + bh.ci->enable_window_32bit(&bh, MV64x60_CPU2SRAM_WIN); + +#ifdef CONFIG_NOT_COHERENT_CACHE + mv64x60_write(&bh, MV64360_SRAM_CONFIG, 0x001600b0); +#else + mv64x60_write(&bh, MV64360_SRAM_CONFIG, 0x001600b2); +#endif + sram_base = ioremap(CHESTNUT_INTERNAL_SRAM_BASE, MV64360_SRAM_SIZE); + memset(sram_base, 0, MV64360_SRAM_SIZE); + + /* + * Configure MPP pins for PCI DMA + * + * PCI Slot GNT pin REQ pin + * 0 MPP16 MPP17 + * 1 MPP18 MPP19 + * 2 MPP20 MPP21 + * 3 MPP22 MPP23 + */ + mv64x60_write(&bh, MV64x60_MPP_CNTL_2, + (0x1 << 0) | /* MPPSel16 PCI0_GNT[0] */ + (0x1 << 4) | /* MPPSel17 PCI0_REQ[0] */ + (0x1 << 8) | /* MPPSel18 PCI0_GNT[1] */ + (0x1 << 12) | /* MPPSel19 PCI0_REQ[1] */ + (0x1 << 16) | /* MPPSel20 PCI0_GNT[2] */ + (0x1 << 20) | /* MPPSel21 PCI0_REQ[2] */ + (0x1 << 24) | /* MPPSel22 PCI0_GNT[3] */ + (0x1 << 28)); /* MPPSel23 PCI0_REQ[3] */ + /* + * Set unused MPP pins for output, as per schematic note + * + * Unused Pins: MPP01, MPP02, MPP04, MPP05, MPP06 + * MPP09, MPP10, MPP13, MPP14, MPP15 + */ + mv64x60_clr_bits(&bh, MV64x60_MPP_CNTL_0, + (0xf << 4) | /* MPPSel01 GPIO[1] */ + (0xf << 8) | /* MPPSel02 GPIO[2] */ + (0xf << 16) | /* MPPSel04 GPIO[4] */ + (0xf << 20) | /* MPPSel05 GPIO[5] */ + (0xf << 24)); /* MPPSel06 GPIO[6] */ + mv64x60_clr_bits(&bh, MV64x60_MPP_CNTL_1, + (0xf << 4) | /* MPPSel09 GPIO[9] */ + (0xf << 8) | /* MPPSel10 GPIO[10] */ + (0xf << 20) | /* MPPSel13 GPIO[13] */ + (0xf << 24) | /* MPPSel14 GPIO[14] */ + (0xf << 28)); /* MPPSel15 GPIO[15] */ + mv64x60_set_bits(&bh, MV64x60_GPP_IO_CNTL, /* Output */ + BIT(1) | BIT(2) | BIT(4) | BIT(5) | BIT(6) | + BIT(9) | BIT(10) | BIT(13) | BIT(14) | BIT(15)); + + /* + * Configure the following MPP pins to indicate a level + * triggered interrupt + * + * MPP24 - Board Reset (just map the MPP & GPP for chestnut_reset) + * MPP25 - UART A (high) + * MPP26 - UART B (high) + * MPP28 - PCI Slot 3 (low) + * MPP29 - PCI Slot 2 (low) + * MPP30 - PCI Slot 1 (low) + * MPP31 - PCI Slot 0 (low) + */ + mv64x60_clr_bits(&bh, MV64x60_MPP_CNTL_3, + BIT(3) | BIT(2) | BIT(1) | BIT(0) | /* MPP 24 */ + BIT(7) | BIT(6) | BIT(5) | BIT(4) | /* MPP 25 */ + BIT(11) | BIT(10) | BIT(9) | BIT(8) | /* MPP 26 */ + BIT(19) | BIT(18) | BIT(17) | BIT(16) | /* MPP 28 */ + BIT(23) | BIT(22) | BIT(21) | BIT(20) | /* MPP 29 */ + BIT(27) | BIT(26) | BIT(25) | BIT(24) | /* MPP 30 */ + BIT(31) | BIT(30) | BIT(29) | BIT(28)); /* MPP 31 */ + + /* + * Define GPP 25 (high), 26 (high), 28 (low), 29 (low), 30 (low), + * 31 (low) interrupt polarity input signal and level triggered + */ + mv64x60_clr_bits(&bh, MV64x60_GPP_LEVEL_CNTL, BIT(25) | BIT(26)); + mv64x60_set_bits(&bh, MV64x60_GPP_LEVEL_CNTL, + BIT(28) | BIT(29) | BIT(30) | BIT(31)); + mv64x60_clr_bits(&bh, MV64x60_GPP_IO_CNTL, + BIT(25) | BIT(26) | BIT(28) | BIT(29) | BIT(30) | + BIT(31)); + + /* Config GPP interrupt controller to respond to level trigger */ + mv64x60_set_bits(&bh, MV64360_COMM_ARBITER_CNTL, BIT(10)); + + /* + * Dismiss and then enable interrupt on GPP interrupt cause for CPU #0 + */ + mv64x60_write(&bh, MV64x60_GPP_INTR_CAUSE, + ~(BIT(25) | BIT(26) | BIT(28) | BIT(29) | BIT(30) | + BIT(31))); + mv64x60_set_bits(&bh, MV64x60_GPP_INTR_MASK, + BIT(25) | BIT(26) | BIT(28) | BIT(29) | BIT(30) | + BIT(31)); + + /* + * Dismiss and then enable interrupt on CPU #0 high cause register + * BIT27 summarizes GPP interrupts 24-31 + */ + mv64x60_set_bits(&bh, MV64360_IC_CPU0_INTR_MASK_HI, BIT(27)); + + if (ppc_md.progress) + ppc_md.progress("chestnut_setup_bridge: exit", 0); +} + +/************************************************************************** + * FUNCTION: chestnut_setup_arch + * + * DESCRIPTION: ppc_md machine configuration callback + * + ****/ +static void __init +chestnut_setup_arch(void) +{ + if (ppc_md.progress) + ppc_md.progress("chestnut_setup_arch: enter", 0); + + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_jiffy = 50000000 / HZ; + + /* if the time base value is greater than bus freq/4 (the TB and + * decrementer tick rate) + signed integer rollover value, we + * can spend a fair amount of time waiting for the rollover to + * happen. To get around this, initialize the time base register + * to a "safe" value. + */ + set_tb(0, 0); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = Root_NFS; +#else + ROOT_DEV = Root_SDA2; +#endif + + /* + * Set up the L2CR register. + */ + _set_L2CR(_get_L2CR() | L2CR_L2E); + + chestnut_setup_bridge(); + chestnut_setup_peripherals(); + +#ifdef CONFIG_DUMMY_CONSOLE + conswitchp = &dummy_con; +#endif + +#if defined(CONFIG_SERIAL_8250) + chestnut_early_serial_map(); +#endif + + /* Identify the system */ + printk(KERN_INFO "System Identification: IBM 750FX/GX Eval Board\n"); + printk(KERN_INFO "IBM 750FX/GX port (C) 2004 MontaVista Software, Inc." + " (source@mvista.com)\n"); + + if (ppc_md.progress) + ppc_md.progress("chestnut_setup_arch: exit", 0); +} + +#ifdef CONFIG_MTD_PHYSMAP +static struct mtd_partition ptbl; + +static int __init +chestnut_setup_mtd(void) +{ + memset(&ptbl, 0, sizeof(ptbl)); + + ptbl.name = "User FS"; + ptbl.size = CHESTNUT_32BIT_SIZE; + + physmap_map.size = CHESTNUT_32BIT_SIZE; + physmap_set_partitions(&ptbl, 1); + return 0; +} + +arch_initcall(chestnut_setup_mtd); +#endif + +/************************************************************************** + * FUNCTION: chestnut_restart + * + * DESCRIPTION: ppc_md machine reset callback + * reset the board via the CPLD command register + * + ****/ +static void +chestnut_restart(char *cmd) +{ + volatile ulong i = 10000000; + + local_irq_disable(); + + /* + * Set CPLD Reg 3 bit 0 to 1 to allow MPP signals on reset to work + * + * MPP24 - board reset + */ + writeb(0x1, cpld_base + 3); + + /* GPP pin tied to MPP earlier */ + mv64x60_set_bits(&bh, MV64x60_GPP_VALUE_SET, BIT(24)); + + while (i-- > 0); + panic("restart failed\n"); +} + +static void +chestnut_halt(void) +{ + local_irq_disable(); + for (;;); + /* NOTREACHED */ +} + +static void +chestnut_power_off(void) +{ + chestnut_halt(); + /* NOTREACHED */ +} + +/************************************************************************** + * FUNCTION: chestnut_map_io + * + * DESCRIPTION: configure fixed memory-mapped IO + * + ****/ +static void __init +chestnut_map_io(void) +{ +#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) + io_block_mapping(CHESTNUT_UART_BASE, CHESTNUT_UART_BASE, 0x100000, + _PAGE_IO); +#endif +} + +/************************************************************************** + * FUNCTION: chestnut_set_bat + * + * DESCRIPTION: configures a (temporary) bat mapping for early access to + * device I/O + * + ****/ +static __inline__ void +chestnut_set_bat(void) +{ + mb(); + mtspr(SPRN_DBAT3U, 0xf0001ffe); + mtspr(SPRN_DBAT3L, 0xf000002a); + mb(); +} + +/************************************************************************** + * FUNCTION: platform_init + * + * DESCRIPTION: main entry point for configuring board-specific machine + * callbacks + * + ****/ +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + + /* Copy the kernel command line arguments to a safe place. */ + + if (r6) { + *(char *) (r7 + KERNELBASE) = 0; + strcpy(cmd_line, (char *) (r6 + KERNELBASE)); + } + + isa_mem_base = 0; + + ppc_md.setup_arch = chestnut_setup_arch; + ppc_md.show_cpuinfo = chestnut_show_cpuinfo; + ppc_md.irq_canonicalize = NULL; + ppc_md.init_IRQ = mv64360_init_irq; + ppc_md.get_irq = mv64360_get_irq; + ppc_md.init = NULL; + + ppc_md.find_end_of_memory = chestnut_find_end_of_memory; + ppc_md.setup_io_mappings = chestnut_map_io; + + ppc_md.restart = chestnut_restart; + ppc_md.power_off = chestnut_power_off; + ppc_md.halt = chestnut_halt; + + ppc_md.time_init = NULL; + ppc_md.set_rtc_time = NULL; + ppc_md.get_rtc_time = NULL; + ppc_md.calibrate_decr = chestnut_calibrate_decr; + + ppc_md.nvram_read_val = NULL; + ppc_md.nvram_write_val = NULL; + + ppc_md.heartbeat = NULL; + + bh.p_base = CONFIG_MV64X60_NEW_BASE; + + chestnut_set_bat(); + +#if defined(CONFIG_SERIAL_TEXT_DEBUG) + ppc_md.progress = gen550_progress; +#endif +#if defined(CONFIG_KGDB) + ppc_md.kgdb_map_scc = gen550_kgdb_map_scc; +#endif + + if (ppc_md.progress) + ppc_md.progress("chestnut_init(): exit", 0); +} diff --git a/arch/ppc/platforms/chestnut.h b/arch/ppc/platforms/chestnut.h new file mode 100644 index 00000000000..0400b2be40a --- /dev/null +++ b/arch/ppc/platforms/chestnut.h @@ -0,0 +1,129 @@ +/* + * arch/ppc/platforms/chestnut.h + * + * Definitions for IBM 750FXGX Eval (Chestnut) + * + * Author: + * + * Based on Artesyn Katana code done by Tim Montgomery + * Based on code done by Rabeeh Khoury - rabeeh@galileo.co.il + * Based on code done by Mark A. Greer + * + * <2004> (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +/* + * This is the CPU physical memory map (windows must be at least 1MB and start + * on a boundary that is a multiple of the window size): + * + * Seems on the IBM 750FXGX Eval board, the MV64460 Registers can be in + * only 2 places per switch U17 0x14000000 or 0xf1000000 easily - chose to + * implement at 0xf1000000 only at this time + * + * 0xfff00000-0xffffffff - 8 Flash + * 0xffe00000-0xffefffff - BOOT SRAM + * 0xffd00000-0xffd00004 - CPLD + * 0xffc00000-0xffc0000f - UART + * 0xffb00000-0xffb07fff - FRAM + * 0xff840000-0xffafffff - *** HOLE *** + * 0xff800000-0xff83ffff - MV64460 Integrated SRAM + * 0xfe000000-0xff8fffff - *** HOLE *** + * 0xfc000000-0xfdffffff - 32bit Flash + * 0xf1010000-0xfbffffff - *** HOLE *** + * 0xf1000000-0xf100ffff - MV64460 Registers + */ + +#ifndef __PPC_PLATFORMS_CHESTNUT_H__ +#define __PPC_PLATFORMS_CHESTNUT_H__ + +#define CHESTNUT_BOOT_8BIT_BASE 0xfff00000 +#define CHESTNUT_BOOT_8BIT_SIZE_ACTUAL (1024*1024) +#define CHESTNUT_BOOT_SRAM_BASE 0xffe00000 +#define CHESTNUT_BOOT_SRAM_SIZE_ACTUAL (1024*1024) +#define CHESTNUT_CPLD_BASE 0xffd00000 +#define CHESTNUT_CPLD_SIZE_ACTUAL 5 +#define CHESTNUT_CPLD_REG3 (CHESTNUT_CPLD_BASE+3) +#define CHESTNUT_UART_BASE 0xffc00000 +#define CHESTNUT_UART_SIZE_ACTUAL 16 +#define CHESTNUT_FRAM_BASE 0xffb00000 +#define CHESTNUT_FRAM_SIZE_ACTUAL (32*1024) +#define CHESTNUT_INTERNAL_SRAM_BASE 0xff800000 +#define CHESTNUT_32BIT_BASE 0xfc000000 +#define CHESTNUT_32BIT_SIZE (32*1024*1024) + +#define CHESTNUT_BOOT_8BIT_SIZE max(MV64360_WINDOW_SIZE_MIN, \ + CHESTNUT_BOOT_8BIT_SIZE_ACTUAL) +#define CHESTNUT_BOOT_SRAM_SIZE max(MV64360_WINDOW_SIZE_MIN, \ + CHESTNUT_BOOT_SRAM_SIZE_ACTUAL) +#define CHESTNUT_CPLD_SIZE max(MV64360_WINDOW_SIZE_MIN, \ + CHESTNUT_CPLD_SIZE_ACTUAL) +#define CHESTNUT_UART_SIZE max(MV64360_WINDOW_SIZE_MIN, \ + CHESTNUT_UART_SIZE_ACTUAL) +#define CHESTNUT_FRAM_SIZE max(MV64360_WINDOW_SIZE_MIN, \ + CHESTNUT_FRAM_SIZE_ACTUAL) + +#define CHESTNUT_BUS_SPEED 200000000 +#define CHESTNUT_PIBS_DATABASE 0xf0000 /* from PIBS src code */ + +#define KATANA_ETH0_PHY_ADDR 12 +#define KATANA_ETH1_PHY_ADDR 11 +#define KATANA_ETH2_PHY_ADDR 4 + +#define CHESTNUT_ETH_TX_QUEUE_SIZE 800 +#define CHESTNUT_ETH_RX_QUEUE_SIZE 400 + +/* + * PCI windows + */ + +#define CHESTNUT_PCI0_MEM_PROC_ADDR 0x80000000 +#define CHESTNUT_PCI0_MEM_PCI_HI_ADDR 0x00000000 +#define CHESTNUT_PCI0_MEM_PCI_LO_ADDR 0x80000000 +#define CHESTNUT_PCI0_MEM_SIZE 0x10000000 +#define CHESTNUT_PCI0_IO_PROC_ADDR 0xa0000000 +#define CHESTNUT_PCI0_IO_PCI_ADDR 0x00000000 +#define CHESTNUT_PCI0_IO_SIZE 0x01000000 + +/* + * Board-specific IRQ info + */ +#define CHESTNUT_PCI_SLOT0_IRQ (64 + 31) +#define CHESTNUT_PCI_SLOT1_IRQ (64 + 30) +#define CHESTNUT_PCI_SLOT2_IRQ (64 + 29) +#define CHESTNUT_PCI_SLOT3_IRQ (64 + 28) + +/* serial port definitions */ +#define CHESTNUT_UART0_IO_BASE (CHESTNUT_UART_BASE + 8) +#define CHESTNUT_UART1_IO_BASE CHESTNUT_UART_BASE + +#define UART0_INT (64 + 25) +#define UART1_INT (64 + 26) + +#ifdef CONFIG_SERIAL_MANY_PORTS +#define RS_TABLE_SIZE 64 +#else +#define RS_TABLE_SIZE 2 +#endif + +/* Rate for the 3.6864 Mhz clock for the onboard serial chip */ +#define BASE_BAUD (3686400 / 16) + +#ifdef CONFIG_SERIAL_DETECT_IRQ +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) +#else +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST) +#endif + +#define STD_UART_OP(num) \ + { 0, BASE_BAUD, 0, UART##num##_INT, STD_COM_FLAGS, \ + iomem_base: (u8 *)CHESTNUT_UART##num##_IO_BASE, \ + io_type: SERIAL_IO_MEM}, + +#define SERIAL_PORT_DFNS \ + STD_UART_OP(0) \ + STD_UART_OP(1) + +#endif /* __PPC_PLATFORMS_CHESTNUT_H__ */ diff --git a/arch/ppc/platforms/chrp_pci.c b/arch/ppc/platforms/chrp_pci.c new file mode 100644 index 00000000000..5bb6492ecf8 --- /dev/null +++ b/arch/ppc/platforms/chrp_pci.c @@ -0,0 +1,309 @@ +/* + * CHRP pci routines. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* LongTrail */ +void __iomem *gg2_pci_config_base; + +/* + * The VLSI Golden Gate II has only 512K of PCI configuration space, so we + * limit the bus number to 3 bits + */ + +int __chrp gg2_read_config(struct pci_bus *bus, unsigned int devfn, int off, + int len, u32 *val) +{ + volatile void __iomem *cfg_data; + struct pci_controller *hose = bus->sysdata; + + if (bus->number > 7) + return PCIBIOS_DEVICE_NOT_FOUND; + /* + * Note: the caller has already checked that off is + * suitably aligned and that len is 1, 2 or 4. + */ + cfg_data = hose->cfg_data + ((bus->number<<16) | (devfn<<8) | off); + switch (len) { + case 1: + *val = in_8(cfg_data); + break; + case 2: + *val = in_le16(cfg_data); + break; + default: + *val = in_le32(cfg_data); + break; + } + return PCIBIOS_SUCCESSFUL; +} + +int __chrp gg2_write_config(struct pci_bus *bus, unsigned int devfn, int off, + int len, u32 val) +{ + volatile void __iomem *cfg_data; + struct pci_controller *hose = bus->sysdata; + + if (bus->number > 7) + return PCIBIOS_DEVICE_NOT_FOUND; + /* + * Note: the caller has already checked that off is + * suitably aligned and that len is 1, 2 or 4. + */ + cfg_data = hose->cfg_data + ((bus->number<<16) | (devfn<<8) | off); + switch (len) { + case 1: + out_8(cfg_data, val); + break; + case 2: + out_le16(cfg_data, val); + break; + default: + out_le32(cfg_data, val); + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops gg2_pci_ops = +{ + gg2_read_config, + gg2_write_config +}; + +/* + * Access functions for PCI config space using RTAS calls. + */ +int __chrp +rtas_read_config(struct pci_bus *bus, unsigned int devfn, int offset, + int len, u32 *val) +{ + struct pci_controller *hose = bus->sysdata; + unsigned long addr = (offset & 0xff) | ((devfn & 0xff) << 8) + | (((bus->number - hose->first_busno) & 0xff) << 16) + | (hose->index << 24); + unsigned long ret = ~0UL; + int rval; + + rval = call_rtas("read-pci-config", 2, 2, &ret, addr, len); + *val = ret; + return rval? PCIBIOS_DEVICE_NOT_FOUND: PCIBIOS_SUCCESSFUL; +} + +int __chrp +rtas_write_config(struct pci_bus *bus, unsigned int devfn, int offset, + int len, u32 val) +{ + struct pci_controller *hose = bus->sysdata; + unsigned long addr = (offset & 0xff) | ((devfn & 0xff) << 8) + | (((bus->number - hose->first_busno) & 0xff) << 16) + | (hose->index << 24); + int rval; + + rval = call_rtas("write-pci-config", 3, 1, NULL, addr, len, val); + return rval? PCIBIOS_DEVICE_NOT_FOUND: PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops rtas_pci_ops = +{ + rtas_read_config, + rtas_write_config +}; + +volatile struct Hydra *Hydra = NULL; + +int __init +hydra_init(void) +{ + struct device_node *np; + + np = find_devices("mac-io"); + if (np == NULL || np->n_addrs == 0) + return 0; + Hydra = ioremap(np->addrs[0].address, np->addrs[0].size); + printk("Hydra Mac I/O at %x\n", np->addrs[0].address); + printk("Hydra Feature_Control was %x", + in_le32(&Hydra->Feature_Control)); + out_le32(&Hydra->Feature_Control, (HYDRA_FC_SCC_CELL_EN | + HYDRA_FC_SCSI_CELL_EN | + HYDRA_FC_SCCA_ENABLE | + HYDRA_FC_SCCB_ENABLE | + HYDRA_FC_ARB_BYPASS | + HYDRA_FC_MPIC_ENABLE | + HYDRA_FC_SLOW_SCC_PCLK | + HYDRA_FC_MPIC_IS_MASTER)); + printk(", now %x\n", in_le32(&Hydra->Feature_Control)); + return 1; +} + +void __init +chrp_pcibios_fixup(void) +{ + struct pci_dev *dev = NULL; + struct device_node *np; + + /* PCI interrupts are controlled by the OpenPIC */ + for_each_pci_dev(dev) { + np = pci_device_to_OF_node(dev); + if ((np != 0) && (np->n_intrs > 0) && (np->intrs[0].line != 0)) + dev->irq = np->intrs[0].line; + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); + } +} + +#define PRG_CL_RESET_VALID 0x00010000 + +static void __init +setup_python(struct pci_controller *hose, struct device_node *dev) +{ + u32 *reg, val; + unsigned long addr = dev->addrs[0].address; + + setup_indirect_pci(hose, addr + 0xf8000, addr + 0xf8010); + + /* Clear the magic go-slow bit */ + reg = (u32 *) ioremap(dev->addrs[0].address + 0xf6000, 0x40); + val = in_be32(®[12]); + if (val & PRG_CL_RESET_VALID) { + out_be32(®[12], val & ~PRG_CL_RESET_VALID); + in_be32(®[12]); + } + iounmap(reg); +} + +/* Marvell Discovery II based Pegasos 2 */ +static void __init setup_peg2(struct pci_controller *hose, struct device_node *dev) +{ + struct device_node *root = find_path_device("/"); + struct device_node *rtas; + + rtas = of_find_node_by_name (root, "rtas"); + if (rtas) { + hose->ops = &rtas_pci_ops; + } else { + printk ("RTAS supporting Pegasos OF not found, please upgrade" + " your firmware\n"); + } + pci_assign_all_busses = 1; +} + +void __init +chrp_find_bridges(void) +{ + struct device_node *dev; + int *bus_range; + int len, index = -1; + struct pci_controller *hose; + unsigned int *dma; + char *model, *machine; + int is_longtrail = 0, is_mot = 0, is_pegasos = 0; + struct device_node *root = find_path_device("/"); + + /* + * The PCI host bridge nodes on some machines don't have + * properties to adequately identify them, so we have to + * look at what sort of machine this is as well. + */ + machine = get_property(root, "model", NULL); + if (machine != NULL) { + is_longtrail = strncmp(machine, "IBM,LongTrail", 13) == 0; + is_mot = strncmp(machine, "MOT", 3) == 0; + if (strncmp(machine, "Pegasos2", 8) == 0) + is_pegasos = 2; + else if (strncmp(machine, "Pegasos", 7) == 0) + is_pegasos = 1; + } + for (dev = root->child; dev != NULL; dev = dev->sibling) { + if (dev->type == NULL || strcmp(dev->type, "pci") != 0) + continue; + ++index; + /* The GG2 bridge on the LongTrail doesn't have an address */ + if (dev->n_addrs < 1 && !is_longtrail) { + printk(KERN_WARNING "Can't use %s: no address\n", + dev->full_name); + continue; + } + bus_range = (int *) get_property(dev, "bus-range", &len); + if (bus_range == NULL || len < 2 * sizeof(int)) { + printk(KERN_WARNING "Can't get bus-range for %s\n", + dev->full_name); + continue; + } + if (bus_range[1] == bus_range[0]) + printk(KERN_INFO "PCI bus %d", bus_range[0]); + else + printk(KERN_INFO "PCI buses %d..%d", + bus_range[0], bus_range[1]); + printk(" controlled by %s", dev->type); + if (dev->n_addrs > 0) + printk(" at %x", dev->addrs[0].address); + printk("\n"); + + hose = pcibios_alloc_controller(); + if (!hose) { + printk("Can't allocate PCI controller structure for %s\n", + dev->full_name); + continue; + } + hose->arch_data = dev; + hose->first_busno = bus_range[0]; + hose->last_busno = bus_range[1]; + + model = get_property(dev, "model", NULL); + if (model == NULL) + model = ""; + if (device_is_compatible(dev, "IBM,python")) { + setup_python(hose, dev); + } else if (is_mot + || strncmp(model, "Motorola, Grackle", 17) == 0) { + setup_grackle(hose); + } else if (is_longtrail) { + void __iomem *p = ioremap(GG2_PCI_CONFIG_BASE, 0x80000); + hose->ops = &gg2_pci_ops; + hose->cfg_data = p; + gg2_pci_config_base = p; + } else if (is_pegasos == 1) { + setup_indirect_pci(hose, 0xfec00cf8, 0xfee00cfc); + } else if (is_pegasos == 2) { + setup_peg2(hose, dev); + } else { + printk("No methods for %s (model %s), using RTAS\n", + dev->full_name, model); + hose->ops = &rtas_pci_ops; + } + + pci_process_bridge_OF_ranges(hose, dev, index == 0); + + /* check the first bridge for a property that we can + use to set pci_dram_offset */ + dma = (unsigned int *) + get_property(dev, "ibm,dma-ranges", &len); + if (index == 0 && dma != NULL && len >= 6 * sizeof(*dma)) { + pci_dram_offset = dma[2] - dma[3]; + printk("pci_dram_offset = %lx\n", pci_dram_offset); + } + } + + /* Do not fixup interrupts from OF tree on pegasos */ + if (is_pegasos == 0) + ppc_md.pcibios_fixup = chrp_pcibios_fixup; +} diff --git a/arch/ppc/platforms/chrp_pegasos_eth.c b/arch/ppc/platforms/chrp_pegasos_eth.c new file mode 100644 index 00000000000..cad5bfa153b --- /dev/null +++ b/arch/ppc/platforms/chrp_pegasos_eth.c @@ -0,0 +1,101 @@ +/* + * arch/ppc/platforms/chrp_pegasos_eth.c + * + * Copyright (C) 2005 Sven Luther + * Thanks to : + * Dale Farnsworth + * Mark A. Greer + * Nicolas DET + * Benjamin Herrenschmidt + * And anyone else who helped me on this. + */ + +#include +#include +#include +#include +#include +#include + +/* Pegasos 2 specific Marvell MV 64361 gigabit ethernet port setup */ +static struct resource mv643xx_eth_shared_resources[] = { + [0] = { + .name = "ethernet shared base", + .start = 0xf1000000 + MV643XX_ETH_SHARED_REGS, + .end = 0xf1000000 + MV643XX_ETH_SHARED_REGS + + MV643XX_ETH_SHARED_REGS_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device mv643xx_eth_shared_device = { + .name = MV643XX_ETH_SHARED_NAME, + .id = 0, + .num_resources = ARRAY_SIZE(mv643xx_eth_shared_resources), + .resource = mv643xx_eth_shared_resources, +}; + +static struct resource mv643xx_eth0_resources[] = { + [0] = { + .name = "eth0 irq", + .start = 9, + .end = 9, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mv643xx_eth_platform_data eth0_pd; + +static struct platform_device eth0_device = { + .name = MV643XX_ETH_NAME, + .id = 0, + .num_resources = ARRAY_SIZE(mv643xx_eth0_resources), + .resource = mv643xx_eth0_resources, + .dev = { + .platform_data = ð0_pd, + }, +}; + +static struct resource mv643xx_eth1_resources[] = { + [0] = { + .name = "eth1 irq", + .start = 9, + .end = 9, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mv643xx_eth_platform_data eth1_pd; + +static struct platform_device eth1_device = { + .name = MV643XX_ETH_NAME, + .id = 1, + .num_resources = ARRAY_SIZE(mv643xx_eth1_resources), + .resource = mv643xx_eth1_resources, + .dev = { + .platform_data = ð1_pd, + }, +}; + +static struct platform_device *mv643xx_eth_pd_devs[] __initdata = { + &mv643xx_eth_shared_device, + ð0_device, + ð1_device, +}; + + +int +mv643xx_eth_add_pds(void) +{ + int ret = 0; + static struct pci_device_id pci_marvell_mv64360[] = { + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_MV64360) }, + { } + }; + + if (pci_dev_present(pci_marvell_mv64360)) { + ret = platform_add_devices(mv643xx_eth_pd_devs, ARRAY_SIZE(mv643xx_eth_pd_devs)); + } + return ret; +} +device_initcall(mv643xx_eth_add_pds); diff --git a/arch/ppc/platforms/chrp_setup.c b/arch/ppc/platforms/chrp_setup.c new file mode 100644 index 00000000000..f23c4f32076 --- /dev/null +++ b/arch/ppc/platforms/chrp_setup.c @@ -0,0 +1,615 @@ +/* + * arch/ppc/platforms/setup.c + * + * Copyright (C) 1995 Linus Torvalds + * Adapted from 'alpha' version by Gary Thomas + * Modified by Cort Dougan (cort@cs.nmt.edu) + */ + +/* + * bootup setup stuff.. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +unsigned long chrp_get_rtc_time(void); +int chrp_set_rtc_time(unsigned long nowtime); +void chrp_calibrate_decr(void); +long chrp_time_init(void); + +void chrp_find_bridges(void); +void chrp_event_scan(void); +void rtas_display_progress(char *, unsigned short); +void rtas_indicator_progress(char *, unsigned short); +void btext_progress(char *, unsigned short); + +extern unsigned long pmac_find_end_of_memory(void); +extern int of_show_percpuinfo(struct seq_file *, int); + +int _chrp_type; +EXPORT_SYMBOL(_chrp_type); + +/* + * XXX this should be in xmon.h, but putting it there means xmon.h + * has to include (to get irqreturn_t), which + * causes all sorts of problems. -- paulus + */ +extern irqreturn_t xmon_irq(int, void *, struct pt_regs *); + +extern dev_t boot_dev; + +extern PTE *Hash, *Hash_end; +extern unsigned long Hash_size, Hash_mask; +extern int probingmem; +extern unsigned long loops_per_jiffy; +static int max_width; + +#ifdef CONFIG_SMP +extern struct smp_ops_t chrp_smp_ops; +#endif + +static const char *gg2_memtypes[4] = { + "FPM", "SDRAM", "EDO", "BEDO" +}; +static const char *gg2_cachesizes[4] = { + "256 KB", "512 KB", "1 MB", "Reserved" +}; +static const char *gg2_cachetypes[4] = { + "Asynchronous", "Reserved", "Flow-Through Synchronous", + "Pipelined Synchronous" +}; +static const char *gg2_cachemodes[4] = { + "Disabled", "Write-Through", "Copy-Back", "Transparent Mode" +}; + +int __chrp +chrp_show_cpuinfo(struct seq_file *m) +{ + int i, sdramen; + unsigned int t; + struct device_node *root; + const char *model = ""; + + root = find_path_device("/"); + if (root) + model = get_property(root, "model", NULL); + seq_printf(m, "machine\t\t: CHRP %s\n", model); + + /* longtrail (goldengate) stuff */ + if (!strncmp(model, "IBM,LongTrail", 13)) { + /* VLSI VAS96011/12 `Golden Gate 2' */ + /* Memory banks */ + sdramen = (in_le32(gg2_pci_config_base + GG2_PCI_DRAM_CTRL) + >>31) & 1; + for (i = 0; i < (sdramen ? 4 : 6); i++) { + t = in_le32(gg2_pci_config_base+ + GG2_PCI_DRAM_BANK0+ + i*4); + if (!(t & 1)) + continue; + switch ((t>>8) & 0x1f) { + case 0x1f: + model = "4 MB"; + break; + case 0x1e: + model = "8 MB"; + break; + case 0x1c: + model = "16 MB"; + break; + case 0x18: + model = "32 MB"; + break; + case 0x10: + model = "64 MB"; + break; + case 0x00: + model = "128 MB"; + break; + default: + model = "Reserved"; + break; + } + seq_printf(m, "memory bank %d\t: %s %s\n", i, model, + gg2_memtypes[sdramen ? 1 : ((t>>1) & 3)]); + } + /* L2 cache */ + t = in_le32(gg2_pci_config_base+GG2_PCI_CC_CTRL); + seq_printf(m, "board l2\t: %s %s (%s)\n", + gg2_cachesizes[(t>>7) & 3], + gg2_cachetypes[(t>>2) & 3], + gg2_cachemodes[t & 3]); + } + return 0; +} + +/* + * Fixes for the National Semiconductor PC78308VUL SuperI/O + * + * Some versions of Open Firmware incorrectly initialize the IRQ settings + * for keyboard and mouse + */ +static inline void __init sio_write(u8 val, u8 index) +{ + outb(index, 0x15c); + outb(val, 0x15d); +} + +static inline u8 __init sio_read(u8 index) +{ + outb(index, 0x15c); + return inb(0x15d); +} + +static void __init sio_fixup_irq(const char *name, u8 device, u8 level, + u8 type) +{ + u8 level0, type0, active; + + /* select logical device */ + sio_write(device, 0x07); + active = sio_read(0x30); + level0 = sio_read(0x70); + type0 = sio_read(0x71); + if (level0 != level || type0 != type || !active) { + printk(KERN_WARNING "sio: %s irq level %d, type %d, %sactive: " + "remapping to level %d, type %d, active\n", + name, level0, type0, !active ? "in" : "", level, type); + sio_write(0x01, 0x30); + sio_write(level, 0x70); + sio_write(type, 0x71); + } +} + +static void __init sio_init(void) +{ + struct device_node *root; + + if ((root = find_path_device("/")) && + !strncmp(get_property(root, "model", NULL), "IBM,LongTrail", 13)) { + /* logical device 0 (KBC/Keyboard) */ + sio_fixup_irq("keyboard", 0, 1, 2); + /* select logical device 1 (KBC/Mouse) */ + sio_fixup_irq("mouse", 1, 12, 2); + } +} + + +static void __init pegasos_set_l2cr(void) +{ + struct device_node *np; + + /* On Pegasos, enable the l2 cache if needed, as the OF forgets it */ + if (_chrp_type != _CHRP_Pegasos) + return; + + /* Enable L2 cache if needed */ + np = find_type_devices("cpu"); + if (np != NULL) { + unsigned int *l2cr = (unsigned int *) + get_property (np, "l2cr", NULL); + if (l2cr == NULL) { + printk ("Pegasos l2cr : no cpu l2cr property found\n"); + return; + } + if (!((*l2cr) & 0x80000000)) { + printk ("Pegasos l2cr : L2 cache was not active, " + "activating\n"); + _set_L2CR(0); + _set_L2CR((*l2cr) | 0x80000000); + } + } +} + +void __init chrp_setup_arch(void) +{ + struct device_node *device; + + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_jiffy = 50000000/HZ; + +#ifdef CONFIG_BLK_DEV_INITRD + /* this is fine for chrp */ + initrd_below_start_ok = 1; + + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif + ROOT_DEV = Root_SDA2; /* sda2 (sda1 is for the kernel) */ + + /* On pegasos, enable the L2 cache if not already done by OF */ + pegasos_set_l2cr(); + + /* Lookup PCI host bridges */ + chrp_find_bridges(); + +#ifndef CONFIG_PPC64BRIDGE + /* + * Temporary fixes for PCI devices. + * -- Geert + */ + hydra_init(); /* Mac I/O */ + +#endif /* CONFIG_PPC64BRIDGE */ + + /* + * Fix the Super I/O configuration + */ + sio_init(); + + /* Get the event scan rate for the rtas so we know how + * often it expects a heartbeat. -- Cort + */ + if ( rtas_data ) { + struct property *p; + device = find_devices("rtas"); + for ( p = device->properties; + p && strncmp(p->name, "rtas-event-scan-rate", 20); + p = p->next ) + /* nothing */ ; + if ( p && *(unsigned long *)p->value ) { + ppc_md.heartbeat = chrp_event_scan; + ppc_md.heartbeat_reset = (HZ/(*(unsigned long *)p->value)*30)-1; + ppc_md.heartbeat_count = 1; + printk("RTAS Event Scan Rate: %lu (%lu jiffies)\n", + *(unsigned long *)p->value, ppc_md.heartbeat_reset ); + } + } + + pci_create_OF_bus_map(); +} + +void __chrp +chrp_event_scan(void) +{ + unsigned char log[1024]; + unsigned long ret = 0; + /* XXX: we should loop until the hardware says no more error logs -- Cort */ + call_rtas( "event-scan", 4, 1, &ret, 0xffffffff, 0, + __pa(log), 1024 ); + ppc_md.heartbeat_count = ppc_md.heartbeat_reset; +} + +void __chrp +chrp_restart(char *cmd) +{ + printk("RTAS system-reboot returned %d\n", + call_rtas("system-reboot", 0, 1, NULL)); + for (;;); +} + +void __chrp +chrp_power_off(void) +{ + /* allow power on only with power button press */ + printk("RTAS power-off returned %d\n", + call_rtas("power-off", 2, 1, NULL,0xffffffff,0xffffffff)); + for (;;); +} + +void __chrp +chrp_halt(void) +{ + chrp_power_off(); +} + +u_int __chrp +chrp_irq_canonicalize(u_int irq) +{ + if (irq == 2) + return 9; + return irq; +} + +/* + * Finds the open-pic node and sets OpenPIC_Addr based on its reg property. + * Then checks if it has an interrupt-ranges property. If it does then + * we have a distributed open-pic, so call openpic_set_sources to tell + * the openpic code where to find the interrupt source registers. + */ +static void __init chrp_find_openpic(void) +{ + struct device_node *np; + int len, i; + unsigned int *iranges; + void *isu; + + np = find_type_devices("open-pic"); + if (np == NULL || np->n_addrs == 0) + return; + printk(KERN_INFO "OpenPIC at %x (size %x)\n", + np->addrs[0].address, np->addrs[0].size); + OpenPIC_Addr = ioremap(np->addrs[0].address, 0x40000); + if (OpenPIC_Addr == NULL) { + printk(KERN_ERR "Failed to map OpenPIC!\n"); + return; + } + + iranges = (unsigned int *) get_property(np, "interrupt-ranges", &len); + if (iranges == NULL || len < 2 * sizeof(unsigned int)) + return; /* not distributed */ + + /* + * The first pair of cells in interrupt-ranges refers to the + * IDU; subsequent pairs refer to the ISUs. + */ + len /= 2 * sizeof(unsigned int); + if (np->n_addrs < len) { + printk(KERN_ERR "Insufficient addresses for distributed" + " OpenPIC (%d < %d)\n", np->n_addrs, len); + return; + } + if (iranges[1] != 0) { + printk(KERN_INFO "OpenPIC irqs %d..%d in IDU\n", + iranges[0], iranges[0] + iranges[1] - 1); + openpic_set_sources(iranges[0], iranges[1], NULL); + } + for (i = 1; i < len; ++i) { + iranges += 2; + printk(KERN_INFO "OpenPIC irqs %d..%d in ISU at %x (%x)\n", + iranges[0], iranges[0] + iranges[1] - 1, + np->addrs[i].address, np->addrs[i].size); + isu = ioremap(np->addrs[i].address, np->addrs[i].size); + if (isu != NULL) + openpic_set_sources(iranges[0], iranges[1], isu); + else + printk(KERN_ERR "Failed to map OpenPIC ISU at %x!\n", + np->addrs[i].address); + } +} + +#if defined(CONFIG_VT) && defined(CONFIG_INPUT_ADBHID) && defined(XMON) +static struct irqaction xmon_irqaction = { + .handler = xmon_irq, + .mask = CPU_MASK_NONE, + .name = "XMON break", +}; +#endif + +void __init chrp_init_IRQ(void) +{ + struct device_node *np; + int i; + unsigned long chrp_int_ack = 0; + unsigned char init_senses[NR_IRQS - NUM_8259_INTERRUPTS]; +#if defined(CONFIG_VT) && defined(CONFIG_INPUT_ADBHID) && defined(XMON) + struct device_node *kbd; +#endif + + for (np = find_devices("pci"); np != NULL; np = np->next) { + unsigned int *addrp = (unsigned int *) + get_property(np, "8259-interrupt-acknowledge", NULL); + + if (addrp == NULL) + continue; + chrp_int_ack = addrp[prom_n_addr_cells(np)-1]; + break; + } + if (np == NULL) + printk(KERN_ERR "Cannot find PCI interrupt acknowledge address\n"); + + chrp_find_openpic(); + + if (OpenPIC_Addr) { + prom_get_irq_senses(init_senses, NUM_8259_INTERRUPTS, NR_IRQS); + OpenPIC_InitSenses = init_senses; + OpenPIC_NumInitSenses = NR_IRQS - NUM_8259_INTERRUPTS; + + openpic_init(NUM_8259_INTERRUPTS); + /* We have a cascade on OpenPIC IRQ 0, Linux IRQ 16 */ + openpic_hookup_cascade(NUM_8259_INTERRUPTS, "82c59 cascade", + i8259_irq); + + } + for (i = 0; i < NUM_8259_INTERRUPTS; i++) + irq_desc[i].handler = &i8259_pic; + i8259_init(chrp_int_ack); + +#if defined(CONFIG_VT) && defined(CONFIG_INPUT_ADBHID) && defined(XMON) + /* see if there is a keyboard in the device tree + with a parent of type "adb" */ + for (kbd = find_devices("keyboard"); kbd; kbd = kbd->next) + if (kbd->parent && kbd->parent->type + && strcmp(kbd->parent->type, "adb") == 0) + break; + if (kbd) + setup_irq(HYDRA_INT_ADB_NMI, &xmon_irqaction); +#endif +} + +void __init +chrp_init2(void) +{ +#ifdef CONFIG_NVRAM +// XX replace this in a more saner way +// pmac_nvram_init(); +#endif + + request_region(0x20,0x20,"pic1"); + request_region(0xa0,0x20,"pic2"); + request_region(0x00,0x20,"dma1"); + request_region(0x40,0x20,"timer"); + request_region(0x80,0x10,"dma page reg"); + request_region(0xc0,0x20,"dma2"); + + if (ppc_md.progress) + ppc_md.progress(" Have fun! ", 0x7777); +} + +void __init +chrp_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + struct device_node *root = find_path_device ("/"); + char *machine = NULL; + +#ifdef CONFIG_BLK_DEV_INITRD + /* take care of initrd if we have one */ + if ( r6 ) + { + initrd_start = r6 + KERNELBASE; + initrd_end = r6 + r7 + KERNELBASE; + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + ISA_DMA_THRESHOLD = ~0L; + DMA_MODE_READ = 0x44; + DMA_MODE_WRITE = 0x48; + isa_io_base = CHRP_ISA_IO_BASE; /* default value */ + + if (root) + machine = get_property(root, "model", NULL); + if (machine && strncmp(machine, "Pegasos", 7) == 0) { + _chrp_type = _CHRP_Pegasos; + } else if (machine && strncmp(machine, "IBM", 3) == 0) { + _chrp_type = _CHRP_IBM; + } else if (machine && strncmp(machine, "MOT", 3) == 0) { + _chrp_type = _CHRP_Motorola; + } else { + /* Let's assume it is an IBM chrp if all else fails */ + _chrp_type = _CHRP_IBM; + } + + ppc_md.setup_arch = chrp_setup_arch; + ppc_md.show_percpuinfo = of_show_percpuinfo; + ppc_md.show_cpuinfo = chrp_show_cpuinfo; + + ppc_md.irq_canonicalize = chrp_irq_canonicalize; + ppc_md.init_IRQ = chrp_init_IRQ; + if (_chrp_type == _CHRP_Pegasos) + ppc_md.get_irq = i8259_irq; + else + ppc_md.get_irq = openpic_get_irq; + + ppc_md.init = chrp_init2; + + ppc_md.phys_mem_access_prot = pci_phys_mem_access_prot; + + ppc_md.restart = chrp_restart; + ppc_md.power_off = chrp_power_off; + ppc_md.halt = chrp_halt; + + ppc_md.time_init = chrp_time_init; + ppc_md.set_rtc_time = chrp_set_rtc_time; + ppc_md.get_rtc_time = chrp_get_rtc_time; + ppc_md.calibrate_decr = chrp_calibrate_decr; + + ppc_md.find_end_of_memory = pmac_find_end_of_memory; + + if (rtas_data) { + struct device_node *rtas; + unsigned int *p; + + rtas = find_devices("rtas"); + if (rtas != NULL) { + if (get_property(rtas, "display-character", NULL)) { + ppc_md.progress = rtas_display_progress; + p = (unsigned int *) get_property + (rtas, "ibm,display-line-length", NULL); + if (p) + max_width = *p; + } else if (get_property(rtas, "set-indicator", NULL)) + ppc_md.progress = rtas_indicator_progress; + } + } +#ifdef CONFIG_BOOTX_TEXT + if (ppc_md.progress == NULL && boot_text_mapped) + ppc_md.progress = btext_progress; +#endif + +#ifdef CONFIG_SMP + ppc_md.smp_ops = &chrp_smp_ops; +#endif /* CONFIG_SMP */ + + /* + * Print the banner, then scroll down so boot progress + * can be printed. -- Cort + */ + if (ppc_md.progress) ppc_md.progress("Linux/PPC "UTS_RELEASE"\n", 0x0); +} + +void __chrp +rtas_display_progress(char *s, unsigned short hex) +{ + int width; + char *os = s; + + if ( call_rtas( "display-character", 1, 1, NULL, '\r' ) ) + return; + + width = max_width; + while ( *os ) + { + if ( (*os == '\n') || (*os == '\r') ) + width = max_width; + else + width--; + call_rtas( "display-character", 1, 1, NULL, *os++ ); + /* if we overwrite the screen length */ + if ( width == 0 ) + while ( (*os != 0) && (*os != '\n') && (*os != '\r') ) + os++; + } + + /*while ( width-- > 0 )*/ + call_rtas( "display-character", 1, 1, NULL, ' ' ); +} + +void __chrp +rtas_indicator_progress(char *s, unsigned short hex) +{ + call_rtas("set-indicator", 3, 1, NULL, 6, 0, hex); +} + +#ifdef CONFIG_BOOTX_TEXT +void +btext_progress(char *s, unsigned short hex) +{ + prom_print(s); + prom_print("\n"); +} +#endif /* CONFIG_BOOTX_TEXT */ diff --git a/arch/ppc/platforms/chrp_smp.c b/arch/ppc/platforms/chrp_smp.c new file mode 100644 index 00000000000..0ea1f7d9e46 --- /dev/null +++ b/arch/ppc/platforms/chrp_smp.c @@ -0,0 +1,98 @@ +/* + * Smp support for CHRP machines. + * + * Written by Cort Dougan (cort@cs.nmt.edu) borrowing a great + * deal of code from the sparc and intel versions. + * + * Copyright (C) 1999 Cort Dougan + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern unsigned long smp_chrp_cpu_nr; + +static int __init +smp_chrp_probe(void) +{ + if (smp_chrp_cpu_nr > 1) + openpic_request_IPIs(); + + return smp_chrp_cpu_nr; +} + +static void __devinit +smp_chrp_kick_cpu(int nr) +{ + *(unsigned long *)KERNELBASE = nr; + asm volatile("dcbf 0,%0"::"r"(KERNELBASE):"memory"); +} + +static void __devinit +smp_chrp_setup_cpu(int cpu_nr) +{ + if (OpenPIC_Addr) + do_openpic_setup_cpu(); +} + +static DEFINE_SPINLOCK(timebase_lock); +static unsigned int timebase_upper = 0, timebase_lower = 0; + +void __devinit +smp_chrp_give_timebase(void) +{ + spin_lock(&timebase_lock); + call_rtas("freeze-time-base", 0, 1, NULL); + timebase_upper = get_tbu(); + timebase_lower = get_tbl(); + spin_unlock(&timebase_lock); + + while (timebase_upper || timebase_lower) + barrier(); + call_rtas("thaw-time-base", 0, 1, NULL); +} + +void __devinit +smp_chrp_take_timebase(void) +{ + while (!(timebase_upper || timebase_lower)) + barrier(); + spin_lock(&timebase_lock); + set_tb(timebase_upper, timebase_lower); + timebase_upper = 0; + timebase_lower = 0; + spin_unlock(&timebase_lock); + printk("CPU %i taken timebase\n", smp_processor_id()); +} + +/* CHRP with openpic */ +struct smp_ops_t chrp_smp_ops __chrpdata = { + .message_pass = smp_openpic_message_pass, + .probe = smp_chrp_probe, + .kick_cpu = smp_chrp_kick_cpu, + .setup_cpu = smp_chrp_setup_cpu, + .give_timebase = smp_chrp_give_timebase, + .take_timebase = smp_chrp_take_timebase, +}; diff --git a/arch/ppc/platforms/chrp_time.c b/arch/ppc/platforms/chrp_time.c new file mode 100644 index 00000000000..e2be0c838d8 --- /dev/null +++ b/arch/ppc/platforms/chrp_time.c @@ -0,0 +1,194 @@ +/* + * arch/ppc/platforms/chrp_time.c + * + * Copyright (C) 1991, 1992, 1995 Linus Torvalds + * + * Adapted for PowerPC (PReP) by Gary Thomas + * Modified by Cort Dougan (cort@cs.nmt.edu). + * Copied and modified from arch/i386/kernel/time.c + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +extern spinlock_t rtc_lock; + +static int nvram_as1 = NVRAM_AS1; +static int nvram_as0 = NVRAM_AS0; +static int nvram_data = NVRAM_DATA; + +long __init chrp_time_init(void) +{ + struct device_node *rtcs; + int base; + + rtcs = find_compatible_devices("rtc", "pnpPNP,b00"); + if (rtcs == NULL) + rtcs = find_compatible_devices("rtc", "ds1385-rtc"); + if (rtcs == NULL || rtcs->addrs == NULL) + return 0; + base = rtcs->addrs[0].address; + nvram_as1 = 0; + nvram_as0 = base; + nvram_data = base + 1; + + return 0; +} + +int __chrp chrp_cmos_clock_read(int addr) +{ + if (nvram_as1 != 0) + outb(addr>>8, nvram_as1); + outb(addr, nvram_as0); + return (inb(nvram_data)); +} + +void __chrp chrp_cmos_clock_write(unsigned long val, int addr) +{ + if (nvram_as1 != 0) + outb(addr>>8, nvram_as1); + outb(addr, nvram_as0); + outb(val, nvram_data); + return; +} + +/* + * Set the hardware clock. -- Cort + */ +int __chrp chrp_set_rtc_time(unsigned long nowtime) +{ + unsigned char save_control, save_freq_select; + struct rtc_time tm; + + spin_lock(&rtc_lock); + to_tm(nowtime, &tm); + + save_control = chrp_cmos_clock_read(RTC_CONTROL); /* tell the clock it's being set */ + + chrp_cmos_clock_write((save_control|RTC_SET), RTC_CONTROL); + + save_freq_select = chrp_cmos_clock_read(RTC_FREQ_SELECT); /* stop and reset prescaler */ + + chrp_cmos_clock_write((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); + + tm.tm_year -= 1900; + if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { + BIN_TO_BCD(tm.tm_sec); + BIN_TO_BCD(tm.tm_min); + BIN_TO_BCD(tm.tm_hour); + BIN_TO_BCD(tm.tm_mon); + BIN_TO_BCD(tm.tm_mday); + BIN_TO_BCD(tm.tm_year); + } + chrp_cmos_clock_write(tm.tm_sec,RTC_SECONDS); + chrp_cmos_clock_write(tm.tm_min,RTC_MINUTES); + chrp_cmos_clock_write(tm.tm_hour,RTC_HOURS); + chrp_cmos_clock_write(tm.tm_mon,RTC_MONTH); + chrp_cmos_clock_write(tm.tm_mday,RTC_DAY_OF_MONTH); + chrp_cmos_clock_write(tm.tm_year,RTC_YEAR); + + /* The following flags have to be released exactly in this order, + * otherwise the DS12887 (popular MC146818A clone with integrated + * battery and quartz) will not reset the oscillator and will not + * update precisely 500 ms later. You won't find this mentioned in + * the Dallas Semiconductor data sheets, but who believes data + * sheets anyway ... -- Markus Kuhn + */ + chrp_cmos_clock_write(save_control, RTC_CONTROL); + chrp_cmos_clock_write(save_freq_select, RTC_FREQ_SELECT); + + if ( (time_state == TIME_ERROR) || (time_state == TIME_BAD) ) + time_state = TIME_OK; + spin_unlock(&rtc_lock); + return 0; +} + +unsigned long __chrp chrp_get_rtc_time(void) +{ + unsigned int year, mon, day, hour, min, sec; + int uip, i; + + /* The Linux interpretation of the CMOS clock register contents: + * When the Update-In-Progress (UIP) flag goes from 1 to 0, the + * RTC registers show the second which has precisely just started. + * Let's hope other operating systems interpret the RTC the same way. + */ + + /* Since the UIP flag is set for about 2.2 ms and the clock + * is typically written with a precision of 1 jiffy, trying + * to obtain a precision better than a few milliseconds is + * an illusion. Only consistency is interesting, this also + * allows to use the routine for /dev/rtc without a potential + * 1 second kernel busy loop triggered by any reader of /dev/rtc. + */ + + for ( i = 0; i<1000000; i++) { + uip = chrp_cmos_clock_read(RTC_FREQ_SELECT); + sec = chrp_cmos_clock_read(RTC_SECONDS); + min = chrp_cmos_clock_read(RTC_MINUTES); + hour = chrp_cmos_clock_read(RTC_HOURS); + day = chrp_cmos_clock_read(RTC_DAY_OF_MONTH); + mon = chrp_cmos_clock_read(RTC_MONTH); + year = chrp_cmos_clock_read(RTC_YEAR); + uip |= chrp_cmos_clock_read(RTC_FREQ_SELECT); + if ((uip & RTC_UIP)==0) break; + } + + if (!(chrp_cmos_clock_read(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) + { + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year); + } + if ((year += 1900) < 1970) + year += 100; + return mktime(year, mon, day, hour, min, sec); +} + + +void __init chrp_calibrate_decr(void) +{ + struct device_node *cpu; + unsigned int freq, *fp; + + if (via_calibrate_decr()) + return; + + /* + * The cpu node should have a timebase-frequency property + * to tell us the rate at which the decrementer counts. + */ + freq = 16666000; /* hardcoded default */ + cpu = find_type_devices("cpu"); + if (cpu != 0) { + fp = (unsigned int *) + get_property(cpu, "timebase-frequency", NULL); + if (fp != 0) + freq = *fp; + } + printk("time_init: decrementer frequency = %u.%.6u MHz\n", + freq/1000000, freq%1000000); + tb_ticks_per_jiffy = freq / HZ; + tb_to_us = mulhwu_scale_factor(freq, 1000000); +} diff --git a/arch/ppc/platforms/cpci690.c b/arch/ppc/platforms/cpci690.c new file mode 100644 index 00000000000..507870c9a97 --- /dev/null +++ b/arch/ppc/platforms/cpci690.c @@ -0,0 +1,491 @@ +/* + * arch/ppc/platforms/cpci690.c + * + * Board setup routines for the Force CPCI690 board. + * + * Author: Mark A. Greer + * + * 2003 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This programr + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BOARD_VENDOR "Force" +#define BOARD_MACHINE "CPCI690" + +/* Set IDE controllers into Native mode? */ +#define SET_PCI_IDE_NATIVE + +static struct mv64x60_handle bh; +static u32 cpci690_br_base; + +static const unsigned int cpu_7xx[16] = { /* 7xx & 74xx (but not 745x) */ + 18, 15, 14, 2, 4, 13, 5, 9, 6, 11, 8, 10, 16, 12, 7, 0 +}; + +TODC_ALLOC(); + +static int __init +cpci690_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + struct pci_controller *hose = pci_bus_to_hose(dev->bus->number); + + if (hose->index == 0) { + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + { 90, 91, 88, 89}, /* IDSEL 30/20 - Sentinel */ + }; + + const long min_idsel = 20, max_idsel = 20, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; + } else { + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + { 93, 94, 95, 92}, /* IDSEL 28/18 - PMC slot 2 */ + { 0, 0, 0, 0}, /* IDSEL 29/19 - Not used */ + { 94, 95, 92, 93}, /* IDSEL 30/20 - PMC slot 1 */ + }; + + const long min_idsel = 18, max_idsel = 20, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; + } +} + +static int +cpci690_get_cpu_speed(void) +{ + unsigned long hid1; + + hid1 = mfspr(SPRN_HID1) >> 28; + return CPCI690_BUS_FREQ * cpu_7xx[hid1]/2; +} + +#define KB (1024UL) +#define MB (1024UL * KB) +#define GB (1024UL * MB) + +unsigned long __init +cpci690_find_end_of_memory(void) +{ + u32 mem_ctlr_size; + static u32 board_size; + static u8 first_time = 1; + + if (first_time) { + /* Using cpci690_set_bat() mapping ==> virt addr == phys addr */ + switch (in_8((u8 *) (cpci690_br_base + + CPCI690_BR_MEM_CTLR)) & 0x07) { + case 0x01: + board_size = 256*MB; + break; + case 0x02: + board_size = 512*MB; + break; + case 0x03: + board_size = 768*MB; + break; + case 0x04: + board_size = 1*GB; + break; + case 0x05: + board_size = 1*GB + 512*MB; + break; + case 0x06: + board_size = 2*GB; + break; + default: + board_size = 0xffffffff; /* use mem ctlr size */ + } /* switch */ + + mem_ctlr_size = mv64x60_get_mem_size(CONFIG_MV64X60_NEW_BASE, + MV64x60_TYPE_GT64260A); + + /* Check that mem ctlr & board reg agree. If not, pick MIN. */ + if (board_size != mem_ctlr_size) { + printk(KERN_WARNING "Board register & memory controller" + "mem size disagree (board reg: 0x%lx, " + "mem ctlr: 0x%lx)\n", + (ulong)board_size, (ulong)mem_ctlr_size); + board_size = min(board_size, mem_ctlr_size); + } + + first_time = 0; + } /* if */ + + return board_size; +} + +static void __init +cpci690_setup_bridge(void) +{ + struct mv64x60_setup_info si; + int i; + + memset(&si, 0, sizeof(si)); + + si.phys_reg_base = CONFIG_MV64X60_NEW_BASE; + + si.pci_0.enable_bus = 1; + si.pci_0.pci_io.cpu_base = CPCI690_PCI0_IO_START_PROC_ADDR; + si.pci_0.pci_io.pci_base_hi = 0; + si.pci_0.pci_io.pci_base_lo = CPCI690_PCI0_IO_START_PCI_ADDR; + si.pci_0.pci_io.size = CPCI690_PCI0_IO_SIZE; + si.pci_0.pci_io.swap = MV64x60_CPU2PCI_SWAP_NONE; + si.pci_0.pci_mem[0].cpu_base = CPCI690_PCI0_MEM_START_PROC_ADDR; + si.pci_0.pci_mem[0].pci_base_hi = CPCI690_PCI0_MEM_START_PCI_HI_ADDR; + si.pci_0.pci_mem[0].pci_base_lo = CPCI690_PCI0_MEM_START_PCI_LO_ADDR; + si.pci_0.pci_mem[0].size = CPCI690_PCI0_MEM_SIZE; + si.pci_0.pci_mem[0].swap = MV64x60_CPU2PCI_SWAP_NONE; + si.pci_0.pci_cmd_bits = 0; + si.pci_0.latency_timer = 0x80; + + si.pci_1.enable_bus = 1; + si.pci_1.pci_io.cpu_base = CPCI690_PCI1_IO_START_PROC_ADDR; + si.pci_1.pci_io.pci_base_hi = 0; + si.pci_1.pci_io.pci_base_lo = CPCI690_PCI1_IO_START_PCI_ADDR; + si.pci_1.pci_io.size = CPCI690_PCI1_IO_SIZE; + si.pci_1.pci_io.swap = MV64x60_CPU2PCI_SWAP_NONE; + si.pci_1.pci_mem[0].cpu_base = CPCI690_PCI1_MEM_START_PROC_ADDR; + si.pci_1.pci_mem[0].pci_base_hi = CPCI690_PCI1_MEM_START_PCI_HI_ADDR; + si.pci_1.pci_mem[0].pci_base_lo = CPCI690_PCI1_MEM_START_PCI_LO_ADDR; + si.pci_1.pci_mem[0].size = CPCI690_PCI1_MEM_SIZE; + si.pci_1.pci_mem[0].swap = MV64x60_CPU2PCI_SWAP_NONE; + si.pci_1.pci_cmd_bits = 0; + si.pci_1.latency_timer = 0x80; + + for (i=0; ifirst_busno = 0; + bh.hose_a->last_busno = 0xff; + bh.hose_a->last_busno = pciauto_bus_scan(bh.hose_a, 0); + + bh.hose_b->first_busno = bh.hose_a->last_busno + 1; + mv64x60_set_bus(&bh, 1, bh.hose_b->first_busno); + bh.hose_b->last_busno = 0xff; + bh.hose_b->last_busno = pciauto_bus_scan(bh.hose_b, + bh.hose_b->first_busno); +} + +static void __init +cpci690_setup_peripherals(void) +{ + /* Set up windows to CPLD, RTC/TODC, IPMI. */ + mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_0_WIN, CPCI690_BR_BASE, + CPCI690_BR_SIZE, 0); + bh.ci->enable_window_32bit(&bh, MV64x60_CPU2DEV_0_WIN); + cpci690_br_base = (u32)ioremap(CPCI690_BR_BASE, CPCI690_BR_SIZE); + + mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_1_WIN, CPCI690_TODC_BASE, + CPCI690_TODC_SIZE, 0); + bh.ci->enable_window_32bit(&bh, MV64x60_CPU2DEV_1_WIN); + TODC_INIT(TODC_TYPE_MK48T35, 0, 0, + ioremap(CPCI690_TODC_BASE, CPCI690_TODC_SIZE), 8); + + mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_2_WIN, CPCI690_IPMI_BASE, + CPCI690_IPMI_SIZE, 0); + bh.ci->enable_window_32bit(&bh, MV64x60_CPU2DEV_2_WIN); + + mv64x60_set_bits(&bh, MV64x60_PCI0_ARBITER_CNTL, (1<<31)); + mv64x60_set_bits(&bh, MV64x60_PCI1_ARBITER_CNTL, (1<<31)); + + mv64x60_set_bits(&bh, MV64x60_CPU_MASTER_CNTL, (1<<9)); /* Only 1 cpu */ + + /* + * Turn off timer/counters. Not turning off watchdog timer because + * can't read its reg on the 64260A so don't know if we'll be enabling + * or disabling. + */ + mv64x60_clr_bits(&bh, MV64x60_TIMR_CNTR_0_3_CNTL, + ((1<<0) | (1<<8) | (1<<16) | (1<<24))); + mv64x60_clr_bits(&bh, GT64260_TIMR_CNTR_4_7_CNTL, + ((1<<0) | (1<<8) | (1<<16) | (1<<24))); + + /* + * Set MPSC Multiplex RMII + * NOTE: ethernet driver modifies bit 0 and 1 + */ + mv64x60_write(&bh, GT64260_MPP_SERIAL_PORTS_MULTIPLEX, 0x00001102); + +#define GPP_EXTERNAL_INTERRUPTS \ + ((1<<24) | (1<<25) | (1<<26) | (1<<27) | \ + (1<<28) | (1<<29) | (1<<30) | (1<<31)) + /* PCI interrupts are inputs */ + mv64x60_clr_bits(&bh, MV64x60_GPP_IO_CNTL, GPP_EXTERNAL_INTERRUPTS); + /* PCI interrupts are active low */ + mv64x60_set_bits(&bh, MV64x60_GPP_LEVEL_CNTL, GPP_EXTERNAL_INTERRUPTS); + + /* Clear any pending interrupts for these inputs and enable them. */ + mv64x60_write(&bh, MV64x60_GPP_INTR_CAUSE, ~GPP_EXTERNAL_INTERRUPTS); + mv64x60_set_bits(&bh, MV64x60_GPP_INTR_MASK, GPP_EXTERNAL_INTERRUPTS); + + /* Route MPP interrupt inputs to GPP */ + mv64x60_write(&bh, MV64x60_MPP_CNTL_2, 0x00000000); + mv64x60_write(&bh, MV64x60_MPP_CNTL_3, 0x00000000); +} + +static void __init +cpci690_setup_arch(void) +{ + if (ppc_md.progress) + ppc_md.progress("cpci690_setup_arch: enter", 0); +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = Root_NFS; +#else + ROOT_DEV = Root_SDA2; +#endif + + if (ppc_md.progress) + ppc_md.progress("cpci690_setup_arch: Enabling L2 cache", 0); + + /* Enable L2 and L3 caches (if 745x) */ + _set_L2CR(_get_L2CR() | L2CR_L2E); + _set_L3CR(_get_L3CR() | L3CR_L3E); + + if (ppc_md.progress) + ppc_md.progress("cpci690_setup_arch: Initializing bridge", 0); + + cpci690_setup_bridge(); /* set up PCI bridge(s) */ + cpci690_setup_peripherals(); /* set up chip selects/GPP/MPP etc */ + + if (ppc_md.progress) + ppc_md.progress("cpci690_setup_arch: bridge init complete", 0); + + printk(KERN_INFO "%s %s port (C) 2003 MontaVista Software, Inc. " + "(source@mvista.com)\n", BOARD_VENDOR, BOARD_MACHINE); + + if (ppc_md.progress) + ppc_md.progress("cpci690_setup_arch: exit", 0); +} + +/* Platform device data fixup routines. */ +#if defined(CONFIG_SERIAL_MPSC) +static void __init +cpci690_fixup_mpsc_pdata(struct platform_device *pdev) +{ + struct mpsc_pdata *pdata; + + pdata = (struct mpsc_pdata *)pdev->dev.platform_data; + + pdata->max_idle = 40; + pdata->default_baud = CPCI690_MPSC_BAUD; + pdata->brg_clk_src = CPCI690_MPSC_CLK_SRC; + pdata->brg_clk_freq = CPCI690_BUS_FREQ; +} + +static int __init +cpci690_platform_notify(struct device *dev) +{ + static struct { + char *bus_id; + void ((*rtn)(struct platform_device *pdev)); + } dev_map[] = { + { MPSC_CTLR_NAME ".0", cpci690_fixup_mpsc_pdata }, + { MPSC_CTLR_NAME ".1", cpci690_fixup_mpsc_pdata }, + }; + struct platform_device *pdev; + int i; + + if (dev && dev->bus_id) + for (i=0; ibus_id, dev_map[i].bus_id, + BUS_ID_SIZE)) { + + pdev = container_of(dev, + struct platform_device, dev); + dev_map[i].rtn(pdev); + } + + return 0; +} +#endif + +static void +cpci690_reset_board(void) +{ + u32 i = 10000; + + local_irq_disable(); + out_8((u8 *)(cpci690_br_base + CPCI690_BR_SW_RESET), 0x11); + + while (i != 0) i++; + panic("restart failed\n"); +} + +static void +cpci690_restart(char *cmd) +{ + cpci690_reset_board(); +} + +static void +cpci690_halt(void) +{ + while (1); + /* NOTREACHED */ +} + +static void +cpci690_power_off(void) +{ + cpci690_halt(); + /* NOTREACHED */ +} + +static int +cpci690_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "vendor\t\t: " BOARD_VENDOR "\n"); + seq_printf(m, "machine\t\t: " BOARD_MACHINE "\n"); + seq_printf(m, "cpu MHz\t\t: %d\n", cpci690_get_cpu_speed()/1000/1000); + seq_printf(m, "bus MHz\t\t: %d\n", CPCI690_BUS_FREQ/1000/1000); + + return 0; +} + +static void __init +cpci690_calibrate_decr(void) +{ + ulong freq; + + freq = CPCI690_BUS_FREQ / 4; + + printk(KERN_INFO "time_init: decrementer frequency = %lu.%.6lu MHz\n", + freq/1000000, freq%1000000); + + tb_ticks_per_jiffy = freq / HZ; + tb_to_us = mulhwu_scale_factor(freq, 1000000); +} + +static __inline__ void +cpci690_set_bat(u32 addr, u32 size) +{ + addr &= 0xfffe0000; + size &= 0x1ffe0000; + size = ((size >> 17) - 1) << 2; + + mb(); + mtspr(SPRN_DBAT1U, addr | size | 0x2); /* Vs == 1; Vp == 0 */ + mtspr(SPRN_DBAT1L, addr | 0x2a); /* WIMG bits == 0101; PP == r/w access */ + mb(); +} + +#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) +static void __init +cpci690_map_io(void) +{ + io_block_mapping(CONFIG_MV64X60_NEW_BASE, CONFIG_MV64X60_NEW_BASE, + 128 * KB, _PAGE_IO); +} +#endif + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ +#ifdef CONFIG_BLK_DEV_INITRD + initrd_start=initrd_end=0; + initrd_below_start_ok=0; +#endif /* CONFIG_BLK_DEV_INITRD */ + + parse_bootinfo(find_bootinfo()); + + loops_per_jiffy = cpci690_get_cpu_speed() / HZ; + + isa_mem_base = 0; + + ppc_md.setup_arch = cpci690_setup_arch; + ppc_md.show_cpuinfo = cpci690_show_cpuinfo; + ppc_md.init_IRQ = gt64260_init_irq; + ppc_md.get_irq = gt64260_get_irq; + ppc_md.restart = cpci690_restart; + ppc_md.power_off = cpci690_power_off; + ppc_md.halt = cpci690_halt; + ppc_md.find_end_of_memory = cpci690_find_end_of_memory; + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; + ppc_md.calibrate_decr = cpci690_calibrate_decr; + + /* + * Need to map in board regs (used by cpci690_find_end_of_memory()) + * and the bridge's regs (used by progress); + */ + cpci690_set_bat(CPCI690_BR_BASE, 32 * MB); + cpci690_br_base = CPCI690_BR_BASE; + +#ifdef CONFIG_SERIAL_TEXT_DEBUG + ppc_md.setup_io_mappings = cpci690_map_io; + ppc_md.progress = mv64x60_mpsc_progress; + mv64x60_progress_init(CONFIG_MV64X60_NEW_BASE); +#endif /* CONFIG_SERIAL_TEXT_DEBUG */ +#ifdef CONFIG_KGDB + ppc_md.setup_io_mappings = cpci690_map_io; + ppc_md.early_serial_map = cpci690_early_serial_map; +#endif /* CONFIG_KGDB */ + +#if defined(CONFIG_SERIAL_MPSC) + platform_notify = cpci690_platform_notify; +#endif +} diff --git a/arch/ppc/platforms/cpci690.h b/arch/ppc/platforms/cpci690.h new file mode 100644 index 00000000000..36cd2673c74 --- /dev/null +++ b/arch/ppc/platforms/cpci690.h @@ -0,0 +1,78 @@ +/* + * arch/ppc/platforms/cpci690.h + * + * Definitions for Force CPCI690 + * + * Author: Mark A. Greer + * + * 2003 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +/* + * The GT64260 has 2 PCI buses each with 1 window from the CPU bus to + * PCI I/O space and 4 windows from the CPU bus to PCI MEM space. + */ + +#ifndef __PPC_PLATFORMS_CPCI690_H +#define __PPC_PLATFORMS_CPCI690_H + +/* + * Define bd_t to pass in the MAC addresses used by the GT64260's enet ctlrs. + */ +#define CPCI690_BI_MAGIC 0xFE8765DC + +typedef struct board_info { + u32 bi_magic; + u8 bi_enetaddr[3][6]; +} bd_t; + +/* PCI bus Resource setup */ +#define CPCI690_PCI0_MEM_START_PROC_ADDR 0x80000000 +#define CPCI690_PCI0_MEM_START_PCI_HI_ADDR 0x00000000 +#define CPCI690_PCI0_MEM_START_PCI_LO_ADDR 0x80000000 +#define CPCI690_PCI0_MEM_SIZE 0x10000000 +#define CPCI690_PCI0_IO_START_PROC_ADDR 0xa0000000 +#define CPCI690_PCI0_IO_START_PCI_ADDR 0x00000000 +#define CPCI690_PCI0_IO_SIZE 0x01000000 + +#define CPCI690_PCI1_MEM_START_PROC_ADDR 0x90000000 +#define CPCI690_PCI1_MEM_START_PCI_HI_ADDR 0x00000000 +#define CPCI690_PCI1_MEM_START_PCI_LO_ADDR 0x90000000 +#define CPCI690_PCI1_MEM_SIZE 0x10000000 +#define CPCI690_PCI1_IO_START_PROC_ADDR 0xa1000000 +#define CPCI690_PCI1_IO_START_PCI_ADDR 0x01000000 +#define CPCI690_PCI1_IO_SIZE 0x01000000 + +/* Board Registers */ +#define CPCI690_BR_BASE 0xf0000000 +#define CPCI690_BR_SIZE_ACTUAL 0x8 +#define CPCI690_BR_SIZE max(GT64260_WINDOW_SIZE_MIN, \ + CPCI690_BR_SIZE_ACTUAL) +#define CPCI690_BR_LED_CNTL 0x00 +#define CPCI690_BR_SW_RESET 0x01 +#define CPCI690_BR_MISC_STATUS 0x02 +#define CPCI690_BR_SWITCH_STATUS 0x03 +#define CPCI690_BR_MEM_CTLR 0x04 +#define CPCI690_BR_LAST_RESET_1 0x05 +#define CPCI690_BR_LAST_RESET_2 0x06 + +#define CPCI690_TODC_BASE 0xf0100000 +#define CPCI690_TODC_SIZE_ACTUAL 0x8000 /* Size or NVRAM + RTC */ +#define CPCI690_TODC_SIZE max(GT64260_WINDOW_SIZE_MIN, \ + CPCI690_TODC_SIZE_ACTUAL) +#define CPCI690_MAC_OFFSET 0x7c10 /* MAC in RTC NVRAM */ + +#define CPCI690_IPMI_BASE 0xf0200000 +#define CPCI690_IPMI_SIZE_ACTUAL 0x10 /* 16 bytes of IPMI */ +#define CPCI690_IPMI_SIZE max(GT64260_WINDOW_SIZE_MIN, \ + CPCI690_IPMI_SIZE_ACTUAL) + +#define CPCI690_MPSC_BAUD 9600 +#define CPCI690_MPSC_CLK_SRC 8 /* TCLK */ + +#define CPCI690_BUS_FREQ 133333333 + +#endif /* __PPC_PLATFORMS_CPCI690_H */ diff --git a/arch/ppc/platforms/est8260.h b/arch/ppc/platforms/est8260.h new file mode 100644 index 00000000000..adba68ecf57 --- /dev/null +++ b/arch/ppc/platforms/est8260.h @@ -0,0 +1,35 @@ +/* Board information for the EST8260, which should be generic for + * all 8260 boards. The IMMR is now given to us so the hard define + * will soon be removed. All of the clock values are computed from + * the configuration SCMR and the Power-On-Reset word. + */ +#ifndef __EST8260_PLATFORM +#define __EST8260_PLATFORM + +#define CPM_MAP_ADDR ((uint)0xf0000000) + +#define BOOTROM_RESTART_ADDR ((uint)0xff000104) + +/* For our show_cpuinfo hooks. */ +#define CPUINFO_VENDOR "EST Corporation" +#define CPUINFO_MACHINE "SBC8260 PowerPC" + +/* A Board Information structure that is given to a program when + * prom starts it up. + */ +typedef struct bd_info { + unsigned int bi_memstart; /* Memory start address */ + unsigned int bi_memsize; /* Memory (end) size in bytes */ + unsigned int bi_intfreq; /* Internal Freq, in Hz */ + unsigned int bi_busfreq; /* Bus Freq, in MHz */ + unsigned int bi_cpmfreq; /* CPM Freq, in MHz */ + unsigned int bi_brgfreq; /* BRG Freq, in MHz */ + unsigned int bi_vco; /* VCO Out from PLL */ + unsigned int bi_baudrate; /* Default console baud rate */ + unsigned int bi_immr; /* IMMR when called from boot rom */ + unsigned char bi_enetaddr[6]; +} bd_t; + +extern bd_t m8xx_board_info; + +#endif /* __EST8260_PLATFORM */ diff --git a/arch/ppc/platforms/ev64260.c b/arch/ppc/platforms/ev64260.c new file mode 100644 index 00000000000..aa50637a5cf --- /dev/null +++ b/arch/ppc/platforms/ev64260.c @@ -0,0 +1,651 @@ +/* + * arch/ppc/platforms/ev64260.c + * + * Board setup routines for the Marvell/Galileo EV-64260-BP Evaluation Board. + * + * Author: Mark A. Greer + * + * 2001-2003 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +/* + * The EV-64260-BP port is the result of hard work from many people from + * many companies. In particular, employees of Marvell/Galileo, Mission + * Critical Linux, Xyterra, and MontaVista Software were heavily involved. + * + * Note: I have not been able to get *all* PCI slots to work reliably + * at 66 MHz. I recommend setting jumpers J15 & J16 to short pins 1&2 + * so that 33 MHz is used. --MAG + * Note: The 750CXe and 7450 are not stable with a 125MHz or 133MHz TCLK/SYSCLK. + * At 100MHz, they are solid. + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if !defined(CONFIG_SERIAL_MPSC_CONSOLE) +#include +#include +#include +#else +#include +#endif +#include +#include +#include +#include +#include + +#include + +#define BOARD_VENDOR "Marvell/Galileo" +#define BOARD_MACHINE "EV-64260-BP" + +static struct mv64x60_handle bh; + +#if !defined(CONFIG_SERIAL_MPSC_CONSOLE) +extern void gen550_progress(char *, unsigned short); +extern void gen550_init(int, struct uart_port *); +#endif + +static const unsigned int cpu_7xx[16] = { /* 7xx & 74xx (but not 745x) */ + 18, 15, 14, 2, 4, 13, 5, 9, 6, 11, 8, 10, 16, 12, 7, 0 +}; +static const unsigned int cpu_745x[2][16] = { /* PLL_EXT 0 & 1 */ + { 1, 15, 14, 2, 4, 13, 5, 9, 6, 11, 8, 10, 16, 12, 7, 0 }, + { 0, 30, 0, 2, 0, 26, 0, 18, 0, 22, 20, 24, 28, 32, 0, 0 } +}; + + +TODC_ALLOC(); + +static int +ev64260_get_bus_speed(void) +{ + return 100000000; +} + +static int +ev64260_get_cpu_speed(void) +{ + unsigned long pvr, hid1, pll_ext; + + pvr = PVR_VER(mfspr(SPRN_PVR)); + + if (pvr != PVR_VER(PVR_7450)) { + hid1 = mfspr(SPRN_HID1) >> 28; + return ev64260_get_bus_speed() * cpu_7xx[hid1]/2; + } + else { + hid1 = (mfspr(SPRN_HID1) & 0x0001e000) >> 13; + pll_ext = 0; /* No way to read; must get from schematic */ + return ev64260_get_bus_speed() * cpu_745x[pll_ext][hid1]/2; + } +} + +unsigned long __init +ev64260_find_end_of_memory(void) +{ + return mv64x60_get_mem_size(CONFIG_MV64X60_NEW_BASE, + MV64x60_TYPE_GT64260A); +} + +/* + * Marvell/Galileo EV-64260-BP Evaluation Board PCI interrupt routing. + * Note: By playing with J8 and JP1-4, you can get 2 IRQ's from the first + * PCI bus (in which cast, INTPIN B would be EV64260_PCI_1_IRQ). + * This is the most IRQs you can get from one bus with this board, though. + */ +static int __init +ev64260_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + struct pci_controller *hose = pci_bus_to_hose(dev->bus->number); + + if (hose->index == 0) { + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {EV64260_PCI_0_IRQ,0,0,0}, /* IDSEL 7 - PCI bus 0 */ + {EV64260_PCI_0_IRQ,0,0,0}, /* IDSEL 8 - PCI bus 0 */ + }; + + const long min_idsel = 7, max_idsel = 8, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; + } + else { + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + { EV64260_PCI_1_IRQ,0,0,0}, /* IDSEL 7 - PCI bus 1 */ + { EV64260_PCI_1_IRQ,0,0,0}, /* IDSEL 8 - PCI bus 1 */ + }; + + const long min_idsel = 7, max_idsel = 8, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; + } +} + +static void __init +ev64260_setup_peripherals(void) +{ + mv64x60_set_32bit_window(&bh, MV64x60_CPU2BOOT_WIN, + EV64260_EMB_FLASH_BASE, EV64260_EMB_FLASH_SIZE, 0); + bh.ci->enable_window_32bit(&bh, MV64x60_CPU2BOOT_WIN); + mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_0_WIN, + EV64260_EXT_SRAM_BASE, EV64260_EXT_SRAM_SIZE, 0); + bh.ci->enable_window_32bit(&bh, MV64x60_CPU2DEV_0_WIN); + mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_1_WIN, + EV64260_TODC_BASE, EV64260_TODC_SIZE, 0); + bh.ci->enable_window_32bit(&bh, MV64x60_CPU2DEV_1_WIN); + mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_2_WIN, + EV64260_UART_BASE, EV64260_UART_SIZE, 0); + bh.ci->enable_window_32bit(&bh, MV64x60_CPU2DEV_2_WIN); + mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_3_WIN, + EV64260_EXT_FLASH_BASE, EV64260_EXT_FLASH_SIZE, 0); + bh.ci->enable_window_32bit(&bh, MV64x60_CPU2DEV_3_WIN); + + TODC_INIT(TODC_TYPE_DS1501, 0, 0, + ioremap(EV64260_TODC_BASE, EV64260_TODC_SIZE), 8); + + mv64x60_clr_bits(&bh, MV64x60_CPU_CONFIG,((1<<12) | (1<<28) | (1<<29))); + mv64x60_set_bits(&bh, MV64x60_CPU_CONFIG, (1<<27)); + + if (ev64260_get_bus_speed() > 100000000) + mv64x60_set_bits(&bh, MV64x60_CPU_CONFIG, (1<<23)); + + mv64x60_set_bits(&bh, MV64x60_PCI0_PCI_DECODE_CNTL, ((1<<0) | (1<<3))); + mv64x60_set_bits(&bh, MV64x60_PCI1_PCI_DECODE_CNTL, ((1<<0) | (1<<3))); + + /* + * Enabling of PCI internal-vs-external arbitration + * is a platform- and errata-dependent decision. + */ + if (bh.type == MV64x60_TYPE_GT64260A ) { + mv64x60_set_bits(&bh, MV64x60_PCI0_ARBITER_CNTL, (1<<31)); + mv64x60_set_bits(&bh, MV64x60_PCI1_ARBITER_CNTL, (1<<31)); + } + + mv64x60_set_bits(&bh, MV64x60_CPU_MASTER_CNTL, (1<<9)); /* Only 1 cpu */ + + /* + * Turn off timer/counters. Not turning off watchdog timer because + * can't read its reg on the 64260A so don't know if we'll be enabling + * or disabling. + */ + mv64x60_clr_bits(&bh, MV64x60_TIMR_CNTR_0_3_CNTL, + ((1<<0) | (1<<8) | (1<<16) | (1<<24))); + mv64x60_clr_bits(&bh, GT64260_TIMR_CNTR_4_7_CNTL, + ((1<<0) | (1<<8) | (1<<16) | (1<<24))); + + /* + * Set MPSC Multiplex RMII + * NOTE: ethernet driver modifies bit 0 and 1 + */ + mv64x60_write(&bh, GT64260_MPP_SERIAL_PORTS_MULTIPLEX, 0x00001102); + + /* + * The EV-64260-BP uses several Multi-Purpose Pins (MPP) on the 64260 + * bridge as interrupt inputs (via the General Purpose Ports (GPP) + * register). Need to route the MPP inputs to the GPP and set the + * polarity correctly. + * + * In MPP Control 2 Register + * MPP 21 -> GPP 21 (DUART channel A intr) bits 20-23 -> 0 + * MPP 22 -> GPP 22 (DUART channel B intr) bits 24-27 -> 0 + */ + mv64x60_clr_bits(&bh, MV64x60_MPP_CNTL_2, (0xf<<20) | (0xf<<24) ); + + /* + * In MPP Control 3 Register + * MPP 26 -> GPP 26 (RTC INT) bits 8-11 -> 0 + * MPP 27 -> GPP 27 (PCI 0 INTA) bits 12-15 -> 0 + * MPP 29 -> GPP 29 (PCI 1 INTA) bits 20-23 -> 0 + */ + mv64x60_clr_bits(&bh, MV64x60_MPP_CNTL_3, (0xf<<8)|(0xf<<12)|(0xf<<20)); + +#define GPP_EXTERNAL_INTERRUPTS \ + ((1<<21) | (1<<22) | (1<<26) | (1<<27) | (1<<29)) + /* DUART & PCI interrupts are inputs */ + mv64x60_clr_bits(&bh, MV64x60_GPP_IO_CNTL, GPP_EXTERNAL_INTERRUPTS); + /* DUART & PCI interrupts are active low */ + mv64x60_set_bits(&bh, MV64x60_GPP_LEVEL_CNTL, GPP_EXTERNAL_INTERRUPTS); + + /* Clear any pending interrupts for these inputs and enable them. */ + mv64x60_write(&bh, MV64x60_GPP_INTR_CAUSE, ~GPP_EXTERNAL_INTERRUPTS); + mv64x60_set_bits(&bh, MV64x60_GPP_INTR_MASK, GPP_EXTERNAL_INTERRUPTS); + + return; +} + +static void __init +ev64260_setup_bridge(void) +{ + struct mv64x60_setup_info si; + int i; + + memset(&si, 0, sizeof(si)); + + si.phys_reg_base = CONFIG_MV64X60_NEW_BASE; + + si.pci_0.enable_bus = 1; + si.pci_0.pci_io.cpu_base = EV64260_PCI0_IO_CPU_BASE; + si.pci_0.pci_io.pci_base_hi = 0; + si.pci_0.pci_io.pci_base_lo = EV64260_PCI0_IO_PCI_BASE; + si.pci_0.pci_io.size = EV64260_PCI0_IO_SIZE; + si.pci_0.pci_io.swap = MV64x60_CPU2PCI_SWAP_NONE; + si.pci_0.pci_mem[0].cpu_base = EV64260_PCI0_MEM_CPU_BASE; + si.pci_0.pci_mem[0].pci_base_hi = 0; + si.pci_0.pci_mem[0].pci_base_lo = EV64260_PCI0_MEM_PCI_BASE; + si.pci_0.pci_mem[0].size = EV64260_PCI0_MEM_SIZE; + si.pci_0.pci_mem[0].swap = MV64x60_CPU2PCI_SWAP_NONE; + si.pci_0.pci_cmd_bits = 0; + si.pci_0.latency_timer = 0x8; + + si.pci_1.enable_bus = 1; + si.pci_1.pci_io.cpu_base = EV64260_PCI1_IO_CPU_BASE; + si.pci_1.pci_io.pci_base_hi = 0; + si.pci_1.pci_io.pci_base_lo = EV64260_PCI1_IO_PCI_BASE; + si.pci_1.pci_io.size = EV64260_PCI1_IO_SIZE; + si.pci_1.pci_io.swap = MV64x60_CPU2PCI_SWAP_NONE; + si.pci_1.pci_mem[0].cpu_base = EV64260_PCI1_MEM_CPU_BASE; + si.pci_1.pci_mem[0].pci_base_hi = 0; + si.pci_1.pci_mem[0].pci_base_lo = EV64260_PCI1_MEM_PCI_BASE; + si.pci_1.pci_mem[0].size = EV64260_PCI1_MEM_SIZE; + si.pci_1.pci_mem[0].swap = MV64x60_CPU2PCI_SWAP_NONE; + si.pci_1.pci_cmd_bits = 0; + si.pci_1.latency_timer = 0x8; + + for (i=0; ifirst_busno = 0; + bh.hose_a->last_busno = 0xff; + bh.hose_a->last_busno = pciauto_bus_scan(bh.hose_a, 0); + + bh.hose_b->first_busno = bh.hose_a->last_busno + 1; + mv64x60_set_bus(&bh, 1, bh.hose_b->first_busno); + bh.hose_b->last_busno = 0xff; + bh.hose_b->last_busno = pciauto_bus_scan(bh.hose_b, + bh.hose_b->first_busno); + + return; +} + +#if defined(CONFIG_SERIAL_8250) && !defined(CONFIG_SERIAL_MPSC_CONSOLE) +static void __init +ev64260_early_serial_map(void) +{ + struct uart_port port; + static char first_time = 1; + + if (first_time) { + memset(&port, 0, sizeof(port)); + + port.membase = ioremap(EV64260_SERIAL_0, EV64260_UART_SIZE); + port.irq = EV64260_UART_0_IRQ; + port.uartclk = BASE_BAUD * 16; + port.regshift = 2; + port.iotype = SERIAL_IO_MEM; + port.flags = STD_COM_FLAGS; + +#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) + gen550_init(0, &port); +#endif + + if (early_serial_setup(&port) != 0) + printk(KERN_WARNING "Early serial init of port 0" + "failed\n"); + + first_time = 0; + } + + return; +} +#elif defined(CONFIG_SERIAL_MPSC_CONSOLE) +static void __init +ev64260_early_serial_map(void) +{ +} +#endif + +static void __init +ev64260_setup_arch(void) +{ + if (ppc_md.progress) + ppc_md.progress("ev64260_setup_arch: enter", 0); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = Root_NFS; +#else + ROOT_DEV = Root_SDA2; +#endif + + if (ppc_md.progress) + ppc_md.progress("ev64260_setup_arch: Enabling L2 cache", 0); + + /* Enable L2 and L3 caches (if 745x) */ + _set_L2CR(_get_L2CR() | L2CR_L2E); + _set_L3CR(_get_L3CR() | L3CR_L3E); + + if (ppc_md.progress) + ppc_md.progress("ev64260_setup_arch: Initializing bridge", 0); + + ev64260_setup_bridge(); /* set up PCI bridge(s) */ + ev64260_setup_peripherals(); /* set up chip selects/GPP/MPP etc */ + + if (ppc_md.progress) + ppc_md.progress("ev64260_setup_arch: bridge init complete", 0); + +#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_SERIAL_MPSC_CONSOLE) + ev64260_early_serial_map(); +#endif + + printk(KERN_INFO "%s %s port (C) 2001 MontaVista Software, Inc." + "(source@mvista.com)\n", BOARD_VENDOR, BOARD_MACHINE); + + if (ppc_md.progress) + ppc_md.progress("ev64260_setup_arch: exit", 0); + + return; +} + +/* Platform device data fixup routines. */ +#if defined(CONFIG_SERIAL_MPSC) +static void __init +ev64260_fixup_mpsc_pdata(struct platform_device *pdev) +{ + struct mpsc_pdata *pdata; + + pdata = (struct mpsc_pdata *)pdev->dev.platform_data; + + pdata->max_idle = 40; + pdata->default_baud = EV64260_DEFAULT_BAUD; + pdata->brg_clk_src = EV64260_MPSC_CLK_SRC; + pdata->brg_clk_freq = EV64260_MPSC_CLK_FREQ; + + return; +} + +static int __init +ev64260_platform_notify(struct device *dev) +{ + static struct { + char *bus_id; + void ((*rtn)(struct platform_device *pdev)); + } dev_map[] = { + { MPSC_CTLR_NAME ".0", ev64260_fixup_mpsc_pdata }, + { MPSC_CTLR_NAME ".1", ev64260_fixup_mpsc_pdata }, + }; + struct platform_device *pdev; + int i; + + if (dev && dev->bus_id) + for (i=0; ibus_id, dev_map[i].bus_id, + BUS_ID_SIZE)) { + + pdev = container_of(dev, + struct platform_device, dev); + dev_map[i].rtn(pdev); + } + + return 0; +} +#endif + +static void +ev64260_reset_board(void *addr) +{ + local_irq_disable(); + + /* disable and invalidate the L2 cache */ + _set_L2CR(0); + _set_L2CR(0x200000); + + /* flush and disable L1 I/D cache */ + __asm__ __volatile__ + ("mfspr 3,1008\n\t" + "ori 5,5,0xcc00\n\t" + "ori 4,3,0xc00\n\t" + "andc 5,3,5\n\t" + "sync\n\t" + "mtspr 1008,4\n\t" + "isync\n\t" + "sync\n\t" + "mtspr 1008,5\n\t" + "isync\n\t" + "sync\n\t"); + + /* unmap any other random cs's that might overlap with bootcs */ + mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_0_WIN, 0, 0, 0); + bh.ci->disable_window_32bit(&bh, MV64x60_CPU2DEV_0_WIN); + mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_1_WIN, 0, 0, 0); + bh.ci->disable_window_32bit(&bh, MV64x60_CPU2DEV_1_WIN); + mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_2_WIN, 0, 0, 0); + bh.ci->disable_window_32bit(&bh, MV64x60_CPU2DEV_2_WIN); + mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_3_WIN, 0, 0, 0); + bh.ci->disable_window_32bit(&bh, MV64x60_CPU2DEV_3_WIN); + + /* map bootrom back in to gt @ reset defaults */ + mv64x60_set_32bit_window(&bh, MV64x60_CPU2BOOT_WIN, + 0xff800000, 8*1024*1024, 0); + bh.ci->enable_window_32bit(&bh, MV64x60_CPU2BOOT_WIN); + + /* move reg base back to default, setup default pci0 */ + mv64x60_write(&bh, MV64x60_INTERNAL_SPACE_DECODE, + (1<<24) | CONFIG_MV64X60_BASE >> 20); + + /* NOTE: FROM NOW ON no more GT_REGS accesses.. 0x1 is not mapped + * via BAT or MMU, and MSR IR/DR is ON */ + /* SRR0 has system reset vector, SRR1 has default MSR value */ + /* rfi restores MSR from SRR1 and sets the PC to the SRR0 value */ + /* NOTE: assumes reset vector is at 0xfff00100 */ + __asm__ __volatile__ + ("mtspr 26, %0\n\t" + "li 4,(1<<6)\n\t" + "mtspr 27,4\n\t" + "rfi\n\t" + :: "r" (addr):"r4"); + + return; +} + +static void +ev64260_restart(char *cmd) +{ + volatile ulong i = 10000000; + + ev64260_reset_board((void *)0xfff00100); + + while (i-- > 0); + panic("restart failed\n"); +} + +static void +ev64260_halt(void) +{ + local_irq_disable(); + while (1); + /* NOTREACHED */ +} + +static void +ev64260_power_off(void) +{ + ev64260_halt(); + /* NOTREACHED */ +} + +static int +ev64260_show_cpuinfo(struct seq_file *m) +{ + uint pvid; + + pvid = mfspr(SPRN_PVR); + seq_printf(m, "vendor\t\t: " BOARD_VENDOR "\n"); + seq_printf(m, "machine\t\t: " BOARD_MACHINE "\n"); + seq_printf(m, "cpu MHz\t\t: %d\n", ev64260_get_cpu_speed()/1000/1000); + seq_printf(m, "bus MHz\t\t: %d\n", ev64260_get_bus_speed()/1000/1000); + + return 0; +} + +/* DS1501 RTC has too much variation to use RTC for calibration */ +static void __init +ev64260_calibrate_decr(void) +{ + ulong freq; + + freq = ev64260_get_bus_speed()/4; + + printk(KERN_INFO "time_init: decrementer frequency = %lu.%.6lu MHz\n", + freq/1000000, freq%1000000); + + tb_ticks_per_jiffy = freq / HZ; + tb_to_us = mulhwu_scale_factor(freq, 1000000); + + return; +} + +/* + * Set BAT 3 to map 0xfb000000 to 0xfc000000 of physical memory space. + */ +static __inline__ void +ev64260_set_bat(void) +{ + mb(); + mtspr(SPRN_DBAT1U, 0xfb0001fe); + mtspr(SPRN_DBAT1L, 0xfb00002a); + mb(); + + return; +} + +#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) +static void __init +ev64260_map_io(void) +{ + io_block_mapping(0xfb000000, 0xfb000000, 0x01000000, _PAGE_IO); +} +#endif + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ +#ifdef CONFIG_BLK_DEV_INITRD + extern int initrd_below_start_ok; + + initrd_start=initrd_end=0; + initrd_below_start_ok=0; +#endif /* CONFIG_BLK_DEV_INITRD */ + + parse_bootinfo(find_bootinfo()); + + isa_mem_base = 0; + isa_io_base = EV64260_PCI0_IO_CPU_BASE; + pci_dram_offset = EV64260_PCI0_MEM_CPU_BASE; + + loops_per_jiffy = ev64260_get_cpu_speed() / HZ; + + ppc_md.setup_arch = ev64260_setup_arch; + ppc_md.show_cpuinfo = ev64260_show_cpuinfo; + ppc_md.init_IRQ = gt64260_init_irq; + ppc_md.get_irq = gt64260_get_irq; + + ppc_md.restart = ev64260_restart; + ppc_md.power_off = ev64260_power_off; + ppc_md.halt = ev64260_halt; + + ppc_md.find_end_of_memory = ev64260_find_end_of_memory; + + ppc_md.init = NULL; + + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; + ppc_md.calibrate_decr = ev64260_calibrate_decr; + + bh.p_base = CONFIG_MV64X60_NEW_BASE; + + ev64260_set_bat(); + +#ifdef CONFIG_SERIAL_8250 +#if defined(CONFIG_SERIAL_TEXT_DEBUG) + ppc_md.setup_io_mappings = ev64260_map_io; + ppc_md.progress = gen550_progress; +#endif +#if defined(CONFIG_KGDB) + ppc_md.setup_io_mappings = ev64260_map_io; + ppc_md.early_serial_map = ev64260_early_serial_map; +#endif +#elif defined(CONFIG_SERIAL_MPSC_CONSOLE) +#ifdef CONFIG_SERIAL_TEXT_DEBUG + ppc_md.setup_io_mappings = ev64260_map_io; + ppc_md.progress = mv64x60_mpsc_progress; + mv64x60_progress_init(CONFIG_MV64X60_NEW_BASE); +#endif /* CONFIG_SERIAL_TEXT_DEBUG */ +#ifdef CONFIG_KGDB + ppc_md.setup_io_mappings = ev64260_map_io; + ppc_md.early_serial_map = ev64260_early_serial_map; +#endif /* CONFIG_KGDB */ + +#endif + +#if defined(CONFIG_SERIAL_MPSC) + platform_notify = ev64260_platform_notify; +#endif + + return; +} diff --git a/arch/ppc/platforms/ev64260.h b/arch/ppc/platforms/ev64260.h new file mode 100644 index 00000000000..bedffced3a0 --- /dev/null +++ b/arch/ppc/platforms/ev64260.h @@ -0,0 +1,128 @@ +/* + * arch/ppc/platforms/ev64260.h + * + * Definitions for Marvell/Galileo EV-64260-BP Evaluation Board. + * + * Author: Mark A. Greer + * + * 2001-2002 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +/* + * The MV64x60 has 2 PCI buses each with 1 window from the CPU bus to + * PCI I/O space and 4 windows from the CPU bus to PCI MEM space. + * We'll only use one PCI MEM window on each PCI bus. + * + * This is the CPU physical memory map (windows must be at least 1MB and start + * on a boundary that is a multiple of the window size): + * + * 0xfc000000-0xffffffff - External FLASH on device module + * 0xfbf00000-0xfbffffff - Embedded (on board) FLASH + * 0xfbe00000-0xfbefffff - GT64260 Registers (preferably) + * but really a config option + * 0xfbd00000-0xfbdfffff - External SRAM on device module + * 0xfbc00000-0xfbcfffff - TODC chip on device module + * 0xfbb00000-0xfbbfffff - External UART on device module + * 0xa2000000-0xfbafffff - + * 0xa1000000-0xa1ffffff - PCI 1 I/O (defined in gt64260.h) + * 0xa0000000-0xa0ffffff - PCI 0 I/O (defined in gt64260.h) + * 0x90000000-0x9fffffff - PCI 1 MEM (defined in gt64260.h) + * 0x80000000-0x8fffffff - PCI 0 MEM (defined in gt64260.h) + */ + +#ifndef __PPC_PLATFORMS_EV64260_H +#define __PPC_PLATFORMS_EV64260_H + +/* PCI mappings */ +#define EV64260_PCI0_IO_CPU_BASE 0xa0000000 +#define EV64260_PCI0_IO_PCI_BASE 0x00000000 +#define EV64260_PCI0_IO_SIZE 0x01000000 + +#define EV64260_PCI0_MEM_CPU_BASE 0x80000000 +#define EV64260_PCI0_MEM_PCI_BASE 0x80000000 +#define EV64260_PCI0_MEM_SIZE 0x10000000 + +#define EV64260_PCI1_IO_CPU_BASE (EV64260_PCI0_IO_CPU_BASE + \ + EV64260_PCI0_IO_SIZE) +#define EV64260_PCI1_IO_PCI_BASE (EV64260_PCI0_IO_PCI_BASE + \ + EV64260_PCI0_IO_SIZE) +#define EV64260_PCI1_IO_SIZE 0x01000000 + +#define EV64260_PCI1_MEM_CPU_BASE (EV64260_PCI0_MEM_CPU_BASE + \ + EV64260_PCI0_MEM_SIZE) +#define EV64260_PCI1_MEM_PCI_BASE (EV64260_PCI0_MEM_PCI_BASE + \ + EV64260_PCI0_MEM_SIZE) +#define EV64260_PCI1_MEM_SIZE 0x10000000 + +/* CPU Physical Memory Map setup (other than PCI) */ +#define EV64260_EXT_FLASH_BASE 0xfc000000 +#define EV64260_EMB_FLASH_BASE 0xfbf00000 +#define EV64260_EXT_SRAM_BASE 0xfbd00000 +#define EV64260_TODC_BASE 0xfbc00000 +#define EV64260_UART_BASE 0xfbb00000 + +#define EV64260_EXT_FLASH_SIZE_ACTUAL 0x04000000 /* <= 64MB Extern FLASH */ +#define EV64260_EMB_FLASH_SIZE_ACTUAL 0x00080000 /* 512KB of Embed FLASH */ +#define EV64260_EXT_SRAM_SIZE_ACTUAL 0x00100000 /* 1MB SDRAM */ +#define EV64260_TODC_SIZE_ACTUAL 0x00000020 /* 32 bytes for TODC */ +#define EV64260_UART_SIZE_ACTUAL 0x00000040 /* 64 bytes for DUART */ + +#define EV64260_EXT_FLASH_SIZE max(GT64260_WINDOW_SIZE_MIN, \ + EV64260_EXT_FLASH_SIZE_ACTUAL) +#define EV64260_EMB_FLASH_SIZE max(GT64260_WINDOW_SIZE_MIN, \ + EV64260_EMB_FLASH_SIZE_ACTUAL) +#define EV64260_EXT_SRAM_SIZE max(GT64260_WINDOW_SIZE_MIN, \ + EV64260_EXT_SRAM_SIZE_ACTUAL) +#define EV64260_TODC_SIZE max(GT64260_WINDOW_SIZE_MIN, \ + EV64260_TODC_SIZE_ACTUAL) +/* Assembler in bootwrapper blows up if 'max' is used */ +#define EV64260_UART_SIZE GT64260_WINDOW_SIZE_MIN +#define EV64260_UART_END ((EV64260_UART_BASE + \ + EV64260_UART_SIZE - 1) & 0xfff00000) + +/* Board-specific IRQ info */ +#define EV64260_UART_0_IRQ 85 +#define EV64260_UART_1_IRQ 86 +#define EV64260_PCI_0_IRQ 91 +#define EV64260_PCI_1_IRQ 93 + +/* Serial port setup */ +#define EV64260_DEFAULT_BAUD 115200 + +#if defined(CONFIG_SERIAL_MPSC_CONSOLE) +#define SERIAL_PORT_DFNS + +#define EV64260_MPSC_CLK_SRC 8 /* TCLK */ +#define EV64260_MPSC_CLK_FREQ 100000000 /* 100MHz clk */ +#else +#define EV64260_SERIAL_0 (EV64260_UART_BASE + 0x20) +#define EV64260_SERIAL_1 EV64260_UART_BASE + +#define BASE_BAUD (EV64260_DEFAULT_BAUD * 2) + +#ifdef CONFIG_SERIAL_MANY_PORTS +#define RS_TABLE_SIZE 64 +#else +#define RS_TABLE_SIZE 2 +#endif + +#ifdef CONFIG_SERIAL_DETECT_IRQ +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) +#else +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST) +#endif + +/* Required for bootloader's ns16550.c code */ +#define STD_SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, EV64260_SERIAL_0, EV64260_UART_0_IRQ, STD_COM_FLAGS, \ + iomem_base: (u8 *)EV64260_SERIAL_0, /* ttyS0 */ \ + iomem_reg_shift: 2, \ + io_type: SERIAL_IO_MEM }, + +#define SERIAL_PORT_DFNS \ + STD_SERIAL_PORT_DFNS +#endif +#endif /* __PPC_PLATFORMS_EV64260_H */ diff --git a/arch/ppc/platforms/fads.h b/arch/ppc/platforms/fads.h new file mode 100644 index 00000000000..632b8178ce6 --- /dev/null +++ b/arch/ppc/platforms/fads.h @@ -0,0 +1,57 @@ +/* + * A collection of structures, addresses, and values associated with + * the Motorola 860T FADS board. Copied from the MBX stuff. + * + * Copyright (c) 1998 Dan Malek (dmalek@jlc.net) + */ +#ifdef __KERNEL__ +#ifndef __ASM_FADS_H__ +#define __ASM_FADS_H__ + +#include + +#include + +/* Memory map is configured by the PROM startup. + * I tried to follow the FADS manual, although the startup PROM + * dictates this and we simply have to move some of the physical + * addresses for Linux. + */ +#define BCSR_ADDR ((uint)0xff010000) +#define BCSR_SIZE ((uint)(64 * 1024)) +#define BCSR0 ((uint)0xff010000) +#define BCSR1 ((uint)0xff010004) +#define BCSR2 ((uint)0xff010008) +#define BCSR3 ((uint)0xff01000c) +#define BCSR4 ((uint)0xff010010) + +#define IMAP_ADDR ((uint)0xff000000) +#define IMAP_SIZE ((uint)(64 * 1024)) + +#define PCMCIA_MEM_ADDR ((uint)0xff020000) +#define PCMCIA_MEM_SIZE ((uint)(64 * 1024)) + +/* Bits of interest in the BCSRs. + */ +#define BCSR1_ETHEN ((uint)0x20000000) +#define BCSR1_RS232EN_1 ((uint)0x01000000) +#define BCSR1_RS232EN_2 ((uint)0x00040000) +#define BCSR4_ETHLOOP ((uint)0x80000000) /* EEST Loopback */ +#define BCSR4_EEFDX ((uint)0x40000000) /* EEST FDX enable */ +#define BCSR4_FETH_EN ((uint)0x08000000) /* PHY enable */ +#define BCSR4_FETHCFG0 ((uint)0x04000000) /* PHY autoneg mode */ +#define BCSR4_FETHCFG1 ((uint)0x00400000) /* PHY autoneg mode */ +#define BCSR4_FETHFDE ((uint)0x02000000) /* PHY FDX advertise */ +#define BCSR4_FETHRST ((uint)0x00200000) /* PHY Reset */ + +/* Interrupt level assignments. + */ +#define FEC_INTERRUPT SIU_LEVEL1 /* FEC interrupt */ +#define PHY_INTERRUPT SIU_IRQ2 /* PHY link change interrupt */ + +/* We don't use the 8259. + */ +#define NR_8259_INTS 0 + +#endif /* __ASM_FADS_H__ */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/gemini.h b/arch/ppc/platforms/gemini.h new file mode 100644 index 00000000000..06de5924891 --- /dev/null +++ b/arch/ppc/platforms/gemini.h @@ -0,0 +1,168 @@ +/* + * arch/ppc/platforms/gemini.h + * + * + * Onboard registers and descriptions for Synergy Microsystems' + * "Gemini" boards. + * + */ +#ifdef __KERNEL__ +#ifndef __PPC_GEMINI_H +#define __PPC_GEMINI_H + +/* Registers */ + +#define GEMINI_SERIAL_B (0xffeffb00) +#define GEMINI_SERIAL_A (0xffeffb08) +#define GEMINI_USWITCH (0xffeffd00) +#define GEMINI_BREV (0xffeffe00) +#define GEMINI_BECO (0xffeffe08) +#define GEMINI_FEAT (0xffeffe10) +#define GEMINI_BSTAT (0xffeffe18) +#define GEMINI_CPUSTAT (0xffeffe20) +#define GEMINI_L2CFG (0xffeffe30) +#define GEMINI_MEMCFG (0xffeffe38) +#define GEMINI_FLROM (0xffeffe40) +#define GEMINI_P0PCI (0xffeffe48) +#define GEMINI_FLWIN (0xffeffe50) +#define GEMINI_P0INTMASK (0xffeffe60) +#define GEMINI_P0INTAP (0xffeffe68) +#define GEMINI_PCIERR (0xffeffe70) +#define GEMINI_LEDBASE (0xffeffe80) +#define GEMINI_RTC (0xffe9fff8) +#define GEMINI_LEDS 8 +#define GEMINI_SWITCHES 8 + + +/* Flash ROM bit definitions */ +#define GEMINI_FLS_WEN (1<<0) +#define GEMINI_FLS_JMP (1<<6) +#define GEMINI_FLS_BOOT (1<<7) + +/* Memory bit definitions */ +#define GEMINI_MEM_TYPE_MASK 0xc0 +#define GEMINI_MEM_SIZE_MASK 0x38 +#define GEMINI_MEM_BANK_MASK 0x07 + +/* L2 cache bit definitions */ +#define GEMINI_L2_SIZE_MASK 0xc0 +#define GEMINI_L2_RATIO_MASK 0x03 + +/* Timebase register bit definitons */ +#define GEMINI_TIMEB0_EN (1<<0) +#define GEMINI_TIMEB1_EN (1<<1) +#define GEMINI_TIMEB2_EN (1<<2) +#define GEMINI_TIMEB3_EN (1<<3) + +/* CPU status bit definitions */ +#define GEMINI_CPU_ID_MASK 0x03 +#define GEMINI_CPU_COUNT_MASK 0x0c +#define GEMINI_CPU0_HALTED (1<<4) +#define GEMINI_CPU1_HALTED (1<<5) +#define GEMINI_CPU2_HALTED (1<<6) +#define GEMINI_CPU3_HALTED (1<<7) + +/* Board status bit definitions */ +#define GEMINI_BRD_FAIL (1<<0) /* FAIL led is lit */ +#define GEMINI_BRD_BUS_MASK 0x0c /* PowerPC bus speed */ + +/* Board family/feature bit descriptions */ +#define GEMINI_FEAT_HAS_FLASH (1<<0) +#define GEMINI_FEAT_HAS_ETH (1<<1) +#define GEMINI_FEAT_HAS_SCSI (1<<2) +#define GEMINI_FEAT_HAS_P0 (1<<3) +#define GEMINI_FEAT_FAM_MASK 0xf0 + +/* Mod/ECO bit definitions */ +#define GEMINI_ECO_LEVEL_MASK 0x0f +#define GEMINI_MOD_MASK 0xf0 + +/* Type/revision bit definitions */ +#define GEMINI_REV_MASK 0x0f +#define GEMINI_TYPE_MASK 0xf0 + +/* User switch definitions */ +#define GEMINI_SWITCH_VERBOSE 1 /* adds "debug" to boot cmd line */ +#define GEMINI_SWITCH_SINGLE_USER 7 /* boots into "single-user" mode */ + +#define SGS_RTC_CONTROL 0 +#define SGS_RTC_SECONDS 1 +#define SGS_RTC_MINUTES 2 +#define SGS_RTC_HOURS 3 +#define SGS_RTC_DAY 4 +#define SGS_RTC_DAY_OF_MONTH 5 +#define SGS_RTC_MONTH 6 +#define SGS_RTC_YEAR 7 + +#define SGS_RTC_SET 0x80 +#define SGS_RTC_IS_STOPPED 0x80 + +#define GRACKLE_CONFIG_ADDR_ADDR (0xfec00000) +#define GRACKLE_CONFIG_DATA_ADDR (0xfee00000) + +#define GEMINI_BOOT_INIT (0xfff00100) + +#ifndef __ASSEMBLY__ + +static inline void grackle_write( unsigned long addr, unsigned long data ) +{ + __asm__ __volatile__( + " stwbrx %1, 0, %0\n \ + sync\n \ + stwbrx %3, 0, %2\n \ + sync " + : /* no output */ + : "r" (GRACKLE_CONFIG_ADDR_ADDR), "r" (addr), + "r" (GRACKLE_CONFIG_DATA_ADDR), "r" (data)); +} + +static inline unsigned long grackle_read( unsigned long addr ) +{ + unsigned long val; + + __asm__ __volatile__( + " stwbrx %1, 0, %2\n \ + sync\n \ + lwbrx %0, 0, %3\n \ + sync " + : "=r" (val) + : "r" (addr), "r" (GRACKLE_CONFIG_ADDR_ADDR), + "r" (GRACKLE_CONFIG_DATA_ADDR)); + + return val; +} + +static inline void gemini_led_on( int led ) +{ + if (led >= 0 && led < GEMINI_LEDS) + *(unsigned char *)(GEMINI_LEDBASE + (led<<3)) = 1; +} + +static inline void gemini_led_off(int led) +{ + if (led >= 0 && led < GEMINI_LEDS) + *(unsigned char *)(GEMINI_LEDBASE + (led<<3)) = 0; +} + +static inline int gemini_led_val(int led) +{ + int val = 0; + if (led >= 0 && led < GEMINI_LEDS) + val = *(unsigned char *)(GEMINI_LEDBASE + (led<<3)); + return (val & 0x1); +} + +/* returns processor id from the board */ +static inline int gemini_processor(void) +{ + unsigned char cpu = *(unsigned char *)(GEMINI_CPUSTAT); + return (int) ((cpu == 0) ? 4 : (cpu & GEMINI_CPU_ID_MASK)); +} + + +extern void _gemini_reboot(void); +extern void gemini_prom_init(void); +extern void gemini_init_l2(void); +#endif /* __ASSEMBLY__ */ +#endif +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/gemini_pci.c b/arch/ppc/platforms/gemini_pci.c new file mode 100644 index 00000000000..95656091ba2 --- /dev/null +++ b/arch/ppc/platforms/gemini_pci.c @@ -0,0 +1,41 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +void __init gemini_pcibios_fixup(void) +{ + int i; + struct pci_dev *dev = NULL; + + for_each_pci_dev(dev) { + for(i = 0; i < 6; i++) { + if (dev->resource[i].flags & IORESOURCE_IO) { + dev->resource[i].start |= (0xfe << 24); + dev->resource[i].end |= (0xfe << 24); + } + } + } +} + + +/* The "bootloader" for Synergy boards does none of this for us, so we need to + lay it all out ourselves... --Dan */ +void __init gemini_find_bridges(void) +{ + struct pci_controller* hose; + + ppc_md.pcibios_fixup = gemini_pcibios_fixup; + + hose = pcibios_alloc_controller(); + if (!hose) + return; + setup_indirect_pci(hose, 0xfec00000, 0xfee00000); +} diff --git a/arch/ppc/platforms/gemini_prom.S b/arch/ppc/platforms/gemini_prom.S new file mode 100644 index 00000000000..8c5065d5650 --- /dev/null +++ b/arch/ppc/platforms/gemini_prom.S @@ -0,0 +1,93 @@ +/* + * arch/ppc/platforms/gemini_prom.S + * + * Not really prom support code (yet), but sort of anti-prom code. The current + * bootloader does a number of things it shouldn't and doesn't do things that it + * should. The stuff in here is mainly a hodge-podge collection of setup code + * to get the board up and running. + * ---Dan + */ + +#include +#include +#include +#include +#include + +/* + * On 750's the MMU is on when Linux is booted, so we need to clear out the + * bootloader's BAT settings, make sure we're in supervisor state (gotcha!), + * and turn off the MMU. + * + */ + +_GLOBAL(gemini_prom_init) +#ifdef CONFIG_SMP + /* Since the MMU's on, get stuff in rom space that we'll need */ + lis r4,GEMINI_CPUSTAT@h + ori r4,r4,GEMINI_CPUSTAT@l + lbz r5,0(r4) + andi. r5,r5,3 + mr r24,r5 /* cpu # used later on */ +#endif + mfmsr r4 + li r3,MSR_PR /* ensure supervisor! */ + ori r3,r3,MSR_IR|MSR_DR + andc r4,r4,r3 + mtmsr r4 + isync +#if 0 + /* zero out the bats now that the MMU is off */ +prom_no_mmu: + li r3,0 + mtspr SPRN_IBAT0U,r3 + mtspr SPRN_IBAT0L,r3 + mtspr SPRN_IBAT1U,r3 + mtspr SPRN_IBAT1L,r3 + mtspr SPRN_IBAT2U,r3 + mtspr SPRN_IBAT2L,r3 + mtspr SPRN_IBAT3U,r3 + mtspr SPRN_IBAT3L,r3 + + mtspr SPRN_DBAT0U,r3 + mtspr SPRN_DBAT0L,r3 + mtspr SPRN_DBAT1U,r3 + mtspr SPRN_DBAT1L,r3 + mtspr SPRN_DBAT2U,r3 + mtspr SPRN_DBAT2L,r3 + mtspr SPRN_DBAT3U,r3 + mtspr SPRN_DBAT3L,r3 +#endif + + /* the bootloader (as far as I'm currently aware) doesn't mess with page + tables, but since we're already here, might as well zap these, too */ + li r4,0 + mtspr SPRN_SDR1,r4 + + li r4,16 + mtctr r4 + li r3,0 + li r4,0 +3: mtsrin r3,r4 + addi r3,r3,1 + bdnz 3b + +#ifdef CONFIG_SMP + /* The 750 book (and Mot/IBM support) says that this will "assist" snooping + when in SMP. Not sure yet whether this should stay or leave... */ + mfspr r4,SPRN_HID0 + ori r4,r4,HID0_ABE + mtspr SPRN_HID0,r4 + sync +#endif /* CONFIG_SMP */ + blr + +/* apparently, SMon doesn't pay attention to HID0[SRST]. Disable the MMU and + branch to 0xfff00100 */ +_GLOBAL(_gemini_reboot) + lis r5,GEMINI_BOOT_INIT@h + ori r5,r5,GEMINI_BOOT_INIT@l + li r6,MSR_IP + mtspr SPRN_SRR0,r5 + mtspr SPRN_SRR1,r6 + rfi diff --git a/arch/ppc/platforms/gemini_serial.h b/arch/ppc/platforms/gemini_serial.h new file mode 100644 index 00000000000..69855aeec88 --- /dev/null +++ b/arch/ppc/platforms/gemini_serial.h @@ -0,0 +1,41 @@ +#ifdef __KERNEL__ +#ifndef __ASMPPC_GEMINI_SERIAL_H +#define __ASMPPC_GEMINI_SERIAL_H + +#include +#include + +#ifdef CONFIG_SERIAL_MANY_PORTS +#define RS_TABLE_SIZE 64 +#else +#define RS_TABLE_SIZE 4 +#endif + +/* Rate for the 24.576 Mhz clock for the onboard serial chip */ +#define BASE_BAUD (24576000 / 16) + +#ifdef CONFIG_SERIAL_DETECT_IRQ +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) +#define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_AUTO_IRQ) +#else +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST) +#define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF) +#endif + +#define STD_SERIAL_PORT_DEFNS \ + { 0, BASE_BAUD, GEMINI_SERIAL_A, 15, STD_COM_FLAGS }, /* ttyS0 */ \ + { 0, BASE_BAUD, GEMINI_SERIAL_B, 14, STD_COM_FLAGS }, /* ttyS1 */ \ + +#ifdef CONFIG_GEMINI_PU32 +#define PU32_SERIAL_PORT_DEFNS \ + { 0, BASE_BAUD, NULL, 0, STD_COM_FLAGS }, +#else +#define PU32_SERIAL_PORT_DEFNS +#endif + +#define SERIAL_PORT_DFNS \ + STD_SERIAL_PORT_DEFNS \ + PU32_SERIAL_PORT_DEFNS + +#endif +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/gemini_setup.c b/arch/ppc/platforms/gemini_setup.c new file mode 100644 index 00000000000..1a42cb9b113 --- /dev/null +++ b/arch/ppc/platforms/gemini_setup.c @@ -0,0 +1,584 @@ +/* + * arch/ppc/platforms/gemini_setup.c + * + * Copyright (C) 1995 Linus Torvalds + * Adapted from 'alpha' version by Gary Thomas + * Modified by Cort Dougan (cort@cs.nmt.edu) + * Synergy Microsystems board support by Dan Cox (dan@synergymicro.com) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void gemini_find_bridges(void); +static int gemini_get_clock_speed(void); +extern void gemini_pcibios_fixup(void); + +static char *gemini_board_families[] = { + "VGM", "VSS", "KGM", "VGR", "VCM", "VCS", "KCM", "VCR" +}; +static int gemini_board_count = sizeof(gemini_board_families) / + sizeof(gemini_board_families[0]); + +static unsigned int cpu_7xx[16] = { + 0, 15, 14, 0, 0, 13, 5, 9, 6, 11, 8, 10, 16, 12, 7, 0 +}; +static unsigned int cpu_6xx[16] = { + 0, 0, 14, 0, 0, 13, 5, 9, 6, 11, 8, 10, 0, 12, 7, 0 +}; + +/* + * prom_init is the Gemini version of prom.c:prom_init. We only need + * the BSS clearing code, so I copied that out of prom.c. This is a + * lot simpler than hacking prom.c so it will build with Gemini. -VAL + */ + +#define PTRRELOC(x) ((typeof(x))((unsigned long)(x) + offset)) + +unsigned long +prom_init(void) +{ + unsigned long offset = reloc_offset(); + unsigned long phys; + extern char __bss_start, _end; + + /* First zero the BSS -- use memset, some arches don't have + * caches on yet */ + memset_io(PTRRELOC(&__bss_start),0 , &_end - &__bss_start); + + /* Default */ + phys = offset + KERNELBASE; + + gemini_prom_init(); + + return phys; +} + +int +gemini_show_cpuinfo(struct seq_file *m) +{ + unsigned char reg, rev; + char *family; + unsigned int type; + + reg = readb(GEMINI_FEAT); + family = gemini_board_families[((reg>>4) & 0xf)]; + if (((reg>>4) & 0xf) > gemini_board_count) + printk(KERN_ERR "cpuinfo(): unable to determine board family\n"); + + reg = readb(GEMINI_BREV); + type = (reg>>4) & 0xf; + rev = reg & 0xf; + + reg = readb(GEMINI_BECO); + + seq_printf(m, "machine\t\t: Gemini %s%d, rev %c, eco %d\n", + family, type, (rev + 'A'), (reg & 0xf)); + + seq_printf(m, "board\t\t: Gemini %s", family); + if (type > 9) + seq_printf(m, "%c", (type - 10) + 'A'); + else + seq_printf(m, "%d", type); + + seq_printf(m, ", rev %c, eco %d\n", (rev + 'A'), (reg & 0xf)); + + seq_printf(m, "clock\t\t: %dMhz\n", gemini_get_clock_speed()); + + return 0; +} + +static u_char gemini_openpic_initsenses[] = { + 1, + 1, + 1, + 1, + 0, + 0, + 1, /* remainder are level-triggered */ +}; + +#define GEMINI_MPIC_ADDR (0xfcfc0000) +#define GEMINI_MPIC_PCI_CFG (0x80005800) + +void __init gemini_openpic_init(void) +{ + + OpenPIC_Addr = (volatile struct OpenPIC *) + grackle_read(GEMINI_MPIC_PCI_CFG + 0x10); + OpenPIC_InitSenses = gemini_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof( gemini_openpic_initsenses ); + + ioremap( GEMINI_MPIC_ADDR, OPENPIC_SIZE); +} + + +extern unsigned long loops_per_jiffy; +extern int root_mountflags; +extern char cmd_line[]; + +void +gemini_heartbeat(void) +{ + static unsigned long led = GEMINI_LEDBASE+(4*8); + static char direction = 8; + + + /* We only want to do this on 1 CPU */ + if (smp_processor_id()) + return; + *(char *)led = 0; + if ( (led + direction) > (GEMINI_LEDBASE+(7*8)) || + (led + direction) < (GEMINI_LEDBASE+(4*8)) ) + direction *= -1; + led += direction; + *(char *)led = 0xff; + ppc_md.heartbeat_count = ppc_md.heartbeat_reset; +} + +void __init gemini_setup_arch(void) +{ + extern char cmd_line[]; + + + loops_per_jiffy = 50000000/HZ; + +#ifdef CONFIG_BLK_DEV_INITRD + /* bootable off CDROM */ + if (initrd_start) + ROOT_DEV = Root_SR0; + else +#endif + ROOT_DEV = Root_SDA1; + + /* nothing but serial consoles... */ + sprintf(cmd_line, "%s console=ttyS0", cmd_line); + + printk("Boot arguments: %s\n", cmd_line); + + ppc_md.heartbeat = gemini_heartbeat; + ppc_md.heartbeat_reset = HZ/8; + ppc_md.heartbeat_count = 1; + + /* Lookup PCI hosts */ + gemini_find_bridges(); + /* take special pains to map the MPIC, since it isn't mapped yet */ + gemini_openpic_init(); + /* start the L2 */ + gemini_init_l2(); +} + + +int +gemini_get_clock_speed(void) +{ + unsigned long hid1, pvr; + int clock; + + pvr = mfspr(SPRN_PVR); + hid1 = (mfspr(SPRN_HID1) >> 28) & 0xf; + if (PVR_VER(pvr) == 8 || + PVR_VER(pvr) == 12) + hid1 = cpu_7xx[hid1]; + else + hid1 = cpu_6xx[hid1]; + + switch((readb(GEMINI_BSTAT) & 0xc) >> 2) { + + case 0: + default: + clock = (hid1*100)/3; + break; + + case 1: + clock = (hid1*125)/3; + break; + + case 2: + clock = (hid1*50); + break; + } + + return clock; +} + +void __init gemini_init_l2(void) +{ + unsigned char reg, brev, fam, creg; + unsigned long cache; + unsigned long pvr; + + reg = readb(GEMINI_L2CFG); + brev = readb(GEMINI_BREV); + fam = readb(GEMINI_FEAT); + pvr = mfspr(SPRN_PVR); + + switch(PVR_VER(pvr)) { + + case 8: + if (reg & 0xc0) + cache = (((reg >> 6) & 0x3) << 28); + else + cache = 0x3 << 28; + +#ifdef CONFIG_SMP + /* Pre-3.0 processor revs had snooping errata. Leave + their L2's disabled with SMP. -- Dan */ + if (PVR_CFG(pvr) < 3) { + printk("Pre-3.0 750; L2 left disabled!\n"); + return; + } +#endif /* CONFIG_SMP */ + + /* Special case: VGM5-B's came before L2 ratios were set on + the board. Processor speed shouldn't be too high, so + set L2 ratio to 1:1.5. */ + if ((brev == 0x51) && ((fam & 0xa0) >> 4) == 0) + reg |= 1; + + /* determine best cache ratio based upon what the board + tells us (which sometimes _may_ not be true) and + the processor speed. */ + else { + if (gemini_get_clock_speed() > 250) + reg = 2; + } + break; + case 12: + { + static unsigned long l2_size_val = 0; + + if (!l2_size_val) + l2_size_val = _get_L2CR(); + cache = l2_size_val; + break; + } + case 4: + case 9: + creg = readb(GEMINI_CPUSTAT); + if (((creg & 0xc) >> 2) != 1) + printk("Dual-604 boards don't support the use of L2\n"); + else + writeb(1, GEMINI_L2CFG); + return; + default: + printk("Unknown processor; L2 left disabled\n"); + return; + } + + cache |= ((1<>2)&0x3) { + case 0: + default: + freq = 66667; + break; + case 1: + freq = 83000; + break; + case 2: + freq = 100000; + break; + } + + freq *= 1000; + divisor = 4; + tb_ticks_per_jiffy = freq / HZ / divisor; + tb_to_us = mulhwu_scale_factor(freq/divisor, 1000000); +} + +unsigned long __init gemini_find_end_of_memory(void) +{ + unsigned long total; + unsigned char reg; + + reg = readb(GEMINI_MEMCFG); + total = ((1<<((reg & 0x7) - 1)) * + (8<<((reg >> 3) & 0x7))); + total *= (1024*1024); + return total; +} + +static void __init +gemini_map_io(void) +{ + io_block_mapping(0xf0000000, 0xf0000000, 0x10000000, _PAGE_IO); + io_block_mapping(0x80000000, 0x80000000, 0x10000000, _PAGE_IO); +} + +#ifdef CONFIG_SMP +static int +smp_gemini_probe(void) +{ + int i, nr; + + nr = (readb(GEMINI_CPUSTAT) & GEMINI_CPU_COUNT_MASK) >> 2; + if (nr == 0) + nr = 4; + + if (nr > 1) { + openpic_request_IPIs(); + for (i = 1; i < nr; ++i) + smp_hw_index[i] = i; + } + + return nr; +} + +static void +smp_gemini_kick_cpu(int nr) +{ + openpic_reset_processor_phys(1 << nr); + openpic_reset_processor_phys(0); +} + +static void +smp_gemini_setup_cpu(int cpu_nr) +{ + if (OpenPIC_Addr) + do_openpic_setup_cpu(); + if (cpu_nr > 0) + gemini_init_l2(); +} + +static struct smp_ops_t gemini_smp_ops = { + smp_openpic_message_pass, + smp_gemini_probe, + smp_gemini_kick_cpu, + smp_gemini_setup_cpu, + .give_timebase = smp_generic_give_timebase, + .take_timebase = smp_generic_take_timebase, +}; +#endif /* CONFIG_SMP */ + +void __init platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + int i; + + /* Restore BATs for now */ + mtspr(SPRN_DBAT3U, 0xf0001fff); + mtspr(SPRN_DBAT3L, 0xf000002a); + + parse_bootinfo(find_bootinfo()); + + for(i = 0; i < GEMINI_LEDS; i++) + gemini_led_off(i); + + ISA_DMA_THRESHOLD = 0; + DMA_MODE_READ = 0; + DMA_MODE_WRITE = 0; + +#ifdef CONFIG_BLK_DEV_INITRD + if ( r4 ) + { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } +#endif + + ppc_md.setup_arch = gemini_setup_arch; + ppc_md.show_cpuinfo = gemini_show_cpuinfo; + ppc_md.irq_canonicalize = NULL; + ppc_md.init_IRQ = gemini_init_IRQ; + ppc_md.get_irq = openpic_get_irq; + ppc_md.init = NULL; + + ppc_md.restart = gemini_restart; + ppc_md.power_off = gemini_power_off; + ppc_md.halt = gemini_halt; + + ppc_md.time_init = gemini_time_init; + ppc_md.set_rtc_time = gemini_set_rtc_time; + ppc_md.get_rtc_time = gemini_get_rtc_time; + ppc_md.calibrate_decr = gemini_calibrate_decr; + + ppc_md.find_end_of_memory = gemini_find_end_of_memory; + ppc_md.setup_io_mappings = gemini_map_io; + + ppc_md.pcibios_fixup_bus = gemini_pcibios_fixup; + +#ifdef CONFIG_SMP + ppc_md.smp_ops = &gemini_smp_ops; +#endif /* CONFIG_SMP */ +} diff --git a/arch/ppc/platforms/hdpu.c b/arch/ppc/platforms/hdpu.c new file mode 100644 index 00000000000..b659d7b3d74 --- /dev/null +++ b/arch/ppc/platforms/hdpu.c @@ -0,0 +1,1062 @@ + +/* + * arch/ppc/platforms/hdpu_setup.c + * + * Board setup routines for the Sky Computers HDPU Compute Blade. + * + * Written by Brian Waite + * + * Based on code done by - Mark A. Greer + * Rabeeh Khoury - rabeeh@galileo.co.il + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BOARD_VENDOR "Sky Computers" +#define BOARD_MACHINE "HDPU-CB-A" + +bd_t ppcboot_bd; +int ppcboot_bd_valid = 0; + +static mv64x60_handle_t bh; + +extern char cmd_line[]; + +unsigned long hdpu_find_end_of_memory(void); +void hdpu_mpsc_progress(char *s, unsigned short hex); +void hdpu_heartbeat(void); + +static void parse_bootinfo(unsigned long r3, + unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7); +static void hdpu_set_l1pe(void); +static void hdpu_cpustate_set(unsigned char new_state); +#ifdef CONFIG_SMP +static spinlock_t timebase_lock = SPIN_LOCK_UNLOCKED; +static unsigned int timebase_upper = 0, timebase_lower = 0; +extern int smp_tb_synchronized; + +void __devinit hdpu_tben_give(void); +void __devinit hdpu_tben_take(void); +#endif + +static int __init +hdpu_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + struct pci_controller *hose = pci_bus_to_hose(dev->bus->number); + + if (hose->index == 0) { + static char pci_irq_table[][4] = { + {HDPU_PCI_0_IRQ, 0, 0, 0}, + {HDPU_PCI_0_IRQ, 0, 0, 0}, + }; + + const long min_idsel = 1, max_idsel = 2, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; + } else { + static char pci_irq_table[][4] = { + {HDPU_PCI_1_IRQ, 0, 0, 0}, + }; + + const long min_idsel = 1, max_idsel = 1, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; + } +} + +static void __init hdpu_intr_setup(void) +{ + mv64x60_write(&bh, MV64x60_GPP_IO_CNTL, + (1 | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) | + (1 << 6) | (1 << 7) | (1 << 12) | (1 << 16) | + (1 << 18) | (1 << 19) | (1 << 20) | (1 << 21) | + (1 << 22) | (1 << 23) | (1 << 24) | (1 << 25) | + (1 << 26) | (1 << 27) | (1 << 28) | (1 << 29))); + + /* XXXX Erranum FEr PCI-#8 */ + mv64x60_clr_bits(&bh, MV64x60_PCI0_CMD, (1 << 5) | (1 << 9)); + mv64x60_clr_bits(&bh, MV64x60_PCI1_CMD, (1 << 5) | (1 << 9)); + + /* + * Dismiss and then enable interrupt on GPP interrupt cause + * for CPU #0 + */ + mv64x60_write(&bh, MV64x60_GPP_INTR_CAUSE, ~((1 << 8) | (1 << 13))); + mv64x60_set_bits(&bh, MV64x60_GPP_INTR_MASK, (1 << 8) | (1 << 13)); + + /* + * Dismiss and then enable interrupt on CPU #0 high cause reg + * BIT25 summarizes GPP interrupts 8-15 + */ + mv64x60_set_bits(&bh, MV64360_IC_CPU0_INTR_MASK_HI, (1 << 25)); +} + +static void __init hdpu_setup_peripherals(void) +{ + unsigned int val; + + mv64x60_set_32bit_window(&bh, MV64x60_CPU2BOOT_WIN, + HDPU_EMB_FLASH_BASE, HDPU_EMB_FLASH_SIZE, 0); + bh.ci->enable_window_32bit(&bh, MV64x60_CPU2BOOT_WIN); + + mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_0_WIN, + HDPU_TBEN_BASE, HDPU_TBEN_SIZE, 0); + bh.ci->enable_window_32bit(&bh, MV64x60_CPU2DEV_0_WIN); + + mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_1_WIN, + HDPU_NEXUS_ID_BASE, HDPU_NEXUS_ID_SIZE, 0); + bh.ci->enable_window_32bit(&bh, MV64x60_CPU2DEV_1_WIN); + + mv64x60_set_32bit_window(&bh, MV64x60_CPU2SRAM_WIN, + HDPU_INTERNAL_SRAM_BASE, + HDPU_INTERNAL_SRAM_SIZE, 0); + bh.ci->enable_window_32bit(&bh, MV64x60_CPU2SRAM_WIN); + + bh.ci->disable_window_32bit(&bh, MV64x60_ENET2MEM_4_WIN); + mv64x60_set_32bit_window(&bh, MV64x60_ENET2MEM_4_WIN, 0, 0, 0); + + mv64x60_clr_bits(&bh, MV64x60_PCI0_PCI_DECODE_CNTL, (1 << 3)); + mv64x60_clr_bits(&bh, MV64x60_PCI1_PCI_DECODE_CNTL, (1 << 3)); + mv64x60_clr_bits(&bh, MV64x60_TIMR_CNTR_0_3_CNTL, + ((1 << 0) | (1 << 8) | (1 << 16) | (1 << 24))); + + /* Enable pipelining */ + mv64x60_set_bits(&bh, MV64x60_CPU_CONFIG, (1 << 13)); + /* Enable Snoop Pipelineing */ + mv64x60_set_bits(&bh, MV64360_D_UNIT_CONTROL_HIGH, (1 << 24)); + + /* + * Change DRAM read buffer assignment. + * Assign read buffer 0 dedicated only for CPU, + * and the rest read buffer 1. + */ + val = mv64x60_read(&bh, MV64360_SDRAM_CONFIG); + val = val & 0x03ffffff; + val = val | 0xf8000000; + mv64x60_write(&bh, MV64360_SDRAM_CONFIG, val); + + /* + * Configure internal SRAM - + * Cache coherent write back, if CONFIG_MV64360_SRAM_CACHE_COHERENT set + * Parity enabled. + * Parity error propagation + * Arbitration not parked for CPU only + * Other bits are reserved. + */ +#ifdef CONFIG_MV64360_SRAM_CACHE_COHERENT + mv64x60_write(&bh, MV64360_SRAM_CONFIG, 0x001600b2); +#else + mv64x60_write(&bh, MV64360_SRAM_CONFIG, 0x001600b0); +#endif + + hdpu_intr_setup(); +} + +static void __init hdpu_setup_bridge(void) +{ + struct mv64x60_setup_info si; + int i; + + memset(&si, 0, sizeof(si)); + + si.phys_reg_base = HDPU_BRIDGE_REG_BASE; + si.pci_0.enable_bus = 1; + si.pci_0.pci_io.cpu_base = HDPU_PCI0_IO_START_PROC_ADDR; + si.pci_0.pci_io.pci_base_hi = 0; + si.pci_0.pci_io.pci_base_lo = HDPU_PCI0_IO_START_PCI_ADDR; + si.pci_0.pci_io.size = HDPU_PCI0_IO_SIZE; + si.pci_0.pci_io.swap = MV64x60_CPU2PCI_SWAP_NONE; + si.pci_0.pci_mem[0].cpu_base = HDPU_PCI0_MEM_START_PROC_ADDR; + si.pci_0.pci_mem[0].pci_base_hi = HDPU_PCI0_MEM_START_PCI_HI_ADDR; + si.pci_0.pci_mem[0].pci_base_lo = HDPU_PCI0_MEM_START_PCI_LO_ADDR; + si.pci_0.pci_mem[0].size = HDPU_PCI0_MEM_SIZE; + si.pci_0.pci_mem[0].swap = MV64x60_CPU2PCI_SWAP_NONE; + si.pci_0.pci_cmd_bits = 0; + si.pci_0.latency_timer = 0x80; + + si.pci_1.enable_bus = 1; + si.pci_1.pci_io.cpu_base = HDPU_PCI1_IO_START_PROC_ADDR; + si.pci_1.pci_io.pci_base_hi = 0; + si.pci_1.pci_io.pci_base_lo = HDPU_PCI1_IO_START_PCI_ADDR; + si.pci_1.pci_io.size = HDPU_PCI1_IO_SIZE; + si.pci_1.pci_io.swap = MV64x60_CPU2PCI_SWAP_NONE; + si.pci_1.pci_mem[0].cpu_base = HDPU_PCI1_MEM_START_PROC_ADDR; + si.pci_1.pci_mem[0].pci_base_hi = HDPU_PCI1_MEM_START_PCI_HI_ADDR; + si.pci_1.pci_mem[0].pci_base_lo = HDPU_PCI1_MEM_START_PCI_LO_ADDR; + si.pci_1.pci_mem[0].size = HDPU_PCI1_MEM_SIZE; + si.pci_1.pci_mem[0].swap = MV64x60_CPU2PCI_SWAP_NONE; + si.pci_1.pci_cmd_bits = 0; + si.pci_1.latency_timer = 0x80; + + for (i = 0; i < MV64x60_CPU2MEM_WINDOWS; i++) { +#if defined(CONFIG_NOT_COHERENT_CACHE) + si.cpu_prot_options[i] = 0; + si.enet_options[i] = MV64360_ENET2MEM_SNOOP_NONE; + si.mpsc_options[i] = MV64360_MPSC2MEM_SNOOP_NONE; + si.idma_options[i] = MV64360_IDMA2MEM_SNOOP_NONE; + + si.pci_1.acc_cntl_options[i] = + MV64360_PCI_ACC_CNTL_SNOOP_NONE | + MV64360_PCI_ACC_CNTL_SWAP_NONE | + MV64360_PCI_ACC_CNTL_MBURST_128_BYTES | + MV64360_PCI_ACC_CNTL_RDSIZE_256_BYTES; + + si.pci_0.acc_cntl_options[i] = + MV64360_PCI_ACC_CNTL_SNOOP_NONE | + MV64360_PCI_ACC_CNTL_SWAP_NONE | + MV64360_PCI_ACC_CNTL_MBURST_128_BYTES | + MV64360_PCI_ACC_CNTL_RDSIZE_256_BYTES; + +#else + si.cpu_prot_options[i] = 0; + si.enet_options[i] = MV64360_ENET2MEM_SNOOP_WB; /* errata */ + si.mpsc_options[i] = MV64360_MPSC2MEM_SNOOP_WB; /* errata */ + si.idma_options[i] = MV64360_IDMA2MEM_SNOOP_WB; /* errata */ + + si.pci_0.acc_cntl_options[i] = + MV64360_PCI_ACC_CNTL_SNOOP_WB | + MV64360_PCI_ACC_CNTL_SWAP_NONE | + MV64360_PCI_ACC_CNTL_MBURST_32_BYTES | + MV64360_PCI_ACC_CNTL_RDSIZE_256_BYTES; + + si.pci_1.acc_cntl_options[i] = + MV64360_PCI_ACC_CNTL_SNOOP_WB | + MV64360_PCI_ACC_CNTL_SWAP_NONE | + MV64360_PCI_ACC_CNTL_MBURST_32_BYTES | + MV64360_PCI_ACC_CNTL_RDSIZE_256_BYTES; +#endif + } + + hdpu_cpustate_set(CPUSTATE_KERNEL_MAJOR | CPUSTATE_KERNEL_INIT_PCI); + + /* Lookup PCI host bridges */ + mv64x60_init(&bh, &si); + pci_dram_offset = 0; /* System mem at same addr on PCI & cpu bus */ + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = hdpu_map_irq; + + mv64x60_set_bus(&bh, 0, 0); + bh.hose_a->first_busno = 0; + bh.hose_a->last_busno = 0xff; + bh.hose_a->last_busno = pciauto_bus_scan(bh.hose_a, 0); + + bh.hose_b->first_busno = bh.hose_a->last_busno + 1; + mv64x60_set_bus(&bh, 1, bh.hose_b->first_busno); + bh.hose_b->last_busno = 0xff; + bh.hose_b->last_busno = pciauto_bus_scan(bh.hose_b, + bh.hose_b->first_busno); + + ppc_md.pci_exclude_device = mv64x60_pci_exclude_device; + + hdpu_cpustate_set(CPUSTATE_KERNEL_MAJOR | CPUSTATE_KERNEL_INIT_REG); + /* + * Enabling of PCI internal-vs-external arbitration + * is a platform- and errata-dependent decision. + */ + return; +} + +#if defined(CONFIG_SERIAL_MPSC_CONSOLE) +static void __init hdpu_early_serial_map(void) +{ +#ifdef CONFIG_KGDB + static char first_time = 1; + +#if defined(CONFIG_KGDB_TTYS0) +#define KGDB_PORT 0 +#elif defined(CONFIG_KGDB_TTYS1) +#define KGDB_PORT 1 +#else +#error "Invalid kgdb_tty port" +#endif + + if (first_time) { + gt_early_mpsc_init(KGDB_PORT, + B9600 | CS8 | CREAD | HUPCL | CLOCAL); + first_time = 0; + } + + return; +#endif +} +#endif + +static void hdpu_init2(void) +{ + return; +} + +#if defined(CONFIG_MV643XX_ETH) +static void __init hdpu_fixup_eth_pdata(struct platform_device *pd) +{ + + struct mv643xx_eth_platform_data *eth_pd; + eth_pd = pd->dev.platform_data; + + eth_pd->port_serial_control = + mv64x60_read(&bh, MV643XX_ETH_PORT_SERIAL_CONTROL_REG(pd->id) & ~1); + + eth_pd->force_phy_addr = 1; + eth_pd->phy_addr = pd->id; + eth_pd->tx_queue_size = 400; + eth_pd->rx_queue_size = 800; +} +#endif + +static void __init hdpu_fixup_mpsc_pdata(struct platform_device *pd) +{ + + struct mpsc_pdata *pdata; + + pdata = (struct mpsc_pdata *)pd->dev.platform_data; + + pdata->max_idle = 40; + if (ppcboot_bd_valid) + pdata->default_baud = ppcboot_bd.bi_baudrate; + else + pdata->default_baud = HDPU_DEFAULT_BAUD; + pdata->brg_clk_src = HDPU_MPSC_CLK_SRC; + pdata->brg_clk_freq = HDPU_MPSC_CLK_FREQ; +} + +#if defined(CONFIG_HDPU_FEATURES) +static void __init hdpu_fixup_cpustate_pdata(struct platform_device *pd) +{ + struct platform_device *pds[1]; + pds[0] = pd; + mv64x60_pd_fixup(&bh, pds, 1); +} +#endif + +static int __init hdpu_platform_notify(struct device *dev) +{ + static struct { + char *bus_id; + void ((*rtn) (struct platform_device * pdev)); + } dev_map[] = { + { + MPSC_CTLR_NAME ".0", hdpu_fixup_mpsc_pdata}, +#if defined(CONFIG_MV643XX_ETH) + { + MV643XX_ETH_NAME ".0", hdpu_fixup_eth_pdata}, +#endif +#if defined(CONFIG_HDPU_FEATURES) + { + HDPU_CPUSTATE_NAME ".0", hdpu_fixup_cpustate_pdata}, +#endif + }; + struct platform_device *pdev; + int i; + + if (dev && dev->bus_id) + for (i = 0; i < ARRAY_SIZE(dev_map); i++) + if (!strncmp(dev->bus_id, dev_map[i].bus_id, + BUS_ID_SIZE)) { + + pdev = container_of(dev, + struct platform_device, + dev); + dev_map[i].rtn(pdev); + } + + return 0; +} + +static void __init hdpu_setup_arch(void) +{ + if (ppc_md.progress) + ppc_md.progress("hdpu_setup_arch: enter", 0); +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = Root_NFS; +#else + ROOT_DEV = Root_SDA2; +#endif + + ppc_md.heartbeat = hdpu_heartbeat; + + ppc_md.heartbeat_reset = HZ; + ppc_md.heartbeat_count = 1; + + if (ppc_md.progress) + ppc_md.progress("hdpu_setup_arch: Enabling L2 cache", 0); + + /* Enable L1 Parity Bits */ + hdpu_set_l1pe(); + + /* Enable L2 and L3 caches (if 745x) */ + _set_L2CR(0x80080000); + + if (ppc_md.progress) + ppc_md.progress("hdpu_setup_arch: enter", 0); + + hdpu_setup_bridge(); + + hdpu_setup_peripherals(); + +#ifdef CONFIG_SERIAL_MPSC_CONSOLE + hdpu_early_serial_map(); +#endif + + printk("SKY HDPU Compute Blade \n"); + + if (ppc_md.progress) + ppc_md.progress("hdpu_setup_arch: exit", 0); + + hdpu_cpustate_set(CPUSTATE_KERNEL_MAJOR | CPUSTATE_KERNEL_OK); + return; +} +static void __init hdpu_init_irq(void) +{ + mv64360_init_irq(); +} + +static void __init hdpu_set_l1pe() +{ + unsigned long ictrl; + asm volatile ("mfspr %0, 1011":"=r" (ictrl):); + ictrl |= ICTRL_EICE | ICTRL_EDC | ICTRL_EICP; + asm volatile ("mtspr 1011, %0"::"r" (ictrl)); +} + +/* + * Set BAT 1 to map 0xf1000000 to end of physical memory space. + */ +static __inline__ void hdpu_set_bat(void) +{ + mb(); + mtspr(SPRN_DBAT1U, 0xf10001fe); + mtspr(SPRN_DBAT1L, 0xf100002a); + mb(); + + return; +} + +unsigned long __init hdpu_find_end_of_memory(void) +{ + return mv64x60_get_mem_size(CONFIG_MV64X60_NEW_BASE, + MV64x60_TYPE_MV64360); +} + +static void hdpu_reset_board(void) +{ + volatile int infinite = 1; + + hdpu_cpustate_set(CPUSTATE_KERNEL_MAJOR | CPUSTATE_KERNEL_RESET); + + local_irq_disable(); + + /* Clear all the LEDs */ + mv64x60_write(&bh, MV64x60_GPP_VALUE_CLR, ((1 << 4) | + (1 << 5) | (1 << 6))); + + /* disable and invalidate the L2 cache */ + _set_L2CR(0); + _set_L2CR(0x200000); + + /* flush and disable L1 I/D cache */ + __asm__ __volatile__ + ("\n" + "mfspr 3,1008\n" + "ori 5,5,0xcc00\n" + "ori 4,3,0xc00\n" + "andc 5,3,5\n" + "sync\n" + "mtspr 1008,4\n" + "isync\n" "sync\n" "mtspr 1008,5\n" "isync\n" "sync\n"); + + /* Hit the reset bit */ + mv64x60_write(&bh, MV64x60_GPP_VALUE_CLR, (1 << 3)); + + while (infinite) + infinite = infinite; + + return; +} + +static void hdpu_restart(char *cmd) +{ + volatile ulong i = 10000000; + + hdpu_reset_board(); + + while (i-- > 0) ; + panic("restart failed\n"); +} + +static void hdpu_halt(void) +{ + local_irq_disable(); + + hdpu_cpustate_set(CPUSTATE_KERNEL_MAJOR | CPUSTATE_KERNEL_HALT); + + /* Clear all the LEDs */ + mv64x60_write(&bh, MV64x60_GPP_VALUE_CLR, ((1 << 4) | (1 << 5) | + (1 << 6))); + while (1) ; + /* NOTREACHED */ +} + +static void hdpu_power_off(void) +{ + hdpu_halt(); + /* NOTREACHED */ +} + +static int hdpu_show_cpuinfo(struct seq_file *m) +{ + uint pvid; + + pvid = mfspr(SPRN_PVR); + seq_printf(m, "vendor\t\t: Sky Computers\n"); + seq_printf(m, "machine\t\t: HDPU Compute Blade\n"); + seq_printf(m, "PVID\t\t: 0x%x, vendor: %s\n", + pvid, (pvid & (1 << 15) ? "IBM" : "Motorola")); + + return 0; +} + +static void __init hdpu_calibrate_decr(void) +{ + ulong freq; + + if (ppcboot_bd_valid) + freq = ppcboot_bd.bi_busfreq / 4; + else + freq = 133000000; + + printk("time_init: decrementer frequency = %lu.%.6lu MHz\n", + freq / 1000000, freq % 1000000); + + tb_ticks_per_jiffy = freq / HZ; + tb_to_us = mulhwu_scale_factor(freq, 1000000); + + return; +} + +static void parse_bootinfo(unsigned long r3, + unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + bd_t *bd = NULL; + char *cmdline_start = NULL; + int cmdline_len = 0; + + if (r3) { + if ((r3 & 0xf0000000) == 0) + r3 += KERNELBASE; + if ((r3 & 0xf0000000) == KERNELBASE) { + bd = (void *)r3; + + memcpy(&ppcboot_bd, bd, sizeof(ppcboot_bd)); + ppcboot_bd_valid = 1; + } + } +#ifdef CONFIG_BLK_DEV_INITRD + if (r4 && r5 && r5 > r4) { + if ((r4 & 0xf0000000) == 0) + r4 += KERNELBASE; + if ((r5 & 0xf0000000) == 0) + r5 += KERNELBASE; + if ((r4 & 0xf0000000) == KERNELBASE) { + initrd_start = r4; + initrd_end = r5; + initrd_below_start_ok = 1; + } + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + if (r6 && r7 && r7 > r6) { + if ((r6 & 0xf0000000) == 0) + r6 += KERNELBASE; + if ((r7 & 0xf0000000) == 0) + r7 += KERNELBASE; + if ((r6 & 0xf0000000) == KERNELBASE) { + cmdline_start = (void *)r6; + cmdline_len = (r7 - r6); + strncpy(cmd_line, cmdline_start, cmdline_len); + } + } +} + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +static int hdpu_ide_check_region(ide_ioreg_t from, unsigned int extent) +{ + return check_region(from, extent); +} + +static void +hdpu_ide_request_region(ide_ioreg_t from, unsigned int extent, const char *name) +{ + request_region(from, extent, name); + return; +} + +static void hdpu_ide_release_region(ide_ioreg_t from, unsigned int extent) +{ + release_region(from, extent); + return; +} + +static void __init +hdpu_ide_pci_init_hwif_ports(hw_regs_t * hw, ide_ioreg_t data_port, + ide_ioreg_t ctrl_port, int *irq) +{ + struct pci_dev *dev; + + pci_for_each_dev(dev) { + if (((dev->class >> 8) == PCI_CLASS_STORAGE_IDE) || + ((dev->class >> 8) == PCI_CLASS_STORAGE_RAID)) { + hw->irq = dev->irq; + + if (irq != NULL) { + *irq = dev->irq; + } + } + } + + return; +} +#endif + +void hdpu_heartbeat(void) +{ + if (mv64x60_read(&bh, MV64x60_GPP_VALUE) & (1 << 5)) + mv64x60_write(&bh, MV64x60_GPP_VALUE_CLR, (1 << 5)); + else + mv64x60_write(&bh, MV64x60_GPP_VALUE_SET, (1 << 5)); + + ppc_md.heartbeat_count = ppc_md.heartbeat_reset; + +} + +static void __init hdpu_map_io(void) +{ + io_block_mapping(0xf1000000, 0xf1000000, 0x20000, _PAGE_IO); +} + +#ifdef CONFIG_SMP +char hdpu_smp0[] = "SMP Cpu #0"; +char hdpu_smp1[] = "SMP Cpu #1"; + +static irqreturn_t hdpu_smp_cpu0_int_handler(int irq, void *dev_id, + struct pt_regs *regs) +{ + volatile unsigned int doorbell; + + doorbell = mv64x60_read(&bh, MV64360_CPU0_DOORBELL); + + /* Ack the doorbell interrupts */ + mv64x60_write(&bh, MV64360_CPU0_DOORBELL_CLR, doorbell); + + if (doorbell & 1) { + smp_message_recv(0, regs); + } + if (doorbell & 2) { + smp_message_recv(1, regs); + } + if (doorbell & 4) { + smp_message_recv(2, regs); + } + if (doorbell & 8) { + smp_message_recv(3, regs); + } + return IRQ_HANDLED; +} + +static irqreturn_t hdpu_smp_cpu1_int_handler(int irq, void *dev_id, + struct pt_regs *regs) +{ + volatile unsigned int doorbell; + + doorbell = mv64x60_read(&bh, MV64360_CPU1_DOORBELL); + + /* Ack the doorbell interrupts */ + mv64x60_write(&bh, MV64360_CPU1_DOORBELL_CLR, doorbell); + + if (doorbell & 1) { + smp_message_recv(0, regs); + } + if (doorbell & 2) { + smp_message_recv(1, regs); + } + if (doorbell & 4) { + smp_message_recv(2, regs); + } + if (doorbell & 8) { + smp_message_recv(3, regs); + } + return IRQ_HANDLED; +} + +static void smp_hdpu_CPU_two(void) +{ + __asm__ __volatile__ + ("\n" + "lis 3,0x0000\n" + "ori 3,3,0x00c0\n" + "mtspr 26, 3\n" "li 4,0\n" "mtspr 27,4\n" "rfi"); + +} + +static int smp_hdpu_probe(void) +{ + int *cpu_count_reg; + int num_cpus = 0; + + cpu_count_reg = ioremap(HDPU_NEXUS_ID_BASE, HDPU_NEXUS_ID_SIZE); + if (cpu_count_reg) { + num_cpus = (*cpu_count_reg >> 20) & 0x3; + iounmap(cpu_count_reg); + } + + /* Validate the bits in the CPLD. If we could not map the reg, return 2. + * If the register reported 0 or 3, return 2. + * Older CPLD revisions set these bits to all ones (val = 3). + */ + if ((num_cpus < 1) || (num_cpus > 2)) { + printk + ("Unable to determine the number of processors %d . deafulting to 2.\n", + num_cpus); + num_cpus = 2; + } + return num_cpus; +} + +static void +smp_hdpu_message_pass(int target, int msg, unsigned long data, int wait) +{ + if (msg > 0x3) { + printk("SMP %d: smp_message_pass: unknown msg %d\n", + smp_processor_id(), msg); + return; + } + switch (target) { + case MSG_ALL: + mv64x60_write(&bh, MV64360_CPU0_DOORBELL, 1 << msg); + mv64x60_write(&bh, MV64360_CPU1_DOORBELL, 1 << msg); + break; + case MSG_ALL_BUT_SELF: + if (smp_processor_id()) + mv64x60_write(&bh, MV64360_CPU0_DOORBELL, 1 << msg); + else + mv64x60_write(&bh, MV64360_CPU1_DOORBELL, 1 << msg); + break; + default: + if (target == 0) + mv64x60_write(&bh, MV64360_CPU0_DOORBELL, 1 << msg); + else + mv64x60_write(&bh, MV64360_CPU1_DOORBELL, 1 << msg); + break; + } +} + +static void smp_hdpu_kick_cpu(int nr) +{ + volatile unsigned int *bootaddr; + + if (ppc_md.progress) + ppc_md.progress("smp_hdpu_kick_cpu", 0); + + hdpu_cpustate_set(CPUSTATE_KERNEL_MAJOR | CPUSTATE_KERNEL_CPU1_KICK); + + /* Disable BootCS. Must also reduce the windows size to zero. */ + bh.ci->disable_window_32bit(&bh, MV64x60_CPU2BOOT_WIN); + mv64x60_set_32bit_window(&bh, MV64x60_CPU2BOOT_WIN, 0, 0, 0); + + bootaddr = ioremap(HDPU_INTERNAL_SRAM_BASE, HDPU_INTERNAL_SRAM_SIZE); + if (!bootaddr) { + if (ppc_md.progress) + ppc_md.progress("smp_hdpu_kick_cpu: ioremap failed", 0); + return; + } + + memcpy((void *)(bootaddr + 0x40), (void *)&smp_hdpu_CPU_two, 0x20); + + /* map SRAM to 0xfff00000 */ + bh.ci->disable_window_32bit(&bh, MV64x60_CPU2SRAM_WIN); + + mv64x60_set_32bit_window(&bh, MV64x60_CPU2SRAM_WIN, + 0xfff00000, HDPU_INTERNAL_SRAM_SIZE, 0); + bh.ci->enable_window_32bit(&bh, MV64x60_CPU2SRAM_WIN); + + /* Enable CPU1 arbitration */ + mv64x60_clr_bits(&bh, MV64x60_CPU_MASTER_CNTL, (1 << 9)); + + /* + * Wait 100mSecond until other CPU has reached __secondary_start. + * When it reaches, it is permittable to rever the SRAM mapping etc... + */ + mdelay(100); + *(unsigned long *)KERNELBASE = nr; + asm volatile ("dcbf 0,%0"::"r" (KERNELBASE):"memory"); + + iounmap(bootaddr); + + /* Set up window for internal sram (256KByte insize) */ + bh.ci->disable_window_32bit(&bh, MV64x60_CPU2SRAM_WIN); + mv64x60_set_32bit_window(&bh, MV64x60_CPU2SRAM_WIN, + HDPU_INTERNAL_SRAM_BASE, + HDPU_INTERNAL_SRAM_SIZE, 0); + bh.ci->enable_window_32bit(&bh, MV64x60_CPU2SRAM_WIN); + /* + * Set up windows for embedded FLASH (using boot CS window). + */ + + bh.ci->disable_window_32bit(&bh, MV64x60_CPU2BOOT_WIN); + mv64x60_set_32bit_window(&bh, MV64x60_CPU2BOOT_WIN, + HDPU_EMB_FLASH_BASE, HDPU_EMB_FLASH_SIZE, 0); + bh.ci->enable_window_32bit(&bh, MV64x60_CPU2BOOT_WIN); +} + +static void smp_hdpu_setup_cpu(int cpu_nr) +{ + if (cpu_nr == 0) { + if (ppc_md.progress) + ppc_md.progress("smp_hdpu_setup_cpu 0", 0); + mv64x60_write(&bh, MV64360_CPU0_DOORBELL_CLR, 0xff); + mv64x60_write(&bh, MV64360_CPU0_DOORBELL_MASK, 0xff); + request_irq(60, hdpu_smp_cpu0_int_handler, + SA_INTERRUPT, hdpu_smp0, 0); + } + + if (cpu_nr == 1) { + if (ppc_md.progress) + ppc_md.progress("smp_hdpu_setup_cpu 1", 0); + + hdpu_cpustate_set(CPUSTATE_KERNEL_MAJOR | + CPUSTATE_KERNEL_CPU1_OK); + + /* Enable L1 Parity Bits */ + hdpu_set_l1pe(); + + /* Enable L2 cache */ + _set_L2CR(0); + _set_L2CR(0x80080000); + + mv64x60_write(&bh, MV64360_CPU1_DOORBELL_CLR, 0x0); + mv64x60_write(&bh, MV64360_CPU1_DOORBELL_MASK, 0xff); + request_irq(28, hdpu_smp_cpu1_int_handler, + SA_INTERRUPT, hdpu_smp1, 0); + } + +} + +void __devinit hdpu_tben_give() +{ + volatile unsigned long *val = 0; + + /* By writing 0 to the TBEN_BASE, the timebases is frozen */ + val = ioremap(HDPU_TBEN_BASE, 4); + *val = 0; + mb(); + + spin_lock(&timebase_lock); + timebase_upper = get_tbu(); + timebase_lower = get_tbl(); + spin_unlock(&timebase_lock); + + while (timebase_upper || timebase_lower) + barrier(); + + /* By writing 1 to the TBEN_BASE, the timebases is thawed */ + *val = 1; + mb(); + + iounmap(val); + +} + +void __devinit hdpu_tben_take() +{ + while (!(timebase_upper || timebase_lower)) + barrier(); + + spin_lock(&timebase_lock); + set_tb(timebase_upper, timebase_lower); + timebase_upper = 0; + timebase_lower = 0; + spin_unlock(&timebase_lock); +} + +static struct smp_ops_t hdpu_smp_ops = { + .message_pass = smp_hdpu_message_pass, + .probe = smp_hdpu_probe, + .kick_cpu = smp_hdpu_kick_cpu, + .setup_cpu = smp_hdpu_setup_cpu, + .give_timebase = hdpu_tben_give, + .take_timebase = hdpu_tben_take, +}; +#endif /* CONFIG_SMP */ + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(r3, r4, r5, r6, r7); + + isa_mem_base = 0; + + ppc_md.setup_arch = hdpu_setup_arch; + ppc_md.init = hdpu_init2; + ppc_md.show_cpuinfo = hdpu_show_cpuinfo; + ppc_md.init_IRQ = hdpu_init_irq; + ppc_md.get_irq = mv64360_get_irq; + ppc_md.restart = hdpu_restart; + ppc_md.power_off = hdpu_power_off; + ppc_md.halt = hdpu_halt; + ppc_md.find_end_of_memory = hdpu_find_end_of_memory; + ppc_md.calibrate_decr = hdpu_calibrate_decr; + ppc_md.setup_io_mappings = hdpu_map_io; + + bh.p_base = CONFIG_MV64X60_NEW_BASE; + bh.v_base = (unsigned long *)bh.p_base; + + hdpu_set_bat(); + +#if defined(CONFIG_SERIAL_TEXT_DEBUG) + ppc_md.progress = hdpu_mpsc_progress; /* embedded UART */ + mv64x60_progress_init(bh.p_base); +#endif /* CONFIG_SERIAL_TEXT_DEBUG */ + +#ifdef CONFIG_SMP + ppc_md.smp_ops = &hdpu_smp_ops; +#endif /* CONFIG_SMP */ + +#if defined(CONFIG_SERIAL_MPSC) || defined(CONFIG_MV643XX_ETH) + platform_notify = hdpu_platform_notify; +#endif + return; +} + +#if defined(CONFIG_SERIAL_TEXT_DEBUG) && defined(CONFIG_SERIAL_MPSC_CONSOLE) +/* SMP safe version of the serial text debug routine. Uses Semaphore 0 */ +void hdpu_mpsc_progress(char *s, unsigned short hex) +{ + while (mv64x60_read(&bh, MV64360_WHO_AM_I) != + mv64x60_read(&bh, MV64360_SEMAPHORE_0)) { + } + mv64x60_mpsc_progress(s, hex); + mv64x60_write(&bh, MV64360_SEMAPHORE_0, 0xff); +} +#endif + +static void hdpu_cpustate_set(unsigned char new_state) +{ + unsigned int state = (new_state << 21); + mv64x60_write(&bh, MV64x60_GPP_VALUE_CLR, (0xff << 21)); + mv64x60_write(&bh, MV64x60_GPP_VALUE_CLR, state); +} + +#ifdef CONFIG_MTD_PHYSMAP +static struct mtd_partition hdpu_partitions[] = { + { + .name = "Root FS", + .size = 0x03400000, + .offset = 0, + .mask_flags = 0, + },{ + .name = "User FS", + .size = 0x00800000, + .offset = 0x03400000, + .mask_flags = 0, + },{ + .name = "Kernel Image", + .size = 0x002C0000, + .offset = 0x03C00000, + .mask_flags = 0, + },{ + .name = "bootEnv", + .size = 0x00040000, + .offset = 0x03EC0000, + .mask_flags = 0, + },{ + .name = "bootROM", + .size = 0x00100000, + .offset = 0x03F00000, + .mask_flags = 0, + } +}; + +static int __init hdpu_setup_mtd(void) +{ + + physmap_set_partitions(hdpu_partitions, 5); + return 0; +} + +arch_initcall(hdpu_setup_mtd); +#endif + +#ifdef CONFIG_HDPU_FEATURES + +static struct resource hdpu_cpustate_resources[] = { + [0] = { + .name = "addr base", + .start = MV64x60_GPP_VALUE_SET, + .end = MV64x60_GPP_VALUE_CLR + 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct resource hdpu_nexus_resources[] = { + [0] = { + .name = "nexus register", + .start = HDPU_NEXUS_ID_BASE, + .end = HDPU_NEXUS_ID_BASE + HDPU_NEXUS_ID_SIZE, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device hdpu_cpustate_device = { + .name = HDPU_CPUSTATE_NAME, + .id = 0, + .num_resources = ARRAY_SIZE(hdpu_cpustate_resources), + .resource = hdpu_cpustate_resources, +}; + +static struct platform_device hdpu_nexus_device = { + .name = HDPU_NEXUS_NAME, + .id = 0, + .num_resources = ARRAY_SIZE(hdpu_nexus_resources), + .resource = hdpu_nexus_resources, +}; + +static int __init hdpu_add_pds(void) +{ + platform_device_register(&hdpu_cpustate_device); + platform_device_register(&hdpu_nexus_device); + return 0; +} + +arch_initcall(hdpu_add_pds); +#endif diff --git a/arch/ppc/platforms/hdpu.h b/arch/ppc/platforms/hdpu.h new file mode 100644 index 00000000000..07c3cffb5c7 --- /dev/null +++ b/arch/ppc/platforms/hdpu.h @@ -0,0 +1,82 @@ +/* + * arch/ppc/platforms/hdpu.h + * + * Definitions for Sky Computers HDPU board. + * + * Brian Waite + * + * Based on code done by Rabeeh Khoury - rabeeh@galileo.co.il + * Based on code done by Mark A. Greer + * Based on code done by Tim Montgomery + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/* + * The MV64360 has 2 PCI buses each with 1 window from the CPU bus to + * PCI I/O space and 4 windows from the CPU bus to PCI MEM space. + * We'll only use one PCI MEM window on each PCI bus. + * + * This is the CPU physical memory map (windows must be at least 64K and start + * on a boundary that is a multiple of the window size): + * + * 0x80000000-0x8fffffff - PCI 0 MEM + * 0xa0000000-0xafffffff - PCI 1 MEM + * 0xc0000000-0xc0ffffff - PCI 0 I/O + * 0xc1000000-0xc1ffffff - PCI 1 I/O + + * 0xf1000000-0xf100ffff - MV64360 Registers + * 0xf1010000-0xfb9fffff - HOLE + * 0xfbfa0000-0xfbfaffff - TBEN + * 0xfbf00000-0xfbfbffff - NEXUS + * 0xfbfc0000-0xfbffffff - Internal SRAM + * 0xfc000000-0xffffffff - Boot window + */ + +#ifndef __PPC_PLATFORMS_HDPU_H +#define __PPC_PLATFORMS_HDPU_H + +/* CPU Physical Memory Map setup. */ +#define HDPU_BRIDGE_REG_BASE 0xf1000000 + +#define HDPU_TBEN_BASE 0xfbfa0000 +#define HDPU_TBEN_SIZE 0x00010000 +#define HDPU_NEXUS_ID_BASE 0xfbfb0000 +#define HDPU_NEXUS_ID_SIZE 0x00010000 +#define HDPU_INTERNAL_SRAM_BASE 0xfbfc0000 +#define HDPU_INTERNAL_SRAM_SIZE 0x00040000 +#define HDPU_EMB_FLASH_BASE 0xfc000000 +#define HDPU_EMB_FLASH_SIZE 0x04000000 + +/* PCI Mappings */ + +#define HDPU_PCI0_MEM_START_PROC_ADDR 0x80000000 +#define HDPU_PCI0_MEM_START_PCI_HI_ADDR 0x00000000 +#define HDPU_PCI0_MEM_START_PCI_LO_ADDR HDPU_PCI0_MEM_START_PROC_ADDR +#define HDPU_PCI0_MEM_SIZE 0x10000000 + +#define HDPU_PCI1_MEM_START_PROC_ADDR 0xc0000000 +#define HDPU_PCI1_MEM_START_PCI_HI_ADDR 0x00000000 +#define HDPU_PCI1_MEM_START_PCI_LO_ADDR HDPU_PCI1_MEM_START_PROC_ADDR +#define HDPU_PCI1_MEM_SIZE 0x20000000 + +#define HDPU_PCI0_IO_START_PROC_ADDR 0xc0000000 +#define HDPU_PCI0_IO_START_PCI_ADDR 0x00000000 +#define HDPU_PCI0_IO_SIZE 0x01000000 + +#define HDPU_PCI1_IO_START_PROC_ADDR 0xc1000000 +#define HDPU_PCI1_IO_START_PCI_ADDR 0x01000000 +#define HDPU_PCI1_IO_SIZE 0x01000000 + +#define HDPU_DEFAULT_BAUD 115200 +#define HDPU_MPSC_CLK_SRC 8 /* TCLK */ +#define HDPU_MPSC_CLK_FREQ 133000000 /* 133 Mhz */ + +#define HDPU_PCI_0_IRQ (8+64) +#define HDPU_PCI_1_IRQ (13+64) + +#endif /* __PPC_PLATFORMS_HDPU_H */ diff --git a/arch/ppc/platforms/hermes.h b/arch/ppc/platforms/hermes.h new file mode 100644 index 00000000000..198fc590b9f --- /dev/null +++ b/arch/ppc/platforms/hermes.h @@ -0,0 +1,27 @@ +/* + * Multidata HERMES-PRO ( / SL ) board specific definitions + * + * Copyright (c) 2000, 2001 Wolfgang Denk (wd@denx.de) + */ + +#ifndef __MACH_HERMES_H +#define __MACH_HERMES_H + +#include + +#include + +#define HERMES_IMMR_BASE 0xFF000000 /* phys. addr of IMMR */ +#define HERMES_IMAP_SIZE (64 * 1024) /* size of mapped area */ + +#define IMAP_ADDR HERMES_IMMR_BASE /* physical base address of IMMR area */ +#define IMAP_SIZE HERMES_IMAP_SIZE /* mapped size of IMMR area */ + +#define FEC_INTERRUPT 9 /* = SIU_LEVEL4 */ +#define CPM_INTERRUPT 11 /* = SIU_LEVEL5 (was: SIU_LEVEL2) */ + +/* We don't use the 8259. +*/ +#define NR_8259_INTS 0 + +#endif /* __MACH_HERMES_H */ diff --git a/arch/ppc/platforms/ip860.h b/arch/ppc/platforms/ip860.h new file mode 100644 index 00000000000..8c3836c5f05 --- /dev/null +++ b/arch/ppc/platforms/ip860.h @@ -0,0 +1,36 @@ +/* + * MicroSys IP860 VMEBus board specific definitions + * + * Copyright (c) 2000, 2001 Wolfgang Denk (wd@denx.de) + */ + +#ifndef __MACH_IP860_H +#define __MACH_IP860_H + +#include + +#include + +#define IP860_IMMR_BASE 0xF1000000 /* phys. addr of IMMR */ +#define IP860_IMAP_SIZE (64 * 1024) /* size of mapped area */ + +#define IMAP_ADDR IP860_IMMR_BASE /* physical base address of IMMR area */ +#define IMAP_SIZE IP860_IMAP_SIZE /* mapped size of IMMR area */ + +/* + * MPC8xx Chip Select Usage + */ +#define IP860_BOOT_CS 0 /* Boot (VMEBus or Flash) Chip Select 0 */ +#define IP860_FLASH_CS 1 /* Flash is on Chip Select 1 */ +#define IP860_SDRAM_CS 2 /* SDRAM is on Chip Select 2 */ +#define IP860_SRAM_CS 3 /* SRAM is on Chip Select 3 */ +#define IP860_BCSR_CS 4 /* BCSR is on Chip Select 4 */ +#define IP860_IP_CS 5 /* IP Slots are on Chip Select 5 */ +#define IP860_VME_STD_CS 6 /* VME Standard I/O is on Chip Select 6 */ +#define IP860_VME_SHORT_CS 7 /* VME Short I/O is on Chip Select 7 */ + +/* We don't use the 8259. +*/ +#define NR_8259_INTS 0 + +#endif /* __MACH_IP860_H */ diff --git a/arch/ppc/platforms/ivms8.h b/arch/ppc/platforms/ivms8.h new file mode 100644 index 00000000000..d4be310f808 --- /dev/null +++ b/arch/ppc/platforms/ivms8.h @@ -0,0 +1,56 @@ +/* + * Speech Design Integrated Voicemail board specific definitions + * - IVMS8 (small, 8 channels) + * - IVML24 (large, 24 channels) + * + * In 2.5 when we force a new bootloader, we can merge these two, and add + * in _MACH_'s for them. -- Tom + * + * Copyright (c) 2000, 2001 Wolfgang Denk (wd@denx.de) + */ + +#ifdef __KERNEL__ +#ifndef __ASM_IVMS8_H__ +#define __ASM_IVMS8_H__ + +#include + +#include + +#define IVMS_IMMR_BASE 0xFFF00000 /* phys. addr of IMMR */ +#define IVMS_IMAP_SIZE (64 * 1024) /* size of mapped area */ + +#define IMAP_ADDR IVMS_IMMR_BASE /* phys. base address of IMMR area */ +#define IMAP_SIZE IVMS_IMAP_SIZE /* mapped size of IMMR area */ + +#define PCMCIA_MEM_ADDR ((uint)0xFE100000) +#define PCMCIA_MEM_SIZE ((uint)(64 * 1024)) + +#define FEC_INTERRUPT 9 /* = SIU_LEVEL4 */ +#define IDE0_INTERRUPT 10 /* = IRQ5 */ +#define CPM_INTERRUPT 11 /* = SIU_LEVEL5 (was: SIU_LEVEL2) */ +#define PHY_INTERRUPT 12 /* = IRQ6 */ + +/* override the default number of IDE hardware interfaces */ +#define MAX_HWIFS 1 + +/* + * Definitions for IDE0 Interface + */ +#define IDE0_BASE_OFFSET 0x0000 /* Offset in PCMCIA memory */ +#define IDE0_DATA_REG_OFFSET 0x0000 +#define IDE0_ERROR_REG_OFFSET 0x0081 +#define IDE0_NSECTOR_REG_OFFSET 0x0082 +#define IDE0_SECTOR_REG_OFFSET 0x0083 +#define IDE0_LCYL_REG_OFFSET 0x0084 +#define IDE0_HCYL_REG_OFFSET 0x0085 +#define IDE0_SELECT_REG_OFFSET 0x0086 +#define IDE0_STATUS_REG_OFFSET 0x0087 +#define IDE0_CONTROL_REG_OFFSET 0x0106 +#define IDE0_IRQ_REG_OFFSET 0x000A /* not used */ + +/* We don't use the 8259. */ +#define NR_8259_INTS 0 + +#endif /* __ASM_IVMS8_H__ */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/k2.c b/arch/ppc/platforms/k2.c new file mode 100644 index 00000000000..aacb438708f --- /dev/null +++ b/arch/ppc/platforms/k2.c @@ -0,0 +1,613 @@ +/* + * arch/ppc/platforms/k2.c + * + * Board setup routines for SBS K2 + * + * Author: Matt Porter + * + * Updated by: Randy Vinson +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "k2.h" + +extern unsigned long loops_per_jiffy; +extern void gen550_progress(char *, unsigned short); + +static unsigned int cpu_7xx[16] = { + 0, 15, 14, 0, 0, 13, 5, 9, 6, 11, 8, 10, 16, 12, 7, 0 +}; +static unsigned int cpu_6xx[16] = { + 0, 0, 14, 0, 0, 13, 5, 9, 6, 11, 8, 10, 0, 12, 7, 0 +}; + +static inline int __init +k2_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + struct pci_controller *hose = pci_bus_to_hose(dev->bus->number); + /* + * Check our hose index. If we are zero then we are on the + * local PCI hose, otherwise we are on the cPCI hose. + */ + if (!hose->index) { + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {1, 0, 0, 0}, /* Ethernet */ + {5, 5, 5, 5}, /* PMC Site 1 */ + {6, 6, 6, 6}, /* PMC Site 2 */ + {0, 0, 0, 0}, /* unused */ + {0, 0, 0, 0}, /* unused */ + {0, 0, 0, 0}, /* PCI-ISA Bridge */ + {0, 0, 0, 0}, /* unused */ + {0, 0, 0, 0}, /* unused */ + {0, 0, 0, 0}, /* unused */ + {0, 0, 0, 0}, /* unused */ + {0, 0, 0, 0}, /* unused */ + {0, 0, 0, 0}, /* unused */ + {0, 0, 0, 0}, /* unused */ + {0, 0, 0, 0}, /* unused */ + {15, 0, 0, 0}, /* M5229 IDE */ + }; + const long min_idsel = 3, max_idsel = 17, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; + } else { + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {10, 11, 12, 9}, /* cPCI slot 8 */ + {11, 12, 9, 10}, /* cPCI slot 7 */ + {12, 9, 10, 11}, /* cPCI slot 6 */ + {9, 10, 11, 12}, /* cPCI slot 5 */ + {10, 11, 12, 9}, /* cPCI slot 4 */ + {11, 12, 9, 10}, /* cPCI slot 3 */ + {12, 9, 10, 11}, /* cPCI slot 2 */ + }; + const long min_idsel = 15, max_idsel = 21, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; + } +} + +void k2_pcibios_fixup(void) +{ +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) + struct pci_dev *ide_dev; + + /* + * Enable DMA support on hdc + */ + ide_dev = pci_get_device(PCI_VENDOR_ID_AL, + PCI_DEVICE_ID_AL_M5229, NULL); + + if (ide_dev) { + + unsigned long ide_dma_base; + + ide_dma_base = pci_resource_start(ide_dev, 4); + outb(0x00, ide_dma_base + 0x2); + outb(0x20, ide_dma_base + 0xa); + pci_dev_put(ide_dev); + } +#endif +} + +void k2_pcibios_fixup_resources(struct pci_dev *dev) +{ + int i; + + if ((dev->vendor == PCI_VENDOR_ID_IBM) && + (dev->device == PCI_DEVICE_ID_IBM_CPC710_PCI64)) { + pr_debug("Fixup CPC710 resources\n"); + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { + dev->resource[i].start = 0; + dev->resource[i].end = 0; + } + } +} + +void k2_setup_hoses(void) +{ + struct pci_controller *hose_a, *hose_b; + + /* + * Reconfigure CPC710 memory map so + * we have some more PCI memory space. + */ + + /* Set FPHB mode */ + __raw_writel(0x808000e0, PGCHP); /* Set FPHB mode */ + + /* PCI32 mappings */ + __raw_writel(0x00000000, K2_PCI32_BAR + PIBAR); /* PCI I/O base */ + __raw_writel(0x00000000, K2_PCI32_BAR + PMBAR); /* PCI Mem base */ + __raw_writel(0xf0000000, K2_PCI32_BAR + MSIZE); /* 256MB */ + __raw_writel(0xfff00000, K2_PCI32_BAR + IOSIZE); /* 1MB */ + __raw_writel(0xc0000000, K2_PCI32_BAR + SMBAR); /* Base@0xc0000000 */ + __raw_writel(0x80000000, K2_PCI32_BAR + SIBAR); /* Base@0x80000000 */ + __raw_writel(0x000000c0, K2_PCI32_BAR + PSSIZE); /* 1GB space */ + __raw_writel(0x000000c0, K2_PCI32_BAR + PPSIZE); /* 1GB space */ + __raw_writel(0x00000000, K2_PCI32_BAR + BARPS); /* Base@0x00000000 */ + __raw_writel(0x00000000, K2_PCI32_BAR + BARPP); /* Base@0x00000000 */ + __raw_writel(0x00000080, K2_PCI32_BAR + PSBAR); /* Base@0x80 */ + __raw_writel(0x00000000, K2_PCI32_BAR + PPBAR); + + __raw_writel(0xc0000000, K2_PCI32_BAR + BPMDLK); + __raw_writel(0xd0000000, K2_PCI32_BAR + TPMDLK); + __raw_writel(0x80000000, K2_PCI32_BAR + BIODLK); + __raw_writel(0x80100000, K2_PCI32_BAR + TIODLK); + __raw_writel(0xe0008000, K2_PCI32_BAR + DLKCTRL); + __raw_writel(0xffffffff, K2_PCI32_BAR + DLKDEV); + + /* PCI64 mappings */ + __raw_writel(0x00100000, K2_PCI64_BAR + PIBAR); /* PCI I/O base */ + __raw_writel(0x10000000, K2_PCI64_BAR + PMBAR); /* PCI Mem base */ + __raw_writel(0xf0000000, K2_PCI64_BAR + MSIZE); /* 256MB */ + __raw_writel(0xfff00000, K2_PCI64_BAR + IOSIZE); /* 1MB */ + __raw_writel(0xd0000000, K2_PCI64_BAR + SMBAR); /* Base@0xd0000000 */ + __raw_writel(0x80100000, K2_PCI64_BAR + SIBAR); /* Base@0x80100000 */ + __raw_writel(0x000000c0, K2_PCI64_BAR + PSSIZE); /* 1GB space */ + __raw_writel(0x000000c0, K2_PCI64_BAR + PPSIZE); /* 1GB space */ + __raw_writel(0x00000000, K2_PCI64_BAR + BARPS); /* Base@0x00000000 */ + __raw_writel(0x00000000, K2_PCI64_BAR + BARPP); /* Base@0x00000000 */ + + /* Setup PCI32 hose */ + hose_a = pcibios_alloc_controller(); + if (!hose_a) + return; + + hose_a->first_busno = 0; + hose_a->last_busno = 0xff; + hose_a->pci_mem_offset = K2_PCI32_MEM_BASE; + + pci_init_resource(&hose_a->io_resource, + K2_PCI32_LOWER_IO, + K2_PCI32_UPPER_IO, + IORESOURCE_IO, "PCI32 host bridge"); + + pci_init_resource(&hose_a->mem_resources[0], + K2_PCI32_LOWER_MEM + K2_PCI32_MEM_BASE, + K2_PCI32_UPPER_MEM + K2_PCI32_MEM_BASE, + IORESOURCE_MEM, "PCI32 host bridge"); + + hose_a->io_space.start = K2_PCI32_LOWER_IO; + hose_a->io_space.end = K2_PCI32_UPPER_IO; + hose_a->mem_space.start = K2_PCI32_LOWER_MEM; + hose_a->mem_space.end = K2_PCI32_UPPER_MEM; + hose_a->io_base_virt = (void *)K2_ISA_IO_BASE; + + setup_indirect_pci(hose_a, K2_PCI32_CONFIG_ADDR, K2_PCI32_CONFIG_DATA); + + /* Initialize PCI32 bus registers */ + early_write_config_byte(hose_a, + hose_a->first_busno, + PCI_DEVFN(0, 0), + CPC710_BUS_NUMBER, hose_a->first_busno); + + early_write_config_byte(hose_a, + hose_a->first_busno, + PCI_DEVFN(0, 0), + CPC710_SUB_BUS_NUMBER, hose_a->last_busno); + + /* Enable PCI interrupt polling */ + early_write_config_byte(hose_a, + hose_a->first_busno, + PCI_DEVFN(8, 0), 0x45, 0x80); + + /* Route polled PCI interrupts */ + early_write_config_byte(hose_a, + hose_a->first_busno, + PCI_DEVFN(8, 0), 0x48, 0x58); + + early_write_config_byte(hose_a, + hose_a->first_busno, + PCI_DEVFN(8, 0), 0x49, 0x07); + + early_write_config_byte(hose_a, + hose_a->first_busno, + PCI_DEVFN(8, 0), 0x4a, 0x31); + + early_write_config_byte(hose_a, + hose_a->first_busno, + PCI_DEVFN(8, 0), 0x4b, 0xb9); + + /* route secondary IDE channel interrupt to IRQ 15 */ + early_write_config_byte(hose_a, + hose_a->first_busno, + PCI_DEVFN(8, 0), 0x75, 0x0f); + + /* enable IDE controller IDSEL */ + early_write_config_byte(hose_a, + hose_a->first_busno, + PCI_DEVFN(8, 0), 0x58, 0x48); + + /* Enable IDE function */ + early_write_config_byte(hose_a, + hose_a->first_busno, + PCI_DEVFN(17, 0), 0x50, 0x03); + + /* Set M5229 IDE controller to native mode */ + early_write_config_byte(hose_a, + hose_a->first_busno, + PCI_DEVFN(17, 0), PCI_CLASS_PROG, 0xdf); + + hose_a->last_busno = pciauto_bus_scan(hose_a, hose_a->first_busno); + + /* Write out correct max subordinate bus number for hose A */ + early_write_config_byte(hose_a, + hose_a->first_busno, + PCI_DEVFN(0, 0), + CPC710_SUB_BUS_NUMBER, hose_a->last_busno); + + /* Only setup PCI64 hose if we are in the system slot */ + if (!(readb(K2_MISC_REG) & K2_SYS_SLOT_MASK)) { + /* Setup PCI64 hose */ + hose_b = pcibios_alloc_controller(); + if (!hose_b) + return; + + hose_b->first_busno = hose_a->last_busno + 1; + hose_b->last_busno = 0xff; + + /* Reminder: quit changing the following, it is correct. */ + hose_b->pci_mem_offset = K2_PCI32_MEM_BASE; + + pci_init_resource(&hose_b->io_resource, + K2_PCI64_LOWER_IO, + K2_PCI64_UPPER_IO, + IORESOURCE_IO, "PCI64 host bridge"); + + pci_init_resource(&hose_b->mem_resources[0], + K2_PCI64_LOWER_MEM + K2_PCI32_MEM_BASE, + K2_PCI64_UPPER_MEM + K2_PCI32_MEM_BASE, + IORESOURCE_MEM, "PCI64 host bridge"); + + hose_b->io_space.start = K2_PCI64_LOWER_IO; + hose_b->io_space.end = K2_PCI64_UPPER_IO; + hose_b->mem_space.start = K2_PCI64_LOWER_MEM; + hose_b->mem_space.end = K2_PCI64_UPPER_MEM; + hose_b->io_base_virt = (void *)K2_ISA_IO_BASE; + + setup_indirect_pci(hose_b, + K2_PCI64_CONFIG_ADDR, K2_PCI64_CONFIG_DATA); + + /* Initialize PCI64 bus registers */ + early_write_config_byte(hose_b, + 0, + PCI_DEVFN(0, 0), + CPC710_SUB_BUS_NUMBER, 0xff); + + early_write_config_byte(hose_b, + 0, + PCI_DEVFN(0, 0), + CPC710_BUS_NUMBER, hose_b->first_busno); + + hose_b->last_busno = pciauto_bus_scan(hose_b, + hose_b->first_busno); + + /* Write out correct max subordinate bus number for hose B */ + early_write_config_byte(hose_b, + hose_b->first_busno, + PCI_DEVFN(0, 0), + CPC710_SUB_BUS_NUMBER, + hose_b->last_busno); + + /* Configure PCI64 PSBAR */ + early_write_config_dword(hose_b, + hose_b->first_busno, + PCI_DEVFN(0, 0), + PCI_BASE_ADDRESS_0, + K2_PCI64_SYS_MEM_BASE); + } + + /* Configure i8259 level/edge settings */ + outb(0x62, 0x4d0); + outb(0xde, 0x4d1); + +#ifdef CONFIG_CPC710_DATA_GATHERING + { + unsigned int tmp; + tmp = __raw_readl(ABCNTL); + /* Enable data gathering on both PCI interfaces */ + __raw_writel(tmp | 0x05000000, ABCNTL); + } +#endif + + ppc_md.pcibios_fixup = k2_pcibios_fixup; + ppc_md.pcibios_fixup_resources = k2_pcibios_fixup_resources; + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = k2_map_irq; +} + +static int k2_get_bus_speed(void) +{ + int bus_speed; + unsigned char board_id; + + board_id = *(unsigned char *)K2_BOARD_ID_REG; + + switch (K2_BUS_SPD(board_id)) { + + case 0: + default: + bus_speed = 100000000; + break; + + case 1: + bus_speed = 83333333; + break; + + case 2: + bus_speed = 75000000; + break; + + case 3: + bus_speed = 66666666; + break; + } + return bus_speed; +} + +static int k2_get_cpu_speed(void) +{ + unsigned long hid1; + int cpu_speed; + + hid1 = mfspr(SPRN_HID1) >> 28; + + if ((mfspr(SPRN_PVR) >> 16) == 8) + hid1 = cpu_7xx[hid1]; + else + hid1 = cpu_6xx[hid1]; + + cpu_speed = k2_get_bus_speed() * hid1 / 2; + return cpu_speed; +} + +static void __init k2_calibrate_decr(void) +{ + int freq, divisor = 4; + + /* determine processor bus speed */ + freq = k2_get_bus_speed(); + tb_ticks_per_jiffy = freq / HZ / divisor; + tb_to_us = mulhwu_scale_factor(freq / divisor, 1000000); +} + +static int k2_show_cpuinfo(struct seq_file *m) +{ + unsigned char k2_geo_bits, k2_system_slot; + + seq_printf(m, "vendor\t\t: SBS\n"); + seq_printf(m, "machine\t\t: K2\n"); + seq_printf(m, "cpu speed\t: %dMhz\n", k2_get_cpu_speed() / 1000000); + seq_printf(m, "bus speed\t: %dMhz\n", k2_get_bus_speed() / 1000000); + seq_printf(m, "memory type\t: SDRAM\n"); + + k2_geo_bits = readb(K2_MSIZ_GEO_REG) & K2_GEO_ADR_MASK; + k2_system_slot = !(readb(K2_MISC_REG) & K2_SYS_SLOT_MASK); + seq_printf(m, "backplane\t: %s slot board", + k2_system_slot ? "System" : "Non system"); + seq_printf(m, "with geographical address %x\n", k2_geo_bits); + + return 0; +} + +TODC_ALLOC(); + +static void __init k2_setup_arch(void) +{ + unsigned int cpu; + + /* Setup TODC access */ + TODC_INIT(TODC_TYPE_MK48T37, 0, 0, + ioremap(K2_RTC_BASE_ADDRESS, K2_RTC_SIZE), 8); + + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_jiffy = 50000000 / HZ; + + /* make FLASH transactions higher priority than PCI to avoid deadlock */ + __raw_writel(__raw_readl(SIOC1) | 0x80000000, SIOC1); + + /* Set hardware to access FLASH page 2 */ + __raw_writel(1 << 29, GPOUT); + + /* Setup PCI host bridges */ + k2_setup_hoses(); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = Root_NFS; +#else + ROOT_DEV = Root_HDC1; +#endif + + /* Identify the system */ + printk(KERN_INFO "System Identification: SBS K2 - PowerPC 750 @ " + "%d Mhz\n", k2_get_cpu_speed() / 1000000); + printk(KERN_INFO "Port by MontaVista Software, Inc. " + "(source@mvista.com)\n"); + + /* Identify the CPU manufacturer */ + cpu = PVR_REV(mfspr(SPRN_PVR)); + printk(KERN_INFO "CPU manufacturer: %s [rev=%04x]\n", + (cpu & (1 << 15)) ? "IBM" : "Motorola", cpu); +} + +static void k2_restart(char *cmd) +{ + local_irq_disable(); + + /* Flip FLASH back to page 1 to access firmware image */ + __raw_writel(0, GPOUT); + + /* SRR0 has system reset vector, SRR1 has default MSR value */ + /* rfi restores MSR from SRR1 and sets the PC to the SRR0 value */ + mtspr(SPRN_SRR0, 0xfff00100); + mtspr(SPRN_SRR1, 0); + __asm__ __volatile__("rfi\n\t"); + + /* not reached */ + for (;;) ; +} + +static void k2_power_off(void) +{ + for (;;) ; +} + +static void k2_halt(void) +{ + k2_restart(NULL); +} + +/* + * Set BAT 3 to map PCI32 I/O space. + */ +static __inline__ void k2_set_bat(void) +{ + /* wait for all outstanding memory accesses to complete */ + mb(); + + /* setup DBATs */ + mtspr(SPRN_DBAT2U, 0x80001ffe); + mtspr(SPRN_DBAT2L, 0x8000002a); + mtspr(SPRN_DBAT3U, 0xf0001ffe); + mtspr(SPRN_DBAT3L, 0xf000002a); + + /* wait for updates */ + mb(); +} + +static unsigned long __init k2_find_end_of_memory(void) +{ + unsigned long total; + unsigned char msize = 7; /* Default to 128MB */ + + msize = K2_MEM_SIZE(readb(K2_MSIZ_GEO_REG)); + + switch (msize) { + case 2: + /* + * This will break without a lowered + * KERNELBASE or CONFIG_HIGHMEM on. + * It seems non 1GB builds exist yet, + * though. + */ + total = K2_MEM_SIZE_1GB; + break; + case 3: + case 4: + total = K2_MEM_SIZE_512MB; + break; + case 5: + case 6: + total = K2_MEM_SIZE_256MB; + break; + case 7: + total = K2_MEM_SIZE_128MB; + break; + default: + printk + ("K2: Invalid memory size detected, defaulting to 128MB\n"); + total = K2_MEM_SIZE_128MB; + break; + } + return total; +} + +static void __init k2_map_io(void) +{ + io_block_mapping(K2_PCI32_IO_BASE, + K2_PCI32_IO_BASE, 0x00200000, _PAGE_IO); + io_block_mapping(0xff000000, 0xff000000, 0x01000000, _PAGE_IO); +} + +static void __init k2_init_irq(void) +{ + int i; + + for (i = 0; i < 16; i++) + irq_desc[i].handler = &i8259_pic; + + i8259_init(0); +} + +void __init platform_init(unsigned long r3, unsigned long r4, + unsigned long r5, unsigned long r6, unsigned long r7) +{ + parse_bootinfo((struct bi_record *)(r3 + KERNELBASE)); + + k2_set_bat(); + + isa_io_base = K2_ISA_IO_BASE; + isa_mem_base = K2_ISA_MEM_BASE; + pci_dram_offset = K2_PCI32_SYS_MEM_BASE; + + ppc_md.setup_arch = k2_setup_arch; + ppc_md.show_cpuinfo = k2_show_cpuinfo; + ppc_md.init_IRQ = k2_init_irq; + ppc_md.get_irq = i8259_irq; + + ppc_md.find_end_of_memory = k2_find_end_of_memory; + ppc_md.setup_io_mappings = k2_map_io; + + ppc_md.restart = k2_restart; + ppc_md.power_off = k2_power_off; + ppc_md.halt = k2_halt; + + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.calibrate_decr = k2_calibrate_decr; + + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; + +#ifdef CONFIG_SERIAL_TEXT_DEBUG + ppc_md.progress = gen550_progress; +#endif +} diff --git a/arch/ppc/platforms/k2.h b/arch/ppc/platforms/k2.h new file mode 100644 index 00000000000..78326aba198 --- /dev/null +++ b/arch/ppc/platforms/k2.h @@ -0,0 +1,82 @@ +/* + * arch/ppc/platforms/k2.h + * + * Definitions for SBS K2 board support + * + * Author: Matt Porter + * + * 2001 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifndef __PPC_PLATFORMS_K2_H +#define __PPC_PLATFORMS_K2_H + +/* + * SBS K2 definitions + */ + +#define K2_PCI64_BAR 0xff400000 +#define K2_PCI32_BAR 0xff500000 + +#define K2_PCI64_CONFIG_ADDR (K2_PCI64_BAR + 0x000f8000) +#define K2_PCI64_CONFIG_DATA (K2_PCI64_BAR + 0x000f8010) + +#define K2_PCI32_CONFIG_ADDR (K2_PCI32_BAR + 0x000f8000) +#define K2_PCI32_CONFIG_DATA (K2_PCI32_BAR + 0x000f8010) + +#define K2_PCI64_MEM_BASE 0xd0000000 +#define K2_PCI64_IO_BASE 0x80100000 + +#define K2_PCI32_MEM_BASE 0xc0000000 +#define K2_PCI32_IO_BASE 0x80000000 + +#define K2_PCI32_SYS_MEM_BASE 0x80000000 +#define K2_PCI64_SYS_MEM_BASE K2_PCI32_SYS_MEM_BASE + +#define K2_PCI32_LOWER_MEM 0x00000000 +#define K2_PCI32_UPPER_MEM 0x0fffffff +#define K2_PCI32_LOWER_IO 0x00000000 +#define K2_PCI32_UPPER_IO 0x000fffff + +#define K2_PCI64_LOWER_MEM 0x10000000 +#define K2_PCI64_UPPER_MEM 0x1fffffff +#define K2_PCI64_LOWER_IO 0x00100000 +#define K2_PCI64_UPPER_IO 0x001fffff + +#define K2_ISA_IO_BASE K2_PCI32_IO_BASE +#define K2_ISA_MEM_BASE K2_PCI32_MEM_BASE + +#define K2_BOARD_ID_REG (K2_ISA_IO_BASE + 0x800) +#define K2_MISC_REG (K2_ISA_IO_BASE + 0x804) +#define K2_MSIZ_GEO_REG (K2_ISA_IO_BASE + 0x808) +#define K2_HOT_SWAP_REG (K2_ISA_IO_BASE + 0x80c) +#define K2_PLD2_REG (K2_ISA_IO_BASE + 0x80e) +#define K2_PLD3_REG (K2_ISA_IO_BASE + 0x80f) + +#define K2_BUS_SPD(board_id) (board_id >> 2) & 3 + +#define K2_RTC_BASE_OFFSET 0x90000 +#define K2_RTC_BASE_ADDRESS (K2_PCI32_MEM_BASE + K2_RTC_BASE_OFFSET) +#define K2_RTC_SIZE 0x8000 + +#define K2_MEM_SIZE_MASK 0xe0 +#define K2_MEM_SIZE(size_reg) (size_reg & K2_MEM_SIZE_MASK) >> 5 +#define K2_MEM_SIZE_1GB 0x40000000 +#define K2_MEM_SIZE_512MB 0x20000000 +#define K2_MEM_SIZE_256MB 0x10000000 +#define K2_MEM_SIZE_128MB 0x08000000 + +#define K2_L2CACHE_MASK 0x03 /* Mask for 2 L2 Cache bits */ +#define K2_L2CACHE_512KB 0x00 /* 512KB */ +#define K2_L2CACHE_256KB 0x01 /* 256KB */ +#define K2_L2CACHE_1MB 0x02 /* 1MB */ +#define K2_L2CACHE_NONE 0x03 /* None */ + +#define K2_GEO_ADR_MASK 0x1f + +#define K2_SYS_SLOT_MASK 0x08 + +#endif /* __PPC_PLATFORMS_K2_H */ diff --git a/arch/ppc/platforms/katana.c b/arch/ppc/platforms/katana.c new file mode 100644 index 00000000000..eda922ac316 --- /dev/null +++ b/arch/ppc/platforms/katana.c @@ -0,0 +1,795 @@ +/* + * arch/ppc/platforms/katana.c + * + * Board setup routines for the Artesyn Katana cPCI boards. + * + * Author: Tim Montgomery + * Maintained by: Mark A. Greer + * + * Based on code done by Rabeeh Khoury - rabeeh@galileo.co.il + * Based on code done by - Mark A. Greer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +/* + * Supports the Artesyn 750i, 752i, and 3750. The 752i is virtually identical + * to the 750i except that it has an mv64460 bridge. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_BOOTIMG +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +static struct mv64x60_handle bh; +static katana_id_t katana_id; +static void __iomem *cpld_base; +static void __iomem *sram_base; + +static u32 katana_flash_size_0; +static u32 katana_flash_size_1; + +static u32 katana_bus_frequency; + +unsigned char __res[sizeof(bd_t)]; + +/* PCI Interrupt routing */ +static int __init +katana_irq_lookup_750i(unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = { + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + /* IDSEL 4 (PMC 1) */ + { KATANA_PCI_INTB_IRQ_750i, KATANA_PCI_INTC_IRQ_750i, + KATANA_PCI_INTD_IRQ_750i, KATANA_PCI_INTA_IRQ_750i }, + /* IDSEL 5 (PMC 2) */ + { KATANA_PCI_INTC_IRQ_750i, KATANA_PCI_INTD_IRQ_750i, + KATANA_PCI_INTA_IRQ_750i, KATANA_PCI_INTB_IRQ_750i }, + /* IDSEL 6 (T8110) */ + {KATANA_PCI_INTD_IRQ_750i, 0, 0, 0 }, + }; + const long min_idsel = 4, max_idsel = 6, irqs_per_slot = 4; + + return PCI_IRQ_TABLE_LOOKUP; +} + +static int __init +katana_irq_lookup_3750(unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = { + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { KATANA_PCI_INTA_IRQ_3750, 0, 0, 0 }, /* IDSEL 3 (BCM5691) */ + { KATANA_PCI_INTB_IRQ_3750, 0, 0, 0 }, /* IDSEL 4 (MV64360 #2)*/ + { KATANA_PCI_INTC_IRQ_3750, 0, 0, 0 }, /* IDSEL 5 (MV64360 #3)*/ + }; + const long min_idsel = 3, max_idsel = 5, irqs_per_slot = 4; + + return PCI_IRQ_TABLE_LOOKUP; +} + +static int __init +katana_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + switch (katana_id) { + case KATANA_ID_750I: + case KATANA_ID_752I: + return katana_irq_lookup_750i(idsel, pin); + + case KATANA_ID_3750: + return katana_irq_lookup_3750(idsel, pin); + + default: + printk(KERN_ERR "Bogus board ID\n"); + return 0; + } +} + +/* Board info retrieval routines */ +void __init +katana_get_board_id(void) +{ + switch (in_8(cpld_base + KATANA_CPLD_PRODUCT_ID)) { + case KATANA_PRODUCT_ID_3750: + katana_id = KATANA_ID_3750; + break; + + case KATANA_PRODUCT_ID_750i: + katana_id = KATANA_ID_750I; + break; + + case KATANA_PRODUCT_ID_752i: + katana_id = KATANA_ID_752I; + break; + + default: + printk(KERN_ERR "Unsupported board\n"); + } +} + +int __init +katana_get_proc_num(void) +{ + u16 val; + u8 save_exclude; + static int proc = -1; + static u8 first_time = 1; + + if (first_time) { + if (katana_id != KATANA_ID_3750) + proc = 0; + else { + save_exclude = mv64x60_pci_exclude_bridge; + mv64x60_pci_exclude_bridge = 0; + + early_read_config_word(bh.hose_a, 0, + PCI_DEVFN(0,0), PCI_DEVICE_ID, &val); + + mv64x60_pci_exclude_bridge = save_exclude; + + switch(val) { + case PCI_DEVICE_ID_KATANA_3750_PROC0: + proc = 0; + break; + + case PCI_DEVICE_ID_KATANA_3750_PROC1: + proc = 1; + break; + + case PCI_DEVICE_ID_KATANA_3750_PROC2: + proc = 2; + break; + + default: + printk(KERN_ERR "Bogus Device ID\n"); + } + } + + first_time = 0; + } + + return proc; +} + +static inline int +katana_is_monarch(void) +{ + return in_8(cpld_base + KATANA_CPLD_BD_CFG_3) & + KATANA_CPLD_BD_CFG_3_MONARCH; +} + +static void __init +katana_setup_bridge(void) +{ + struct pci_controller hose; + struct mv64x60_setup_info si; + void __iomem *vaddr; + int i; + u16 val; + u8 save_exclude; + + /* + * Some versions of the Katana firmware mistakenly change the vendor + * & device id fields in the bridge's pci device (visible via pci + * config accesses). This breaks mv64x60_init() because those values + * are used to identify the type of bridge that's there. Artesyn + * claims that the subsystem vendor/device id's will have the correct + * Marvell values so this code puts back the correct values from there. + */ + memset(&hose, 0, sizeof(hose)); + vaddr = ioremap(CONFIG_MV64X60_NEW_BASE, MV64x60_INTERNAL_SPACE_SIZE); + setup_indirect_pci_nomap(&hose, vaddr + MV64x60_PCI0_CONFIG_ADDR, + vaddr + MV64x60_PCI0_CONFIG_DATA); + save_exclude = mv64x60_pci_exclude_bridge; + mv64x60_pci_exclude_bridge = 0; + + early_read_config_word(&hose, 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID, &val); + + if (val != PCI_VENDOR_ID_MARVELL) { + early_read_config_word(&hose, 0, PCI_DEVFN(0, 0), + PCI_SUBSYSTEM_VENDOR_ID, &val); + early_write_config_word(&hose, 0, PCI_DEVFN(0, 0), + PCI_VENDOR_ID, val); + early_read_config_word(&hose, 0, PCI_DEVFN(0, 0), + PCI_SUBSYSTEM_ID, &val); + early_write_config_word(&hose, 0, PCI_DEVFN(0, 0), + PCI_DEVICE_ID, val); + } + + mv64x60_pci_exclude_bridge = save_exclude; + iounmap(vaddr); + + memset(&si, 0, sizeof(si)); + + si.phys_reg_base = CONFIG_MV64X60_NEW_BASE; + + si.pci_1.enable_bus = 1; + si.pci_1.pci_io.cpu_base = KATANA_PCI1_IO_START_PROC_ADDR; + si.pci_1.pci_io.pci_base_hi = 0; + si.pci_1.pci_io.pci_base_lo = KATANA_PCI1_IO_START_PCI_ADDR; + si.pci_1.pci_io.size = KATANA_PCI1_IO_SIZE; + si.pci_1.pci_io.swap = MV64x60_CPU2PCI_SWAP_NONE; + si.pci_1.pci_mem[0].cpu_base = KATANA_PCI1_MEM_START_PROC_ADDR; + si.pci_1.pci_mem[0].pci_base_hi = KATANA_PCI1_MEM_START_PCI_HI_ADDR; + si.pci_1.pci_mem[0].pci_base_lo = KATANA_PCI1_MEM_START_PCI_LO_ADDR; + si.pci_1.pci_mem[0].size = KATANA_PCI1_MEM_SIZE; + si.pci_1.pci_mem[0].swap = MV64x60_CPU2PCI_SWAP_NONE; + si.pci_1.pci_cmd_bits = 0; + si.pci_1.latency_timer = 0x80; + + for (i = 0; i < MV64x60_CPU2MEM_WINDOWS; i++) { +#if defined(CONFIG_NOT_COHERENT_CACHE) + si.cpu_prot_options[i] = 0; + si.enet_options[i] = MV64360_ENET2MEM_SNOOP_NONE; + si.mpsc_options[i] = MV64360_MPSC2MEM_SNOOP_NONE; + si.idma_options[i] = MV64360_IDMA2MEM_SNOOP_NONE; + + si.pci_1.acc_cntl_options[i] = + MV64360_PCI_ACC_CNTL_SNOOP_NONE | + MV64360_PCI_ACC_CNTL_SWAP_NONE | + MV64360_PCI_ACC_CNTL_MBURST_128_BYTES | + MV64360_PCI_ACC_CNTL_RDSIZE_256_BYTES; +#else + si.cpu_prot_options[i] = 0; + si.enet_options[i] = MV64360_ENET2MEM_SNOOP_NONE; /* errata */ + si.mpsc_options[i] = MV64360_MPSC2MEM_SNOOP_NONE; /* errata */ + si.idma_options[i] = MV64360_IDMA2MEM_SNOOP_NONE; /* errata */ + + si.pci_1.acc_cntl_options[i] = + MV64360_PCI_ACC_CNTL_SNOOP_WB | + MV64360_PCI_ACC_CNTL_SWAP_NONE | + MV64360_PCI_ACC_CNTL_MBURST_32_BYTES | + MV64360_PCI_ACC_CNTL_RDSIZE_32_BYTES; +#endif + } + + /* Lookup PCI host bridges */ + if (mv64x60_init(&bh, &si)) + printk(KERN_WARNING "Bridge initialization failed.\n"); + + pci_dram_offset = 0; /* sys mem at same addr on PCI & cpu bus */ + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = katana_map_irq; + ppc_md.pci_exclude_device = mv64x60_pci_exclude_device; + + mv64x60_set_bus(&bh, 1, 0); + bh.hose_b->first_busno = 0; + bh.hose_b->last_busno = 0xff; +} + +/* Bridge & platform setup routines */ +void __init +katana_intr_setup(void) +{ + /* MPP 8, 9, and 10 */ + mv64x60_clr_bits(&bh, MV64x60_MPP_CNTL_1, 0xfff); + + /* MPP 14 */ + if ((katana_id == KATANA_ID_750I) || (katana_id == KATANA_ID_752I)) + mv64x60_clr_bits(&bh, MV64x60_MPP_CNTL_1, 0x0f000000); + + /* + * Define GPP 8,9,and 10 interrupt polarity as active low + * input signal and level triggered + */ + mv64x60_set_bits(&bh, MV64x60_GPP_LEVEL_CNTL, 0x700); + mv64x60_clr_bits(&bh, MV64x60_GPP_IO_CNTL, 0x700); + + if ((katana_id == KATANA_ID_750I) || (katana_id == KATANA_ID_752I)) { + mv64x60_set_bits(&bh, MV64x60_GPP_LEVEL_CNTL, (1<<14)); + mv64x60_clr_bits(&bh, MV64x60_GPP_IO_CNTL, (1<<14)); + } + + /* Config GPP intr ctlr to respond to level trigger */ + mv64x60_set_bits(&bh, MV64x60_COMM_ARBITER_CNTL, (1<<10)); + + /* Erranum FEr PCI-#8 */ + mv64x60_clr_bits(&bh, MV64x60_PCI0_CMD, (1<<5) | (1<<9)); + mv64x60_clr_bits(&bh, MV64x60_PCI1_CMD, (1<<5) | (1<<9)); + + /* + * Dismiss and then enable interrupt on GPP interrupt cause + * for CPU #0 + */ + mv64x60_write(&bh, MV64x60_GPP_INTR_CAUSE, ~0x700); + mv64x60_set_bits(&bh, MV64x60_GPP_INTR_MASK, 0x700); + + if ((katana_id == KATANA_ID_750I) || (katana_id == KATANA_ID_752I)) { + mv64x60_write(&bh, MV64x60_GPP_INTR_CAUSE, ~(1<<14)); + mv64x60_set_bits(&bh, MV64x60_GPP_INTR_MASK, (1<<14)); + } + + /* + * Dismiss and then enable interrupt on CPU #0 high cause reg + * BIT25 summarizes GPP interrupts 8-15 + */ + mv64x60_set_bits(&bh, MV64360_IC_CPU0_INTR_MASK_HI, (1<<25)); +} + +void __init +katana_setup_peripherals(void) +{ + u32 base; + + /* Set up windows for boot CS, soldered & socketed flash, and CPLD */ + mv64x60_set_32bit_window(&bh, MV64x60_CPU2BOOT_WIN, + KATANA_BOOT_WINDOW_BASE, KATANA_BOOT_WINDOW_SIZE, 0); + bh.ci->enable_window_32bit(&bh, MV64x60_CPU2BOOT_WIN); + + /* Assume firmware set up window sizes correctly for dev 0 & 1 */ + mv64x60_get_32bit_window(&bh, MV64x60_CPU2DEV_0_WIN, &base, + &katana_flash_size_0); + + if (katana_flash_size_0 > 0) { + mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_0_WIN, + KATANA_SOLDERED_FLASH_BASE, katana_flash_size_0, 0); + bh.ci->enable_window_32bit(&bh, MV64x60_CPU2DEV_0_WIN); + } + + mv64x60_get_32bit_window(&bh, MV64x60_CPU2DEV_1_WIN, &base, + &katana_flash_size_1); + + if (katana_flash_size_1 > 0) { + mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_1_WIN, + (KATANA_SOLDERED_FLASH_BASE + katana_flash_size_0), + katana_flash_size_1, 0); + bh.ci->enable_window_32bit(&bh, MV64x60_CPU2DEV_1_WIN); + } + + mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_2_WIN, + KATANA_SOCKET_BASE, KATANA_SOCKETED_FLASH_SIZE, 0); + bh.ci->enable_window_32bit(&bh, MV64x60_CPU2DEV_2_WIN); + + mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_3_WIN, + KATANA_CPLD_BASE, KATANA_CPLD_SIZE, 0); + bh.ci->enable_window_32bit(&bh, MV64x60_CPU2DEV_3_WIN); + cpld_base = ioremap(KATANA_CPLD_BASE, KATANA_CPLD_SIZE); + + mv64x60_set_32bit_window(&bh, MV64x60_CPU2SRAM_WIN, + KATANA_INTERNAL_SRAM_BASE, MV64360_SRAM_SIZE, 0); + bh.ci->enable_window_32bit(&bh, MV64x60_CPU2SRAM_WIN); + sram_base = ioremap(KATANA_INTERNAL_SRAM_BASE, MV64360_SRAM_SIZE); + + /* Set up Enet->SRAM window */ + mv64x60_set_32bit_window(&bh, MV64x60_ENET2MEM_4_WIN, + KATANA_INTERNAL_SRAM_BASE, MV64360_SRAM_SIZE, 0x2); + bh.ci->enable_window_32bit(&bh, MV64x60_ENET2MEM_4_WIN); + + /* Give enet r/w access to memory region */ + mv64x60_set_bits(&bh, MV64360_ENET2MEM_ACC_PROT_0, (0x3 << (4 << 1))); + mv64x60_set_bits(&bh, MV64360_ENET2MEM_ACC_PROT_1, (0x3 << (4 << 1))); + mv64x60_set_bits(&bh, MV64360_ENET2MEM_ACC_PROT_2, (0x3 << (4 << 1))); + + mv64x60_clr_bits(&bh, MV64x60_PCI1_PCI_DECODE_CNTL, (1 << 3)); + mv64x60_clr_bits(&bh, MV64x60_TIMR_CNTR_0_3_CNTL, + ((1 << 0) | (1 << 8) | (1 << 16) | (1 << 24))); + + /* Must wait until window set up before retrieving board id */ + katana_get_board_id(); + + /* Enumerate pci bus (must know board id before getting proc number) */ + if (katana_get_proc_num() == 0) + bh.hose_b->last_busno = pciauto_bus_scan(bh.hose_b, 0); + +#if defined(CONFIG_NOT_COHERENT_CACHE) + mv64x60_write(&bh, MV64360_SRAM_CONFIG, 0x00160000); +#else + mv64x60_write(&bh, MV64360_SRAM_CONFIG, 0x001600b2); +#endif + + /* + * Setting the SRAM to 0. Note that this generates parity errors on + * internal data path in SRAM since it's first time accessing it + * while after reset it's not configured. + */ + memset(sram_base, 0, MV64360_SRAM_SIZE); + + /* Only processor zero [on 3750] is an PCI interrupt controller */ + if (katana_get_proc_num() == 0) + katana_intr_setup(); +} + +static void __init +katana_enable_ipmi(void) +{ + u8 reset_out; + + /* Enable access to IPMI ctlr by clearing IPMI PORTSEL bit in CPLD */ + reset_out = in_8(cpld_base + KATANA_CPLD_RESET_OUT); + reset_out &= ~KATANA_CPLD_RESET_OUT_PORTSEL; + out_8(cpld_base + KATANA_CPLD_RESET_OUT, reset_out); +} + +static void __init +katana_setup_arch(void) +{ + if (ppc_md.progress) + ppc_md.progress("katana_setup_arch: enter", 0); + + set_tb(0, 0); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = Root_NFS; +#else + ROOT_DEV = Root_SDA2; +#endif + + /* + * Set up the L2CR register. + * + * 750FX has only L2E, L2PE (bits 2-8 are reserved) + * DD2.0 has bug that requires the L2 to be in WRT mode + * avoid dirty data in cache + */ + if (PVR_REV(mfspr(SPRN_PVR)) == 0x0200) { + printk(KERN_INFO "DD2.0 detected. Setting L2 cache" + "to Writethrough mode\n"); + _set_L2CR(L2CR_L2E | L2CR_L2PE | L2CR_L2WT); + } else + _set_L2CR(L2CR_L2E | L2CR_L2PE); + + if (ppc_md.progress) + ppc_md.progress("katana_setup_arch: calling setup_bridge", 0); + + katana_setup_bridge(); + katana_setup_peripherals(); + katana_enable_ipmi(); + + katana_bus_frequency = katana_bus_freq(cpld_base); + + printk(KERN_INFO "Artesyn Communication Products, LLC - Katana(TM)\n"); + if (ppc_md.progress) + ppc_md.progress("katana_setup_arch: exit", 0); +} + +/* Platform device data fixup routines. */ +#if defined(CONFIG_SERIAL_MPSC) +static void __init +katana_fixup_mpsc_pdata(struct platform_device *pdev) +{ + struct mpsc_pdata *pdata; + + pdata = (struct mpsc_pdata *)pdev->dev.platform_data; + + pdata->max_idle = 40; + pdata->default_baud = KATANA_DEFAULT_BAUD; + pdata->brg_clk_src = KATANA_MPSC_CLK_SRC; + /* + * TCLK (not SysCLk) is routed to BRG, then to the MPSC. On most parts, + * TCLK == SysCLK but on 64460, they are separate pins. + * SysCLK can go up to 200 MHz but TCLK can only go up to 133 MHz. + */ + pdata->brg_clk_freq = min(katana_bus_frequency, MV64x60_TCLK_FREQ_MAX); +} +#endif + +#if defined(CONFIG_MV643XX_ETH) +static void __init +katana_fixup_eth_pdata(struct platform_device *pdev) +{ + struct mv643xx_eth_platform_data *eth_pd; + static u16 phy_addr[] = { + KATANA_ETH0_PHY_ADDR, + KATANA_ETH1_PHY_ADDR, + KATANA_ETH2_PHY_ADDR, + }; + + eth_pd = pdev->dev.platform_data; + eth_pd->force_phy_addr = 1; + eth_pd->phy_addr = phy_addr[pdev->id]; + eth_pd->tx_queue_size = KATANA_ETH_TX_QUEUE_SIZE; + eth_pd->rx_queue_size = KATANA_ETH_RX_QUEUE_SIZE; +} +#endif + +static int __init +katana_platform_notify(struct device *dev) +{ + static struct { + char *bus_id; + void ((*rtn)(struct platform_device *pdev)); + } dev_map[] = { +#if defined(CONFIG_SERIAL_MPSC) + { MPSC_CTLR_NAME ".0", katana_fixup_mpsc_pdata }, + { MPSC_CTLR_NAME ".1", katana_fixup_mpsc_pdata }, +#endif +#if defined(CONFIG_MV643XX_ETH) + { MV643XX_ETH_NAME ".0", katana_fixup_eth_pdata }, + { MV643XX_ETH_NAME ".1", katana_fixup_eth_pdata }, + { MV643XX_ETH_NAME ".2", katana_fixup_eth_pdata }, +#endif + }; + struct platform_device *pdev; + int i; + + if (dev && dev->bus_id) + for (i=0; ibus_id, dev_map[i].bus_id, + BUS_ID_SIZE)) { + + pdev = container_of(dev, + struct platform_device, dev); + dev_map[i].rtn(pdev); + } + + return 0; +} + +#ifdef CONFIG_MTD_PHYSMAP + +#ifndef MB +#define MB (1 << 20) +#endif + +/* + * MTD Layout depends on amount of soldered FLASH in system. Sizes in MB. + * + * FLASH Amount: 128 64 32 16 + * ------------- --- -- -- -- + * Monitor: 1 1 1 1 + * Primary Kernel: 1.5 1.5 1.5 1.5 + * Primary fs: 30 30 + * Secondary Kernel: 1.5 1.5 N/A N/A + * Secondary fs: N/A N/A + * User: + */ +static int __init +katana_setup_mtd(void) +{ + u32 size; + int ptbl_entries; + static struct mtd_partition *ptbl; + + size = katana_flash_size_0 + katana_flash_size_1; + if (!size) + return -ENOMEM; + + ptbl_entries = (size >= (64*MB)) ? 6 : 4; + + if ((ptbl = kmalloc(ptbl_entries * sizeof(struct mtd_partition), + GFP_KERNEL)) == NULL) { + + printk(KERN_WARNING "Can't alloc MTD partition table\n"); + return -ENOMEM; + } + memset(ptbl, 0, ptbl_entries * sizeof(struct mtd_partition)); + + ptbl[0].name = "Monitor"; + ptbl[0].size = KATANA_MTD_MONITOR_SIZE; + ptbl[1].name = "Primary Kernel"; + ptbl[1].offset = MTDPART_OFS_NXTBLK; + ptbl[1].size = 0x00180000; /* 1.5 MB */ + ptbl[2].name = "Primary Filesystem"; + ptbl[2].offset = MTDPART_OFS_APPEND; + ptbl[2].size = MTDPART_SIZ_FULL; /* Correct for 16 & 32 MB */ + ptbl[ptbl_entries-1].name = "User FLASH"; + ptbl[ptbl_entries-1].offset = KATANA_MTD_MONITOR_SIZE; + ptbl[ptbl_entries-1].size = MTDPART_SIZ_FULL; + + if (size >= (64*MB)) { + ptbl[2].size = 30*MB; + ptbl[3].name = "Secondary Kernel"; + ptbl[3].offset = MTDPART_OFS_NXTBLK; + ptbl[3].size = 0x00180000; /* 1.5 MB */ + ptbl[4].name = "Secondary Filesystem"; + ptbl[4].offset = MTDPART_OFS_APPEND; + ptbl[4].size = MTDPART_SIZ_FULL; + } + + physmap_map.size = size; + physmap_set_partitions(ptbl, ptbl_entries); + return 0; +} + +arch_initcall(katana_setup_mtd); +#endif + +static void +katana_restart(char *cmd) +{ + ulong i = 10000000; + + /* issue hard reset to the reset command register */ + out_8(cpld_base + KATANA_CPLD_RST_CMD, KATANA_CPLD_RST_CMD_HR); + + while (i-- > 0) ; + panic("restart failed\n"); +} + +static void +katana_halt(void) +{ + u8 v; + + if (katana_id == KATANA_ID_752I) { + v = in_8(cpld_base + HSL_PLD_BASE + HSL_PLD_HOT_SWAP_OFF); + v |= HSL_PLD_HOT_SWAP_LED_BIT; + out_8(cpld_base + HSL_PLD_BASE + HSL_PLD_HOT_SWAP_OFF, v); + } + + while (1) ; + /* NOTREACHED */ +} + +static void +katana_power_off(void) +{ + katana_halt(); + /* NOTREACHED */ +} + +static int +katana_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "vendor\t\t: Artesyn Communication Products, LLC\n"); + + seq_printf(m, "board\t\t: "); + + switch (katana_id) { + case KATANA_ID_3750: + seq_printf(m, "Katana 3750\n"); + break; + + case KATANA_ID_750I: + seq_printf(m, "Katana 750i\n"); + break; + + case KATANA_ID_752I: + seq_printf(m, "Katana 752i\n"); + break; + + default: + seq_printf(m, "Unknown\n"); + break; + } + + seq_printf(m, "product ID\t: 0x%x\n", + in_8(cpld_base + KATANA_CPLD_PRODUCT_ID)); + seq_printf(m, "hardware rev\t: 0x%x\n", + in_8(cpld_base+KATANA_CPLD_HARDWARE_VER)); + seq_printf(m, "PLD rev\t\t: 0x%x\n", + in_8(cpld_base + KATANA_CPLD_PLD_VER)); + seq_printf(m, "PLB freq\t: %ldMhz\n", + (long)katana_bus_frequency / 1000000); + seq_printf(m, "PCI\t\t: %sMonarch\n", katana_is_monarch()? "" : "Non-"); + + return 0; +} + +static void __init +katana_calibrate_decr(void) +{ + u32 freq; + + freq = katana_bus_frequency / 4; + + printk(KERN_INFO "time_init: decrementer frequency = %lu.%.6lu MHz\n", + (long)freq / 1000000, (long)freq % 1000000); + + tb_ticks_per_jiffy = freq / HZ; + tb_to_us = mulhwu_scale_factor(freq, 1000000); +} + +unsigned long __init +katana_find_end_of_memory(void) +{ + return mv64x60_get_mem_size(CONFIG_MV64X60_NEW_BASE, + MV64x60_TYPE_MV64360); +} + +#if defined(CONFIG_I2C_MV64XXX) && defined(CONFIG_SENSORS_M41T00) +extern ulong m41t00_get_rtc_time(void); +extern int m41t00_set_rtc_time(ulong); + +static int __init +katana_rtc_hookup(void) +{ + struct timespec tv; + + ppc_md.get_rtc_time = m41t00_get_rtc_time; + ppc_md.set_rtc_time = m41t00_set_rtc_time; + + tv.tv_nsec = 0; + tv.tv_sec = (ppc_md.get_rtc_time)(); + do_settimeofday(&tv); + + return 0; +} +late_initcall(katana_rtc_hookup); +#endif + +static inline void +katana_set_bat(void) +{ + mb(); + mtspr(SPRN_DBAT2U, 0xf0001ffe); + mtspr(SPRN_DBAT2L, 0xf000002a); + mb(); +} + +#if defined(CONFIG_SERIAL_TEXT_DEBUG) && defined(CONFIG_SERIAL_MPSC_CONSOLE) +static void __init +katana_map_io(void) +{ + io_block_mapping(0xf8100000, 0xf8100000, 0x00020000, _PAGE_IO); +} +#endif + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + + /* ASSUMPTION: If both r3 (bd_t pointer) and r6 (cmdline pointer) + * are non-zero, then we should use the board info from the bd_t + * structure and the cmdline pointed to by r6 instead of the + * information from birecs, if any. Otherwise, use the information + * from birecs as discovered by the preceeding call to + * parse_bootinfo(). This rule should work with both PPCBoot, which + * uses a bd_t board info structure, and the kernel boot wrapper, + * which uses birecs. + */ + if (r3 && r6) { + /* copy board info structure */ + memcpy( (void *)__res,(void *)(r3+KERNELBASE), sizeof(bd_t) ); + /* copy command line */ + *(char *)(r7+KERNELBASE) = 0; + strcpy(cmd_line, (char *)(r6+KERNELBASE)); + } + + isa_mem_base = 0; + + ppc_md.setup_arch = katana_setup_arch; + ppc_md.show_cpuinfo = katana_show_cpuinfo; + ppc_md.init_IRQ = mv64360_init_irq; + ppc_md.get_irq = mv64360_get_irq; + ppc_md.restart = katana_restart; + ppc_md.power_off = katana_power_off; + ppc_md.halt = katana_halt; + ppc_md.find_end_of_memory = katana_find_end_of_memory; + ppc_md.calibrate_decr = katana_calibrate_decr; + +#if defined(CONFIG_SERIAL_TEXT_DEBUG) && defined(CONFIG_SERIAL_MPSC_CONSOLE) + ppc_md.setup_io_mappings = katana_map_io; + ppc_md.progress = mv64x60_mpsc_progress; + mv64x60_progress_init(CONFIG_MV64X60_NEW_BASE); +#endif + +#if defined(CONFIG_SERIAL_MPSC) || defined(CONFIG_MV643XX_ETH) + platform_notify = katana_platform_notify; +#endif + + katana_set_bat(); /* Need for katana_find_end_of_memory and progress */ +} diff --git a/arch/ppc/platforms/katana.h b/arch/ppc/platforms/katana.h new file mode 100644 index 00000000000..b82ed81950f --- /dev/null +++ b/arch/ppc/platforms/katana.h @@ -0,0 +1,255 @@ +/* + * arch/ppc/platforms/katana.h + * + * Definitions for Artesyn Katana750i/3750 board. + * + * Author: Tim Montgomery + * Maintained by: Mark A. Greer + * + * Based on code done by Rabeeh Khoury - rabeeh@galileo.co.il + * Based on code done by Mark A. Greer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/* + * The MV64360 has 2 PCI buses each with 1 window from the CPU bus to + * PCI I/O space and 4 windows from the CPU bus to PCI MEM space. + * We'll only use one PCI MEM window on each PCI bus. + * + * This is the CPU physical memory map (windows must be at least 64 KB and start + * on a boundary that is a multiple of the window size): + * + * 0xff800000-0xffffffff - Boot window + * 0xf8400000-0xf843ffff - Internal SRAM + * 0xf8200000-0xf83fffff - CPLD + * 0xf8100000-0xf810ffff - MV64360 Registers (CONFIG_MV64X60_NEW_BASE) + * 0xf8000000-0xf80fffff - Socketed FLASH + * 0xe0000000-0xefffffff - Soldered FLASH + * 0xc0000000-0xc3ffffff - PCI I/O (second hose) + * 0x80000000-0xbfffffff - PCI MEM (second hose) + */ + +#ifndef __PPC_PLATFORMS_KATANA_H +#define __PPC_PLATFORMS_KATANA_H + +/* CPU Physical Memory Map setup. */ +#define KATANA_BOOT_WINDOW_BASE 0xff800000 +#define KATANA_BOOT_WINDOW_SIZE 0x00800000 /* 8 MB */ +#define KATANA_INTERNAL_SRAM_BASE 0xf8400000 +#define KATANA_CPLD_BASE 0xf8200000 +#define KATANA_CPLD_SIZE 0x00200000 /* 2 MB */ +#define KATANA_SOCKET_BASE 0xf8000000 +#define KATANA_SOCKETED_FLASH_SIZE 0x00100000 /* 1 MB */ +#define KATANA_SOLDERED_FLASH_BASE 0xe0000000 +#define KATANA_SOLDERED_FLASH_SIZE 0x10000000 /* 256 MB */ + +#define KATANA_PCI1_MEM_START_PROC_ADDR 0x80000000 +#define KATANA_PCI1_MEM_START_PCI_HI_ADDR 0x00000000 +#define KATANA_PCI1_MEM_START_PCI_LO_ADDR 0x80000000 +#define KATANA_PCI1_MEM_SIZE 0x40000000 /* 1 GB */ +#define KATANA_PCI1_IO_START_PROC_ADDR 0xc0000000 +#define KATANA_PCI1_IO_START_PCI_ADDR 0x00000000 +#define KATANA_PCI1_IO_SIZE 0x04000000 /* 64 MB */ + +/* Board-specific IRQ info */ +#define KATANA_PCI_INTA_IRQ_3750 64+8 +#define KATANA_PCI_INTB_IRQ_3750 64+9 +#define KATANA_PCI_INTC_IRQ_3750 64+10 + +#define KATANA_PCI_INTA_IRQ_750i 64+8 +#define KATANA_PCI_INTB_IRQ_750i 64+9 +#define KATANA_PCI_INTC_IRQ_750i 64+10 +#define KATANA_PCI_INTD_IRQ_750i 64+14 + +#define KATANA_CPLD_RST_EVENT 0x00000000 +#define KATANA_CPLD_RST_CMD 0x00001000 +#define KATANA_CPLD_PCI_ERR_INT_EN 0x00002000 +#define KATANA_CPLD_PCI_ERR_INT_PEND 0x00003000 +#define KATANA_CPLD_PRODUCT_ID 0x00004000 +#define KATANA_CPLD_EREADY 0x00005000 + +#define KATANA_CPLD_HARDWARE_VER 0x00007000 +#define KATANA_CPLD_PLD_VER 0x00008000 +#define KATANA_CPLD_BD_CFG_0 0x00009000 +#define KATANA_CPLD_BD_CFG_1 0x0000a000 +#define KATANA_CPLD_BD_CFG_3 0x0000c000 +#define KATANA_CPLD_LED 0x0000d000 +#define KATANA_CPLD_RESET_OUT 0x0000e000 + +#define KATANA_CPLD_RST_EVENT_INITACT 0x80 +#define KATANA_CPLD_RST_EVENT_SW 0x40 +#define KATANA_CPLD_RST_EVENT_WD 0x20 +#define KATANA_CPLD_RST_EVENT_COPS 0x10 +#define KATANA_CPLD_RST_EVENT_COPH 0x08 +#define KATANA_CPLD_RST_EVENT_CPCI 0x02 +#define KATANA_CPLD_RST_EVENT_FP 0x01 + +#define KATANA_CPLD_RST_CMD_SCL 0x80 +#define KATANA_CPLD_RST_CMD_SDA 0x40 +#define KATANA_CPLD_RST_CMD_I2C 0x10 +#define KATANA_CPLD_RST_CMD_FR 0x08 +#define KATANA_CPLD_RST_CMD_SR 0x04 +#define KATANA_CPLD_RST_CMD_HR 0x01 + +#define KATANA_CPLD_BD_CFG_0_SYSCLK_MASK 0xc0 +#define KATANA_CPLD_BD_CFG_0_SYSCLK_200 0x00 +#define KATANA_CPLD_BD_CFG_0_SYSCLK_166 0x80 +#define KATANA_CPLD_BD_CFG_0_SYSCLK_133 0xc0 +#define KATANA_CPLD_BD_CFG_0_SYSCLK_100 0x40 + +#define KATANA_CPLD_BD_CFG_1_FL_BANK_MASK 0x03 +#define KATANA_CPLD_BD_CFG_1_FL_BANK_16MB 0x00 +#define KATANA_CPLD_BD_CFG_1_FL_BANK_32MB 0x01 +#define KATANA_CPLD_BD_CFG_1_FL_BANK_64MB 0x02 +#define KATANA_CPLD_BD_CFG_1_FL_BANK_128MB 0x03 + +#define KATANA_CPLD_BD_CFG_1_FL_NUM_BANKS_MASK 0x04 +#define KATANA_CPLD_BD_CFG_1_FL_NUM_BANKS_ONE 0x00 +#define KATANA_CPLD_BD_CFG_1_FL_NUM_BANKS_TWO 0x04 + +#define KATANA_CPLD_BD_CFG_3_MONARCH 0x04 + +#define KATANA_CPLD_RESET_OUT_PORTSEL 0x80 +#define KATANA_CPLD_RESET_OUT_WD 0x20 +#define KATANA_CPLD_RESET_OUT_COPH 0x08 +#define KATANA_CPLD_RESET_OUT_PCI_RST_PCI 0x02 +#define KATANA_CPLD_RESET_OUT_PCI_RST_FP 0x01 + +#define KATANA_MBOX_RESET_REQUEST 0xC83A +#define KATANA_MBOX_RESET_ACK 0xE430 +#define KATANA_MBOX_RESET_DONE 0x32E5 + +#define HSL_PLD_BASE 0x00010000 +#define HSL_PLD_J4SGA_REG_OFF 0 +#define HSL_PLD_J4GA_REG_OFF 1 +#define HSL_PLD_J2GA_REG_OFF 2 +#define HSL_PLD_HOT_SWAP_OFF 6 +#define HSL_PLD_HOT_SWAP_LED_BIT 0x1 +#define GA_MASK 0x1f +#define HSL_PLD_SIZE 0x1000 +#define K3750_GPP_GEO_ADDR_PINS 0xf8000000 +#define K3750_GPP_GEO_ADDR_SHIFT 27 + +#define K3750_GPP_EVENT_PROC_0 (1 << 21) +#define K3750_GPP_EVENT_PROC_1_2 (1 << 2) + +#define PCI_VENDOR_ID_ARTESYN 0x1223 +#define PCI_DEVICE_ID_KATANA_3750_PROC0 0x0041 +#define PCI_DEVICE_ID_KATANA_3750_PROC1 0x0042 +#define PCI_DEVICE_ID_KATANA_3750_PROC2 0x0043 + +#define COPROC_MEM_FUNCTION 0 +#define COPROC_MEM_BAR 0 +#define COPROC_REGS_FUNCTION 0 +#define COPROC_REGS_BAR 4 +#define COPROC_FLASH_FUNCTION 2 +#define COPROC_FLASH_BAR 4 + +#define KATANA_IPMB_LOCAL_I2C_ADDR 0x08 + +#define KATANA_DEFAULT_BAUD 9600 +#define KATANA_MPSC_CLK_SRC 8 /* TCLK */ + +#define KATANA_MTD_MONITOR_SIZE (1 << 20) /* 1 MB */ + +#define KATANA_ETH0_PHY_ADDR 12 +#define KATANA_ETH1_PHY_ADDR 11 +#define KATANA_ETH2_PHY_ADDR 4 + +#define KATANA_PRODUCT_ID_3750 0x01 +#define KATANA_PRODUCT_ID_750i 0x02 +#define KATANA_PRODUCT_ID_752i 0x04 + +#define KATANA_ETH_TX_QUEUE_SIZE 800 +#define KATANA_ETH_RX_QUEUE_SIZE 400 + +#define KATANA_ETH_PORT_CONFIG_VALUE \ + ETH_UNICAST_NORMAL_MODE | \ + ETH_DEFAULT_RX_QUEUE_0 | \ + ETH_DEFAULT_RX_ARP_QUEUE_0 | \ + ETH_RECEIVE_BC_IF_NOT_IP_OR_ARP | \ + ETH_RECEIVE_BC_IF_IP | \ + ETH_RECEIVE_BC_IF_ARP | \ + ETH_CAPTURE_TCP_FRAMES_DIS | \ + ETH_CAPTURE_UDP_FRAMES_DIS | \ + ETH_DEFAULT_RX_TCP_QUEUE_0 | \ + ETH_DEFAULT_RX_UDP_QUEUE_0 | \ + ETH_DEFAULT_RX_BPDU_QUEUE_0 + +#define KATANA_ETH_PORT_CONFIG_EXTEND_VALUE \ + ETH_SPAN_BPDU_PACKETS_AS_NORMAL | \ + ETH_PARTITION_DISABLE + +#define GT_ETH_IPG_INT_RX(value) \ + ((value & 0x3fff) << 8) + +#define KATANA_ETH_PORT_SDMA_CONFIG_VALUE \ + ETH_RX_BURST_SIZE_4_64BIT | \ + GT_ETH_IPG_INT_RX(0) | \ + ETH_TX_BURST_SIZE_4_64BIT + +#define KATANA_ETH_PORT_SERIAL_CONTROL_VALUE \ + ETH_FORCE_LINK_PASS | \ + ETH_ENABLE_AUTO_NEG_FOR_DUPLX | \ + ETH_DISABLE_AUTO_NEG_FOR_FLOW_CTRL | \ + ETH_ADV_SYMMETRIC_FLOW_CTRL | \ + ETH_FORCE_FC_MODE_NO_PAUSE_DIS_TX | \ + ETH_FORCE_BP_MODE_NO_JAM | \ + BIT9 | \ + ETH_DO_NOT_FORCE_LINK_FAIL | \ + ETH_RETRANSMIT_16_ATTEMPTS | \ + ETH_ENABLE_AUTO_NEG_SPEED_GMII | \ + ETH_DTE_ADV_0 | \ + ETH_DISABLE_AUTO_NEG_BYPASS | \ + ETH_AUTO_NEG_NO_CHANGE | \ + ETH_MAX_RX_PACKET_9700BYTE | \ + ETH_CLR_EXT_LOOPBACK | \ + ETH_SET_FULL_DUPLEX_MODE | \ + ETH_ENABLE_FLOW_CTRL_TX_RX_IN_FULL_DUPLEX + +#ifndef __ASSEMBLY__ + +typedef enum { + KATANA_ID_3750, + KATANA_ID_750I, + KATANA_ID_752I, + KATANA_ID_MAX +} katana_id_t; + +#endif + +static inline u32 +katana_bus_freq(void __iomem *cpld_base) +{ + u8 bd_cfg_0; + + bd_cfg_0 = in_8(cpld_base + KATANA_CPLD_BD_CFG_0); + + switch (bd_cfg_0 & KATANA_CPLD_BD_CFG_0_SYSCLK_MASK) { + case KATANA_CPLD_BD_CFG_0_SYSCLK_200: + return 200000000; + break; + + case KATANA_CPLD_BD_CFG_0_SYSCLK_166: + return 166666666; + break; + + case KATANA_CPLD_BD_CFG_0_SYSCLK_133: + return 133333333; + break; + + case KATANA_CPLD_BD_CFG_0_SYSCLK_100: + return 100000000; + break; + + default: + return 133333333; + break; + } +} + +#endif /* __PPC_PLATFORMS_KATANA_H */ diff --git a/arch/ppc/platforms/lantec.h b/arch/ppc/platforms/lantec.h new file mode 100644 index 00000000000..8c87642c510 --- /dev/null +++ b/arch/ppc/platforms/lantec.h @@ -0,0 +1,21 @@ +/* + * LANTEC board specific definitions + * + * Copyright (c) 2001 Wolfgang Denk (wd@denx.de) + */ + +#ifndef __MACH_LANTEC_H +#define __MACH_LANTEC_H + +#include + +#include + +#define IMAP_ADDR 0xFFF00000 /* physical base address of IMMR area */ +#define IMAP_SIZE (64 * 1024) /* mapped size of IMMR area */ + +/* We don't use the 8259. +*/ +#define NR_8259_INTS 0 + +#endif /* __MACH_LANTEC_H */ diff --git a/arch/ppc/platforms/lite5200.c b/arch/ppc/platforms/lite5200.c new file mode 100644 index 00000000000..b604cf8b3ca --- /dev/null +++ b/arch/ppc/platforms/lite5200.c @@ -0,0 +1,236 @@ +/* + * arch/ppc/platforms/lite5200.c + * + * Platform support file for the Freescale LITE5200 based on MPC52xx. + * A maximum of this file should be moved to syslib/mpc52xx_????? + * so that new platform based on MPC52xx need a minimal platform file + * ( avoid code duplication ) + * + * + * Maintainer : Sylvain Munaut + * + * Based on the 2.4 code written by Kent Borg, + * Dale Farnsworth and + * Wolfgang Denk + * + * Copyright 2004-2005 Sylvain Munaut + * Copyright 2003 Motorola Inc. + * Copyright 2003 MontaVista Software Inc. + * Copyright 2003 DENX Software Engineering (wd@denx.de) + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + + +extern int powersave_nap; + +/* Board data given by U-Boot */ +bd_t __res; +EXPORT_SYMBOL(__res); /* For modules */ + + +/* ======================================================================== */ +/* Platform specific code */ +/* ======================================================================== */ + +/* Supported PSC function in "preference" order */ +struct mpc52xx_psc_func mpc52xx_psc_functions[] = { + { .id = 0, + .func = "uart", + }, + { .id = -1, /* End entry */ + .func = NULL, + } + }; + + +static int +lite5200_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "machine\t\t: Freescale LITE5200\n"); + return 0; +} + +#ifdef CONFIG_PCI +static int +lite5200_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + return (pin == 1) && (idsel==24) ? MPC52xx_IRQ0 : -1; +} +#endif + +static void __init +lite5200_setup_cpu(void) +{ + struct mpc52xx_cdm __iomem *cdm; + struct mpc52xx_gpio __iomem *gpio; + struct mpc52xx_intr __iomem *intr; + struct mpc52xx_xlb __iomem *xlb; + + u32 port_config; + u32 intr_ctrl; + + /* Map zones */ + cdm = ioremap(MPC52xx_PA(MPC52xx_CDM_OFFSET), MPC52xx_CDM_SIZE); + gpio = ioremap(MPC52xx_PA(MPC52xx_GPIO_OFFSET), MPC52xx_GPIO_SIZE); + xlb = ioremap(MPC52xx_PA(MPC52xx_XLB_OFFSET), MPC52xx_XLB_SIZE); + intr = ioremap(MPC52xx_PA(MPC52xx_INTR_OFFSET), MPC52xx_INTR_SIZE); + + if (!cdm || !gpio || !xlb || !intr) { + printk("lite5200.c: Error while mapping CDM/GPIO/XLB/INTR during" + "lite5200_setup_cpu\n"); + goto unmap_regs; + } + + /* Use internal 48 Mhz */ + out_8(&cdm->ext_48mhz_en, 0x00); + out_8(&cdm->fd_enable, 0x01); + if (in_be32(&cdm->rstcfg) & 0x40) /* Assumes 33Mhz clock */ + out_be16(&cdm->fd_counters, 0x0001); + else + out_be16(&cdm->fd_counters, 0x5555); + + /* Get port mux config */ + port_config = in_be32(&gpio->port_config); + + /* 48Mhz internal, pin is GPIO */ + port_config &= ~0x00800000; + + /* USB port */ + port_config &= ~0x00007000; /* Differential mode - USB1 only */ + port_config |= 0x00001000; + + /* Commit port config */ + out_be32(&gpio->port_config, port_config); + + /* Configure the XLB Arbiter */ + out_be32(&xlb->master_pri_enable, 0xff); + out_be32(&xlb->master_priority, 0x11111111); + + /* Enable ram snooping for 1GB window */ + out_be32(&xlb->config, in_be32(&xlb->config) | MPC52xx_XLB_CFG_SNOOP); + out_be32(&xlb->snoop_window, MPC52xx_PCI_TARGET_MEM | 0x1d); + + /* IRQ[0-3] setup : IRQ0 - Level Active Low */ + /* IRQ[1-3] - Level Active High */ + intr_ctrl = in_be32(&intr->ctrl); + intr_ctrl &= ~0x00ff0000; + intr_ctrl |= 0x00c00000; + out_be32(&intr->ctrl, intr_ctrl); + + /* Unmap reg zone */ +unmap_regs: + if (cdm) iounmap(cdm); + if (gpio) iounmap(gpio); + if (xlb) iounmap(xlb); + if (intr) iounmap(intr); +} + +static void __init +lite5200_setup_arch(void) +{ + /* CPU & Port mux setup */ + lite5200_setup_cpu(); + +#ifdef CONFIG_PCI + /* PCI Bridge setup */ + mpc52xx_find_bridges(); +#endif +} + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + /* Generic MPC52xx platform initialization */ + /* TODO Create one and move a max of stuff in it. + Put this init in the syslib */ + + struct bi_record *bootinfo = find_bootinfo(); + + if (bootinfo) + parse_bootinfo(bootinfo); + else { + /* Load the bd_t board info structure */ + if (r3) + memcpy((void*)&__res,(void*)(r3+KERNELBASE), + sizeof(bd_t)); + +#ifdef CONFIG_BLK_DEV_INITRD + /* Load the initrd */ + if (r4) { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } +#endif + + /* Load the command line */ + if (r6) { + *(char *)(r7+KERNELBASE) = 0; + strcpy(cmd_line, (char *)(r6+KERNELBASE)); + } + } + + /* PPC Sys identification */ + identify_ppc_sys_by_id(mfspr(SPRN_SVR)); + + /* BAT setup */ + mpc52xx_set_bat(); + + /* No ISA bus by default */ + isa_io_base = 0; + isa_mem_base = 0; + + /* Powersave */ + /* This is provided as an example on how to do it. But you + need to be aware that NAP disable bus snoop and that may + be required for some devices to work properly, like USB ... */ + /* powersave_nap = 1; */ + + + /* Setup the ppc_md struct */ + ppc_md.setup_arch = lite5200_setup_arch; + ppc_md.show_cpuinfo = lite5200_show_cpuinfo; + ppc_md.show_percpuinfo = NULL; + ppc_md.init_IRQ = mpc52xx_init_irq; + ppc_md.get_irq = mpc52xx_get_irq; + +#ifdef CONFIG_PCI + ppc_md.pci_map_irq = lite5200_map_irq; +#endif + + ppc_md.find_end_of_memory = mpc52xx_find_end_of_memory; + ppc_md.setup_io_mappings = mpc52xx_map_io; + + ppc_md.restart = mpc52xx_restart; + ppc_md.power_off = mpc52xx_power_off; + ppc_md.halt = mpc52xx_halt; + + /* No time keeper on the LITE5200 */ + ppc_md.time_init = NULL; + ppc_md.get_rtc_time = NULL; + ppc_md.set_rtc_time = NULL; + + ppc_md.calibrate_decr = mpc52xx_calibrate_decr; +#ifdef CONFIG_SERIAL_TEXT_DEBUG + ppc_md.progress = mpc52xx_progress; +#endif +} + diff --git a/arch/ppc/platforms/lite5200.h b/arch/ppc/platforms/lite5200.h new file mode 100644 index 00000000000..c1de2aa4717 --- /dev/null +++ b/arch/ppc/platforms/lite5200.h @@ -0,0 +1,23 @@ +/* + * arch/ppc/platforms/lite5200.h + * + * Definitions for Freescale LITE5200 : MPC52xx Standard Development + * Platform board support + * + * Maintainer : Sylvain Munaut + * + * Copyright (C) 2004 Sylvain Munaut + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#ifndef __PLATFORMS_LITE5200_H__ +#define __PLATFORMS_LITE5200_H__ + +/* Serial port used for low-level debug */ +#define MPC52xx_PF_CONSOLE_PORT 1 /* PSC1 */ + + +#endif /* __PLATFORMS_LITE5200_H__ */ diff --git a/arch/ppc/platforms/lopec.c b/arch/ppc/platforms/lopec.c new file mode 100644 index 00000000000..a5569525e0a --- /dev/null +++ b/arch/ppc/platforms/lopec.c @@ -0,0 +1,411 @@ +/* + * arch/ppc/platforms/lopec.c + * + * Setup routines for the Motorola LoPEC. + * + * Author: Dan Cox + * Maintainer: Tom Rini + * + * 2001-2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Define all of the IRQ senses and polarities. Taken from the + * LoPEC Programmer's Reference Guide. + */ +static u_char lopec_openpic_initsenses[16] __initdata = { + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* IRQ 0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 1 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* IRQ 2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 3 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* IRQ 4 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* IRQ 5 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 6 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 7 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 8 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 9 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 10 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 11 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 12 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* IRQ 13 */ + (IRQ_SENSE_EDGE | IRQ_POLARITY_NEGATIVE), /* IRQ 14 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE) /* IRQ 15 */ +}; + +static inline int __init +lopec_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + int irq; + static char pci_irq_table[][4] = { + {16, 0, 0, 0}, /* ID 11 - Winbond */ + {22, 0, 0, 0}, /* ID 12 - SCSI */ + {0, 0, 0, 0}, /* ID 13 - nothing */ + {17, 0, 0, 0}, /* ID 14 - 82559 Ethernet */ + {27, 0, 0, 0}, /* ID 15 - USB */ + {23, 0, 0, 0}, /* ID 16 - PMC slot 1 */ + {24, 0, 0, 0}, /* ID 17 - PMC slot 2 */ + {25, 0, 0, 0}, /* ID 18 - PCI slot */ + {0, 0, 0, 0}, /* ID 19 - nothing */ + {0, 0, 0, 0}, /* ID 20 - nothing */ + {0, 0, 0, 0}, /* ID 21 - nothing */ + {0, 0, 0, 0}, /* ID 22 - nothing */ + {0, 0, 0, 0}, /* ID 23 - nothing */ + {0, 0, 0, 0}, /* ID 24 - PMC slot 1b */ + {0, 0, 0, 0}, /* ID 25 - nothing */ + {0, 0, 0, 0} /* ID 26 - PMC Slot 2b */ + }; + const long min_idsel = 11, max_idsel = 26, irqs_per_slot = 4; + + irq = PCI_IRQ_TABLE_LOOKUP; + if (!irq) + return 0; + + return irq; +} + +static void __init +lopec_setup_winbond_83553(struct pci_controller *hose) +{ + int devfn; + + devfn = PCI_DEVFN(11,0); + + /* IDE interrupt routing (primary 14, secondary 15) */ + early_write_config_byte(hose, 0, devfn, 0x43, 0xef); + /* PCI interrupt routing */ + early_write_config_word(hose, 0, devfn, 0x44, 0x0000); + + /* ISA-PCI address decoder */ + early_write_config_byte(hose, 0, devfn, 0x48, 0xf0); + + /* RTC, kb, not used in PPC */ + early_write_config_byte(hose, 0, devfn, 0x4d, 0x00); + early_write_config_byte(hose, 0, devfn, 0x4e, 0x04); + devfn = PCI_DEVFN(11, 1); + early_write_config_byte(hose, 0, devfn, 0x09, 0x8f); + early_write_config_dword(hose, 0, devfn, 0x40, 0x00ff0011); +} + +static void __init +lopec_find_bridges(void) +{ + struct pci_controller *hose; + + hose = pcibios_alloc_controller(); + if (!hose) + return; + + hose->first_busno = 0; + hose->last_busno = 0xff; + + if (mpc10x_bridge_init(hose, MPC10X_MEM_MAP_B, MPC10X_MEM_MAP_B, + MPC10X_MAPB_EUMB_BASE) == 0) { + + hose->mem_resources[0].end = 0xffffffff; + lopec_setup_winbond_83553(hose); + hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = lopec_map_irq; + } +} + +static int +lopec_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "machine\t\t: Motorola LoPEC\n"); + return 0; +} + +static u32 +lopec_irq_canonicalize(u32 irq) +{ + if (irq == 2) + return 9; + else + return irq; +} + +static void +lopec_restart(char *cmd) +{ +#define LOPEC_SYSSTAT1 0xffe00000 + /* force a hard reset, if possible */ + unsigned char reg = *((unsigned char *) LOPEC_SYSSTAT1); + reg |= 0x80; + *((unsigned char *) LOPEC_SYSSTAT1) = reg; + + local_irq_disable(); + while(1); +#undef LOPEC_SYSSTAT1 +} + +static void +lopec_halt(void) +{ + local_irq_disable(); + while(1); +} + +static void +lopec_power_off(void) +{ + lopec_halt(); +} + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +int lopec_ide_ports_known = 0; +static unsigned long lopec_ide_regbase[MAX_HWIFS]; +static unsigned long lopec_ide_ctl_regbase[MAX_HWIFS]; +static unsigned long lopec_idedma_regbase; + +static void +lopec_ide_probe(void) +{ + struct pci_dev *dev = pci_get_device(PCI_VENDOR_ID_WINBOND, + PCI_DEVICE_ID_WINBOND_82C105, + NULL); + lopec_ide_ports_known = 1; + + if (dev) { + lopec_ide_regbase[0] = dev->resource[0].start; + lopec_ide_regbase[1] = dev->resource[2].start; + lopec_ide_ctl_regbase[0] = dev->resource[1].start; + lopec_ide_ctl_regbase[1] = dev->resource[3].start; + lopec_idedma_regbase = dev->resource[4].start; + pci_dev_put(dev); + } +} + +static int +lopec_ide_default_irq(unsigned long base) +{ + if (lopec_ide_ports_known == 0) + lopec_ide_probe(); + + if (base == lopec_ide_regbase[0]) + return 14; + else if (base == lopec_ide_regbase[1]) + return 15; + else + return 0; +} + +static unsigned long +lopec_ide_default_io_base(int index) +{ + if (lopec_ide_ports_known == 0) + lopec_ide_probe(); + return lopec_ide_regbase[index]; +} + +static void __init +lopec_ide_init_hwif_ports(hw_regs_t *hw, unsigned long data, + unsigned long ctl, int *irq) +{ + unsigned long reg = data; + uint alt_status_base; + int i; + + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) + hw->io_ports[i] = reg++; + + if (data == lopec_ide_regbase[0]) { + alt_status_base = lopec_ide_ctl_regbase[0] + 2; + hw->irq = 14; + } else if (data == lopec_ide_regbase[1]) { + alt_status_base = lopec_ide_ctl_regbase[1] + 2; + hw->irq = 15; + } else { + alt_status_base = 0; + hw->irq = 0; + } + + if (ctl) + hw->io_ports[IDE_CONTROL_OFFSET] = ctl; + else + hw->io_ports[IDE_CONTROL_OFFSET] = alt_status_base; + + if (irq != NULL) + *irq = hw->irq; + +} +#endif /* BLK_DEV_IDE */ + +static void __init +lopec_init_IRQ(void) +{ + int i; + + /* + * Provide the open_pic code with the correct table of interrupts. + */ + OpenPIC_InitSenses = lopec_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof(lopec_openpic_initsenses); + + mpc10x_set_openpic(); + + /* We have a cascade on OpenPIC IRQ 0, Linux IRQ 16 */ + openpic_hookup_cascade(NUM_8259_INTERRUPTS, "82c59 cascade", + &i8259_irq); + + /* Map i8259 interrupts */ + for(i = 0; i < NUM_8259_INTERRUPTS; i++) + irq_desc[i].handler = &i8259_pic; + + /* + * The EPIC allows for a read in the range of 0xFEF00000 -> + * 0xFEFFFFFF to generate a PCI interrupt-acknowledge transaction. + */ + i8259_init(0xfef00000); +} + +static int __init +lopec_request_io(void) +{ + outb(0x00, 0x4d0); + outb(0xc0, 0x4d1); + + request_region(0x00, 0x20, "dma1"); + request_region(0x20, 0x20, "pic1"); + request_region(0x40, 0x20, "timer"); + request_region(0x80, 0x10, "dma page reg"); + request_region(0xa0, 0x20, "pic2"); + request_region(0xc0, 0x20, "dma2"); + + return 0; +} + +device_initcall(lopec_request_io); + +static void __init +lopec_map_io(void) +{ + io_block_mapping(0xf0000000, 0xf0000000, 0x10000000, _PAGE_IO); + io_block_mapping(0xb0000000, 0xb0000000, 0x10000000, _PAGE_IO); +} + +/* + * Set BAT 3 to map 0xf8000000 to end of physical memory space 1-to-1. + */ +static __inline__ void +lopec_set_bat(void) +{ + mb(); + mtspr(SPRN_DBAT1U, 0xf8000ffe); + mtspr(SPRN_DBAT1L, 0xf800002a); + mb(); +} + +TODC_ALLOC(); + +static void __init +lopec_setup_arch(void) +{ + + TODC_INIT(TODC_TYPE_MK48T37, 0, 0, + ioremap(0xffe80000, 0x8000), 8); + + loops_per_jiffy = 100000000/HZ; + + lopec_find_bridges(); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#elif defined(CONFIG_ROOT_NFS) + ROOT_DEV = Root_NFS; +#elif defined(CONFIG_BLK_DEV_IDEDISK) + ROOT_DEV = Root_HDA1; +#else + ROOT_DEV = Root_SDA1; +#endif + +#ifdef CONFIG_PPCBUG_NVRAM + /* Read in NVRAM data */ + init_prep_nvram(); + + /* if no bootargs, look in NVRAM */ + if ( cmd_line[0] == '\0' ) { + char *bootargs; + bootargs = prep_nvram_get_var("bootargs"); + if (bootargs != NULL) { + strcpy(cmd_line, bootargs); + /* again.. */ + strcpy(saved_command_line, cmd_line); + } + } +#endif +} + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + lopec_set_bat(); + + isa_io_base = MPC10X_MAPB_ISA_IO_BASE; + isa_mem_base = MPC10X_MAPB_ISA_MEM_BASE; + pci_dram_offset = MPC10X_MAPB_DRAM_OFFSET; + ISA_DMA_THRESHOLD = 0x00ffffff; + DMA_MODE_READ = 0x44; + DMA_MODE_WRITE = 0x48; + + ppc_md.setup_arch = lopec_setup_arch; + ppc_md.show_cpuinfo = lopec_show_cpuinfo; + ppc_md.irq_canonicalize = lopec_irq_canonicalize; + ppc_md.init_IRQ = lopec_init_IRQ; + ppc_md.get_irq = openpic_get_irq; + + ppc_md.restart = lopec_restart; + ppc_md.power_off = lopec_power_off; + ppc_md.halt = lopec_halt; + + ppc_md.setup_io_mappings = lopec_map_io; + + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.calibrate_decr = todc_calibrate_decr; + + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) + ppc_ide_md.default_irq = lopec_ide_default_irq; + ppc_ide_md.default_io_base = lopec_ide_default_io_base; + ppc_ide_md.ide_init_hwif = lopec_ide_init_hwif_ports; +#endif +#ifdef CONFIG_SERIAL_TEXT_DEBUG + ppc_md.progress = gen550_progress; +#endif +} diff --git a/arch/ppc/platforms/lopec.h b/arch/ppc/platforms/lopec.h new file mode 100644 index 00000000000..5490edb2d26 --- /dev/null +++ b/arch/ppc/platforms/lopec.h @@ -0,0 +1,39 @@ +/* + * include/asm-ppc/lopec_serial.h + * + * Definitions for Motorola LoPEC board. + * + * Author: Dan Cox + * danc@mvista.com (or, alternately, source@mvista.com) + * + * 2001 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifndef __H_LOPEC_SERIAL +#define __H_LOPEC_SERIAL + +#define RS_TABLE_SIZE 3 + +#define BASE_BAUD (1843200 / 16) + +#ifdef CONFIG_SERIAL_DETECT_IRQ +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) +#else +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST) +#endif + +#define SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, 0xffe10000, 29, STD_COM_FLAGS, \ + iomem_base: (u8 *) 0xffe10000, \ + io_type: SERIAL_IO_MEM }, \ + { 0, BASE_BAUD, 0xffe11000, 20, STD_COM_FLAGS, \ + iomem_base: (u8 *) 0xffe11000, \ + io_type: SERIAL_IO_MEM }, \ + { 0, BASE_BAUD, 0xffe12000, 21, STD_COM_FLAGS, \ + iomem_base: (u8 *) 0xffe12000, \ + io_type: SERIAL_IO_MEM } + +#endif diff --git a/arch/ppc/platforms/lwmon.h b/arch/ppc/platforms/lwmon.h new file mode 100644 index 00000000000..995bf5112df --- /dev/null +++ b/arch/ppc/platforms/lwmon.h @@ -0,0 +1,60 @@ +/* + * Liebherr LWMON board specific definitions + * + * Copyright (c) 2001 Wolfgang Denk (wd@denx.de) + */ + +#ifndef __MACH_LWMON_H +#define __MACH_LWMON_H + +#include + +#include + +#define IMAP_ADDR 0xFFF00000 /* physical base address of IMMR area */ +#define IMAP_SIZE (64 * 1024) /* mapped size of IMMR area */ + +/*----------------------------------------------------------------------- + * PCMCIA stuff + *----------------------------------------------------------------------- + * + */ +#define PCMCIA_MEM_SIZE ( 64 << 20 ) + +#define MAX_HWIFS 1 /* overwrite default in include/asm-ppc/ide.h */ + +/* + * Definitions for IDE0 Interface + */ +#define IDE0_BASE_OFFSET 0 +#define IDE0_DATA_REG_OFFSET (PCMCIA_MEM_SIZE + 0x320) +#define IDE0_ERROR_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 1) +#define IDE0_NSECTOR_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 2) +#define IDE0_SECTOR_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 3) +#define IDE0_LCYL_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 4) +#define IDE0_HCYL_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 5) +#define IDE0_SELECT_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 6) +#define IDE0_STATUS_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 7) +#define IDE0_CONTROL_REG_OFFSET 0x0106 +#define IDE0_IRQ_REG_OFFSET 0x000A /* not used */ + +#define IDE0_INTERRUPT 13 + +/* + * Definitions for I2C devices + */ +#define I2C_ADDR_AUDIO 0x28 /* Audio volume control */ +#define I2C_ADDR_SYSMON 0x2E /* LM87 System Monitor */ +#define I2C_ADDR_RTC 0x51 /* PCF8563 RTC */ +#define I2C_ADDR_POWER_A 0x52 /* PCMCIA/USB power switch, channel A */ +#define I2C_ADDR_POWER_B 0x53 /* PCMCIA/USB power switch, channel B */ +#define I2C_ADDR_KEYBD 0x56 /* PIC LWE keyboard */ +#define I2C_ADDR_PICIO 0x57 /* PIC IO Expander */ +#define I2C_ADDR_EEPROM 0x58 /* EEPROM AT24C164 */ + + +/* We don't use the 8259. +*/ +#define NR_8259_INTS 0 + +#endif /* __MACH_LWMON_H */ diff --git a/arch/ppc/platforms/mbx.h b/arch/ppc/platforms/mbx.h new file mode 100644 index 00000000000..fe81ca4ea0a --- /dev/null +++ b/arch/ppc/platforms/mbx.h @@ -0,0 +1,117 @@ +/* + * A collection of structures, addresses, and values associated with + * the Motorola MBX boards. This was originally created for the + * MBX860, and probably needs revisions for other boards (like the 821). + * When this file gets out of control, we can split it up into more + * meaningful pieces. + * + * Copyright (c) 1997 Dan Malek (dmalek@jlc.net) + */ +#ifdef __KERNEL__ +#ifndef __MACH_MBX_DEFS +#define __MACH_MBX_DEFS + +#ifndef __ASSEMBLY__ +/* A Board Information structure that is given to a program when + * EPPC-Bug starts it up. + */ +typedef struct bd_info { + unsigned int bi_tag; /* Should be 0x42444944 "BDID" */ + unsigned int bi_size; /* Size of this structure */ + unsigned int bi_revision; /* revision of this structure */ + unsigned int bi_bdate; /* EPPCbug date, i.e. 0x11061997 */ + unsigned int bi_memstart; /* Memory start address */ + unsigned int bi_memsize; /* Memory (end) size in bytes */ + unsigned int bi_intfreq; /* Internal Freq, in Hz */ + unsigned int bi_busfreq; /* Bus Freq, in Hz */ + unsigned int bi_clun; /* Boot device controller */ + unsigned int bi_dlun; /* Boot device logical dev */ + + /* These fields are not part of the board information structure + * provided by the boot rom. They are filled in by embed_config.c + * so we have the information consistent with other platforms. + */ + unsigned char bi_enetaddr[6]; + unsigned int bi_baudrate; +} bd_t; + +/* Memory map for the MBX as configured by EPPC-Bug. We could reprogram + * The SIU and PCI bridge, and try to use larger MMU pages, but the + * performance gain is not measureable and it certainly complicates the + * generic MMU model. + * + * In a effort to minimize memory usage for embedded applications, any + * PCI driver or ISA driver must request or map the region required by + * the device. For convenience (and since we can map up to 4 Mbytes with + * a single page table page), the MMU initialization will map the + * NVRAM, Status/Control registers, CPM Dual Port RAM, and the PCI + * Bridge CSRs 1:1 into the kernel address space. + */ +#define PCI_ISA_IO_ADDR ((unsigned)0x80000000) +#define PCI_ISA_IO_SIZE ((uint)(512 * 1024 * 1024)) +#define PCI_IDE_ADDR ((unsigned)0x81000000) +#define PCI_ISA_MEM_ADDR ((unsigned)0xc0000000) +#define PCI_ISA_MEM_SIZE ((uint)(512 * 1024 * 1024)) +#define PCMCIA_MEM_ADDR ((uint)0xe0000000) +#define PCMCIA_MEM_SIZE ((uint)(64 * 1024 * 1024)) +#define PCMCIA_DMA_ADDR ((uint)0xe4000000) +#define PCMCIA_DMA_SIZE ((uint)(64 * 1024 * 1024)) +#define PCMCIA_ATTRB_ADDR ((uint)0xe8000000) +#define PCMCIA_ATTRB_SIZE ((uint)(64 * 1024 * 1024)) +#define PCMCIA_IO_ADDR ((uint)0xec000000) +#define PCMCIA_IO_SIZE ((uint)(64 * 1024 * 1024)) +#define NVRAM_ADDR ((uint)0xfa000000) +#define NVRAM_SIZE ((uint)(1 * 1024 * 1024)) +#define MBX_CSR_ADDR ((uint)0xfa100000) +#define MBX_CSR_SIZE ((uint)(1 * 1024 * 1024)) +#define IMAP_ADDR ((uint)0xfa200000) +#define IMAP_SIZE ((uint)(64 * 1024)) +#define PCI_CSR_ADDR ((uint)0xfa210000) +#define PCI_CSR_SIZE ((uint)(64 * 1024)) + +/* Map additional physical space into well known virtual addresses. Due + * to virtual address mapping, these physical addresses are not accessible + * in a 1:1 virtual to physical mapping. + */ +#define ISA_IO_VIRT_ADDR ((uint)0xfa220000) +#define ISA_IO_VIRT_SIZE ((uint)64 * 1024) + +/* Interrupt assignments. + * These are defined (and fixed) by the MBX hardware implementation. + */ +#define POWER_FAIL_INT SIU_IRQ0 /* Power fail */ +#define TEMP_HILO_INT SIU_IRQ1 /* Temperature sensor */ +#define QSPAN_INT SIU_IRQ2 /* PCI Bridge (DMA CTLR?) */ +#define ISA_BRIDGE_INT SIU_IRQ3 /* All those PC things */ +#define COMM_L_INT SIU_IRQ6 /* MBX Comm expansion connector pin */ +#define STOP_ABRT_INT SIU_IRQ7 /* Stop/Abort header pin */ + +/* CPM Ethernet through SCCx. + * + * Bits in parallel I/O port registers that have to be set/cleared + * to configure the pins for SCC1 use. The TCLK and RCLK seem unique + * to the MBX860 board. Any two of the four available clocks could be + * used, and the MPC860 cookbook manual has an example using different + * clock pins. + */ +#define PA_ENET_RXD ((ushort)0x0001) +#define PA_ENET_TXD ((ushort)0x0002) +#define PA_ENET_TCLK ((ushort)0x0200) +#define PA_ENET_RCLK ((ushort)0x0800) +#define PC_ENET_TENA ((ushort)0x0001) +#define PC_ENET_CLSN ((ushort)0x0010) +#define PC_ENET_RENA ((ushort)0x0020) + +/* Control bits in the SICR to route TCLK (CLK2) and RCLK (CLK4) to + * SCC1. Also, make sure GR1 (bit 24) and SC1 (bit 25) are zero. + */ +#define SICR_ENET_MASK ((uint)0x000000ff) +#define SICR_ENET_CLKRT ((uint)0x0000003d) + +/* The MBX uses the 8259. +*/ +#define NR_8259_INTS 16 + +#endif /* !__ASSEMBLY__ */ +#endif /* __MACH_MBX_DEFS */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/mcpn765.c b/arch/ppc/platforms/mcpn765.c new file mode 100644 index 00000000000..e88d294ea59 --- /dev/null +++ b/arch/ppc/platforms/mcpn765.c @@ -0,0 +1,527 @@ +/* + * arch/ppc/platforms/mcpn765.c + * + * Board setup routines for the Motorola MCG MCPN765 cPCI Board. + * + * Author: Mark A. Greer + * mgreer@mvista.com + * + * Modified by Randy Vinson (rvinson@mvista.com) + * + * 2001-2002 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +/* + * This file adds support for the Motorola MCG MCPN765. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for linux/serial_core.h */ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mcpn765.h" + +static u_char mcpn765_openpic_initsenses[] __initdata = { + (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE),/* 16: i8259 cascade */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),/* 17: COM1,2,3,4 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),/* 18: Enet 1 (front) */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),/* 19: HAWK WDT XXXX */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),/* 20: 21554 bridge */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),/* 21: cPCI INTA# */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),/* 22: cPCI INTB# */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),/* 23: cPCI INTC# */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),/* 24: cPCI INTD# */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),/* 25: PMC1 INTA#,PMC2 INTB#*/ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),/* 26: PMC1 INTB#,PMC2 INTC#*/ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),/* 27: PMC1 INTC#,PMC2 INTD#*/ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),/* 28: PMC1 INTD#,PMC2 INTA#*/ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),/* 29: Enet 2 (J3) */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),/* 30: Abort Switch */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),/* 31: RTC Alarm */ +}; + +extern void mcpn765_set_VIA_IDE_native(void); + +extern u_int openpic_irq(void); +extern char cmd_line[]; + +extern void gen550_progress(char *, unsigned short); +extern void gen550_init(int, struct uart_port *); + +int use_of_interrupt_tree = 0; + +static void mcpn765_halt(void); + +TODC_ALLOC(); + +/* + * Motorola MCG MCPN765 interrupt routing. + */ +static inline int +mcpn765_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + { 14, 0, 0, 0 }, /* IDSEL 11 - have to manually set */ + { 0, 0, 0, 0 }, /* IDSEL 12 - unused */ + { 0, 0, 0, 0 }, /* IDSEL 13 - unused */ + { 18, 0, 0, 0 }, /* IDSEL 14 - Enet 0 */ + { 0, 0, 0, 0 }, /* IDSEL 15 - unused */ + { 25, 26, 27, 28 }, /* IDSEL 16 - PMC Slot 1 */ + { 28, 25, 26, 27 }, /* IDSEL 17 - PMC Slot 2 */ + { 0, 0, 0, 0 }, /* IDSEL 18 - PMC 2B Connector XXXX */ + { 29, 0, 0, 0 }, /* IDSEL 19 - Enet 1 */ + { 20, 0, 0, 0 }, /* IDSEL 20 - 21554 cPCI bridge */ + }; + + const long min_idsel = 11, max_idsel = 20, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +} + +void __init +mcpn765_set_VIA_IDE_legacy(void) +{ + unsigned short vend, dev; + + early_read_config_word(0, 0, PCI_DEVFN(0xb, 1), PCI_VENDOR_ID, &vend); + early_read_config_word(0, 0, PCI_DEVFN(0xb, 1), PCI_DEVICE_ID, &dev); + + if ((vend == PCI_VENDOR_ID_VIA) && + (dev == PCI_DEVICE_ID_VIA_82C586_1)) { + + unsigned char temp; + + /* put back original "standard" port base addresses */ + early_write_config_dword(0, 0, PCI_DEVFN(0xb, 1), + PCI_BASE_ADDRESS_0, 0x1f1); + early_write_config_dword(0, 0, PCI_DEVFN(0xb, 1), + PCI_BASE_ADDRESS_1, 0x3f5); + early_write_config_dword(0, 0, PCI_DEVFN(0xb, 1), + PCI_BASE_ADDRESS_2, 0x171); + early_write_config_dword(0, 0, PCI_DEVFN(0xb, 1), + PCI_BASE_ADDRESS_3, 0x375); + early_write_config_dword(0, 0, PCI_DEVFN(0xb, 1), + PCI_BASE_ADDRESS_4, 0xcc01); + + /* put into legacy mode */ + early_read_config_byte(0, 0, PCI_DEVFN(0xb, 1), PCI_CLASS_PROG, + &temp); + temp &= ~0x05; + early_write_config_byte(0, 0, PCI_DEVFN(0xb, 1), PCI_CLASS_PROG, + temp); + } +} + +void +mcpn765_set_VIA_IDE_native(void) +{ + unsigned short vend, dev; + + early_read_config_word(0, 0, PCI_DEVFN(0xb, 1), PCI_VENDOR_ID, &vend); + early_read_config_word(0, 0, PCI_DEVFN(0xb, 1), PCI_DEVICE_ID, &dev); + + if ((vend == PCI_VENDOR_ID_VIA) && + (dev == PCI_DEVICE_ID_VIA_82C586_1)) { + + unsigned char temp; + + /* put into native mode */ + early_read_config_byte(0, 0, PCI_DEVFN(0xb, 1), PCI_CLASS_PROG, + &temp); + temp |= 0x05; + early_write_config_byte(0, 0, PCI_DEVFN(0xb, 1), PCI_CLASS_PROG, + temp); + } +} + +/* + * Initialize the VIA 82c586b. + */ +static void __init +mcpn765_setup_via_82c586b(void) +{ + struct pci_dev *dev; + u_char c; + + if ((dev = pci_get_device(PCI_VENDOR_ID_VIA, + PCI_DEVICE_ID_VIA_82C586_0, + NULL)) == NULL) { + printk("No VIA ISA bridge found\n"); + mcpn765_halt(); + /* NOTREACHED */ + } + + /* + * If the firmware left the EISA 4d0/4d1 ports enabled, make sure + * IRQ 14 is set for edge. + */ + pci_read_config_byte(dev, 0x47, &c); + + if (c & (1<<5)) { + c = inb(0x4d1); + c &= ~(1<<6); + outb(c, 0x4d1); + } + + /* Disable PNP IRQ routing since we use the Hawk's MPIC */ + pci_write_config_dword(dev, 0x54, 0); + pci_write_config_byte(dev, 0x58, 0); + + pci_dev_put(dev); + if ((dev = pci_get_device(PCI_VENDOR_ID_VIA, + PCI_DEVICE_ID_VIA_82C586_1, + NULL)) == NULL) { + printk("No VIA ISA bridge found\n"); + mcpn765_halt(); + /* NOTREACHED */ + } + + /* + * PPCBug doesn't set the enable bits for the IDE device. + * Turn them on now. + */ + pci_read_config_byte(dev, 0x40, &c); + c |= 0x03; + pci_write_config_byte(dev, 0x40, c); + pci_dev_put(dev); + + return; +} + +void __init +mcpn765_pcibios_fixup(void) +{ + /* Do MCPN765 board specific initialization. */ + mcpn765_setup_via_82c586b(); +} + +void __init +mcpn765_find_bridges(void) +{ + struct pci_controller *hose; + + hose = pcibios_alloc_controller(); + + if (!hose) + return; + + hose->first_busno = 0; + hose->last_busno = 0xff; + hose->pci_mem_offset = MCPN765_PCI_PHY_MEM_OFFSET; + + pci_init_resource(&hose->io_resource, + MCPN765_PCI_IO_START, + MCPN765_PCI_IO_END, + IORESOURCE_IO, + "PCI host bridge"); + + pci_init_resource(&hose->mem_resources[0], + MCPN765_PCI_MEM_START, + MCPN765_PCI_MEM_END, + IORESOURCE_MEM, + "PCI host bridge"); + + hose->io_space.start = MCPN765_PCI_IO_START; + hose->io_space.end = MCPN765_PCI_IO_END; + hose->mem_space.start = MCPN765_PCI_MEM_START; + hose->mem_space.end = MCPN765_PCI_MEM_END - HAWK_MPIC_SIZE; + + if (hawk_init(hose, + MCPN765_HAWK_PPC_REG_BASE, + MCPN765_PROC_PCI_MEM_START, + MCPN765_PROC_PCI_MEM_END - HAWK_MPIC_SIZE, + MCPN765_PROC_PCI_IO_START, + MCPN765_PROC_PCI_IO_END, + MCPN765_PCI_MEM_END - HAWK_MPIC_SIZE + 1) != 0) { + printk("Could not initialize HAWK bridge\n"); + } + + /* VIA IDE BAR decoders are only 16-bits wide. PCI Auto Config + * will reassign the bars outside of 16-bit I/O space, which will + * "break" things. To prevent this, we'll set the IDE chip into + * legacy mode and seed the bars with their legacy addresses (in 16-bit + * I/O space). The Auto Config code will skip the IDE contoller in + * legacy mode, so our bar values will stick. + */ + mcpn765_set_VIA_IDE_legacy(); + + hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); + + /* Now that we've got 16-bit addresses in the bars, we can switch the + * IDE controller back into native mode so we can do "modern" resource + * and interrupt management. + */ + mcpn765_set_VIA_IDE_native(); + + ppc_md.pcibios_fixup = mcpn765_pcibios_fixup; + ppc_md.pcibios_fixup_bus = NULL; + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = mcpn765_map_irq; + + return; +} +static void __init +mcpn765_setup_arch(void) +{ + struct pci_controller *hose; + + if ( ppc_md.progress ) + ppc_md.progress("mcpn765_setup_arch: enter", 0); + + loops_per_jiffy = 50000000 / HZ; + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = Root_NFS; +#else + ROOT_DEV = Root_SDA2; +#endif + + if ( ppc_md.progress ) + ppc_md.progress("mcpn765_setup_arch: find_bridges", 0); + + /* Lookup PCI host bridges */ + mcpn765_find_bridges(); + + hose = pci_bus_to_hose(0); + isa_io_base = (ulong)hose->io_base_virt; + + TODC_INIT(TODC_TYPE_MK48T37, + (MCPN765_PHYS_NVRAM_AS0 - isa_io_base), + (MCPN765_PHYS_NVRAM_AS1 - isa_io_base), + (MCPN765_PHYS_NVRAM_DATA - isa_io_base), + 8); + + OpenPIC_InitSenses = mcpn765_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof(mcpn765_openpic_initsenses); + + printk("Motorola MCG MCPN765 cPCI Non-System Board\n"); + printk("MCPN765 port (MontaVista Software, Inc. (source@mvista.com))\n"); + + if ( ppc_md.progress ) + ppc_md.progress("mcpn765_setup_arch: exit", 0); + + return; +} + +static void __init +mcpn765_init2(void) +{ + + request_region(0x00,0x20,"dma1"); + request_region(0x20,0x20,"pic1"); + request_region(0x40,0x20,"timer"); + request_region(0x80,0x10,"dma page reg"); + request_region(0xa0,0x20,"pic2"); + request_region(0xc0,0x20,"dma2"); + + return; +} + +/* + * Interrupt setup and service. + * Have MPIC on HAWK and cascaded 8259s on VIA 82586 cascaded to MPIC. + */ +static void __init +mcpn765_init_IRQ(void) +{ + int i; + + if ( ppc_md.progress ) + ppc_md.progress("init_irq: enter", 0); + + openpic_init(NUM_8259_INTERRUPTS); + openpic_hookup_cascade(NUM_8259_INTERRUPTS, "82c59 cascade", + i8259_irq); + + for(i=0; i < NUM_8259_INTERRUPTS; i++) + irq_desc[i].handler = &i8259_pic; + + i8259_init(0); + + if ( ppc_md.progress ) + ppc_md.progress("init_irq: exit", 0); + + return; +} + +static u32 +mcpn765_irq_canonicalize(u32 irq) +{ + if (irq == 2) + return 9; + else + return irq; +} + +static unsigned long __init +mcpn765_find_end_of_memory(void) +{ + return hawk_get_mem_size(MCPN765_HAWK_SMC_BASE); +} + +static void __init +mcpn765_map_io(void) +{ + io_block_mapping(0xfe800000, 0xfe800000, 0x00800000, _PAGE_IO); +} + +static void +mcpn765_reset_board(void) +{ + local_irq_disable(); + + /* set VIA IDE controller into native mode */ + mcpn765_set_VIA_IDE_native(); + + /* Set exception prefix high - to the firmware */ + _nmask_and_or_msr(0, MSR_IP); + + out_8((u_char *)MCPN765_BOARD_MODRST_REG, 0x01); + + return; +} + +static void +mcpn765_restart(char *cmd) +{ + volatile ulong i = 10000000; + + mcpn765_reset_board(); + + while (i-- > 0); + panic("restart failed\n"); +} + +static void +mcpn765_power_off(void) +{ + mcpn765_halt(); + /* NOTREACHED */ +} + +static void +mcpn765_halt(void) +{ + local_irq_disable(); + while (1); + /* NOTREACHED */ +} + +static int +mcpn765_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "vendor\t\t: Motorola MCG\n"); + seq_printf(m, "machine\t\t: MCPN765\n"); + + return 0; +} + +/* + * Set BAT 3 to map 0xf0000000 to end of physical memory space. + */ +static __inline__ void +mcpn765_set_bat(void) +{ + mb(); + mtspr(SPRN_DBAT1U, 0xfe8000fe); + mtspr(SPRN_DBAT1L, 0xfe80002a); + mb(); +} + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + + /* Map in board regs, etc. */ + mcpn765_set_bat(); + + isa_mem_base = MCPN765_ISA_MEM_BASE; + pci_dram_offset = MCPN765_PCI_DRAM_OFFSET; + ISA_DMA_THRESHOLD = 0x00ffffff; + DMA_MODE_READ = 0x44; + DMA_MODE_WRITE = 0x48; + + ppc_md.setup_arch = mcpn765_setup_arch; + ppc_md.show_cpuinfo = mcpn765_show_cpuinfo; + ppc_md.irq_canonicalize = mcpn765_irq_canonicalize; + ppc_md.init_IRQ = mcpn765_init_IRQ; + ppc_md.get_irq = openpic_get_irq; + ppc_md.init = mcpn765_init2; + + ppc_md.restart = mcpn765_restart; + ppc_md.power_off = mcpn765_power_off; + ppc_md.halt = mcpn765_halt; + + ppc_md.find_end_of_memory = mcpn765_find_end_of_memory; + ppc_md.setup_io_mappings = mcpn765_map_io; + + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.calibrate_decr = todc_calibrate_decr; + + ppc_md.nvram_read_val = todc_m48txx_read_val; + ppc_md.nvram_write_val = todc_m48txx_write_val; + + ppc_md.heartbeat = NULL; + ppc_md.heartbeat_reset = 0; + ppc_md.heartbeat_count = 0; + +#ifdef CONFIG_SERIAL_TEXT_DEBUG + ppc_md.progress = gen550_progress; +#endif +#ifdef CONFIG_KGDB + ppc_md.kgdb_map_scc = gen550_kgdb_map_scc; +#endif + + return; +} diff --git a/arch/ppc/platforms/mcpn765.h b/arch/ppc/platforms/mcpn765.h new file mode 100644 index 00000000000..4d35ecad097 --- /dev/null +++ b/arch/ppc/platforms/mcpn765.h @@ -0,0 +1,122 @@ +/* + * arch/ppc/platforms/mcpn765.h + * + * Definitions for Motorola MCG MCPN765 cPCI Board. + * + * Author: Mark A. Greer + * mgreer@mvista.com + * + * 2001-2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +/* + * From Processor to PCI: + * PCI Mem Space: 0x80000000 - 0xc0000000 -> 0x80000000 - 0xc0000000 (1 GB) + * PCI I/O Space: 0xfd800000 - 0xfe000000 -> 0x00000000 - 0x00800000 (8 MB) + * Note: Must skip 0xfe000000-0xfe400000 for CONFIG_HIGHMEM/PKMAP area + * MPIC in PCI Mem Space: 0xfe800000 - 0xfe830000 (not all used by MPIC) + * + * From PCI to Processor: + * System Memory: 0x00000000 -> 0x00000000 + */ + +#ifndef __PPC_PLATFORMS_MCPN765_H +#define __PPC_PLATFORMS_MCPN765_H +#include + +/* PCI Memory space mapping info */ +#define MCPN765_PCI_MEM_SIZE 0x40000000U +#define MCPN765_PROC_PCI_MEM_START 0x80000000U +#define MCPN765_PROC_PCI_MEM_END (MCPN765_PROC_PCI_MEM_START + \ + MCPN765_PCI_MEM_SIZE - 1) +#define MCPN765_PCI_MEM_START 0x80000000U +#define MCPN765_PCI_MEM_END (MCPN765_PCI_MEM_START + \ + MCPN765_PCI_MEM_SIZE - 1) + +/* PCI I/O space mapping info */ +#define MCPN765_PCI_IO_SIZE 0x00800000U +#define MCPN765_PROC_PCI_IO_START 0xfd800000U +#define MCPN765_PROC_PCI_IO_END (MCPN765_PROC_PCI_IO_START + \ + MCPN765_PCI_IO_SIZE - 1) +#define MCPN765_PCI_IO_START 0x00000000U +#define MCPN765_PCI_IO_END (MCPN765_PCI_IO_START + \ + MCPN765_PCI_IO_SIZE - 1) + +/* System memory mapping info */ +#define MCPN765_PCI_DRAM_OFFSET 0x00000000U +#define MCPN765_PCI_PHY_MEM_OFFSET 0x00000000U + +#define MCPN765_ISA_MEM_BASE 0x00000000U +#define MCPN765_ISA_IO_BASE MCPN765_PROC_PCI_IO_START + +/* Define base addresses for important sets of registers */ +#define MCPN765_HAWK_MPIC_BASE 0xfe800000U +#define MCPN765_HAWK_SMC_BASE 0xfef80000U +#define MCPN765_HAWK_PPC_REG_BASE 0xfeff0000U + +/* Define MCPN765 board register addresses. */ +#define MCPN765_BOARD_STATUS_REG 0xfef88080U +#define MCPN765_BOARD_MODFAIL_REG 0xfef88090U +#define MCPN765_BOARD_MODRST_REG 0xfef880a0U +#define MCPN765_BOARD_TBEN_REG 0xfef880c0U +#define MCPN765_BOARD_GEOGRAPHICAL_REG 0xfef880e8U +#define MCPN765_BOARD_EXT_FEATURE_REG 0xfef880f0U +#define MCPN765_BOARD_LAST_RESET_REG 0xfef880f8U + +/* Defines for UART */ + +/* Define the UART base addresses */ +#define MCPN765_SERIAL_1 0xfef88000 +#define MCPN765_SERIAL_2 0xfef88200 +#define MCPN765_SERIAL_3 0xfef88400 +#define MCPN765_SERIAL_4 0xfef88600 + +#ifdef CONFIG_SERIAL_MANY_PORTS +#define RS_TABLE_SIZE 64 +#else +#define RS_TABLE_SIZE 4 +#endif + +/* Rate for the 1.8432 Mhz clock for the onboard serial chip */ +#define BASE_BAUD ( 1843200 / 16 ) +#define UART_CLK 1843200 + +#ifdef CONFIG_SERIAL_DETECT_IRQ +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) +#else +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST) +#endif + +/* All UART IRQ's are wire-OR'd to IRQ 17 */ +#define STD_SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, MCPN765_SERIAL_1, 17, STD_COM_FLAGS, /* ttyS0 */\ + iomem_base: (u8 *)MCPN765_SERIAL_1, \ + iomem_reg_shift: 4, \ + io_type: SERIAL_IO_MEM }, \ + { 0, BASE_BAUD, MCPN765_SERIAL_2, 17, STD_COM_FLAGS, /* ttyS1 */\ + iomem_base: (u8 *)MCPN765_SERIAL_2, \ + iomem_reg_shift: 4, \ + io_type: SERIAL_IO_MEM }, \ + { 0, BASE_BAUD, MCPN765_SERIAL_3, 17, STD_COM_FLAGS, /* ttyS2 */\ + iomem_base: (u8 *)MCPN765_SERIAL_3, \ + iomem_reg_shift: 4, \ + io_type: SERIAL_IO_MEM }, \ + { 0, BASE_BAUD, MCPN765_SERIAL_4, 17, STD_COM_FLAGS, /* ttyS3 */\ + iomem_base: (u8 *)MCPN765_SERIAL_4, \ + iomem_reg_shift: 4, \ + io_type: SERIAL_IO_MEM }, + +#define SERIAL_PORT_DFNS \ + STD_SERIAL_PORT_DFNS + +/* Define the NVRAM/RTC address strobe & data registers */ +#define MCPN765_PHYS_NVRAM_AS0 0xfef880c8U +#define MCPN765_PHYS_NVRAM_AS1 0xfef880d0U +#define MCPN765_PHYS_NVRAM_DATA 0xfef880d8U + +extern void mcpn765_find_bridges(void); + +#endif /* __PPC_PLATFORMS_MCPN765_H */ diff --git a/arch/ppc/platforms/mpc5200.c b/arch/ppc/platforms/mpc5200.c new file mode 100644 index 00000000000..a58db438c16 --- /dev/null +++ b/arch/ppc/platforms/mpc5200.c @@ -0,0 +1,53 @@ +/* + * arch/ppc/platforms/mpc5200.c + * + * OCP Definitions for the boards based on MPC5200 processor. Contains + * definitions for every common peripherals. (Mostly all but PSCs) + * + * Maintainer : Sylvain Munaut + * + * Copyright 2004 Sylvain Munaut + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include +#include + + +static struct ocp_fs_i2c_data mpc5200_i2c_def = { + .flags = FS_I2C_CLOCK_5200, +}; + + +/* Here is the core_ocp struct. + * With all the devices common to all board. Even if port multiplexing is + * not setup for them (if the user don't want them, just don't select the + * config option). The potentially conflicting devices (like PSCs) goes in + * board specific file. + */ +struct ocp_def core_ocp[] = { + { + .vendor = OCP_VENDOR_FREESCALE, + .function = OCP_FUNC_IIC, + .index = 0, + .paddr = MPC52xx_I2C1, + .irq = OCP_IRQ_NA, /* MPC52xx_IRQ_I2C1 - Buggy */ + .pm = OCP_CPM_NA, + .additions = &mpc5200_i2c_def, + }, + { + .vendor = OCP_VENDOR_FREESCALE, + .function = OCP_FUNC_IIC, + .index = 1, + .paddr = MPC52xx_I2C2, + .irq = OCP_IRQ_NA, /* MPC52xx_IRQ_I2C2 - Buggy */ + .pm = OCP_CPM_NA, + .additions = &mpc5200_i2c_def, + }, + { /* Terminating entry */ + .vendor = OCP_VENDOR_INVALID + } +}; diff --git a/arch/ppc/platforms/mvme5100.c b/arch/ppc/platforms/mvme5100.c new file mode 100644 index 00000000000..b292b44b760 --- /dev/null +++ b/arch/ppc/platforms/mvme5100.c @@ -0,0 +1,349 @@ +/* + * arch/ppc/platforms/mvme5100.c + * + * Board setup routines for the Motorola MVME5100. + * + * Author: Matt Porter + * + * 2001-2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static u_char mvme5100_openpic_initsenses[16] __initdata = { + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* i8259 cascade */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* TL16C550 UART 1,2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Enet1 front panel or P2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Hawk Watchdog 1,2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* DS1621 thermal alarm */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Universe II LINT0# */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Universe II LINT1# */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Universe II LINT2# */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Universe II LINT3# */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PMC1 INTA#, PMC2 INTB# */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PMC1 INTB#, PMC2 INTC# */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PMC1 INTC#, PMC2 INTD# */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PMC1 INTD#, PMC2 INTA# */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Enet 2 (front panel) */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Abort Switch */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* RTC Alarm */ +}; + +static inline int +mvme5100_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + int irq; + + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + { 0, 0, 0, 0 }, /* IDSEL 11 - Winbond */ + { 0, 0, 0, 0 }, /* IDSEL 12 - unused */ + { 21, 22, 23, 24 }, /* IDSEL 13 - Universe II */ + { 18, 0, 0, 0 }, /* IDSEL 14 - Enet 1 */ + { 0, 0, 0, 0 }, /* IDSEL 15 - unused */ + { 25, 26, 27, 28 }, /* IDSEL 16 - PMC Slot 1 */ + { 28, 25, 26, 27 }, /* IDSEL 17 - PMC Slot 2 */ + { 0, 0, 0, 0 }, /* IDSEL 18 - unused */ + { 29, 0, 0, 0 }, /* IDSEL 19 - Enet 2 */ + { 0, 0, 0, 0 }, /* IDSEL 20 - PMCSPAN */ + }; + + const long min_idsel = 11, max_idsel = 20, irqs_per_slot = 4; + irq = PCI_IRQ_TABLE_LOOKUP; + /* If lookup is zero, always return 0 */ + if (!irq) + return 0; + else +#ifdef CONFIG_MVME5100_IPMC761_PRESENT + /* If IPMC761 present, return table value */ + return irq; +#else + /* If IPMC761 not present, we don't have an i8259 so adjust */ + return (irq - NUM_8259_INTERRUPTS); +#endif +} + +static void +mvme5100_pcibios_fixup_resources(struct pci_dev *dev) +{ + int i; + + if ((dev->vendor == PCI_VENDOR_ID_MOTOROLA) && + (dev->device == PCI_DEVICE_ID_MOTOROLA_HAWK)) + for (i=0; iresource[i].start = 0; + dev->resource[i].end = 0; + } +} + +static void __init +mvme5100_setup_bridge(void) +{ + struct pci_controller* hose; + + hose = pcibios_alloc_controller(); + + if (!hose) + return; + + hose->first_busno = 0; + hose->last_busno = 0xff; + hose->pci_mem_offset = MVME5100_PCI_MEM_OFFSET; + + pci_init_resource(&hose->io_resource, MVME5100_PCI_LOWER_IO, + MVME5100_PCI_UPPER_IO, IORESOURCE_IO, + "PCI host bridge"); + + pci_init_resource(&hose->mem_resources[0], MVME5100_PCI_LOWER_MEM, + MVME5100_PCI_UPPER_MEM, IORESOURCE_MEM, + "PCI host bridge"); + + hose->io_space.start = MVME5100_PCI_LOWER_IO; + hose->io_space.end = MVME5100_PCI_UPPER_IO; + hose->mem_space.start = MVME5100_PCI_LOWER_MEM; + hose->mem_space.end = MVME5100_PCI_UPPER_MEM; + hose->io_base_virt = (void *)MVME5100_ISA_IO_BASE; + + /* Use indirect method of Hawk */ + setup_indirect_pci(hose, MVME5100_PCI_CONFIG_ADDR, + MVME5100_PCI_CONFIG_DATA); + + hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); + + ppc_md.pcibios_fixup_resources = mvme5100_pcibios_fixup_resources; + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = mvme5100_map_irq; +} + +static void __init +mvme5100_setup_arch(void) +{ + if ( ppc_md.progress ) + ppc_md.progress("mvme5100_setup_arch: enter", 0); + + loops_per_jiffy = 50000000 / HZ; + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = Root_NFS; +#else + ROOT_DEV = Root_SDA2; +#endif + + if ( ppc_md.progress ) + ppc_md.progress("mvme5100_setup_arch: find_bridges", 0); + + /* Setup PCI host bridge */ + mvme5100_setup_bridge(); + + /* Find and map our OpenPIC */ + hawk_mpic_init(MVME5100_PCI_MEM_OFFSET); + OpenPIC_InitSenses = mvme5100_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof(mvme5100_openpic_initsenses); + + printk("MVME5100 port (C) 2001 MontaVista Software, Inc. (source@mvista.com)\n"); + + if ( ppc_md.progress ) + ppc_md.progress("mvme5100_setup_arch: exit", 0); + + return; +} + +static void __init +mvme5100_init2(void) +{ +#ifdef CONFIG_MVME5100_IPMC761_PRESENT + request_region(0x00,0x20,"dma1"); + request_region(0x20,0x20,"pic1"); + request_region(0x40,0x20,"timer"); + request_region(0x80,0x10,"dma page reg"); + request_region(0xa0,0x20,"pic2"); + request_region(0xc0,0x20,"dma2"); +#endif + return; +} + +/* + * Interrupt setup and service. + * Have MPIC on HAWK and cascaded 8259s on Winbond cascaded to MPIC. + */ +static void __init +mvme5100_init_IRQ(void) +{ +#ifdef CONFIG_MVME5100_IPMC761_PRESENT + int i; +#endif + + if ( ppc_md.progress ) + ppc_md.progress("init_irq: enter", 0); + + openpic_set_sources(0, 16, OpenPIC_Addr + 0x10000); +#ifdef CONFIG_MVME5100_IPMC761_PRESENT + openpic_init(NUM_8259_INTERRUPTS); + openpic_hookup_cascade(NUM_8259_INTERRUPTS, "82c59 cascade", + &i8259_irq); + + /* Map i8259 interrupts. */ + for (i = 0; i < NUM_8259_INTERRUPTS; i++) + irq_desc[i].handler = &i8259_pic; + + i8259_init(0); +#else + openpic_init(0); +#endif + + if ( ppc_md.progress ) + ppc_md.progress("init_irq: exit", 0); + + return; +} + +/* + * Set BAT 3 to map 0xf0000000 to end of physical memory space. + */ +static __inline__ void +mvme5100_set_bat(void) +{ + mb(); + mtspr(SPRN_DBAT1U, 0xf0001ffe); + mtspr(SPRN_DBAT1L, 0xf000002a); + mb(); +} + +static unsigned long __init +mvme5100_find_end_of_memory(void) +{ + return hawk_get_mem_size(MVME5100_HAWK_SMC_BASE); +} + +static void __init +mvme5100_map_io(void) +{ + io_block_mapping(0xfe000000, 0xfe000000, 0x02000000, _PAGE_IO); + ioremap_base = 0xfe000000; +} + +static void +mvme5100_reset_board(void) +{ + local_irq_disable(); + + /* Set exception prefix high - to the firmware */ + _nmask_and_or_msr(0, MSR_IP); + + out_8((u_char *)MVME5100_BOARD_MODRST_REG, 0x01); + + return; +} + +static void +mvme5100_restart(char *cmd) +{ + volatile ulong i = 10000000; + + mvme5100_reset_board(); + + while (i-- > 0); + panic("restart failed\n"); +} + +static void +mvme5100_halt(void) +{ + local_irq_disable(); + while (1); +} + +static void +mvme5100_power_off(void) +{ + mvme5100_halt(); +} + +static int +mvme5100_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "vendor\t\t: Motorola\n"); + seq_printf(m, "machine\t\t: MVME5100\n"); + + return 0; +} + +TODC_ALLOC(); + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + mvme5100_set_bat(); + + isa_io_base = MVME5100_ISA_IO_BASE; + isa_mem_base = MVME5100_ISA_MEM_BASE; + pci_dram_offset = MVME5100_PCI_DRAM_OFFSET; + + ppc_md.setup_arch = mvme5100_setup_arch; + ppc_md.show_cpuinfo = mvme5100_show_cpuinfo; + ppc_md.init_IRQ = mvme5100_init_IRQ; + ppc_md.get_irq = openpic_get_irq; + ppc_md.init = mvme5100_init2; + + ppc_md.restart = mvme5100_restart; + ppc_md.power_off = mvme5100_power_off; + ppc_md.halt = mvme5100_halt; + + ppc_md.find_end_of_memory = mvme5100_find_end_of_memory; + ppc_md.setup_io_mappings = mvme5100_map_io; + + TODC_INIT(TODC_TYPE_MK48T37, MVME5100_NVRAM_AS0, MVME5100_NVRAM_AS1, + MVME5100_NVRAM_DATA, 8); + + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.calibrate_decr = todc_calibrate_decr; + + ppc_md.nvram_read_val = todc_m48txx_read_val; + ppc_md.nvram_write_val = todc_m48txx_write_val; +} diff --git a/arch/ppc/platforms/mvme5100.h b/arch/ppc/platforms/mvme5100.h new file mode 100644 index 00000000000..edd479439a4 --- /dev/null +++ b/arch/ppc/platforms/mvme5100.h @@ -0,0 +1,91 @@ +/* + * include/asm-ppc/platforms/mvme5100.h + * + * Definitions for Motorola MVME5100. + * + * Author: Matt Porter + * + * 2001 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_MVME5100_H__ +#define __ASM_MVME5100_H__ + +#define MVME5100_HAWK_SMC_BASE 0xfef80000 + +#define MVME5100_PCI_CONFIG_ADDR 0xfe000cf8 +#define MVME5100_PCI_CONFIG_DATA 0xfe000cfc + +#define MVME5100_PCI_IO_BASE 0xfe000000 +#define MVME5100_PCI_MEM_BASE 0x80000000 + +#define MVME5100_PCI_MEM_OFFSET 0x00000000 + +#define MVME5100_PCI_DRAM_OFFSET 0x00000000 +#define MVME5100_ISA_MEM_BASE 0x00000000 +#define MVME5100_ISA_IO_BASE MVME5100_PCI_IO_BASE + +#define MVME5100_PCI_LOWER_MEM 0x80000000 +#define MVME5100_PCI_UPPER_MEM 0xf3f7ffff +#define MVME5100_PCI_LOWER_IO 0x00000000 +#define MVME5100_PCI_UPPER_IO 0x0077ffff + +/* MVME5100 board register addresses. */ +#define MVME5100_BOARD_STATUS_REG 0xfef88080 +#define MVME5100_BOARD_MODFAIL_REG 0xfef88090 +#define MVME5100_BOARD_MODRST_REG 0xfef880a0 +#define MVME5100_BOARD_TBEN_REG 0xfef880c0 +#define MVME5100_BOARD_SW_READ_REG 0xfef880e0 +#define MVME5100_BOARD_GEO_ADDR_REG 0xfef880e8 +#define MVME5100_BOARD_EXT_FEATURE1_REG 0xfef880f0 +#define MVME5100_BOARD_EXT_FEATURE2_REG 0xfef88100 + +/* Define the NVRAM/RTC address strobe & data registers */ +#define MVME5100_PHYS_NVRAM_AS0 0xfef880c8 +#define MVME5100_PHYS_NVRAM_AS1 0xfef880d0 +#define MVME5100_PHYS_NVRAM_DATA 0xfef880d8 + +#define MVME5100_NVRAM_AS0 (MVME5100_PHYS_NVRAM_AS0 - MVME5100_ISA_IO_BASE) +#define MVME5100_NVRAM_AS1 (MVME5100_PHYS_NVRAM_AS1 - MVME5100_ISA_IO_BASE) +#define MVME5100_NVRAM_DATA (MVME5100_PHYS_NVRAM_DATA - MVME5100_ISA_IO_BASE) + +/* UART clock, addresses, and irq */ +#define MVME5100_BASE_BAUD 1843200 +#define MVME5100_SERIAL_1 0xfef88000 +#define MVME5100_SERIAL_2 0xfef88200 +#ifdef CONFIG_MVME5100_IPMC761_PRESENT +#define MVME5100_SERIAL_IRQ 17 +#else +#define MVME5100_SERIAL_IRQ 1 +#endif + +#define RS_TABLE_SIZE 4 + +#define BASE_BAUD ( MVME5100_BASE_BAUD / 16 ) + +#define STD_COM_FLAGS ASYNC_BOOT_AUTOCONF + +/* All UART IRQ's are wire-OR'd to one MPIC IRQ */ +#define STD_SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, MVME5100_SERIAL_1, \ + MVME5100_SERIAL_IRQ, \ + STD_COM_FLAGS, /* ttyS0 */ \ + iomem_base: (unsigned char *)MVME5100_SERIAL_1, \ + iomem_reg_shift: 4, \ + io_type: SERIAL_IO_MEM }, \ + { 0, BASE_BAUD, MVME5100_SERIAL_2, \ + MVME5100_SERIAL_IRQ, \ + STD_COM_FLAGS, /* ttyS1 */ \ + iomem_base: (unsigned char *)MVME5100_SERIAL_2, \ + iomem_reg_shift: 4, \ + io_type: SERIAL_IO_MEM }, + +#define SERIAL_PORT_DFNS \ + STD_SERIAL_PORT_DFNS + +#endif /* __ASM_MVME5100_H__ */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/pal4.h b/arch/ppc/platforms/pal4.h new file mode 100644 index 00000000000..641a11a3165 --- /dev/null +++ b/arch/ppc/platforms/pal4.h @@ -0,0 +1,42 @@ +/* + * arch/ppc/platforms/pal4.h + * + * Definitions for SBS Palomar IV board + * + * Author: Dan Cox + * + * 2002 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifndef __PPC_PLATFORMS_PAL4_H +#define __PPC_PLATFORMS_PAL4_H + +#define PAL4_NVRAM 0xfffc0000 +#define PAL4_NVRAM_SIZE 0x8000 + +#define PAL4_DRAM 0xfff80000 +#define PAL4_DRAM_BR_MASK 0xc0 +#define PAL4_DRAM_BR_SHIFT 6 +#define PAL4_DRAM_RESET 0x10 +#define PAL4_DRAM_EREADY 0x40 + +#define PAL4_MISC 0xfff80004 +#define PAL4_MISC_FB_MASK 0xc0 +#define PAL4_MISC_FLASH 0x20 /* StratFlash mapping: 1->0xff80, 0->0xfff0 */ +#define PAL4_MISC_MISC 0x08 +#define PAL4_MISC_BITF 0x02 +#define PAL4_MISC_NVKS 0x01 + +#define PAL4_L2 0xfff80008 +#define PAL4_L2_MASK 0x07 + +#define PAL4_PLDR 0xfff8000c + +/* Only two Ethernet devices on the board... */ +#define PAL4_ETH 31 +#define PAL4_INTA 20 + +#endif /* __PPC_PLATFORMS_PAL4_H */ diff --git a/arch/ppc/platforms/pal4_pci.c b/arch/ppc/platforms/pal4_pci.c new file mode 100644 index 00000000000..c3b1b757a48 --- /dev/null +++ b/arch/ppc/platforms/pal4_pci.c @@ -0,0 +1,77 @@ +/* + * arch/ppc/platforms/pal4_pci.c + * + * PCI support for SBS Palomar IV + * + * Author: Dan Cox + * + * 2002 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "pal4.h" + +/* not much to this.... */ +static inline int __init +pal4_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + if (idsel == 9) + return PAL4_ETH; + else + return PAL4_INTA + (idsel - 3); +} + +void __init +pal4_find_bridges(void) +{ + struct pci_controller *hose; + + hose = pcibios_alloc_controller(); + if (!hose) + return; + + hose->first_busno = 0; + hose->last_busno = 0xff; + hose->pci_mem_offset = 0; + + /* Could snatch these from the CPC700.... */ + pci_init_resource(&hose->io_resource, + 0x0, + 0x03ffffff, + IORESOURCE_IO, + "PCI host bridge"); + + pci_init_resource(&hose->mem_resources[0], + 0x90000000, + 0x9fffffff, + IORESOURCE_MEM, + "PCI host bridge"); + + hose->io_space.start = 0x00800000; + hose->io_space.end = 0x03ffffff; + hose->mem_space.start = 0x90000000; + hose->mem_space.end = 0x9fffffff; + hose->io_base_virt = (void *) 0xf8000000; + + setup_indirect_pci(hose, CPC700_PCI_CONFIG_ADDR, + CPC700_PCI_CONFIG_DATA); + + hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); + + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = pal4_map_irq; +} diff --git a/arch/ppc/platforms/pal4_serial.h b/arch/ppc/platforms/pal4_serial.h new file mode 100644 index 00000000000..a715c66e1ad --- /dev/null +++ b/arch/ppc/platforms/pal4_serial.h @@ -0,0 +1,39 @@ +/* + * arch/ppc/platforms/pal4_serial.h + * + * Definitions for SBS PalomarIV serial support + * + * Author: Dan Cox + * + * 2002 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifndef __PPC_PAL4_SERIAL_H +#define __PPC_PAL4_SERIAL_H + +#define CPC700_SERIAL_1 0xff600300 +#define CPC700_SERIAL_2 0xff600400 + +#define RS_TABLE_SIZE 2 +#define BASE_BAUD (33333333 / 4 / 16) + +#ifdef CONFIG_SERIAL_DETECT_IRQ +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) +#define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_AUTO_IRQ) +#else +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST) +#define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF) +#endif + +#define SERIAL_PORT_DFNS \ + {0, BASE_BAUD, CPC700_SERIAL_1, 3, STD_COM_FLAGS, \ + iomem_base: (unsigned char *) CPC700_SERIAL_1, \ + io_type: SERIAL_IO_MEM}, /* ttyS0 */ \ + {0, BASE_BAUD, CPC700_SERIAL_2, 4, STD_COM_FLAGS, \ + iomem_base: (unsigned char *) CPC700_SERIAL_2, \ + io_type: SERIAL_IO_MEM} + +#endif diff --git a/arch/ppc/platforms/pal4_setup.c b/arch/ppc/platforms/pal4_setup.c new file mode 100644 index 00000000000..12446b93e38 --- /dev/null +++ b/arch/ppc/platforms/pal4_setup.c @@ -0,0 +1,175 @@ +/* + * arch/ppc/platforms/pal4_setup.c + * + * Board setup routines for the SBS PalomarIV. + * + * Author: Dan Cox + * + * 2002 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "pal4.h" + +extern void pal4_find_bridges(void); + +unsigned int cpc700_irq_assigns[][2] = { + {1, 1}, /* IRQ 0: ECC correctable error */ + {1, 1}, /* IRQ 1: PCI write to memory range */ + {0, 1}, /* IRQ 2: PCI write to command register */ + {0, 1}, /* IRQ 3: UART 0 */ + {0, 1}, /* IRQ 4: UART 1 */ + {0, 1}, /* IRQ 5: ICC 0 */ + {0, 1}, /* IRQ 6: ICC 1 */ + {0, 1}, /* IRQ 7: GPT compare 0 */ + {0, 1}, /* IRQ 8: GPT compare 1 */ + {0, 1}, /* IRQ 9: GPT compare 2 */ + {0, 1}, /* IRQ 10: GPT compare 3 */ + {0, 1}, /* IRQ 11: GPT compare 4 */ + {0, 1}, /* IRQ 12: GPT capture 0 */ + {0, 1}, /* IRQ 13: GPT capture 1 */ + {0, 1}, /* IRQ 14: GPT capture 2 */ + {0, 1}, /* IRQ 15: GPT capture 3 */ + {0, 1}, /* IRQ 16: GPT capture 4 */ + {0, 0}, /* IRQ 17: reserved */ + {0, 0}, /* IRQ 18: reserved */ + {0, 0}, /* IRQ 19: reserved */ + {0, 0}, /* IRQ 20: reserved */ + {0, 1}, /* IRQ 21: Ethernet */ + {0, 0}, /* IRQ 22: reserved */ + {0, 0}, /* IRQ 23: reserved */ + {0, 0}, /* IRQ 24: resreved */ + {0, 0}, /* IRQ 25: reserved */ + {0, 0}, /* IRQ 26: reserved */ + {0, 0}, /* IRQ 27: reserved */ + {0, 0}, /* IRQ 28: reserved */ + {0, 0}, /* IRQ 29: reserved */ + {0, 0}, /* IRQ 30: reserved */ + {0, 0}, /* IRQ 31: reserved */ +}; + +static int +pal4_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "board\t\t: SBS Palomar IV\n"); + + return 0; +} + +static void +pal4_restart(char *cmd) +{ + local_irq_disable(); + __asm__ __volatile__("lis 3,0xfff0\n \ + ori 3,3,0x100\n \ + mtspr 26,3\n \ + li 3,0\n \ + mtspr 27,3\n \ + rfi"); + + for(;;); +} + +static void +pal4_power_off(void) +{ + local_irq_disable(); + for(;;); +} + +static void +pal4_halt(void) +{ + pal4_power_off(); +} + +TODC_ALLOC(); + +static void __init +pal4_setup_arch(void) +{ + unsigned long l2; + + TODC_INIT(TODC_TYPE_MK48T37, 0, 0, + ioremap(PAL4_NVRAM, PAL4_NVRAM_SIZE), 8); + + pal4_find_bridges(); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif + ROOT_DEV = Root_NFS; + + /* The L2 gets disabled in the bootloader, but all the proper + bits should be present from the fw, so just re-enable it */ + l2 = _get_L2CR(); + if (!(l2 & L2CR_L2E)) { + /* presume that it was initially set if the size is + still present. */ + if (l2 ^ L2CR_L2SIZ_MASK) + _set_L2CR(l2 | L2CR_L2E); + else + printk("L2 not set by firmware; left disabled.\n"); + } +} + +static void __init +pal4_map_io(void) +{ + io_block_mapping(0xf0000000, 0xf0000000, 0x10000000, _PAGE_IO); +} + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + + isa_io_base = 0 /*PAL4_ISA_IO_BASE*/; + pci_dram_offset = 0 /*PAL4_PCI_SYS_MEM_BASE*/; + + ppc_md.setup_arch = pal4_setup_arch; + ppc_md.show_cpuinfo = pal4_show_cpuinfo; + + ppc_md.setup_io_mappings = pal4_map_io; + + ppc_md.init_IRQ = cpc700_init_IRQ; + ppc_md.get_irq = cpc700_get_irq; + + ppc_md.restart = pal4_restart; + ppc_md.halt = pal4_halt; + ppc_md.power_off = pal4_power_off; + + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.calibrate_decr = todc_calibrate_decr; + + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; +} + diff --git a/arch/ppc/platforms/pcore.c b/arch/ppc/platforms/pcore.c new file mode 100644 index 00000000000..d7191630a65 --- /dev/null +++ b/arch/ppc/platforms/pcore.c @@ -0,0 +1,352 @@ +/* + * arch/ppc/platforms/pcore_setup.c + * + * Setup routines for Force PCORE boards + * + * Author: Matt Porter + * + * 2001 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcore.h" + +extern unsigned long loops_per_jiffy; + +static int board_type; + +static inline int __init +pcore_6750_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {9, 10, 11, 12}, /* IDSEL 24 - DEC 21554 */ + {10, 0, 0, 0}, /* IDSEL 25 - DEC 21143 */ + {11, 12, 9, 10}, /* IDSEL 26 - PMC I */ + {12, 9, 10, 11}, /* IDSEL 27 - PMC II */ + {0, 0, 0, 0}, /* IDSEL 28 - unused */ + {0, 0, 9, 0}, /* IDSEL 29 - unused */ + {0, 0, 0, 0}, /* IDSEL 30 - Winbond */ + }; + const long min_idsel = 24, max_idsel = 30, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +}; + +static inline int __init +pcore_680_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {9, 10, 11, 12}, /* IDSEL 24 - Sentinel */ + {10, 0, 0, 0}, /* IDSEL 25 - i82559 #1 */ + {11, 12, 9, 10}, /* IDSEL 26 - PMC I */ + {12, 9, 10, 11}, /* IDSEL 27 - PMC II */ + {9, 0, 0, 0}, /* IDSEL 28 - i82559 #2 */ + {0, 0, 0, 0}, /* IDSEL 29 - unused */ + {0, 0, 0, 0}, /* IDSEL 30 - Winbond */ + }; + const long min_idsel = 24, max_idsel = 30, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +}; + +void __init +pcore_pcibios_fixup(void) +{ + struct pci_dev *dev; + + if ((dev = pci_get_device(PCI_VENDOR_ID_WINBOND, + PCI_DEVICE_ID_WINBOND_83C553, + 0))) + { + /* Reroute interrupts both IDE channels to 15 */ + pci_write_config_byte(dev, + PCORE_WINBOND_IDE_INT, + 0xff); + + /* Route INTA-D to IRQ9-12, respectively */ + pci_write_config_word(dev, + PCORE_WINBOND_PCI_INT, + 0x9abc); + + /* + * Set up 8259 edge/level triggering + */ + outb(0x00, PCORE_WINBOND_PRI_EDG_LVL); + outb(0x1e, PCORE_WINBOND_SEC_EDG_LVL); + pci_dev_put(dev); + } +} + +int __init +pcore_find_bridges(void) +{ + struct pci_controller* hose; + int host_bridge, board_type; + + hose = pcibios_alloc_controller(); + if (!hose) + return 0; + + mpc10x_bridge_init(hose, + MPC10X_MEM_MAP_B, + MPC10X_MEM_MAP_B, + MPC10X_MAPB_EUMB_BASE); + + /* Determine board type */ + early_read_config_dword(hose, + 0, + PCI_DEVFN(0,0), + PCI_VENDOR_ID, + &host_bridge); + if (host_bridge == MPC10X_BRIDGE_106) + board_type = PCORE_TYPE_6750; + else /* MPC10X_BRIDGE_107 */ + board_type = PCORE_TYPE_680; + + hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); + + ppc_md.pcibios_fixup = pcore_pcibios_fixup; + ppc_md.pci_swizzle = common_swizzle; + + if (board_type == PCORE_TYPE_6750) + ppc_md.pci_map_irq = pcore_6750_map_irq; + else /* PCORE_TYPE_680 */ + ppc_md.pci_map_irq = pcore_680_map_irq; + + return board_type; +} + +/* Dummy variable to satisfy mpc10x_common.o */ +void *OpenPIC_Addr; + +static int +pcore_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "vendor\t\t: Force Computers\n"); + + if (board_type == PCORE_TYPE_6750) + seq_printf(m, "machine\t\t: PowerCore 6750\n"); + else /* PCORE_TYPE_680 */ + seq_printf(m, "machine\t\t: PowerCore 680\n"); + + seq_printf(m, "L2\t\t: " ); + if (board_type == PCORE_TYPE_6750) + switch (readb(PCORE_DCCR_REG) & PCORE_DCCR_L2_MASK) + { + case PCORE_DCCR_L2_0KB: + seq_printf(m, "nocache"); + break; + case PCORE_DCCR_L2_256KB: + seq_printf(m, "256KB"); + break; + case PCORE_DCCR_L2_1MB: + seq_printf(m, "1MB"); + break; + case PCORE_DCCR_L2_512KB: + seq_printf(m, "512KB"); + break; + default: + seq_printf(m, "error"); + break; + } + else /* PCORE_TYPE_680 */ + switch (readb(PCORE_DCCR_REG) & PCORE_DCCR_L2_MASK) + { + case PCORE_DCCR_L2_2MB: + seq_printf(m, "2MB"); + break; + case PCORE_DCCR_L2_256KB: + seq_printf(m, "reserved"); + break; + case PCORE_DCCR_L2_1MB: + seq_printf(m, "1MB"); + break; + case PCORE_DCCR_L2_512KB: + seq_printf(m, "512KB"); + break; + default: + seq_printf(m, "error"); + break; + } + + seq_printf(m, "\n"); + + return 0; +} + +static void __init +pcore_setup_arch(void) +{ + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_jiffy = 50000000/HZ; + + /* Lookup PCI host bridges */ + board_type = pcore_find_bridges(); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = Root_NFS; +#else + ROOT_DEV = Root_SDA2; +#endif + + printk(KERN_INFO "Force PowerCore "); + if (board_type == PCORE_TYPE_6750) + printk("6750\n"); + else + printk("680\n"); + printk(KERN_INFO "Port by MontaVista Software, Inc. (source@mvista.com)\n"); + _set_L2CR(L2CR_L2E | _get_L2CR()); + +} + +static void +pcore_restart(char *cmd) +{ + local_irq_disable(); + /* Hard reset */ + writeb(0x11, 0xfe000332); + while(1); +} + +static void +pcore_halt(void) +{ + local_irq_disable(); + /* Turn off user LEDs */ + writeb(0x00, 0xfe000300); + while (1); +} + +static void +pcore_power_off(void) +{ + pcore_halt(); +} + + +static void __init +pcore_init_IRQ(void) +{ + int i; + + for ( i = 0 ; i < 16 ; i++ ) + irq_desc[i].handler = &i8259_pic; + + i8259_init(0); +} + +/* + * Set BAT 3 to map 0xf0000000 to end of physical memory space. + */ +static __inline__ void +pcore_set_bat(void) +{ + mb(); + mtspr(SPRN_DBAT3U, 0xf0001ffe); + mtspr(SPRN_DBAT3L, 0xfe80002a); + mb(); + +} + +static unsigned long __init +pcore_find_end_of_memory(void) +{ + + return mpc10x_get_mem_size(MPC10X_MEM_MAP_B); +} + +static void __init +pcore_map_io(void) +{ + io_block_mapping(0xfe000000, 0xfe000000, 0x02000000, _PAGE_IO); +} + +TODC_ALLOC(); + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + + /* Cover I/O space with a BAT */ + /* yuck, better hope your ram size is a power of 2 -- paulus */ + pcore_set_bat(); + + isa_io_base = MPC10X_MAPB_ISA_IO_BASE; + isa_mem_base = MPC10X_MAPB_ISA_MEM_BASE; + pci_dram_offset = MPC10X_MAPB_DRAM_OFFSET; + + ppc_md.setup_arch = pcore_setup_arch; + ppc_md.show_cpuinfo = pcore_show_cpuinfo; + ppc_md.init_IRQ = pcore_init_IRQ; + ppc_md.get_irq = i8259_irq; + + ppc_md.find_end_of_memory = pcore_find_end_of_memory; + ppc_md.setup_io_mappings = pcore_map_io; + + ppc_md.restart = pcore_restart; + ppc_md.power_off = pcore_power_off; + ppc_md.halt = pcore_halt; + + TODC_INIT(TODC_TYPE_MK48T59, + PCORE_NVRAM_AS0, + PCORE_NVRAM_AS1, + PCORE_NVRAM_DATA, + 8); + + ppc_md.time_init = todc_time_init; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.calibrate_decr = todc_calibrate_decr; + + ppc_md.nvram_read_val = todc_m48txx_read_val; + ppc_md.nvram_write_val = todc_m48txx_write_val; + +#ifdef CONFIG_SERIAL_TEXT_DEBUG + ppc_md.progress = gen550_progress; +#endif +#ifdef CONFIG_KGDB + ppc_md.kgdb_map_scc = gen550_kgdb_map_scc; +#endif +} diff --git a/arch/ppc/platforms/pcore.h b/arch/ppc/platforms/pcore.h new file mode 100644 index 00000000000..c6a26e76492 --- /dev/null +++ b/arch/ppc/platforms/pcore.h @@ -0,0 +1,39 @@ +/* + * arch/ppc/platforms/pcore.h + * + * Definitions for Force PowerCore board support + * + * Author: Matt Porter + * + * 2001 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifndef __PPC_PLATFORMS_PCORE_H +#define __PPC_PLATFORMS_PCORE_H + +#include + +#define PCORE_TYPE_6750 1 +#define PCORE_TYPE_680 2 + +#define PCORE_NVRAM_AS0 0x73 +#define PCORE_NVRAM_AS1 0x75 +#define PCORE_NVRAM_DATA 0x77 + +#define PCORE_DCCR_REG (MPC10X_MAPB_ISA_IO_BASE + 0x308) +#define PCORE_DCCR_L2_MASK 0xc0 +#define PCORE_DCCR_L2_0KB 0x00 +#define PCORE_DCCR_L2_256KB 0x40 +#define PCORE_DCCR_L2_512KB 0xc0 +#define PCORE_DCCR_L2_1MB 0x80 +#define PCORE_DCCR_L2_2MB 0x00 + +#define PCORE_WINBOND_IDE_INT 0x43 +#define PCORE_WINBOND_PCI_INT 0x44 +#define PCORE_WINBOND_PRI_EDG_LVL 0x4d0 +#define PCORE_WINBOND_SEC_EDG_LVL 0x4d1 + +#endif /* __PPC_PLATFORMS_PCORE_H */ diff --git a/arch/ppc/platforms/pcu_e.h b/arch/ppc/platforms/pcu_e.h new file mode 100644 index 00000000000..91a820a6fbc --- /dev/null +++ b/arch/ppc/platforms/pcu_e.h @@ -0,0 +1,28 @@ +/* + * Siemens PCU E board specific definitions + * + * Copyright (c) 2001 Wolfgang Denk (wd@denx.de) + */ + +#ifndef __MACH_PCU_E_H +#define __MACH_PCU_E_H + +#include + +#include + +#define PCU_E_IMMR_BASE 0xFE000000 /* phys. addr of IMMR */ +#define PCU_E_IMAP_SIZE (64 * 1024) /* size of mapped area */ + +#define IMAP_ADDR PCU_E_IMMR_BASE /* physical base address of IMMR area */ +#define IMAP_SIZE PCU_E_IMAP_SIZE /* mapped size of IMMR area */ + +#define FEC_INTERRUPT 15 /* = SIU_LEVEL7 */ +#define DEC_INTERRUPT 13 /* = SIU_LEVEL6 */ +#define CPM_INTERRUPT 11 /* = SIU_LEVEL5 (was: SIU_LEVEL2) */ + +/* We don't use the 8259. +*/ +#define NR_8259_INTS 0 + +#endif /* __MACH_PCU_E_H */ diff --git a/arch/ppc/platforms/pmac_backlight.c b/arch/ppc/platforms/pmac_backlight.c new file mode 100644 index 00000000000..ed2b1cebc19 --- /dev/null +++ b/arch/ppc/platforms/pmac_backlight.c @@ -0,0 +1,202 @@ +/* + * Miscellaneous procedures for dealing with the PowerMac hardware. + * Contains support for the backlight. + * + * Copyright (C) 2000 Benjamin Herrenschmidt + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static struct backlight_controller *backlighter; +static void* backlighter_data; +static int backlight_autosave; +static int backlight_level = BACKLIGHT_MAX; +static int backlight_enabled = 1; +static int backlight_req_level = -1; +static int backlight_req_enable = -1; + +static void backlight_callback(void *); +static DECLARE_WORK(backlight_work, backlight_callback, NULL); + +void __pmac register_backlight_controller(struct backlight_controller *ctrler, + void *data, char *type) +{ + struct device_node* bk_node; + char *prop; + int valid = 0; + + /* There's already a matching controller, bail out */ + if (backlighter != NULL) + return; + + bk_node = find_devices("backlight"); + +#ifdef CONFIG_ADB_PMU + /* Special case for the old PowerBook since I can't test on it */ + backlight_autosave = machine_is_compatible("AAPL,3400/2400") + || machine_is_compatible("AAPL,3500"); + if ((backlight_autosave + || machine_is_compatible("AAPL,PowerBook1998") + || machine_is_compatible("PowerBook1,1")) + && !strcmp(type, "pmu")) + valid = 1; +#endif + if (bk_node) { + prop = get_property(bk_node, "backlight-control", NULL); + if (prop && !strncmp(prop, type, strlen(type))) + valid = 1; + } + if (!valid) + return; + backlighter = ctrler; + backlighter_data = data; + + if (bk_node && !backlight_autosave) + prop = get_property(bk_node, "bklt", NULL); + else + prop = NULL; + if (prop) { + backlight_level = ((*prop)+1) >> 1; + if (backlight_level > BACKLIGHT_MAX) + backlight_level = BACKLIGHT_MAX; + } + +#ifdef CONFIG_ADB_PMU + if (backlight_autosave) { + struct adb_request req; + pmu_request(&req, NULL, 2, 0xd9, 0); + while (!req.complete) + pmu_poll(); + backlight_level = req.reply[0] >> 4; + } +#endif + acquire_console_sem(); + if (!backlighter->set_enable(1, backlight_level, data)) + backlight_enabled = 1; + release_console_sem(); + + printk(KERN_INFO "Registered \"%s\" backlight controller," + "level: %d/15\n", type, backlight_level); +} +EXPORT_SYMBOL(register_backlight_controller); + +void __pmac unregister_backlight_controller(struct backlight_controller + *ctrler, void *data) +{ + /* We keep the current backlight level (for now) */ + if (ctrler == backlighter && data == backlighter_data) + backlighter = NULL; +} +EXPORT_SYMBOL(unregister_backlight_controller); + +static int __pmac __set_backlight_enable(int enable) +{ + int rc; + + if (!backlighter) + return -ENODEV; + acquire_console_sem(); + rc = backlighter->set_enable(enable, backlight_level, + backlighter_data); + if (!rc) + backlight_enabled = enable; + release_console_sem(); + return rc; +} +int __pmac set_backlight_enable(int enable) +{ + if (!backlighter) + return -ENODEV; + backlight_req_enable = enable; + schedule_work(&backlight_work); + return 0; +} + +EXPORT_SYMBOL(set_backlight_enable); + +int __pmac get_backlight_enable(void) +{ + if (!backlighter) + return -ENODEV; + return backlight_enabled; +} +EXPORT_SYMBOL(get_backlight_enable); + +static int __pmac __set_backlight_level(int level) +{ + int rc = 0; + + if (!backlighter) + return -ENODEV; + if (level < BACKLIGHT_MIN) + level = BACKLIGHT_OFF; + if (level > BACKLIGHT_MAX) + level = BACKLIGHT_MAX; + acquire_console_sem(); + if (backlight_enabled) + rc = backlighter->set_level(level, backlighter_data); + if (!rc) + backlight_level = level; + release_console_sem(); + if (!rc && !backlight_autosave) { + level <<=1; + if (level & 0x10) + level |= 0x01; + // -- todo: save to property "bklt" + } + return rc; +} +int __pmac set_backlight_level(int level) +{ + if (!backlighter) + return -ENODEV; + backlight_req_level = level; + schedule_work(&backlight_work); + return 0; +} + +EXPORT_SYMBOL(set_backlight_level); + +int __pmac get_backlight_level(void) +{ + if (!backlighter) + return -ENODEV; + return backlight_level; +} +EXPORT_SYMBOL(get_backlight_level); + +static void backlight_callback(void *dummy) +{ + int level, enable; + + do { + level = backlight_req_level; + enable = backlight_req_enable; + mb(); + + if (level >= 0) + __set_backlight_level(level); + if (enable >= 0) + __set_backlight_enable(enable); + } while(cmpxchg(&backlight_req_level, level, -1) != level || + cmpxchg(&backlight_req_enable, enable, -1) != enable); +} diff --git a/arch/ppc/platforms/pmac_cache.S b/arch/ppc/platforms/pmac_cache.S new file mode 100644 index 00000000000..c00e0352044 --- /dev/null +++ b/arch/ppc/platforms/pmac_cache.S @@ -0,0 +1,325 @@ +/* + * This file contains low-level cache management functions + * used for sleep and CPU speed changes on Apple machines. + * (In fact the only thing that is Apple-specific is that we assume + * that we can read from ROM at physical address 0xfff00000.) + * + * Copyright (C) 2004 Paul Mackerras (paulus@samba.org) and + * Benjamin Herrenschmidt (benh@kernel.crashing.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include + +/* + * Flush and disable all data caches (dL1, L2, L3). This is used + * when going to sleep, when doing a PMU based cpufreq transition, + * or when "offlining" a CPU on SMP machines. This code is over + * paranoid, but I've had enough issues with various CPU revs and + * bugs that I decided it was worth beeing over cautious + */ + +_GLOBAL(flush_disable_caches) +BEGIN_FTR_SECTION + b flush_disable_745x +END_FTR_SECTION_IFSET(CPU_FTR_SPEC7450) +BEGIN_FTR_SECTION + b flush_disable_75x +END_FTR_SECTION_IFSET(CPU_FTR_L2CR) + b __flush_disable_L1 + +/* This is the code for G3 and 74[01]0 */ +flush_disable_75x: + mflr r10 + + /* Turn off EE and DR in MSR */ + mfmsr r11 + rlwinm r0,r11,0,~MSR_EE + rlwinm r0,r0,0,~MSR_DR + sync + mtmsr r0 + isync + + /* Stop DST streams */ +BEGIN_FTR_SECTION + DSSALL + sync +END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) + + /* Stop DPM */ + mfspr r8,SPRN_HID0 /* Save SPRN_HID0 in r8 */ + rlwinm r4,r8,0,12,10 /* Turn off HID0[DPM] */ + sync + mtspr SPRN_HID0,r4 /* Disable DPM */ + sync + + /* disp-flush L1 */ + li r4,0x4000 + mtctr r4 + lis r4,0xfff0 +1: lwzx r0,r0,r4 + addi r4,r4,32 + bdnz 1b + sync + isync + + /* disable / invalidate / enable L1 data */ + mfspr r3,SPRN_HID0 + rlwinm r0,r0,0,~HID0_DCE + mtspr SPRN_HID0,r3 + sync + isync + ori r3,r3,HID0_DCE|HID0_DCI + sync + isync + mtspr SPRN_HID0,r3 + xori r3,r3,HID0_DCI + mtspr SPRN_HID0,r3 + sync + + /* Get the current enable bit of the L2CR into r4 */ + mfspr r5,SPRN_L2CR + /* Set to data-only (pre-745x bit) */ + oris r3,r5,L2CR_L2DO@h + b 2f + /* When disabling L2, code must be in L1 */ + .balign 32 +1: mtspr SPRN_L2CR,r3 +3: sync + isync + b 1f +2: b 3f +3: sync + isync + b 1b +1: /* disp-flush L2. The interesting thing here is that the L2 can be + * up to 2Mb ... so using the ROM, we'll end up wrapping back to memory + * but that is probbaly fine. We disp-flush over 4Mb to be safe + */ + lis r4,2 + mtctr r4 + lis r4,0xfff0 +1: lwzx r0,r0,r4 + addi r4,r4,32 + bdnz 1b + sync + isync + /* now disable L2 */ + rlwinm r5,r5,0,~L2CR_L2E + b 2f + /* When disabling L2, code must be in L1 */ + .balign 32 +1: mtspr SPRN_L2CR,r5 +3: sync + isync + b 1f +2: b 3f +3: sync + isync + b 1b +1: sync + isync + /* Invalidate L2. This is pre-745x, we clear the L2I bit ourselves */ + oris r4,r5,L2CR_L2I@h + mtspr SPRN_L2CR,r4 + sync + isync + xoris r4,r4,L2CR_L2I@h + sync + mtspr SPRN_L2CR,r4 + sync + + /* now disable the L1 data cache */ + mfspr r0,SPRN_HID0 + rlwinm r0,r0,0,~HID0_DCE + mtspr SPRN_HID0,r0 + sync + isync + + /* Restore HID0[DPM] to whatever it was before */ + sync + mtspr SPRN_HID0,r8 + sync + + /* restore DR and EE */ + sync + mtmsr r11 + isync + + mtlr r10 + blr + +/* This code is for 745x processors */ +flush_disable_745x: + /* Turn off EE and DR in MSR */ + mfmsr r11 + rlwinm r0,r11,0,~MSR_EE + rlwinm r0,r0,0,~MSR_DR + sync + mtmsr r0 + isync + + /* Stop prefetch streams */ + DSSALL + sync + + /* Disable L2 prefetching */ + mfspr r0,SPRN_MSSCR0 + rlwinm r0,r0,0,0,29 + mtspr SPRN_MSSCR0,r0 + sync + isync + lis r4,0 + dcbf 0,r4 + dcbf 0,r4 + dcbf 0,r4 + dcbf 0,r4 + dcbf 0,r4 + dcbf 0,r4 + dcbf 0,r4 + dcbf 0,r4 + + /* Due to a bug with the HW flush on some CPU revs, we occasionally + * experience data corruption. I'm adding a displacement flush along + * with a dcbf loop over a few Mb to "help". The problem isn't totally + * fixed by this in theory, but at least, in practice, I couldn't reproduce + * it even with a big hammer... + */ + + lis r4,0x0002 + mtctr r4 + li r4,0 +1: + lwzx r0,r0,r4 + addi r4,r4,32 /* Go to start of next cache line */ + bdnz 1b + isync + + /* Now, flush the first 4MB of memory */ + lis r4,0x0002 + mtctr r4 + li r4,0 + sync +1: + dcbf 0,r4 + addi r4,r4,32 /* Go to start of next cache line */ + bdnz 1b + + /* Flush and disable the L1 data cache */ + mfspr r6,SPRN_LDSTCR + lis r3,0xfff0 /* read from ROM for displacement flush */ + li r4,0xfe /* start with only way 0 unlocked */ + li r5,128 /* 128 lines in each way */ +1: mtctr r5 + rlwimi r6,r4,0,24,31 + mtspr SPRN_LDSTCR,r6 + sync + isync +2: lwz r0,0(r3) /* touch each cache line */ + addi r3,r3,32 + bdnz 2b + rlwinm r4,r4,1,24,30 /* move on to the next way */ + ori r4,r4,1 + cmpwi r4,0xff /* all done? */ + bne 1b + /* now unlock the L1 data cache */ + li r4,0 + rlwimi r6,r4,0,24,31 + sync + mtspr SPRN_LDSTCR,r6 + sync + isync + + /* Flush the L2 cache using the hardware assist */ + mfspr r3,SPRN_L2CR + cmpwi r3,0 /* check if it is enabled first */ + bge 4f + oris r0,r3,(L2CR_L2IO_745x|L2CR_L2DO_745x)@h + b 2f + /* When disabling/locking L2, code must be in L1 */ + .balign 32 +1: mtspr SPRN_L2CR,r0 /* lock the L2 cache */ +3: sync + isync + b 1f +2: b 3f +3: sync + isync + b 1b +1: sync + isync + ori r0,r3,L2CR_L2HWF_745x + sync + mtspr SPRN_L2CR,r0 /* set the hardware flush bit */ +3: mfspr r0,SPRN_L2CR /* wait for it to go to 0 */ + andi. r0,r0,L2CR_L2HWF_745x + bne 3b + sync + rlwinm r3,r3,0,~L2CR_L2E + b 2f + /* When disabling L2, code must be in L1 */ + .balign 32 +1: mtspr SPRN_L2CR,r3 /* disable the L2 cache */ +3: sync + isync + b 1f +2: b 3f +3: sync + isync + b 1b +1: sync + isync + oris r4,r3,L2CR_L2I@h + mtspr SPRN_L2CR,r4 + sync + isync +1: mfspr r4,SPRN_L2CR + andis. r0,r4,L2CR_L2I@h + bne 1b + sync + +BEGIN_FTR_SECTION + /* Flush the L3 cache using the hardware assist */ +4: mfspr r3,SPRN_L3CR + cmpwi r3,0 /* check if it is enabled */ + bge 6f + oris r0,r3,L3CR_L3IO@h + ori r0,r0,L3CR_L3DO + sync + mtspr SPRN_L3CR,r0 /* lock the L3 cache */ + sync + isync + ori r0,r0,L3CR_L3HWF + sync + mtspr SPRN_L3CR,r0 /* set the hardware flush bit */ +5: mfspr r0,SPRN_L3CR /* wait for it to go to zero */ + andi. r0,r0,L3CR_L3HWF + bne 5b + rlwinm r3,r3,0,~L3CR_L3E + sync + mtspr SPRN_L3CR,r3 /* disable the L3 cache */ + sync + ori r4,r3,L3CR_L3I + mtspr SPRN_L3CR,r4 +1: mfspr r4,SPRN_L3CR + andi. r0,r4,L3CR_L3I + bne 1b + sync +END_FTR_SECTION_IFSET(CPU_FTR_L3CR) + +6: mfspr r0,SPRN_HID0 /* now disable the L1 data cache */ + rlwinm r0,r0,0,~HID0_DCE + mtspr SPRN_HID0,r0 + sync + isync + mtmsr r11 /* restore DR and EE */ + isync + blr diff --git a/arch/ppc/platforms/pmac_cpufreq.c b/arch/ppc/platforms/pmac_cpufreq.c new file mode 100644 index 00000000000..9c85f9ca1cf --- /dev/null +++ b/arch/ppc/platforms/pmac_cpufreq.c @@ -0,0 +1,571 @@ +/* + * arch/ppc/platforms/pmac_cpufreq.c + * + * Copyright (C) 2002 - 2004 Benjamin Herrenschmidt + * Copyright (C) 2004 John Steele Scott + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* WARNING !!! This will cause calibrate_delay() to be called, + * but this is an __init function ! So you MUST go edit + * init/main.c to make it non-init before enabling DEBUG_FREQ + */ +#undef DEBUG_FREQ + +/* + * There is a problem with the core cpufreq code on SMP kernels, + * it won't recalculate the Bogomips properly + */ +#ifdef CONFIG_SMP +#warning "WARNING, CPUFREQ not recommended on SMP kernels" +#endif + +extern void low_choose_7447a_dfs(int dfs); +extern void low_choose_750fx_pll(int pll); +extern void low_sleep_handler(void); + +/* + * Currently, PowerMac cpufreq supports only high & low frequencies + * that are set by the firmware + */ +static unsigned int low_freq; +static unsigned int hi_freq; +static unsigned int cur_freq; + +/* + * Different models uses different mecanisms to switch the frequency + */ +static int (*set_speed_proc)(int low_speed); + +/* + * Some definitions used by the various speedprocs + */ +static u32 voltage_gpio; +static u32 frequency_gpio; +static u32 slew_done_gpio; + + +#define PMAC_CPU_LOW_SPEED 1 +#define PMAC_CPU_HIGH_SPEED 0 + +/* There are only two frequency states for each processor. Values + * are in kHz for the time being. + */ +#define CPUFREQ_HIGH PMAC_CPU_HIGH_SPEED +#define CPUFREQ_LOW PMAC_CPU_LOW_SPEED + +static struct cpufreq_frequency_table pmac_cpu_freqs[] = { + {CPUFREQ_HIGH, 0}, + {CPUFREQ_LOW, 0}, + {0, CPUFREQ_TABLE_END}, +}; + +static inline void wakeup_decrementer(void) +{ + set_dec(tb_ticks_per_jiffy); + /* No currently-supported powerbook has a 601, + * so use get_tbl, not native + */ + last_jiffy_stamp(0) = tb_last_stamp = get_tbl(); +} + +#ifdef DEBUG_FREQ +static inline void debug_calc_bogomips(void) +{ + /* This will cause a recalc of bogomips and display the + * result. We backup/restore the value to avoid affecting the + * core cpufreq framework's own calculation. + */ + extern void calibrate_delay(void); + + unsigned long save_lpj = loops_per_jiffy; + calibrate_delay(); + loops_per_jiffy = save_lpj; +} +#endif /* DEBUG_FREQ */ + +/* Switch CPU speed under 750FX CPU control + */ +static int __pmac cpu_750fx_cpu_speed(int low_speed) +{ +#ifdef DEBUG_FREQ + printk(KERN_DEBUG "HID1, before: %x\n", mfspr(SPRN_HID1)); +#endif +#ifdef CONFIG_6xx + low_choose_750fx_pll(low_speed); +#endif +#ifdef DEBUG_FREQ + printk(KERN_DEBUG "HID1, after: %x\n", mfspr(SPRN_HID1)); + debug_calc_bogomips(); +#endif + + return 0; +} + +/* Switch CPU speed using DFS */ +static int __pmac dfs_set_cpu_speed(int low_speed) +{ + if (low_speed == 0) { + /* ramping up, set voltage first */ + pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x05); + /* Make sure we sleep for at least 1ms */ + msleep(1); + } + + /* set frequency */ + low_choose_7447a_dfs(low_speed); + + if (low_speed == 1) { + /* ramping down, set voltage last */ + pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x04); + msleep(1); + } + + return 0; +} + +static unsigned int __pmac dfs_get_cpu_speed(unsigned int cpu) +{ + if (mfspr(SPRN_HID1) & HID1_DFS) + return low_freq; + else + return hi_freq; +} + + +/* Switch CPU speed using slewing GPIOs + */ +static int __pmac gpios_set_cpu_speed(int low_speed) +{ + int gpio; + + /* If ramping up, set voltage first */ + if (low_speed == 0) { + pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x05); + /* Delay is way too big but it's ok, we schedule */ + msleep(10); + } + + /* Set frequency */ + pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, frequency_gpio, + low_speed ? 0x04 : 0x05); + udelay(200); + do { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + gpio = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, slew_done_gpio, 0); + } while((gpio & 0x02) == 0); + + /* If ramping down, set voltage last */ + if (low_speed == 1) { + pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x04); + /* Delay is way too big but it's ok, we schedule */ + msleep(10); + } + +#ifdef DEBUG_FREQ + debug_calc_bogomips(); +#endif + + return 0; +} + +/* Switch CPU speed under PMU control + */ +static int __pmac pmu_set_cpu_speed(int low_speed) +{ + struct adb_request req; + unsigned long save_l2cr; + unsigned long save_l3cr; + + preempt_disable(); + +#ifdef DEBUG_FREQ + printk(KERN_DEBUG "HID1, before: %x\n", mfspr(SPRN_HID1)); +#endif + /* Disable all interrupt sources on openpic */ + openpic_set_priority(0xf); + + /* Make sure the decrementer won't interrupt us */ + asm volatile("mtdec %0" : : "r" (0x7fffffff)); + /* Make sure any pending DEC interrupt occuring while we did + * the above didn't re-enable the DEC */ + mb(); + asm volatile("mtdec %0" : : "r" (0x7fffffff)); + + /* We can now disable MSR_EE */ + local_irq_disable(); + + /* Giveup the FPU & vec */ + enable_kernel_fp(); + +#ifdef CONFIG_ALTIVEC + if (cpu_has_feature(CPU_FTR_ALTIVEC)) + enable_kernel_altivec(); +#endif /* CONFIG_ALTIVEC */ + + /* Save & disable L2 and L3 caches */ + save_l3cr = _get_L3CR(); /* (returns -1 if not available) */ + save_l2cr = _get_L2CR(); /* (returns -1 if not available) */ + + /* Send the new speed command. My assumption is that this command + * will cause PLL_CFG[0..3] to be changed next time CPU goes to sleep + */ + pmu_request(&req, NULL, 6, PMU_CPU_SPEED, 'W', 'O', 'O', 'F', low_speed); + while (!req.complete) + pmu_poll(); + + /* Prepare the northbridge for the speed transition */ + pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,1,1); + + /* Call low level code to backup CPU state and recover from + * hardware reset + */ + low_sleep_handler(); + + /* Restore the northbridge */ + pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,1,0); + + /* Restore L2 cache */ + if (save_l2cr != 0xffffffff && (save_l2cr & L2CR_L2E) != 0) + _set_L2CR(save_l2cr); + /* Restore L3 cache */ + if (save_l3cr != 0xffffffff && (save_l3cr & L3CR_L3E) != 0) + _set_L3CR(save_l3cr); + + /* Restore userland MMU context */ + set_context(current->active_mm->context, current->active_mm->pgd); + +#ifdef DEBUG_FREQ + printk(KERN_DEBUG "HID1, after: %x\n", mfspr(SPRN_HID1)); +#endif + + /* Restore low level PMU operations */ + pmu_unlock(); + + /* Restore decrementer */ + wakeup_decrementer(); + + /* Restore interrupts */ + openpic_set_priority(0); + + /* Let interrupts flow again ... */ + local_irq_enable(); + +#ifdef DEBUG_FREQ + debug_calc_bogomips(); +#endif + + preempt_enable(); + + return 0; +} + +static int __pmac do_set_cpu_speed(int speed_mode) +{ + struct cpufreq_freqs freqs; + + freqs.old = cur_freq; + freqs.new = (speed_mode == PMAC_CPU_HIGH_SPEED) ? hi_freq : low_freq; + freqs.cpu = smp_processor_id(); + + if (freqs.old == freqs.new) + return 0; + + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + set_speed_proc(speed_mode == PMAC_CPU_LOW_SPEED); + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + cur_freq = (speed_mode == PMAC_CPU_HIGH_SPEED) ? hi_freq : low_freq; + + return 0; +} + +static int __pmac pmac_cpufreq_verify(struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, pmac_cpu_freqs); +} + +static int __pmac pmac_cpufreq_target( struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + unsigned int newstate = 0; + + if (cpufreq_frequency_table_target(policy, pmac_cpu_freqs, + target_freq, relation, &newstate)) + return -EINVAL; + + return do_set_cpu_speed(newstate); +} + +unsigned int __pmac pmac_get_one_cpufreq(int i) +{ + /* Supports only one CPU for now */ + return (i == 0) ? cur_freq : 0; +} + +static int __pmac pmac_cpufreq_cpu_init(struct cpufreq_policy *policy) +{ + if (policy->cpu != 0) + return -ENODEV; + + policy->governor = CPUFREQ_DEFAULT_GOVERNOR; + policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; + policy->cur = cur_freq; + + return cpufreq_frequency_table_cpuinfo(policy, &pmac_cpu_freqs[0]); +} + +static u32 __pmac read_gpio(struct device_node *np) +{ + u32 *reg = (u32 *)get_property(np, "reg", NULL); + + if (reg == NULL) + return 0; + /* That works for all keylargos but shall be fixed properly + * some day... + */ + return 0x50 + (*reg); +} + +static struct cpufreq_driver pmac_cpufreq_driver = { + .verify = pmac_cpufreq_verify, + .target = pmac_cpufreq_target, + .init = pmac_cpufreq_cpu_init, + .name = "powermac", + .owner = THIS_MODULE, +}; + + +static int __pmac pmac_cpufreq_init_MacRISC3(struct device_node *cpunode) +{ + struct device_node *volt_gpio_np = of_find_node_by_name(NULL, + "voltage-gpio"); + struct device_node *freq_gpio_np = of_find_node_by_name(NULL, + "frequency-gpio"); + struct device_node *slew_done_gpio_np = of_find_node_by_name(NULL, + "slewing-done"); + u32 *value; + + /* + * Check to see if it's GPIO driven or PMU only + * + * The way we extract the GPIO address is slightly hackish, but it + * works well enough for now. We need to abstract the whole GPIO + * stuff sooner or later anyway + */ + + if (volt_gpio_np) + voltage_gpio = read_gpio(volt_gpio_np); + if (freq_gpio_np) + frequency_gpio = read_gpio(freq_gpio_np); + if (slew_done_gpio_np) + slew_done_gpio = read_gpio(slew_done_gpio_np); + + /* If we use the frequency GPIOs, calculate the min/max speeds based + * on the bus frequencies + */ + if (frequency_gpio && slew_done_gpio) { + int lenp, rc; + u32 *freqs, *ratio; + + freqs = (u32 *)get_property(cpunode, "bus-frequencies", &lenp); + lenp /= sizeof(u32); + if (freqs == NULL || lenp != 2) { + printk(KERN_ERR "cpufreq: bus-frequencies incorrect or missing\n"); + return 1; + } + ratio = (u32 *)get_property(cpunode, "processor-to-bus-ratio*2", NULL); + if (ratio == NULL) { + printk(KERN_ERR "cpufreq: processor-to-bus-ratio*2 missing\n"); + return 1; + } + + /* Get the min/max bus frequencies */ + low_freq = min(freqs[0], freqs[1]); + hi_freq = max(freqs[0], freqs[1]); + + /* Grrrr.. It _seems_ that the device-tree is lying on the low bus + * frequency, it claims it to be around 84Mhz on some models while + * it appears to be approx. 101Mhz on all. Let's hack around here... + * fortunately, we don't need to be too precise + */ + if (low_freq < 98000000) + low_freq = 101000000; + + /* Convert those to CPU core clocks */ + low_freq = (low_freq * (*ratio)) / 2000; + hi_freq = (hi_freq * (*ratio)) / 2000; + + /* Now we get the frequencies, we read the GPIO to see what is out current + * speed + */ + rc = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, frequency_gpio, 0); + cur_freq = (rc & 0x01) ? hi_freq : low_freq; + + set_speed_proc = gpios_set_cpu_speed; + return 1; + } + + /* If we use the PMU, look for the min & max frequencies in the + * device-tree + */ + value = (u32 *)get_property(cpunode, "min-clock-frequency", NULL); + if (!value) + return 1; + low_freq = (*value) / 1000; + /* The PowerBook G4 12" (PowerBook6,1) has an error in the device-tree + * here */ + if (low_freq < 100000) + low_freq *= 10; + + value = (u32 *)get_property(cpunode, "max-clock-frequency", NULL); + if (!value) + return 1; + hi_freq = (*value) / 1000; + set_speed_proc = pmu_set_cpu_speed; + + return 0; +} + +static int __pmac pmac_cpufreq_init_7447A(struct device_node *cpunode) +{ + struct device_node *volt_gpio_np; + u32 *reg; + struct cpufreq_driver *driver = &pmac_cpufreq_driver; + + /* Look for voltage GPIO */ + volt_gpio_np = of_find_node_by_name(NULL, "cpu-vcore-select"); + reg = (u32 *)get_property(volt_gpio_np, "reg", NULL); + voltage_gpio = *reg; + if (!volt_gpio_np){ + printk(KERN_ERR "cpufreq: missing cpu-vcore-select gpio\n"); + return 1; + } + + /* OF only reports the high frequency */ + hi_freq = cur_freq; + low_freq = cur_freq/2; + + /* Read actual frequency from CPU */ + driver->get = dfs_get_cpu_speed; + cur_freq = driver->get(0); + set_speed_proc = dfs_set_cpu_speed; + + return 0; +} + +/* Currently, we support the following machines: + * + * - Titanium PowerBook 1Ghz (PMU based, 667Mhz & 1Ghz) + * - Titanium PowerBook 800 (PMU based, 667Mhz & 800Mhz) + * - Titanium PowerBook 400 (PMU based, 300Mhz & 400Mhz) + * - Titanium PowerBook 500 (PMU based, 300Mhz & 500Mhz) + * - iBook2 500/600 (PMU based, 400Mhz & 500/600Mhz) + * - iBook2 700 (CPU based, 400Mhz & 700Mhz, support low voltage) + * - Recent MacRISC3 laptops + * - All new machines with 7447A CPUs + */ +static int __init pmac_cpufreq_setup(void) +{ + struct device_node *cpunode; + u32 *value; + + if (strstr(cmd_line, "nocpufreq")) + return 0; + + /* Assume only one CPU */ + cpunode = find_type_devices("cpu"); + if (!cpunode) + goto out; + + /* Get current cpu clock freq */ + value = (u32 *)get_property(cpunode, "clock-frequency", NULL); + if (!value) + goto out; + cur_freq = (*value) / 1000; + + /* Check for 7447A based MacRISC3 */ + if (machine_is_compatible("MacRISC3") && + get_property(cpunode, "dynamic-power-step", NULL) && + PVR_VER(mfspr(SPRN_PVR)) == 0x8003) { + pmac_cpufreq_init_7447A(cpunode); + /* Check for other MacRISC3 machines */ + } else if (machine_is_compatible("PowerBook3,4") || + machine_is_compatible("PowerBook3,5") || + machine_is_compatible("MacRISC3")) { + pmac_cpufreq_init_MacRISC3(cpunode); + /* Else check for iBook2 500/600 */ + } else if (machine_is_compatible("PowerBook4,1")) { + hi_freq = cur_freq; + low_freq = 400000; + set_speed_proc = pmu_set_cpu_speed; + } + /* Else check for TiPb 400 & 500 */ + else if (machine_is_compatible("PowerBook3,2")) { + /* We only know about the 400 MHz and the 500Mhz model + * they both have 300 MHz as low frequency + */ + if (cur_freq < 350000 || cur_freq > 550000) + goto out; + hi_freq = cur_freq; + low_freq = 300000; + set_speed_proc = pmu_set_cpu_speed; + } + /* Else check for 750FX */ + else if (PVR_VER(mfspr(SPRN_PVR)) == 0x7000) { + if (get_property(cpunode, "dynamic-power-step", NULL) == NULL) + goto out; + hi_freq = cur_freq; + value = (u32 *)get_property(cpunode, "reduced-clock-frequency", NULL); + if (!value) + goto out; + low_freq = (*value) / 1000; + set_speed_proc = cpu_750fx_cpu_speed; + } +out: + if (set_speed_proc == NULL) + return -ENODEV; + + pmac_cpu_freqs[CPUFREQ_LOW].frequency = low_freq; + pmac_cpu_freqs[CPUFREQ_HIGH].frequency = hi_freq; + + printk(KERN_INFO "Registering PowerMac CPU frequency driver\n"); + printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Boot: %d Mhz\n", + low_freq/1000, hi_freq/1000, cur_freq/1000); + + return cpufreq_register_driver(&pmac_cpufreq_driver); +} + +module_init(pmac_cpufreq_setup); + diff --git a/arch/ppc/platforms/pmac_feature.c b/arch/ppc/platforms/pmac_feature.c new file mode 100644 index 00000000000..8e60550863a --- /dev/null +++ b/arch/ppc/platforms/pmac_feature.c @@ -0,0 +1,2972 @@ +/* + * arch/ppc/platforms/pmac_feature.c + * + * Copyright (C) 1996-2001 Paul Mackerras (paulus@cs.anu.edu.au) + * Ben. Herrenschmidt (benh@kernel.crashing.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * TODO: + * + * - Replace mdelay with some schedule loop if possible + * - Shorten some obfuscated delays on some routines (like modem + * power) + * - Refcount some clocks (see darwin) + * - Split split split... + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG_FEATURE + +#ifdef DEBUG_FEATURE +#define DBG(fmt,...) printk(KERN_DEBUG fmt) +#else +#define DBG(fmt,...) +#endif + +#ifdef CONFIG_6xx +extern int powersave_lowspeed; +#endif + +extern int powersave_nap; +extern struct device_node *k2_skiplist[2]; + + +/* + * We use a single global lock to protect accesses. Each driver has + * to take care of its own locking + */ +static DEFINE_SPINLOCK(feature_lock __pmacdata); + +#define LOCK(flags) spin_lock_irqsave(&feature_lock, flags); +#define UNLOCK(flags) spin_unlock_irqrestore(&feature_lock, flags); + + +/* + * Instance of some macio stuffs + */ +struct macio_chip macio_chips[MAX_MACIO_CHIPS] __pmacdata; + +struct macio_chip* __pmac +macio_find(struct device_node* child, int type) +{ + while(child) { + int i; + + for (i=0; i < MAX_MACIO_CHIPS && macio_chips[i].of_node; i++) + if (child == macio_chips[i].of_node && + (!type || macio_chips[i].type == type)) + return &macio_chips[i]; + child = child->parent; + } + return NULL; +} + +static const char* macio_names[] __pmacdata = +{ + "Unknown", + "Grand Central", + "OHare", + "OHareII", + "Heathrow", + "Gatwick", + "Paddington", + "Keylargo", + "Pangea", + "Intrepid", + "K2" +}; + + + +/* + * Uninorth reg. access. Note that Uni-N regs are big endian + */ + +#define UN_REG(r) (uninorth_base + ((r) >> 2)) +#define UN_IN(r) (in_be32(UN_REG(r))) +#define UN_OUT(r,v) (out_be32(UN_REG(r), (v))) +#define UN_BIS(r,v) (UN_OUT((r), UN_IN(r) | (v))) +#define UN_BIC(r,v) (UN_OUT((r), UN_IN(r) & ~(v))) + +static struct device_node* uninorth_node __pmacdata; +static u32 __iomem * uninorth_base __pmacdata; +static u32 uninorth_rev __pmacdata; +static int uninorth_u3 __pmacdata; +static void __iomem *u3_ht; + +/* + * For each motherboard family, we have a table of functions pointers + * that handle the various features. + */ + +typedef long (*feature_call)(struct device_node* node, long param, long value); + +struct feature_table_entry { + unsigned int selector; + feature_call function; +}; + +struct pmac_mb_def +{ + const char* model_string; + const char* model_name; + int model_id; + struct feature_table_entry* features; + unsigned long board_flags; +}; +static struct pmac_mb_def pmac_mb __pmacdata; + +/* + * Here are the chip specific feature functions + */ + +static inline int __pmac +simple_feature_tweak(struct device_node* node, int type, int reg, u32 mask, int value) +{ + struct macio_chip* macio; + unsigned long flags; + + macio = macio_find(node, type); + if (!macio) + return -ENODEV; + LOCK(flags); + if (value) + MACIO_BIS(reg, mask); + else + MACIO_BIC(reg, mask); + (void)MACIO_IN32(reg); + UNLOCK(flags); + + return 0; +} + +#ifndef CONFIG_POWER4 + +static long __pmac +ohare_htw_scc_enable(struct device_node* node, long param, long value) +{ + struct macio_chip* macio; + unsigned long chan_mask; + unsigned long fcr; + unsigned long flags; + int htw, trans; + unsigned long rmask; + + macio = macio_find(node, 0); + if (!macio) + return -ENODEV; + if (!strcmp(node->name, "ch-a")) + chan_mask = MACIO_FLAG_SCCA_ON; + else if (!strcmp(node->name, "ch-b")) + chan_mask = MACIO_FLAG_SCCB_ON; + else + return -ENODEV; + + htw = (macio->type == macio_heathrow || macio->type == macio_paddington + || macio->type == macio_gatwick); + /* On these machines, the HRW_SCC_TRANS_EN_N bit mustn't be touched */ + trans = (pmac_mb.model_id != PMAC_TYPE_YOSEMITE && + pmac_mb.model_id != PMAC_TYPE_YIKES); + if (value) { +#ifdef CONFIG_ADB_PMU + if ((param & 0xfff) == PMAC_SCC_IRDA) + pmu_enable_irled(1); +#endif /* CONFIG_ADB_PMU */ + LOCK(flags); + fcr = MACIO_IN32(OHARE_FCR); + /* Check if scc cell need enabling */ + if (!(fcr & OH_SCC_ENABLE)) { + fcr |= OH_SCC_ENABLE; + if (htw) { + /* Side effect: this will also power up the + * modem, but it's too messy to figure out on which + * ports this controls the tranceiver and on which + * it controls the modem + */ + if (trans) + fcr &= ~HRW_SCC_TRANS_EN_N; + MACIO_OUT32(OHARE_FCR, fcr); + fcr |= (rmask = HRW_RESET_SCC); + MACIO_OUT32(OHARE_FCR, fcr); + } else { + fcr |= (rmask = OH_SCC_RESET); + MACIO_OUT32(OHARE_FCR, fcr); + } + UNLOCK(flags); + (void)MACIO_IN32(OHARE_FCR); + mdelay(15); + LOCK(flags); + fcr &= ~rmask; + MACIO_OUT32(OHARE_FCR, fcr); + } + if (chan_mask & MACIO_FLAG_SCCA_ON) + fcr |= OH_SCCA_IO; + if (chan_mask & MACIO_FLAG_SCCB_ON) + fcr |= OH_SCCB_IO; + MACIO_OUT32(OHARE_FCR, fcr); + macio->flags |= chan_mask; + UNLOCK(flags); + if (param & PMAC_SCC_FLAG_XMON) + macio->flags |= MACIO_FLAG_SCC_LOCKED; + } else { + if (macio->flags & MACIO_FLAG_SCC_LOCKED) + return -EPERM; + LOCK(flags); + fcr = MACIO_IN32(OHARE_FCR); + if (chan_mask & MACIO_FLAG_SCCA_ON) + fcr &= ~OH_SCCA_IO; + if (chan_mask & MACIO_FLAG_SCCB_ON) + fcr &= ~OH_SCCB_IO; + MACIO_OUT32(OHARE_FCR, fcr); + if ((fcr & (OH_SCCA_IO | OH_SCCB_IO)) == 0) { + fcr &= ~OH_SCC_ENABLE; + if (htw && trans) + fcr |= HRW_SCC_TRANS_EN_N; + MACIO_OUT32(OHARE_FCR, fcr); + } + macio->flags &= ~(chan_mask); + UNLOCK(flags); + mdelay(10); +#ifdef CONFIG_ADB_PMU + if ((param & 0xfff) == PMAC_SCC_IRDA) + pmu_enable_irled(0); +#endif /* CONFIG_ADB_PMU */ + } + return 0; +} + +static long __pmac +ohare_floppy_enable(struct device_node* node, long param, long value) +{ + return simple_feature_tweak(node, macio_ohare, + OHARE_FCR, OH_FLOPPY_ENABLE, value); +} + +static long __pmac +ohare_mesh_enable(struct device_node* node, long param, long value) +{ + return simple_feature_tweak(node, macio_ohare, + OHARE_FCR, OH_MESH_ENABLE, value); +} + +static long __pmac +ohare_ide_enable(struct device_node* node, long param, long value) +{ + switch(param) { + case 0: + /* For some reason, setting the bit in set_initial_features() + * doesn't stick. I'm still investigating... --BenH. + */ + if (value) + simple_feature_tweak(node, macio_ohare, + OHARE_FCR, OH_IOBUS_ENABLE, 1); + return simple_feature_tweak(node, macio_ohare, + OHARE_FCR, OH_IDE0_ENABLE, value); + case 1: + return simple_feature_tweak(node, macio_ohare, + OHARE_FCR, OH_BAY_IDE_ENABLE, value); + default: + return -ENODEV; + } +} + +static long __pmac +ohare_ide_reset(struct device_node* node, long param, long value) +{ + switch(param) { + case 0: + return simple_feature_tweak(node, macio_ohare, + OHARE_FCR, OH_IDE0_RESET_N, !value); + case 1: + return simple_feature_tweak(node, macio_ohare, + OHARE_FCR, OH_IDE1_RESET_N, !value); + default: + return -ENODEV; + } +} + +static long __pmac +ohare_sleep_state(struct device_node* node, long param, long value) +{ + struct macio_chip* macio = &macio_chips[0]; + + if ((pmac_mb.board_flags & PMAC_MB_CAN_SLEEP) == 0) + return -EPERM; + if (value == 1) { + MACIO_BIC(OHARE_FCR, OH_IOBUS_ENABLE); + } else if (value == 0) { + MACIO_BIS(OHARE_FCR, OH_IOBUS_ENABLE); + } + + return 0; +} + +static long __pmac +heathrow_modem_enable(struct device_node* node, long param, long value) +{ + struct macio_chip* macio; + u8 gpio; + unsigned long flags; + + macio = macio_find(node, macio_unknown); + if (!macio) + return -ENODEV; + gpio = MACIO_IN8(HRW_GPIO_MODEM_RESET) & ~1; + if (!value) { + LOCK(flags); + MACIO_OUT8(HRW_GPIO_MODEM_RESET, gpio); + UNLOCK(flags); + (void)MACIO_IN8(HRW_GPIO_MODEM_RESET); + mdelay(250); + } + if (pmac_mb.model_id != PMAC_TYPE_YOSEMITE && + pmac_mb.model_id != PMAC_TYPE_YIKES) { + LOCK(flags); + if (value) + MACIO_BIC(HEATHROW_FCR, HRW_SCC_TRANS_EN_N); + else + MACIO_BIS(HEATHROW_FCR, HRW_SCC_TRANS_EN_N); + UNLOCK(flags); + (void)MACIO_IN32(HEATHROW_FCR); + mdelay(250); + } + if (value) { + LOCK(flags); + MACIO_OUT8(HRW_GPIO_MODEM_RESET, gpio | 1); + (void)MACIO_IN8(HRW_GPIO_MODEM_RESET); + UNLOCK(flags); mdelay(250); LOCK(flags); + MACIO_OUT8(HRW_GPIO_MODEM_RESET, gpio); + (void)MACIO_IN8(HRW_GPIO_MODEM_RESET); + UNLOCK(flags); mdelay(250); LOCK(flags); + MACIO_OUT8(HRW_GPIO_MODEM_RESET, gpio | 1); + (void)MACIO_IN8(HRW_GPIO_MODEM_RESET); + UNLOCK(flags); mdelay(250); + } + return 0; +} + +static long __pmac +heathrow_floppy_enable(struct device_node* node, long param, long value) +{ + return simple_feature_tweak(node, macio_unknown, + HEATHROW_FCR, + HRW_SWIM_ENABLE|HRW_BAY_FLOPPY_ENABLE, + value); +} + +static long __pmac +heathrow_mesh_enable(struct device_node* node, long param, long value) +{ + struct macio_chip* macio; + unsigned long flags; + + macio = macio_find(node, macio_unknown); + if (!macio) + return -ENODEV; + LOCK(flags); + /* Set clear mesh cell enable */ + if (value) + MACIO_BIS(HEATHROW_FCR, HRW_MESH_ENABLE); + else + MACIO_BIC(HEATHROW_FCR, HRW_MESH_ENABLE); + (void)MACIO_IN32(HEATHROW_FCR); + udelay(10); + /* Set/Clear termination power */ + if (value) + MACIO_BIC(HEATHROW_MBCR, 0x04000000); + else + MACIO_BIS(HEATHROW_MBCR, 0x04000000); + (void)MACIO_IN32(HEATHROW_MBCR); + udelay(10); + UNLOCK(flags); + + return 0; +} + +static long __pmac +heathrow_ide_enable(struct device_node* node, long param, long value) +{ + switch(param) { + case 0: + return simple_feature_tweak(node, macio_unknown, + HEATHROW_FCR, HRW_IDE0_ENABLE, value); + case 1: + return simple_feature_tweak(node, macio_unknown, + HEATHROW_FCR, HRW_BAY_IDE_ENABLE, value); + default: + return -ENODEV; + } +} + +static long __pmac +heathrow_ide_reset(struct device_node* node, long param, long value) +{ + switch(param) { + case 0: + return simple_feature_tweak(node, macio_unknown, + HEATHROW_FCR, HRW_IDE0_RESET_N, !value); + case 1: + return simple_feature_tweak(node, macio_unknown, + HEATHROW_FCR, HRW_IDE1_RESET_N, !value); + default: + return -ENODEV; + } +} + +static long __pmac +heathrow_bmac_enable(struct device_node* node, long param, long value) +{ + struct macio_chip* macio; + unsigned long flags; + + macio = macio_find(node, 0); + if (!macio) + return -ENODEV; + if (value) { + LOCK(flags); + MACIO_BIS(HEATHROW_FCR, HRW_BMAC_IO_ENABLE); + MACIO_BIS(HEATHROW_FCR, HRW_BMAC_RESET); + UNLOCK(flags); + (void)MACIO_IN32(HEATHROW_FCR); + mdelay(10); + LOCK(flags); + MACIO_BIC(HEATHROW_FCR, HRW_BMAC_RESET); + UNLOCK(flags); + (void)MACIO_IN32(HEATHROW_FCR); + mdelay(10); + } else { + LOCK(flags); + MACIO_BIC(HEATHROW_FCR, HRW_BMAC_IO_ENABLE); + UNLOCK(flags); + } + return 0; +} + +static long __pmac +heathrow_sound_enable(struct device_node* node, long param, long value) +{ + struct macio_chip* macio; + unsigned long flags; + + /* B&W G3 and Yikes don't support that properly (the + * sound appear to never come back after beeing shut down). + */ + if (pmac_mb.model_id == PMAC_TYPE_YOSEMITE || + pmac_mb.model_id == PMAC_TYPE_YIKES) + return 0; + + macio = macio_find(node, 0); + if (!macio) + return -ENODEV; + if (value) { + LOCK(flags); + MACIO_BIS(HEATHROW_FCR, HRW_SOUND_CLK_ENABLE); + MACIO_BIC(HEATHROW_FCR, HRW_SOUND_POWER_N); + UNLOCK(flags); + (void)MACIO_IN32(HEATHROW_FCR); + } else { + LOCK(flags); + MACIO_BIS(HEATHROW_FCR, HRW_SOUND_POWER_N); + MACIO_BIC(HEATHROW_FCR, HRW_SOUND_CLK_ENABLE); + UNLOCK(flags); + } + return 0; +} + +static u32 save_fcr[6] __pmacdata; +static u32 save_mbcr __pmacdata; +static u32 save_gpio_levels[2] __pmacdata; +static u8 save_gpio_extint[KEYLARGO_GPIO_EXTINT_CNT] __pmacdata; +static u8 save_gpio_normal[KEYLARGO_GPIO_CNT] __pmacdata; +static u32 save_unin_clock_ctl __pmacdata; +static struct dbdma_regs save_dbdma[13] __pmacdata; +static struct dbdma_regs save_alt_dbdma[13] __pmacdata; + +static void __pmac +dbdma_save(struct macio_chip* macio, struct dbdma_regs* save) +{ + int i; + + /* Save state & config of DBDMA channels */ + for (i=0; i<13; i++) { + volatile struct dbdma_regs __iomem * chan = (void __iomem *) + (macio->base + ((0x8000+i*0x100)>>2)); + save[i].cmdptr_hi = in_le32(&chan->cmdptr_hi); + save[i].cmdptr = in_le32(&chan->cmdptr); + save[i].intr_sel = in_le32(&chan->intr_sel); + save[i].br_sel = in_le32(&chan->br_sel); + save[i].wait_sel = in_le32(&chan->wait_sel); + } +} + +static void __pmac +dbdma_restore(struct macio_chip* macio, struct dbdma_regs* save) +{ + int i; + + /* Save state & config of DBDMA channels */ + for (i=0; i<13; i++) { + volatile struct dbdma_regs __iomem * chan = (void __iomem *) + (macio->base + ((0x8000+i*0x100)>>2)); + out_le32(&chan->control, (ACTIVE|DEAD|WAKE|FLUSH|PAUSE|RUN)<<16); + while (in_le32(&chan->status) & ACTIVE) + mb(); + out_le32(&chan->cmdptr_hi, save[i].cmdptr_hi); + out_le32(&chan->cmdptr, save[i].cmdptr); + out_le32(&chan->intr_sel, save[i].intr_sel); + out_le32(&chan->br_sel, save[i].br_sel); + out_le32(&chan->wait_sel, save[i].wait_sel); + } +} + +static void __pmac +heathrow_sleep(struct macio_chip* macio, int secondary) +{ + if (secondary) { + dbdma_save(macio, save_alt_dbdma); + save_fcr[2] = MACIO_IN32(0x38); + save_fcr[3] = MACIO_IN32(0x3c); + } else { + dbdma_save(macio, save_dbdma); + save_fcr[0] = MACIO_IN32(0x38); + save_fcr[1] = MACIO_IN32(0x3c); + save_mbcr = MACIO_IN32(0x34); + /* Make sure sound is shut down */ + MACIO_BIS(HEATHROW_FCR, HRW_SOUND_POWER_N); + MACIO_BIC(HEATHROW_FCR, HRW_SOUND_CLK_ENABLE); + /* This seems to be necessary as well or the fan + * keeps coming up and battery drains fast */ + MACIO_BIC(HEATHROW_FCR, HRW_IOBUS_ENABLE); + MACIO_BIC(HEATHROW_FCR, HRW_IDE0_RESET_N); + /* Make sure eth is down even if module or sleep + * won't work properly */ + MACIO_BIC(HEATHROW_FCR, HRW_BMAC_IO_ENABLE | HRW_BMAC_RESET); + } + /* Make sure modem is shut down */ + MACIO_OUT8(HRW_GPIO_MODEM_RESET, + MACIO_IN8(HRW_GPIO_MODEM_RESET) & ~1); + MACIO_BIS(HEATHROW_FCR, HRW_SCC_TRANS_EN_N); + MACIO_BIC(HEATHROW_FCR, OH_SCCA_IO|OH_SCCB_IO|HRW_SCC_ENABLE); + + /* Let things settle */ + (void)MACIO_IN32(HEATHROW_FCR); +} + +static void __pmac +heathrow_wakeup(struct macio_chip* macio, int secondary) +{ + if (secondary) { + MACIO_OUT32(0x38, save_fcr[2]); + (void)MACIO_IN32(0x38); + mdelay(1); + MACIO_OUT32(0x3c, save_fcr[3]); + (void)MACIO_IN32(0x38); + mdelay(10); + dbdma_restore(macio, save_alt_dbdma); + } else { + MACIO_OUT32(0x38, save_fcr[0] | HRW_IOBUS_ENABLE); + (void)MACIO_IN32(0x38); + mdelay(1); + MACIO_OUT32(0x3c, save_fcr[1]); + (void)MACIO_IN32(0x38); + mdelay(1); + MACIO_OUT32(0x34, save_mbcr); + (void)MACIO_IN32(0x38); + mdelay(10); + dbdma_restore(macio, save_dbdma); + } +} + +static long __pmac +heathrow_sleep_state(struct device_node* node, long param, long value) +{ + if ((pmac_mb.board_flags & PMAC_MB_CAN_SLEEP) == 0) + return -EPERM; + if (value == 1) { + if (macio_chips[1].type == macio_gatwick) + heathrow_sleep(&macio_chips[0], 1); + heathrow_sleep(&macio_chips[0], 0); + } else if (value == 0) { + heathrow_wakeup(&macio_chips[0], 0); + if (macio_chips[1].type == macio_gatwick) + heathrow_wakeup(&macio_chips[0], 1); + } + return 0; +} + +static long __pmac +core99_scc_enable(struct device_node* node, long param, long value) +{ + struct macio_chip* macio; + unsigned long flags; + unsigned long chan_mask; + u32 fcr; + + macio = macio_find(node, 0); + if (!macio) + return -ENODEV; + if (!strcmp(node->name, "ch-a")) + chan_mask = MACIO_FLAG_SCCA_ON; + else if (!strcmp(node->name, "ch-b")) + chan_mask = MACIO_FLAG_SCCB_ON; + else + return -ENODEV; + + if (value) { + int need_reset_scc = 0; + int need_reset_irda = 0; + + LOCK(flags); + fcr = MACIO_IN32(KEYLARGO_FCR0); + /* Check if scc cell need enabling */ + if (!(fcr & KL0_SCC_CELL_ENABLE)) { + fcr |= KL0_SCC_CELL_ENABLE; + need_reset_scc = 1; + } + if (chan_mask & MACIO_FLAG_SCCA_ON) { + fcr |= KL0_SCCA_ENABLE; + /* Don't enable line drivers for I2S modem */ + if ((param & 0xfff) == PMAC_SCC_I2S1) + fcr &= ~KL0_SCC_A_INTF_ENABLE; + else + fcr |= KL0_SCC_A_INTF_ENABLE; + } + if (chan_mask & MACIO_FLAG_SCCB_ON) { + fcr |= KL0_SCCB_ENABLE; + /* Perform irda specific inits */ + if ((param & 0xfff) == PMAC_SCC_IRDA) { + fcr &= ~KL0_SCC_B_INTF_ENABLE; + fcr |= KL0_IRDA_ENABLE; + fcr |= KL0_IRDA_CLK32_ENABLE | KL0_IRDA_CLK19_ENABLE; + fcr |= KL0_IRDA_SOURCE1_SEL; + fcr &= ~(KL0_IRDA_FAST_CONNECT|KL0_IRDA_DEFAULT1|KL0_IRDA_DEFAULT0); + fcr &= ~(KL0_IRDA_SOURCE2_SEL|KL0_IRDA_HIGH_BAND); + need_reset_irda = 1; + } else + fcr |= KL0_SCC_B_INTF_ENABLE; + } + MACIO_OUT32(KEYLARGO_FCR0, fcr); + macio->flags |= chan_mask; + if (need_reset_scc) { + MACIO_BIS(KEYLARGO_FCR0, KL0_SCC_RESET); + (void)MACIO_IN32(KEYLARGO_FCR0); + UNLOCK(flags); + mdelay(15); + LOCK(flags); + MACIO_BIC(KEYLARGO_FCR0, KL0_SCC_RESET); + } + if (need_reset_irda) { + MACIO_BIS(KEYLARGO_FCR0, KL0_IRDA_RESET); + (void)MACIO_IN32(KEYLARGO_FCR0); + UNLOCK(flags); + mdelay(15); + LOCK(flags); + MACIO_BIC(KEYLARGO_FCR0, KL0_IRDA_RESET); + } + UNLOCK(flags); + if (param & PMAC_SCC_FLAG_XMON) + macio->flags |= MACIO_FLAG_SCC_LOCKED; + } else { + if (macio->flags & MACIO_FLAG_SCC_LOCKED) + return -EPERM; + LOCK(flags); + fcr = MACIO_IN32(KEYLARGO_FCR0); + if (chan_mask & MACIO_FLAG_SCCA_ON) + fcr &= ~KL0_SCCA_ENABLE; + if (chan_mask & MACIO_FLAG_SCCB_ON) { + fcr &= ~KL0_SCCB_ENABLE; + /* Perform irda specific clears */ + if ((param & 0xfff) == PMAC_SCC_IRDA) { + fcr &= ~KL0_IRDA_ENABLE; + fcr &= ~(KL0_IRDA_CLK32_ENABLE | KL0_IRDA_CLK19_ENABLE); + fcr &= ~(KL0_IRDA_FAST_CONNECT|KL0_IRDA_DEFAULT1|KL0_IRDA_DEFAULT0); + fcr &= ~(KL0_IRDA_SOURCE1_SEL|KL0_IRDA_SOURCE2_SEL|KL0_IRDA_HIGH_BAND); + } + } + MACIO_OUT32(KEYLARGO_FCR0, fcr); + if ((fcr & (KL0_SCCA_ENABLE | KL0_SCCB_ENABLE)) == 0) { + fcr &= ~KL0_SCC_CELL_ENABLE; + MACIO_OUT32(KEYLARGO_FCR0, fcr); + } + macio->flags &= ~(chan_mask); + UNLOCK(flags); + mdelay(10); + } + return 0; +} + +static long __pmac +core99_modem_enable(struct device_node* node, long param, long value) +{ + struct macio_chip* macio; + u8 gpio; + unsigned long flags; + + /* Hack for internal USB modem */ + if (node == NULL) { + if (macio_chips[0].type != macio_keylargo) + return -ENODEV; + node = macio_chips[0].of_node; + } + macio = macio_find(node, 0); + if (!macio) + return -ENODEV; + gpio = MACIO_IN8(KL_GPIO_MODEM_RESET); + gpio |= KEYLARGO_GPIO_OUTPUT_ENABLE; + gpio &= ~KEYLARGO_GPIO_OUTOUT_DATA; + + if (!value) { + LOCK(flags); + MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio); + UNLOCK(flags); + (void)MACIO_IN8(KL_GPIO_MODEM_RESET); + mdelay(250); + } + LOCK(flags); + if (value) { + MACIO_BIC(KEYLARGO_FCR2, KL2_ALT_DATA_OUT); + UNLOCK(flags); + (void)MACIO_IN32(KEYLARGO_FCR2); + mdelay(250); + } else { + MACIO_BIS(KEYLARGO_FCR2, KL2_ALT_DATA_OUT); + UNLOCK(flags); + } + if (value) { + LOCK(flags); + MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio | KEYLARGO_GPIO_OUTOUT_DATA); + (void)MACIO_IN8(KL_GPIO_MODEM_RESET); + UNLOCK(flags); mdelay(250); LOCK(flags); + MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio); + (void)MACIO_IN8(KL_GPIO_MODEM_RESET); + UNLOCK(flags); mdelay(250); LOCK(flags); + MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio | KEYLARGO_GPIO_OUTOUT_DATA); + (void)MACIO_IN8(KL_GPIO_MODEM_RESET); + UNLOCK(flags); mdelay(250); + } + return 0; +} + +static long __pmac +pangea_modem_enable(struct device_node* node, long param, long value) +{ + struct macio_chip* macio; + u8 gpio; + unsigned long flags; + + /* Hack for internal USB modem */ + if (node == NULL) { + if (macio_chips[0].type != macio_pangea && + macio_chips[0].type != macio_intrepid) + return -ENODEV; + node = macio_chips[0].of_node; + } + macio = macio_find(node, 0); + if (!macio) + return -ENODEV; + gpio = MACIO_IN8(KL_GPIO_MODEM_RESET); + gpio |= KEYLARGO_GPIO_OUTPUT_ENABLE; + gpio &= ~KEYLARGO_GPIO_OUTOUT_DATA; + + if (!value) { + LOCK(flags); + MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio); + UNLOCK(flags); + (void)MACIO_IN8(KL_GPIO_MODEM_RESET); + mdelay(250); + } + LOCK(flags); + if (value) { + MACIO_OUT8(KL_GPIO_MODEM_POWER, + KEYLARGO_GPIO_OUTPUT_ENABLE); + UNLOCK(flags); + (void)MACIO_IN32(KEYLARGO_FCR2); + mdelay(250); + } else { + MACIO_OUT8(KL_GPIO_MODEM_POWER, + KEYLARGO_GPIO_OUTPUT_ENABLE | KEYLARGO_GPIO_OUTOUT_DATA); + UNLOCK(flags); + } + if (value) { + LOCK(flags); + MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio | KEYLARGO_GPIO_OUTOUT_DATA); + (void)MACIO_IN8(KL_GPIO_MODEM_RESET); + UNLOCK(flags); mdelay(250); LOCK(flags); + MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio); + (void)MACIO_IN8(KL_GPIO_MODEM_RESET); + UNLOCK(flags); mdelay(250); LOCK(flags); + MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio | KEYLARGO_GPIO_OUTOUT_DATA); + (void)MACIO_IN8(KL_GPIO_MODEM_RESET); + UNLOCK(flags); mdelay(250); + } + return 0; +} + +static long __pmac +core99_ata100_enable(struct device_node* node, long value) +{ + unsigned long flags; + struct pci_dev *pdev = NULL; + u8 pbus, pid; + + if (uninorth_rev < 0x24) + return -ENODEV; + + LOCK(flags); + if (value) + UN_BIS(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_ATA100); + else + UN_BIC(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_ATA100); + (void)UN_IN(UNI_N_CLOCK_CNTL); + UNLOCK(flags); + udelay(20); + + if (value) { + if (pci_device_from_OF_node(node, &pbus, &pid) == 0) + pdev = pci_find_slot(pbus, pid); + if (pdev == NULL) + return 0; + pci_enable_device(pdev); + pci_set_master(pdev); + } + return 0; +} + +static long __pmac +core99_ide_enable(struct device_node* node, long param, long value) +{ + /* Bus ID 0 to 2 are KeyLargo based IDE, busID 3 is U2 + * based ata-100 + */ + switch(param) { + case 0: + return simple_feature_tweak(node, macio_unknown, + KEYLARGO_FCR1, KL1_EIDE0_ENABLE, value); + case 1: + return simple_feature_tweak(node, macio_unknown, + KEYLARGO_FCR1, KL1_EIDE1_ENABLE, value); + case 2: + return simple_feature_tweak(node, macio_unknown, + KEYLARGO_FCR1, KL1_UIDE_ENABLE, value); + case 3: + return core99_ata100_enable(node, value); + default: + return -ENODEV; + } +} + +static long __pmac +core99_ide_reset(struct device_node* node, long param, long value) +{ + switch(param) { + case 0: + return simple_feature_tweak(node, macio_unknown, + KEYLARGO_FCR1, KL1_EIDE0_RESET_N, !value); + case 1: + return simple_feature_tweak(node, macio_unknown, + KEYLARGO_FCR1, KL1_EIDE1_RESET_N, !value); + case 2: + return simple_feature_tweak(node, macio_unknown, + KEYLARGO_FCR1, KL1_UIDE_RESET_N, !value); + default: + return -ENODEV; + } +} + +static long __pmac +core99_gmac_enable(struct device_node* node, long param, long value) +{ + unsigned long flags; + + LOCK(flags); + if (value) + UN_BIS(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_GMAC); + else + UN_BIC(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_GMAC); + (void)UN_IN(UNI_N_CLOCK_CNTL); + UNLOCK(flags); + udelay(20); + + return 0; +} + +static long __pmac +core99_gmac_phy_reset(struct device_node* node, long param, long value) +{ + unsigned long flags; + struct macio_chip* macio; + + macio = &macio_chips[0]; + if (macio->type != macio_keylargo && macio->type != macio_pangea && + macio->type != macio_intrepid) + return -ENODEV; + + LOCK(flags); + MACIO_OUT8(KL_GPIO_ETH_PHY_RESET, KEYLARGO_GPIO_OUTPUT_ENABLE); + (void)MACIO_IN8(KL_GPIO_ETH_PHY_RESET); + UNLOCK(flags); + mdelay(10); + LOCK(flags); + MACIO_OUT8(KL_GPIO_ETH_PHY_RESET, /*KEYLARGO_GPIO_OUTPUT_ENABLE | */ + KEYLARGO_GPIO_OUTOUT_DATA); + UNLOCK(flags); + mdelay(10); + + return 0; +} + +static long __pmac +core99_sound_chip_enable(struct device_node* node, long param, long value) +{ + struct macio_chip* macio; + unsigned long flags; + + macio = macio_find(node, 0); + if (!macio) + return -ENODEV; + + /* Do a better probe code, screamer G4 desktops & + * iMacs can do that too, add a recalibrate in + * the driver as well + */ + if (pmac_mb.model_id == PMAC_TYPE_PISMO || + pmac_mb.model_id == PMAC_TYPE_TITANIUM) { + LOCK(flags); + if (value) + MACIO_OUT8(KL_GPIO_SOUND_POWER, + KEYLARGO_GPIO_OUTPUT_ENABLE | + KEYLARGO_GPIO_OUTOUT_DATA); + else + MACIO_OUT8(KL_GPIO_SOUND_POWER, + KEYLARGO_GPIO_OUTPUT_ENABLE); + (void)MACIO_IN8(KL_GPIO_SOUND_POWER); + UNLOCK(flags); + } + return 0; +} + +static long __pmac +core99_airport_enable(struct device_node* node, long param, long value) +{ + struct macio_chip* macio; + unsigned long flags; + int state; + + macio = macio_find(node, 0); + if (!macio) + return -ENODEV; + + /* Hint: we allow passing of macio itself for the sake of the + * sleep code + */ + if (node != macio->of_node && + (!node->parent || node->parent != macio->of_node)) + return -ENODEV; + state = (macio->flags & MACIO_FLAG_AIRPORT_ON) != 0; + if (value == state) + return 0; + if (value) { + /* This code is a reproduction of OF enable-cardslot + * and init-wireless methods, slightly hacked until + * I got it working. + */ + LOCK(flags); + MACIO_OUT8(KEYLARGO_GPIO_0+0xf, 5); + (void)MACIO_IN8(KEYLARGO_GPIO_0+0xf); + UNLOCK(flags); + mdelay(10); + LOCK(flags); + MACIO_OUT8(KEYLARGO_GPIO_0+0xf, 4); + (void)MACIO_IN8(KEYLARGO_GPIO_0+0xf); + UNLOCK(flags); + + mdelay(10); + + LOCK(flags); + MACIO_BIC(KEYLARGO_FCR2, KL2_CARDSEL_16); + (void)MACIO_IN32(KEYLARGO_FCR2); + udelay(10); + MACIO_OUT8(KEYLARGO_GPIO_EXTINT_0+0xb, 0); + (void)MACIO_IN8(KEYLARGO_GPIO_EXTINT_0+0xb); + udelay(10); + MACIO_OUT8(KEYLARGO_GPIO_EXTINT_0+0xa, 0x28); + (void)MACIO_IN8(KEYLARGO_GPIO_EXTINT_0+0xa); + udelay(10); + MACIO_OUT8(KEYLARGO_GPIO_EXTINT_0+0xd, 0x28); + (void)MACIO_IN8(KEYLARGO_GPIO_EXTINT_0+0xd); + udelay(10); + MACIO_OUT8(KEYLARGO_GPIO_0+0xd, 0x28); + (void)MACIO_IN8(KEYLARGO_GPIO_0+0xd); + udelay(10); + MACIO_OUT8(KEYLARGO_GPIO_0+0xe, 0x28); + (void)MACIO_IN8(KEYLARGO_GPIO_0+0xe); + UNLOCK(flags); + udelay(10); + MACIO_OUT32(0x1c000, 0); + mdelay(1); + MACIO_OUT8(0x1a3e0, 0x41); + (void)MACIO_IN8(0x1a3e0); + udelay(10); + LOCK(flags); + MACIO_BIS(KEYLARGO_FCR2, KL2_CARDSEL_16); + (void)MACIO_IN32(KEYLARGO_FCR2); + UNLOCK(flags); + mdelay(100); + + macio->flags |= MACIO_FLAG_AIRPORT_ON; + } else { + LOCK(flags); + MACIO_BIC(KEYLARGO_FCR2, KL2_CARDSEL_16); + (void)MACIO_IN32(KEYLARGO_FCR2); + MACIO_OUT8(KL_GPIO_AIRPORT_0, 0); + MACIO_OUT8(KL_GPIO_AIRPORT_1, 0); + MACIO_OUT8(KL_GPIO_AIRPORT_2, 0); + MACIO_OUT8(KL_GPIO_AIRPORT_3, 0); + MACIO_OUT8(KL_GPIO_AIRPORT_4, 0); + (void)MACIO_IN8(KL_GPIO_AIRPORT_4); + UNLOCK(flags); + + macio->flags &= ~MACIO_FLAG_AIRPORT_ON; + } + return 0; +} + +#ifdef CONFIG_SMP +static long __pmac +core99_reset_cpu(struct device_node* node, long param, long value) +{ + unsigned int reset_io = 0; + unsigned long flags; + struct macio_chip* macio; + struct device_node* np; + const int dflt_reset_lines[] = { KL_GPIO_RESET_CPU0, + KL_GPIO_RESET_CPU1, + KL_GPIO_RESET_CPU2, + KL_GPIO_RESET_CPU3 }; + + macio = &macio_chips[0]; + if (macio->type != macio_keylargo) + return -ENODEV; + + np = find_path_device("/cpus"); + if (np == NULL) + return -ENODEV; + for (np = np->child; np != NULL; np = np->sibling) { + u32* num = (u32 *)get_property(np, "reg", NULL); + u32* rst = (u32 *)get_property(np, "soft-reset", NULL); + if (num == NULL || rst == NULL) + continue; + if (param == *num) { + reset_io = *rst; + break; + } + } + if (np == NULL || reset_io == 0) + reset_io = dflt_reset_lines[param]; + + LOCK(flags); + MACIO_OUT8(reset_io, KEYLARGO_GPIO_OUTPUT_ENABLE); + (void)MACIO_IN8(reset_io); + udelay(1); + MACIO_OUT8(reset_io, 0); + (void)MACIO_IN8(reset_io); + UNLOCK(flags); + + return 0; +} +#endif /* CONFIG_SMP */ + +static long __pmac +core99_usb_enable(struct device_node* node, long param, long value) +{ + struct macio_chip* macio; + unsigned long flags; + char* prop; + int number; + u32 reg; + + macio = &macio_chips[0]; + if (macio->type != macio_keylargo && macio->type != macio_pangea && + macio->type != macio_intrepid) + return -ENODEV; + + prop = (char *)get_property(node, "AAPL,clock-id", NULL); + if (!prop) + return -ENODEV; + if (strncmp(prop, "usb0u048", 8) == 0) + number = 0; + else if (strncmp(prop, "usb1u148", 8) == 0) + number = 2; + else if (strncmp(prop, "usb2u248", 8) == 0) + number = 4; + else + return -ENODEV; + + /* Sorry for the brute-force locking, but this is only used during + * sleep and the timing seem to be critical + */ + LOCK(flags); + if (value) { + /* Turn ON */ + if (number == 0) { + MACIO_BIC(KEYLARGO_FCR0, (KL0_USB0_PAD_SUSPEND0 | KL0_USB0_PAD_SUSPEND1)); + (void)MACIO_IN32(KEYLARGO_FCR0); + UNLOCK(flags); + mdelay(1); + LOCK(flags); + MACIO_BIS(KEYLARGO_FCR0, KL0_USB0_CELL_ENABLE); + } else if (number == 2) { + MACIO_BIC(KEYLARGO_FCR0, (KL0_USB1_PAD_SUSPEND0 | KL0_USB1_PAD_SUSPEND1)); + UNLOCK(flags); + (void)MACIO_IN32(KEYLARGO_FCR0); + mdelay(1); + LOCK(flags); + MACIO_BIS(KEYLARGO_FCR0, KL0_USB1_CELL_ENABLE); + } else if (number == 4) { + MACIO_BIC(KEYLARGO_FCR1, (KL1_USB2_PAD_SUSPEND0 | KL1_USB2_PAD_SUSPEND1)); + UNLOCK(flags); + (void)MACIO_IN32(KEYLARGO_FCR1); + mdelay(1); + LOCK(flags); + MACIO_BIS(KEYLARGO_FCR1, KL1_USB2_CELL_ENABLE); + } + if (number < 4) { + reg = MACIO_IN32(KEYLARGO_FCR4); + reg &= ~(KL4_PORT_WAKEUP_ENABLE(number) | KL4_PORT_RESUME_WAKE_EN(number) | + KL4_PORT_CONNECT_WAKE_EN(number) | KL4_PORT_DISCONNECT_WAKE_EN(number)); + reg &= ~(KL4_PORT_WAKEUP_ENABLE(number+1) | KL4_PORT_RESUME_WAKE_EN(number+1) | + KL4_PORT_CONNECT_WAKE_EN(number+1) | KL4_PORT_DISCONNECT_WAKE_EN(number+1)); + MACIO_OUT32(KEYLARGO_FCR4, reg); + (void)MACIO_IN32(KEYLARGO_FCR4); + udelay(10); + } else { + reg = MACIO_IN32(KEYLARGO_FCR3); + reg &= ~(KL3_IT_PORT_WAKEUP_ENABLE(0) | KL3_IT_PORT_RESUME_WAKE_EN(0) | + KL3_IT_PORT_CONNECT_WAKE_EN(0) | KL3_IT_PORT_DISCONNECT_WAKE_EN(0)); + reg &= ~(KL3_IT_PORT_WAKEUP_ENABLE(1) | KL3_IT_PORT_RESUME_WAKE_EN(1) | + KL3_IT_PORT_CONNECT_WAKE_EN(1) | KL3_IT_PORT_DISCONNECT_WAKE_EN(1)); + MACIO_OUT32(KEYLARGO_FCR3, reg); + (void)MACIO_IN32(KEYLARGO_FCR3); + udelay(10); + } + if (macio->type == macio_intrepid) { + /* wait for clock stopped bits to clear */ + u32 test0 = 0, test1 = 0; + u32 status0, status1; + int timeout = 1000; + + UNLOCK(flags); + switch (number) { + case 0: + test0 = UNI_N_CLOCK_STOPPED_USB0; + test1 = UNI_N_CLOCK_STOPPED_USB0PCI; + break; + case 2: + test0 = UNI_N_CLOCK_STOPPED_USB1; + test1 = UNI_N_CLOCK_STOPPED_USB1PCI; + break; + case 4: + test0 = UNI_N_CLOCK_STOPPED_USB2; + test1 = UNI_N_CLOCK_STOPPED_USB2PCI; + break; + } + do { + if (--timeout <= 0) { + printk(KERN_ERR "core99_usb_enable: " + "Timeout waiting for clocks\n"); + break; + } + mdelay(1); + status0 = UN_IN(UNI_N_CLOCK_STOP_STATUS0); + status1 = UN_IN(UNI_N_CLOCK_STOP_STATUS1); + } while ((status0 & test0) | (status1 & test1)); + LOCK(flags); + } + } else { + /* Turn OFF */ + if (number < 4) { + reg = MACIO_IN32(KEYLARGO_FCR4); + reg |= KL4_PORT_WAKEUP_ENABLE(number) | KL4_PORT_RESUME_WAKE_EN(number) | + KL4_PORT_CONNECT_WAKE_EN(number) | KL4_PORT_DISCONNECT_WAKE_EN(number); + reg |= KL4_PORT_WAKEUP_ENABLE(number+1) | KL4_PORT_RESUME_WAKE_EN(number+1) | + KL4_PORT_CONNECT_WAKE_EN(number+1) | KL4_PORT_DISCONNECT_WAKE_EN(number+1); + MACIO_OUT32(KEYLARGO_FCR4, reg); + (void)MACIO_IN32(KEYLARGO_FCR4); + udelay(1); + } else { + reg = MACIO_IN32(KEYLARGO_FCR3); + reg |= KL3_IT_PORT_WAKEUP_ENABLE(0) | KL3_IT_PORT_RESUME_WAKE_EN(0) | + KL3_IT_PORT_CONNECT_WAKE_EN(0) | KL3_IT_PORT_DISCONNECT_WAKE_EN(0); + reg |= KL3_IT_PORT_WAKEUP_ENABLE(1) | KL3_IT_PORT_RESUME_WAKE_EN(1) | + KL3_IT_PORT_CONNECT_WAKE_EN(1) | KL3_IT_PORT_DISCONNECT_WAKE_EN(1); + MACIO_OUT32(KEYLARGO_FCR3, reg); + (void)MACIO_IN32(KEYLARGO_FCR3); + udelay(1); + } + if (number == 0) { + if (macio->type != macio_intrepid) + MACIO_BIC(KEYLARGO_FCR0, KL0_USB0_CELL_ENABLE); + (void)MACIO_IN32(KEYLARGO_FCR0); + udelay(1); + MACIO_BIS(KEYLARGO_FCR0, (KL0_USB0_PAD_SUSPEND0 | KL0_USB0_PAD_SUSPEND1)); + (void)MACIO_IN32(KEYLARGO_FCR0); + } else if (number == 2) { + if (macio->type != macio_intrepid) + MACIO_BIC(KEYLARGO_FCR0, KL0_USB1_CELL_ENABLE); + (void)MACIO_IN32(KEYLARGO_FCR0); + udelay(1); + MACIO_BIS(KEYLARGO_FCR0, (KL0_USB1_PAD_SUSPEND0 | KL0_USB1_PAD_SUSPEND1)); + (void)MACIO_IN32(KEYLARGO_FCR0); + } else if (number == 4) { + udelay(1); + MACIO_BIS(KEYLARGO_FCR1, (KL1_USB2_PAD_SUSPEND0 | KL1_USB2_PAD_SUSPEND1)); + (void)MACIO_IN32(KEYLARGO_FCR1); + } + udelay(1); + } + UNLOCK(flags); + + return 0; +} + +static long __pmac +core99_firewire_enable(struct device_node* node, long param, long value) +{ + unsigned long flags; + struct macio_chip* macio; + + macio = &macio_chips[0]; + if (macio->type != macio_keylargo && macio->type != macio_pangea && + macio->type != macio_intrepid) + return -ENODEV; + if (!(macio->flags & MACIO_FLAG_FW_SUPPORTED)) + return -ENODEV; + + LOCK(flags); + if (value) { + UN_BIS(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_FW); + (void)UN_IN(UNI_N_CLOCK_CNTL); + } else { + UN_BIC(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_FW); + (void)UN_IN(UNI_N_CLOCK_CNTL); + } + UNLOCK(flags); + mdelay(1); + + return 0; +} + +static long __pmac +core99_firewire_cable_power(struct device_node* node, long param, long value) +{ + unsigned long flags; + struct macio_chip* macio; + + /* Trick: we allow NULL node */ + if ((pmac_mb.board_flags & PMAC_MB_HAS_FW_POWER) == 0) + return -ENODEV; + macio = &macio_chips[0]; + if (macio->type != macio_keylargo && macio->type != macio_pangea && + macio->type != macio_intrepid) + return -ENODEV; + if (!(macio->flags & MACIO_FLAG_FW_SUPPORTED)) + return -ENODEV; + + LOCK(flags); + if (value) { + MACIO_OUT8(KL_GPIO_FW_CABLE_POWER , 0); + MACIO_IN8(KL_GPIO_FW_CABLE_POWER); + udelay(10); + } else { + MACIO_OUT8(KL_GPIO_FW_CABLE_POWER , 4); + MACIO_IN8(KL_GPIO_FW_CABLE_POWER); udelay(10); + } + UNLOCK(flags); + mdelay(1); + + return 0; +} + +static long __pmac +intrepid_aack_delay_enable(struct device_node* node, long param, long value) +{ + unsigned long flags; + + if (uninorth_rev < 0xd2) + return -ENODEV; + + LOCK(flags); + if (param) + UN_BIS(UNI_N_AACK_DELAY, UNI_N_AACK_DELAY_ENABLE); + else + UN_BIC(UNI_N_AACK_DELAY, UNI_N_AACK_DELAY_ENABLE); + UNLOCK(flags); + + return 0; +} + + +#endif /* CONFIG_POWER4 */ + +static long __pmac +core99_read_gpio(struct device_node* node, long param, long value) +{ + struct macio_chip* macio = &macio_chips[0]; + + return MACIO_IN8(param); +} + + +static long __pmac +core99_write_gpio(struct device_node* node, long param, long value) +{ + struct macio_chip* macio = &macio_chips[0]; + + MACIO_OUT8(param, (u8)(value & 0xff)); + return 0; +} + +#ifdef CONFIG_POWER4 + +static long __pmac +g5_gmac_enable(struct device_node* node, long param, long value) +{ + struct macio_chip* macio = &macio_chips[0]; + unsigned long flags; + u8 pbus, pid; + + LOCK(flags); + if (value) { + MACIO_BIS(KEYLARGO_FCR1, K2_FCR1_GMAC_CLK_ENABLE); + mb(); + k2_skiplist[0] = NULL; + } else { + k2_skiplist[0] = node; + mb(); + MACIO_BIC(KEYLARGO_FCR1, K2_FCR1_GMAC_CLK_ENABLE); + } + + UNLOCK(flags); + mdelay(1); + + return 0; +} + +static long __pmac +g5_fw_enable(struct device_node* node, long param, long value) +{ + struct macio_chip* macio = &macio_chips[0]; + unsigned long flags; + + LOCK(flags); + if (value) { + MACIO_BIS(KEYLARGO_FCR1, K2_FCR1_FW_CLK_ENABLE); + mb(); + k2_skiplist[1] = NULL; + } else { + k2_skiplist[1] = node; + mb(); + MACIO_BIC(KEYLARGO_FCR1, K2_FCR1_FW_CLK_ENABLE); + } + + UNLOCK(flags); + mdelay(1); + + return 0; +} + +static long __pmac +g5_mpic_enable(struct device_node* node, long param, long value) +{ + unsigned long flags; + + if (node->parent == NULL || strcmp(node->parent->name, "u3")) + return 0; + + LOCK(flags); + UN_BIS(U3_TOGGLE_REG, U3_MPIC_RESET | U3_MPIC_OUTPUT_ENABLE); + UNLOCK(flags); + + return 0; +} + +#ifdef CONFIG_SMP +static long __pmac +g5_reset_cpu(struct device_node* node, long param, long value) +{ + unsigned int reset_io = 0; + unsigned long flags; + struct macio_chip* macio; + struct device_node* np; + + macio = &macio_chips[0]; + if (macio->type != macio_keylargo2) + return -ENODEV; + + np = find_path_device("/cpus"); + if (np == NULL) + return -ENODEV; + for (np = np->child; np != NULL; np = np->sibling) { + u32* num = (u32 *)get_property(np, "reg", NULL); + u32* rst = (u32 *)get_property(np, "soft-reset", NULL); + if (num == NULL || rst == NULL) + continue; + if (param == *num) { + reset_io = *rst; + break; + } + } + if (np == NULL || reset_io == 0) + return -ENODEV; + + LOCK(flags); + MACIO_OUT8(reset_io, KEYLARGO_GPIO_OUTPUT_ENABLE); + (void)MACIO_IN8(reset_io); + udelay(1); + MACIO_OUT8(reset_io, 0); + (void)MACIO_IN8(reset_io); + UNLOCK(flags); + + return 0; +} +#endif /* CONFIG_SMP */ + +/* + * This can be called from pmac_smp so isn't static + * + * This takes the second CPU off the bus on dual CPU machines + * running UP + */ +void __pmac g5_phy_disable_cpu1(void) +{ + UN_OUT(U3_API_PHY_CONFIG_1, 0); +} + +#endif /* CONFIG_POWER4 */ + +#ifndef CONFIG_POWER4 + +static void __pmac +keylargo_shutdown(struct macio_chip* macio, int sleep_mode) +{ + u32 temp; + + if (sleep_mode) { + mdelay(1); + MACIO_BIS(KEYLARGO_FCR0, KL0_USB_REF_SUSPEND); + (void)MACIO_IN32(KEYLARGO_FCR0); + mdelay(1); + } + + MACIO_BIC(KEYLARGO_FCR0,KL0_SCCA_ENABLE | KL0_SCCB_ENABLE | + KL0_SCC_CELL_ENABLE | + KL0_IRDA_ENABLE | KL0_IRDA_CLK32_ENABLE | + KL0_IRDA_CLK19_ENABLE); + + MACIO_BIC(KEYLARGO_MBCR, KL_MBCR_MB0_DEV_MASK); + MACIO_BIS(KEYLARGO_MBCR, KL_MBCR_MB0_IDE_ENABLE); + + MACIO_BIC(KEYLARGO_FCR1, + KL1_AUDIO_SEL_22MCLK | KL1_AUDIO_CLK_ENABLE_BIT | + KL1_AUDIO_CLK_OUT_ENABLE | KL1_AUDIO_CELL_ENABLE | + KL1_I2S0_CELL_ENABLE | KL1_I2S0_CLK_ENABLE_BIT | + KL1_I2S0_ENABLE | KL1_I2S1_CELL_ENABLE | + KL1_I2S1_CLK_ENABLE_BIT | KL1_I2S1_ENABLE | + KL1_EIDE0_ENABLE | KL1_EIDE0_RESET_N | + KL1_EIDE1_ENABLE | KL1_EIDE1_RESET_N | + KL1_UIDE_ENABLE); + + MACIO_BIS(KEYLARGO_FCR2, KL2_ALT_DATA_OUT); + MACIO_BIC(KEYLARGO_FCR2, KL2_IOBUS_ENABLE); + + temp = MACIO_IN32(KEYLARGO_FCR3); + if (macio->rev >= 2) { + temp |= KL3_SHUTDOWN_PLL2X; + if (sleep_mode) + temp |= KL3_SHUTDOWN_PLL_TOTAL; + } + + temp |= KL3_SHUTDOWN_PLLKW6 | KL3_SHUTDOWN_PLLKW4 | + KL3_SHUTDOWN_PLLKW35; + if (sleep_mode) + temp |= KL3_SHUTDOWN_PLLKW12; + temp &= ~(KL3_CLK66_ENABLE | KL3_CLK49_ENABLE | KL3_CLK45_ENABLE + | KL3_CLK31_ENABLE | KL3_I2S1_CLK18_ENABLE | KL3_I2S0_CLK18_ENABLE); + if (sleep_mode) + temp &= ~(KL3_TIMER_CLK18_ENABLE | KL3_VIA_CLK16_ENABLE); + MACIO_OUT32(KEYLARGO_FCR3, temp); + + /* Flush posted writes & wait a bit */ + (void)MACIO_IN32(KEYLARGO_FCR0); mdelay(1); +} + +static void __pmac +pangea_shutdown(struct macio_chip* macio, int sleep_mode) +{ + u32 temp; + + MACIO_BIC(KEYLARGO_FCR0,KL0_SCCA_ENABLE | KL0_SCCB_ENABLE | + KL0_SCC_CELL_ENABLE | + KL0_USB0_CELL_ENABLE | KL0_USB1_CELL_ENABLE); + + MACIO_BIC(KEYLARGO_FCR1, + KL1_AUDIO_SEL_22MCLK | KL1_AUDIO_CLK_ENABLE_BIT | + KL1_AUDIO_CLK_OUT_ENABLE | KL1_AUDIO_CELL_ENABLE | + KL1_I2S0_CELL_ENABLE | KL1_I2S0_CLK_ENABLE_BIT | + KL1_I2S0_ENABLE | KL1_I2S1_CELL_ENABLE | + KL1_I2S1_CLK_ENABLE_BIT | KL1_I2S1_ENABLE | + KL1_UIDE_ENABLE); + if (pmac_mb.board_flags & PMAC_MB_MOBILE) + MACIO_BIC(KEYLARGO_FCR1, KL1_UIDE_RESET_N); + + MACIO_BIS(KEYLARGO_FCR2, KL2_ALT_DATA_OUT); + + temp = MACIO_IN32(KEYLARGO_FCR3); + temp |= KL3_SHUTDOWN_PLLKW6 | KL3_SHUTDOWN_PLLKW4 | + KL3_SHUTDOWN_PLLKW35; + temp &= ~(KL3_CLK49_ENABLE | KL3_CLK45_ENABLE | KL3_CLK31_ENABLE + | KL3_I2S0_CLK18_ENABLE | KL3_I2S1_CLK18_ENABLE); + if (sleep_mode) + temp &= ~(KL3_VIA_CLK16_ENABLE | KL3_TIMER_CLK18_ENABLE); + MACIO_OUT32(KEYLARGO_FCR3, temp); + + /* Flush posted writes & wait a bit */ + (void)MACIO_IN32(KEYLARGO_FCR0); mdelay(1); +} + +static void __pmac +intrepid_shutdown(struct macio_chip* macio, int sleep_mode) +{ + u32 temp; + + MACIO_BIC(KEYLARGO_FCR0,KL0_SCCA_ENABLE | KL0_SCCB_ENABLE | + KL0_SCC_CELL_ENABLE); + + MACIO_BIC(KEYLARGO_FCR1, + /*KL1_USB2_CELL_ENABLE |*/ + KL1_I2S0_CELL_ENABLE | KL1_I2S0_CLK_ENABLE_BIT | + KL1_I2S0_ENABLE | KL1_I2S1_CELL_ENABLE | + KL1_I2S1_CLK_ENABLE_BIT | KL1_I2S1_ENABLE); + if (pmac_mb.board_flags & PMAC_MB_MOBILE) + MACIO_BIC(KEYLARGO_FCR1, KL1_UIDE_RESET_N); + + temp = MACIO_IN32(KEYLARGO_FCR3); + temp &= ~(KL3_CLK49_ENABLE | KL3_CLK45_ENABLE | + KL3_I2S1_CLK18_ENABLE | KL3_I2S0_CLK18_ENABLE); + if (sleep_mode) + temp &= ~(KL3_TIMER_CLK18_ENABLE | KL3_IT_VIA_CLK32_ENABLE); + MACIO_OUT32(KEYLARGO_FCR3, temp); + + /* Flush posted writes & wait a bit */ + (void)MACIO_IN32(KEYLARGO_FCR0); + mdelay(10); +} + +static int __pmac +core99_sleep(void) +{ + struct macio_chip* macio; + int i; + + macio = &macio_chips[0]; + if (macio->type != macio_keylargo && macio->type != macio_pangea && + macio->type != macio_intrepid) + return -ENODEV; + + /* The device-tree contains that in the hwclock node */ + if (macio->type == macio_intrepid) { + UN_OUT(UNI_N_CLOCK_SPREADING, 0); + mdelay(40); + } + + /* We power off the wireless slot in case it was not done + * by the driver. We don't power it on automatically however + */ + if (macio->flags & MACIO_FLAG_AIRPORT_ON) + core99_airport_enable(macio->of_node, 0, 0); + + /* We power off the FW cable. Should be done by the driver... */ + if (macio->flags & MACIO_FLAG_FW_SUPPORTED) { + core99_firewire_enable(NULL, 0, 0); + core99_firewire_cable_power(NULL, 0, 0); + } + + /* We make sure int. modem is off (in case driver lost it) */ + if (macio->type == macio_keylargo) + core99_modem_enable(macio->of_node, 0, 0); + else + pangea_modem_enable(macio->of_node, 0, 0); + + /* We make sure the sound is off as well */ + core99_sound_chip_enable(macio->of_node, 0, 0); + + /* + * Save various bits of KeyLargo + */ + + /* Save the state of the various GPIOs */ + save_gpio_levels[0] = MACIO_IN32(KEYLARGO_GPIO_LEVELS0); + save_gpio_levels[1] = MACIO_IN32(KEYLARGO_GPIO_LEVELS1); + for (i=0; itype == macio_keylargo) + save_mbcr = MACIO_IN32(KEYLARGO_MBCR); + save_fcr[0] = MACIO_IN32(KEYLARGO_FCR0); + save_fcr[1] = MACIO_IN32(KEYLARGO_FCR1); + save_fcr[2] = MACIO_IN32(KEYLARGO_FCR2); + save_fcr[3] = MACIO_IN32(KEYLARGO_FCR3); + save_fcr[4] = MACIO_IN32(KEYLARGO_FCR4); + if (macio->type == macio_pangea || macio->type == macio_intrepid) + save_fcr[5] = MACIO_IN32(KEYLARGO_FCR5); + + /* Save state & config of DBDMA channels */ + dbdma_save(macio, save_dbdma); + + /* + * Turn off as much as we can + */ + if (macio->type == macio_pangea) + pangea_shutdown(macio, 1); + else if (macio->type == macio_intrepid) + intrepid_shutdown(macio, 1); + else if (macio->type == macio_keylargo) + keylargo_shutdown(macio, 1); + + /* + * Put the host bridge to sleep + */ + + save_unin_clock_ctl = UN_IN(UNI_N_CLOCK_CNTL); + /* Note: do not switch GMAC off, driver does it when necessary, WOL must keep it + * enabled ! + */ + UN_OUT(UNI_N_CLOCK_CNTL, save_unin_clock_ctl & + ~(/*UNI_N_CLOCK_CNTL_GMAC|*/UNI_N_CLOCK_CNTL_FW/*|UNI_N_CLOCK_CNTL_PCI*/)); + udelay(100); + UN_OUT(UNI_N_HWINIT_STATE, UNI_N_HWINIT_STATE_SLEEPING); + UN_OUT(UNI_N_POWER_MGT, UNI_N_POWER_MGT_SLEEP); + mdelay(10); + + /* + * FIXME: A bit of black magic with OpenPIC (don't ask me why) + */ + if (pmac_mb.model_id == PMAC_TYPE_SAWTOOTH) { + MACIO_BIS(0x506e0, 0x00400000); + MACIO_BIS(0x506e0, 0x80000000); + } + return 0; +} + +static int __pmac +core99_wake_up(void) +{ + struct macio_chip* macio; + int i; + + macio = &macio_chips[0]; + if (macio->type != macio_keylargo && macio->type != macio_pangea && + macio->type != macio_intrepid) + return -ENODEV; + + /* + * Wakeup the host bridge + */ + UN_OUT(UNI_N_POWER_MGT, UNI_N_POWER_MGT_NORMAL); + udelay(10); + UN_OUT(UNI_N_HWINIT_STATE, UNI_N_HWINIT_STATE_RUNNING); + udelay(10); + + /* + * Restore KeyLargo + */ + + if (macio->type == macio_keylargo) { + MACIO_OUT32(KEYLARGO_MBCR, save_mbcr); + (void)MACIO_IN32(KEYLARGO_MBCR); udelay(10); + } + MACIO_OUT32(KEYLARGO_FCR0, save_fcr[0]); + (void)MACIO_IN32(KEYLARGO_FCR0); udelay(10); + MACIO_OUT32(KEYLARGO_FCR1, save_fcr[1]); + (void)MACIO_IN32(KEYLARGO_FCR1); udelay(10); + MACIO_OUT32(KEYLARGO_FCR2, save_fcr[2]); + (void)MACIO_IN32(KEYLARGO_FCR2); udelay(10); + MACIO_OUT32(KEYLARGO_FCR3, save_fcr[3]); + (void)MACIO_IN32(KEYLARGO_FCR3); udelay(10); + MACIO_OUT32(KEYLARGO_FCR4, save_fcr[4]); + (void)MACIO_IN32(KEYLARGO_FCR4); udelay(10); + if (macio->type == macio_pangea || macio->type == macio_intrepid) { + MACIO_OUT32(KEYLARGO_FCR5, save_fcr[5]); + (void)MACIO_IN32(KEYLARGO_FCR5); udelay(10); + } + + dbdma_restore(macio, save_dbdma); + + MACIO_OUT32(KEYLARGO_GPIO_LEVELS0, save_gpio_levels[0]); + MACIO_OUT32(KEYLARGO_GPIO_LEVELS1, save_gpio_levels[1]); + for (i=0; itype == macio_intrepid) { + UN_OUT(UNI_N_CLOCK_SPREADING, 2); + mdelay(40); + } + + return 0; +} + +static long __pmac +core99_sleep_state(struct device_node* node, long param, long value) +{ + /* Param == 1 means to enter the "fake sleep" mode that is + * used for CPU speed switch + */ + if (param == 1) { + if (value == 1) { + UN_OUT(UNI_N_HWINIT_STATE, UNI_N_HWINIT_STATE_SLEEPING); + UN_OUT(UNI_N_POWER_MGT, UNI_N_POWER_MGT_IDLE2); + } else { + UN_OUT(UNI_N_POWER_MGT, UNI_N_POWER_MGT_NORMAL); + udelay(10); + UN_OUT(UNI_N_HWINIT_STATE, UNI_N_HWINIT_STATE_RUNNING); + udelay(10); + } + return 0; + } + if ((pmac_mb.board_flags & PMAC_MB_CAN_SLEEP) == 0) + return -EPERM; + +#ifdef CONFIG_CPU_FREQ_PMAC + /* XXX should be elsewhere */ + if (machine_is_compatible("PowerBook6,5") || + machine_is_compatible("PowerBook6,4") || + machine_is_compatible("PowerBook5,5") || + machine_is_compatible("PowerBook5,4")) { + struct device_node *volt_gpio_np; + u32 *reg = NULL; + + volt_gpio_np = of_find_node_by_name(NULL, "cpu-vcore-select"); + if (volt_gpio_np != NULL) + reg = (u32 *)get_property(volt_gpio_np, "reg", NULL); + if (reg != NULL) { + /* Set the CPU voltage high if sleeping */ + if (value == 1) { + pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, + *reg, 0x05); + } else if (value == 0 && (mfspr(SPRN_HID1) & HID1_DFS)) { + pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, + *reg, 0x04); + } + mdelay(2); + } + } +#endif /* CONFIG_CPU_FREQ_PMAC */ + + if (value == 1) + return core99_sleep(); + else if (value == 0) + return core99_wake_up(); + return 0; +} + +#endif /* CONFIG_POWER4 */ + +static long __pmac +generic_dev_can_wake(struct device_node* node, long param, long value) +{ + /* Todo: eventually check we are really dealing with on-board + * video device ... + */ + + if (pmac_mb.board_flags & PMAC_MB_MAY_SLEEP) + pmac_mb.board_flags |= PMAC_MB_CAN_SLEEP; + return 0; +} + +static long __pmac +generic_get_mb_info(struct device_node* node, long param, long value) +{ + switch(param) { + case PMAC_MB_INFO_MODEL: + return pmac_mb.model_id; + case PMAC_MB_INFO_FLAGS: + return pmac_mb.board_flags; + case PMAC_MB_INFO_NAME: + /* hack hack hack... but should work */ + *((const char **)value) = pmac_mb.model_name; + return 0; + } + return -EINVAL; +} + + +/* + * Table definitions + */ + +/* Used on any machine + */ +static struct feature_table_entry any_features[] __pmacdata = { + { PMAC_FTR_GET_MB_INFO, generic_get_mb_info }, + { PMAC_FTR_DEVICE_CAN_WAKE, generic_dev_can_wake }, + { 0, NULL } +}; + +#ifndef CONFIG_POWER4 + +/* OHare based motherboards. Currently, we only use these on the + * 2400,3400 and 3500 series powerbooks. Some older desktops seem + * to have issues with turning on/off those asic cells + */ +static struct feature_table_entry ohare_features[] __pmacdata = { + { PMAC_FTR_SCC_ENABLE, ohare_htw_scc_enable }, + { PMAC_FTR_SWIM3_ENABLE, ohare_floppy_enable }, + { PMAC_FTR_MESH_ENABLE, ohare_mesh_enable }, + { PMAC_FTR_IDE_ENABLE, ohare_ide_enable}, + { PMAC_FTR_IDE_RESET, ohare_ide_reset}, + { PMAC_FTR_SLEEP_STATE, ohare_sleep_state }, + { 0, NULL } +}; + +/* Heathrow desktop machines (Beige G3). + * Separated as some features couldn't be properly tested + * and the serial port control bits appear to confuse it. + */ +static struct feature_table_entry heathrow_desktop_features[] __pmacdata = { + { PMAC_FTR_SWIM3_ENABLE, heathrow_floppy_enable }, + { PMAC_FTR_MESH_ENABLE, heathrow_mesh_enable }, + { PMAC_FTR_IDE_ENABLE, heathrow_ide_enable }, + { PMAC_FTR_IDE_RESET, heathrow_ide_reset }, + { PMAC_FTR_BMAC_ENABLE, heathrow_bmac_enable }, + { 0, NULL } +}; + +/* Heathrow based laptop, that is the Wallstreet and mainstreet + * powerbooks. + */ +static struct feature_table_entry heathrow_laptop_features[] __pmacdata = { + { PMAC_FTR_SCC_ENABLE, ohare_htw_scc_enable }, + { PMAC_FTR_MODEM_ENABLE, heathrow_modem_enable }, + { PMAC_FTR_SWIM3_ENABLE, heathrow_floppy_enable }, + { PMAC_FTR_MESH_ENABLE, heathrow_mesh_enable }, + { PMAC_FTR_IDE_ENABLE, heathrow_ide_enable }, + { PMAC_FTR_IDE_RESET, heathrow_ide_reset }, + { PMAC_FTR_BMAC_ENABLE, heathrow_bmac_enable }, + { PMAC_FTR_SOUND_CHIP_ENABLE, heathrow_sound_enable }, + { PMAC_FTR_SLEEP_STATE, heathrow_sleep_state }, + { 0, NULL } +}; + +/* Paddington based machines + * The lombard (101) powerbook, first iMac models, B&W G3 and Yikes G4. + */ +static struct feature_table_entry paddington_features[] __pmacdata = { + { PMAC_FTR_SCC_ENABLE, ohare_htw_scc_enable }, + { PMAC_FTR_MODEM_ENABLE, heathrow_modem_enable }, + { PMAC_FTR_SWIM3_ENABLE, heathrow_floppy_enable }, + { PMAC_FTR_MESH_ENABLE, heathrow_mesh_enable }, + { PMAC_FTR_IDE_ENABLE, heathrow_ide_enable }, + { PMAC_FTR_IDE_RESET, heathrow_ide_reset }, + { PMAC_FTR_BMAC_ENABLE, heathrow_bmac_enable }, + { PMAC_FTR_SOUND_CHIP_ENABLE, heathrow_sound_enable }, + { PMAC_FTR_SLEEP_STATE, heathrow_sleep_state }, + { 0, NULL } +}; + +/* Core99 & MacRISC 2 machines (all machines released since the + * iBook (included), that is all AGP machines, except pangea + * chipset. The pangea chipset is the "combo" UniNorth/KeyLargo + * used on iBook2 & iMac "flow power". + */ +static struct feature_table_entry core99_features[] __pmacdata = { + { PMAC_FTR_SCC_ENABLE, core99_scc_enable }, + { PMAC_FTR_MODEM_ENABLE, core99_modem_enable }, + { PMAC_FTR_IDE_ENABLE, core99_ide_enable }, + { PMAC_FTR_IDE_RESET, core99_ide_reset }, + { PMAC_FTR_GMAC_ENABLE, core99_gmac_enable }, + { PMAC_FTR_GMAC_PHY_RESET, core99_gmac_phy_reset }, + { PMAC_FTR_SOUND_CHIP_ENABLE, core99_sound_chip_enable }, + { PMAC_FTR_AIRPORT_ENABLE, core99_airport_enable }, + { PMAC_FTR_USB_ENABLE, core99_usb_enable }, + { PMAC_FTR_1394_ENABLE, core99_firewire_enable }, + { PMAC_FTR_1394_CABLE_POWER, core99_firewire_cable_power }, + { PMAC_FTR_SLEEP_STATE, core99_sleep_state }, +#ifdef CONFIG_SMP + { PMAC_FTR_RESET_CPU, core99_reset_cpu }, +#endif /* CONFIG_SMP */ + { PMAC_FTR_READ_GPIO, core99_read_gpio }, + { PMAC_FTR_WRITE_GPIO, core99_write_gpio }, + { 0, NULL } +}; + +/* RackMac + */ +static struct feature_table_entry rackmac_features[] __pmacdata = { + { PMAC_FTR_SCC_ENABLE, core99_scc_enable }, + { PMAC_FTR_IDE_ENABLE, core99_ide_enable }, + { PMAC_FTR_IDE_RESET, core99_ide_reset }, + { PMAC_FTR_GMAC_ENABLE, core99_gmac_enable }, + { PMAC_FTR_GMAC_PHY_RESET, core99_gmac_phy_reset }, + { PMAC_FTR_USB_ENABLE, core99_usb_enable }, + { PMAC_FTR_1394_ENABLE, core99_firewire_enable }, + { PMAC_FTR_1394_CABLE_POWER, core99_firewire_cable_power }, + { PMAC_FTR_SLEEP_STATE, core99_sleep_state }, +#ifdef CONFIG_SMP + { PMAC_FTR_RESET_CPU, core99_reset_cpu }, +#endif /* CONFIG_SMP */ + { PMAC_FTR_READ_GPIO, core99_read_gpio }, + { PMAC_FTR_WRITE_GPIO, core99_write_gpio }, + { 0, NULL } +}; + +/* Pangea features + */ +static struct feature_table_entry pangea_features[] __pmacdata = { + { PMAC_FTR_SCC_ENABLE, core99_scc_enable }, + { PMAC_FTR_MODEM_ENABLE, pangea_modem_enable }, + { PMAC_FTR_IDE_ENABLE, core99_ide_enable }, + { PMAC_FTR_IDE_RESET, core99_ide_reset }, + { PMAC_FTR_GMAC_ENABLE, core99_gmac_enable }, + { PMAC_FTR_GMAC_PHY_RESET, core99_gmac_phy_reset }, + { PMAC_FTR_SOUND_CHIP_ENABLE, core99_sound_chip_enable }, + { PMAC_FTR_AIRPORT_ENABLE, core99_airport_enable }, + { PMAC_FTR_USB_ENABLE, core99_usb_enable }, + { PMAC_FTR_1394_ENABLE, core99_firewire_enable }, + { PMAC_FTR_1394_CABLE_POWER, core99_firewire_cable_power }, + { PMAC_FTR_SLEEP_STATE, core99_sleep_state }, + { PMAC_FTR_READ_GPIO, core99_read_gpio }, + { PMAC_FTR_WRITE_GPIO, core99_write_gpio }, + { 0, NULL } +}; + +/* Intrepid features + */ +static struct feature_table_entry intrepid_features[] __pmacdata = { + { PMAC_FTR_SCC_ENABLE, core99_scc_enable }, + { PMAC_FTR_MODEM_ENABLE, pangea_modem_enable }, + { PMAC_FTR_IDE_ENABLE, core99_ide_enable }, + { PMAC_FTR_IDE_RESET, core99_ide_reset }, + { PMAC_FTR_GMAC_ENABLE, core99_gmac_enable }, + { PMAC_FTR_GMAC_PHY_RESET, core99_gmac_phy_reset }, + { PMAC_FTR_SOUND_CHIP_ENABLE, core99_sound_chip_enable }, + { PMAC_FTR_AIRPORT_ENABLE, core99_airport_enable }, + { PMAC_FTR_USB_ENABLE, core99_usb_enable }, + { PMAC_FTR_1394_ENABLE, core99_firewire_enable }, + { PMAC_FTR_1394_CABLE_POWER, core99_firewire_cable_power }, + { PMAC_FTR_SLEEP_STATE, core99_sleep_state }, + { PMAC_FTR_READ_GPIO, core99_read_gpio }, + { PMAC_FTR_WRITE_GPIO, core99_write_gpio }, + { PMAC_FTR_AACK_DELAY_ENABLE, intrepid_aack_delay_enable }, + { 0, NULL } +}; + +#else /* CONFIG_POWER4 */ + +/* G5 features + */ +static struct feature_table_entry g5_features[] __pmacdata = { + { PMAC_FTR_GMAC_ENABLE, g5_gmac_enable }, + { PMAC_FTR_1394_ENABLE, g5_fw_enable }, + { PMAC_FTR_ENABLE_MPIC, g5_mpic_enable }, +#ifdef CONFIG_SMP + { PMAC_FTR_RESET_CPU, g5_reset_cpu }, +#endif /* CONFIG_SMP */ + { PMAC_FTR_READ_GPIO, core99_read_gpio }, + { PMAC_FTR_WRITE_GPIO, core99_write_gpio }, + { 0, NULL } +}; + +#endif /* CONFIG_POWER4 */ + +static struct pmac_mb_def pmac_mb_defs[] __pmacdata = { +#ifndef CONFIG_POWER4 + /* + * Desktops + */ + + { "AAPL,8500", "PowerMac 8500/8600", + PMAC_TYPE_PSURGE, NULL, + 0 + }, + { "AAPL,9500", "PowerMac 9500/9600", + PMAC_TYPE_PSURGE, NULL, + 0 + }, + { "AAPL,7200", "PowerMac 7200", + PMAC_TYPE_PSURGE, NULL, + 0 + }, + { "AAPL,7300", "PowerMac 7200/7300", + PMAC_TYPE_PSURGE, NULL, + 0 + }, + { "AAPL,7500", "PowerMac 7500", + PMAC_TYPE_PSURGE, NULL, + 0 + }, + { "AAPL,ShinerESB", "Apple Network Server", + PMAC_TYPE_ANS, NULL, + 0 + }, + { "AAPL,e407", "Alchemy", + PMAC_TYPE_ALCHEMY, NULL, + 0 + }, + { "AAPL,e411", "Gazelle", + PMAC_TYPE_GAZELLE, NULL, + 0 + }, + { "AAPL,Gossamer", "PowerMac G3 (Gossamer)", + PMAC_TYPE_GOSSAMER, heathrow_desktop_features, + 0 + }, + { "AAPL,PowerMac G3", "PowerMac G3 (Silk)", + PMAC_TYPE_SILK, heathrow_desktop_features, + 0 + }, + { "PowerMac1,1", "Blue&White G3", + PMAC_TYPE_YOSEMITE, paddington_features, + 0 + }, + { "PowerMac1,2", "PowerMac G4 PCI Graphics", + PMAC_TYPE_YIKES, paddington_features, + 0 + }, + { "PowerMac2,1", "iMac FireWire", + PMAC_TYPE_FW_IMAC, core99_features, + PMAC_MB_MAY_SLEEP | PMAC_MB_OLD_CORE99 + }, + { "PowerMac2,2", "iMac FireWire", + PMAC_TYPE_FW_IMAC, core99_features, + PMAC_MB_MAY_SLEEP | PMAC_MB_OLD_CORE99 + }, + { "PowerMac3,1", "PowerMac G4 AGP Graphics", + PMAC_TYPE_SAWTOOTH, core99_features, + PMAC_MB_OLD_CORE99 + }, + { "PowerMac3,2", "PowerMac G4 AGP Graphics", + PMAC_TYPE_SAWTOOTH, core99_features, + PMAC_MB_MAY_SLEEP | PMAC_MB_OLD_CORE99 + }, + { "PowerMac3,3", "PowerMac G4 AGP Graphics", + PMAC_TYPE_SAWTOOTH, core99_features, + PMAC_MB_MAY_SLEEP | PMAC_MB_OLD_CORE99 + }, + { "PowerMac3,4", "PowerMac G4 Silver", + PMAC_TYPE_QUICKSILVER, core99_features, + PMAC_MB_MAY_SLEEP + }, + { "PowerMac3,5", "PowerMac G4 Silver", + PMAC_TYPE_QUICKSILVER, core99_features, + PMAC_MB_MAY_SLEEP + }, + { "PowerMac3,6", "PowerMac G4 Windtunnel", + PMAC_TYPE_WINDTUNNEL, core99_features, + PMAC_MB_MAY_SLEEP, + }, + { "PowerMac4,1", "iMac \"Flower Power\"", + PMAC_TYPE_PANGEA_IMAC, pangea_features, + PMAC_MB_MAY_SLEEP + }, + { "PowerMac4,2", "Flat panel iMac", + PMAC_TYPE_FLAT_PANEL_IMAC, pangea_features, + PMAC_MB_CAN_SLEEP + }, + { "PowerMac4,4", "eMac", + PMAC_TYPE_EMAC, core99_features, + PMAC_MB_MAY_SLEEP + }, + { "PowerMac5,1", "PowerMac G4 Cube", + PMAC_TYPE_CUBE, core99_features, + PMAC_MB_MAY_SLEEP | PMAC_MB_OLD_CORE99 + }, + { "PowerMac6,1", "Flat panel iMac", + PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features, + PMAC_MB_MAY_SLEEP, + }, + { "PowerMac6,3", "Flat panel iMac", + PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features, + PMAC_MB_MAY_SLEEP, + }, + { "PowerMac6,4", "eMac", + PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features, + PMAC_MB_MAY_SLEEP, + }, + { "PowerMac10,1", "Mac mini", + PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features, + PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER, + }, + { "iMac,1", "iMac (first generation)", + PMAC_TYPE_ORIG_IMAC, paddington_features, + 0 + }, + + /* + * Xserve's + */ + + { "RackMac1,1", "XServe", + PMAC_TYPE_RACKMAC, rackmac_features, + 0, + }, + { "RackMac1,2", "XServe rev. 2", + PMAC_TYPE_RACKMAC, rackmac_features, + 0, + }, + + /* + * Laptops + */ + + { "AAPL,3400/2400", "PowerBook 3400", + PMAC_TYPE_HOOPER, ohare_features, + PMAC_MB_CAN_SLEEP | PMAC_MB_MOBILE + }, + { "AAPL,3500", "PowerBook 3500", + PMAC_TYPE_KANGA, ohare_features, + PMAC_MB_CAN_SLEEP | PMAC_MB_MOBILE + }, + { "AAPL,PowerBook1998", "PowerBook Wallstreet", + PMAC_TYPE_WALLSTREET, heathrow_laptop_features, + PMAC_MB_CAN_SLEEP | PMAC_MB_MOBILE + }, + { "PowerBook1,1", "PowerBook 101 (Lombard)", + PMAC_TYPE_101_PBOOK, paddington_features, + PMAC_MB_MAY_SLEEP | PMAC_MB_MOBILE + }, + { "PowerBook2,1", "iBook (first generation)", + PMAC_TYPE_ORIG_IBOOK, core99_features, + PMAC_MB_CAN_SLEEP | PMAC_MB_OLD_CORE99 | PMAC_MB_MOBILE + }, + { "PowerBook2,2", "iBook FireWire", + PMAC_TYPE_FW_IBOOK, core99_features, + PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | + PMAC_MB_OLD_CORE99 | PMAC_MB_MOBILE + }, + { "PowerBook3,1", "PowerBook Pismo", + PMAC_TYPE_PISMO, core99_features, + PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | + PMAC_MB_OLD_CORE99 | PMAC_MB_MOBILE + }, + { "PowerBook3,2", "PowerBook Titanium", + PMAC_TYPE_TITANIUM, core99_features, + PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE + }, + { "PowerBook3,3", "PowerBook Titanium II", + PMAC_TYPE_TITANIUM2, core99_features, + PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE + }, + { "PowerBook3,4", "PowerBook Titanium III", + PMAC_TYPE_TITANIUM3, core99_features, + PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE + }, + { "PowerBook3,5", "PowerBook Titanium IV", + PMAC_TYPE_TITANIUM4, core99_features, + PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE + }, + { "PowerBook4,1", "iBook 2", + PMAC_TYPE_IBOOK2, pangea_features, + PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE + }, + { "PowerBook4,2", "iBook 2", + PMAC_TYPE_IBOOK2, pangea_features, + PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE + }, + { "PowerBook4,3", "iBook 2 rev. 2", + PMAC_TYPE_IBOOK2, pangea_features, + PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE + }, + { "PowerBook5,1", "PowerBook G4 17\"", + PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features, + PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE, + }, + { "PowerBook5,2", "PowerBook G4 15\"", + PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features, + PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE, + }, + { "PowerBook5,3", "PowerBook G4 17\"", + PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features, + PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE, + }, + { "PowerBook5,4", "PowerBook G4 15\"", + PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features, + PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE, + }, + { "PowerBook5,5", "PowerBook G4 17\"", + PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features, + PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE, + }, + { "PowerBook5,6", "PowerBook G4 15\"", + PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features, + PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE, + }, + { "PowerBook5,7", "PowerBook G4 17\"", + PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features, + PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE, + }, + { "PowerBook6,1", "PowerBook G4 12\"", + PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features, + PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE, + }, + { "PowerBook6,2", "PowerBook G4", + PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features, + PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE, + }, + { "PowerBook6,3", "iBook G4", + PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features, + PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE, + }, + { "PowerBook6,4", "PowerBook G4 12\"", + PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features, + PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE, + }, + { "PowerBook6,5", "iBook G4", + PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features, + PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE, + }, + { "PowerBook6,8", "PowerBook G4 12\"", + PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features, + PMAC_MB_MAY_SLEEP | PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE, + }, +#else /* CONFIG_POWER4 */ + { "PowerMac7,2", "PowerMac G5", + PMAC_TYPE_POWERMAC_G5, g5_features, + 0, + }, +#endif /* CONFIG_POWER4 */ +}; + +/* + * The toplevel feature_call callback + */ +long __pmac +pmac_do_feature_call(unsigned int selector, ...) +{ + struct device_node* node; + long param, value; + int i; + feature_call func = NULL; + va_list args; + + if (pmac_mb.features) + for (i=0; pmac_mb.features[i].function; i++) + if (pmac_mb.features[i].selector == selector) { + func = pmac_mb.features[i].function; + break; + } + if (!func) + for (i=0; any_features[i].function; i++) + if (any_features[i].selector == selector) { + func = any_features[i].function; + break; + } + if (!func) + return -ENODEV; + + va_start(args, selector); + node = (struct device_node*)va_arg(args, void*); + param = va_arg(args, long); + value = va_arg(args, long); + va_end(args); + + return func(node, param, value); +} + +static int __init +probe_motherboard(void) +{ + int i; + struct macio_chip* macio = &macio_chips[0]; + const char* model = NULL; + struct device_node *dt; + + /* Lookup known motherboard type in device-tree. First try an + * exact match on the "model" property, then try a "compatible" + * match is none is found. + */ + dt = find_devices("device-tree"); + if (dt != NULL) + model = (const char *) get_property(dt, "model", NULL); + for(i=0; model && i<(sizeof(pmac_mb_defs)/sizeof(struct pmac_mb_def)); i++) { + if (strcmp(model, pmac_mb_defs[i].model_string) == 0) { + pmac_mb = pmac_mb_defs[i]; + goto found; + } + } + for(i=0; i<(sizeof(pmac_mb_defs)/sizeof(struct pmac_mb_def)); i++) { + if (machine_is_compatible(pmac_mb_defs[i].model_string)) { + pmac_mb = pmac_mb_defs[i]; + goto found; + } + } + + /* Fallback to selection depending on mac-io chip type */ + switch(macio->type) { +#ifndef CONFIG_POWER4 + case macio_grand_central: + pmac_mb.model_id = PMAC_TYPE_PSURGE; + pmac_mb.model_name = "Unknown PowerSurge"; + break; + case macio_ohare: + pmac_mb.model_id = PMAC_TYPE_UNKNOWN_OHARE; + pmac_mb.model_name = "Unknown OHare-based"; + break; + case macio_heathrow: + pmac_mb.model_id = PMAC_TYPE_UNKNOWN_HEATHROW; + pmac_mb.model_name = "Unknown Heathrow-based"; + pmac_mb.features = heathrow_desktop_features; + break; + case macio_paddington: + pmac_mb.model_id = PMAC_TYPE_UNKNOWN_PADDINGTON; + pmac_mb.model_name = "Unknown Paddington-based"; + pmac_mb.features = paddington_features; + break; + case macio_keylargo: + pmac_mb.model_id = PMAC_TYPE_UNKNOWN_CORE99; + pmac_mb.model_name = "Unknown Keylargo-based"; + pmac_mb.features = core99_features; + break; + case macio_pangea: + pmac_mb.model_id = PMAC_TYPE_UNKNOWN_PANGEA; + pmac_mb.model_name = "Unknown Pangea-based"; + pmac_mb.features = pangea_features; + break; + case macio_intrepid: + pmac_mb.model_id = PMAC_TYPE_UNKNOWN_INTREPID; + pmac_mb.model_name = "Unknown Intrepid-based"; + pmac_mb.features = intrepid_features; + break; +#else /* CONFIG_POWER4 */ + case macio_keylargo2: + pmac_mb.model_id = PMAC_TYPE_UNKNOWN_K2; + pmac_mb.model_name = "Unknown G5"; + pmac_mb.features = g5_features; + break; +#endif /* CONFIG_POWER4 */ + default: + return -ENODEV; + } +found: +#ifndef CONFIG_POWER4 + /* Fixup Hooper vs. Comet */ + if (pmac_mb.model_id == PMAC_TYPE_HOOPER) { + u32 __iomem * mach_id_ptr = ioremap(0xf3000034, 4); + if (!mach_id_ptr) + return -ENODEV; + /* Here, I used to disable the media-bay on comet. It + * appears this is wrong, the floppy connector is actually + * a kind of media-bay and works with the current driver. + */ + if (__raw_readl(mach_id_ptr) & 0x20000000UL) + pmac_mb.model_id = PMAC_TYPE_COMET; + iounmap(mach_id_ptr); + } +#endif /* CONFIG_POWER4 */ + +#ifdef CONFIG_6xx + /* Set default value of powersave_nap on machines that support it. + * It appears that uninorth rev 3 has a problem with it, we don't + * enable it on those. In theory, the flush-on-lock property is + * supposed to be set when not supported, but I'm not very confident + * that all Apple OF revs did it properly, I do it the paranoid way. + */ + while (uninorth_base && uninorth_rev > 3) { + struct device_node* np = find_path_device("/cpus"); + if (!np || !np->child) { + printk(KERN_WARNING "Can't find CPU(s) in device tree !\n"); + break; + } + np = np->child; + /* Nap mode not supported on SMP */ + if (np->sibling) + break; + /* Nap mode not supported if flush-on-lock property is present */ + if (get_property(np, "flush-on-lock", NULL)) + break; + powersave_nap = 1; + printk(KERN_INFO "Processor NAP mode on idle enabled.\n"); + break; + } + + /* On CPUs that support it (750FX), lowspeed by default during + * NAP mode + */ + powersave_lowspeed = 1; +#endif /* CONFIG_6xx */ +#ifdef CONFIG_POWER4 + powersave_nap = 1; +#endif + /* Check for "mobile" machine */ + if (model && (strncmp(model, "PowerBook", 9) == 0 + || strncmp(model, "iBook", 5) == 0)) + pmac_mb.board_flags |= PMAC_MB_MOBILE; + + + printk(KERN_INFO "PowerMac motherboard: %s\n", pmac_mb.model_name); + return 0; +} + +/* Initialize the Core99 UniNorth host bridge and memory controller + */ +static void __init +probe_uninorth(void) +{ + unsigned long actrl; + + /* Locate core99 Uni-N */ + uninorth_node = of_find_node_by_name(NULL, "uni-n"); + /* Locate G5 u3 */ + if (uninorth_node == NULL) { + uninorth_node = of_find_node_by_name(NULL, "u3"); + uninorth_u3 = 1; + } + if (uninorth_node && uninorth_node->n_addrs > 0) { + unsigned long address = uninorth_node->addrs[0].address; + uninorth_base = ioremap(address, 0x40000); + uninorth_rev = in_be32(UN_REG(UNI_N_VERSION)); + if (uninorth_u3) + u3_ht = ioremap(address + U3_HT_CONFIG_BASE, 0x1000); + } else + uninorth_node = NULL; + + if (!uninorth_node) + return; + + printk(KERN_INFO "Found %s memory controller & host bridge, revision: %d\n", + uninorth_u3 ? "U3" : "UniNorth", uninorth_rev); + printk(KERN_INFO "Mapped at 0x%08lx\n", (unsigned long)uninorth_base); + + /* Set the arbitrer QAck delay according to what Apple does + */ + if (uninorth_rev < 0x11) { + actrl = UN_IN(UNI_N_ARB_CTRL) & ~UNI_N_ARB_CTRL_QACK_DELAY_MASK; + actrl |= ((uninorth_rev < 3) ? UNI_N_ARB_CTRL_QACK_DELAY105 : + UNI_N_ARB_CTRL_QACK_DELAY) << UNI_N_ARB_CTRL_QACK_DELAY_SHIFT; + UN_OUT(UNI_N_ARB_CTRL, actrl); + } + + /* Some more magic as done by them in recent MacOS X on UniNorth + * revs 1.5 to 2.O and Pangea. Seem to toggle the UniN Maxbus/PCI + * memory timeout + */ + if ((uninorth_rev >= 0x11 && uninorth_rev <= 0x24) || uninorth_rev == 0xc0) + UN_OUT(0x2160, UN_IN(0x2160) & 0x00ffffff); +} + +static void __init +probe_one_macio(const char* name, const char* compat, int type) +{ + struct device_node* node; + int i; + volatile u32 __iomem * base; + u32* revp; + + node = find_devices(name); + if (!node || !node->n_addrs) + return; + if (compat) + do { + if (device_is_compatible(node, compat)) + break; + node = node->next; + } while (node); + if (!node) + return; + for(i=0; i= MAX_MACIO_CHIPS) { + printk(KERN_ERR "pmac_feature: Please increase MAX_MACIO_CHIPS !\n"); + printk(KERN_ERR "pmac_feature: %s skipped\n", node->full_name); + return; + } + base = ioremap(node->addrs[0].address, node->addrs[0].size); + if (!base) { + printk(KERN_ERR "pmac_feature: Can't map mac-io chip !\n"); + return; + } + if (type == macio_keylargo) { + u32* did = (u32 *)get_property(node, "device-id", NULL); + if (*did == 0x00000025) + type = macio_pangea; + if (*did == 0x0000003e) + type = macio_intrepid; + } + macio_chips[i].of_node = node; + macio_chips[i].type = type; + macio_chips[i].base = base; + macio_chips[i].flags = MACIO_FLAG_SCCB_ON | MACIO_FLAG_SCCB_ON; + macio_chips[i].name = macio_names[type]; + revp = (u32 *)get_property(node, "revision-id", NULL); + if (revp) + macio_chips[i].rev = *revp; + printk(KERN_INFO "Found a %s mac-io controller, rev: %d, mapped at 0x%p\n", + macio_names[type], macio_chips[i].rev, macio_chips[i].base); +} + +static int __init +probe_macios(void) +{ + /* Warning, ordering is important */ + probe_one_macio("gc", NULL, macio_grand_central); + probe_one_macio("ohare", NULL, macio_ohare); + probe_one_macio("pci106b,7", NULL, macio_ohareII); + probe_one_macio("mac-io", "keylargo", macio_keylargo); + probe_one_macio("mac-io", "paddington", macio_paddington); + probe_one_macio("mac-io", "gatwick", macio_gatwick); + probe_one_macio("mac-io", "heathrow", macio_heathrow); + probe_one_macio("mac-io", "K2-Keylargo", macio_keylargo2); + + /* Make sure the "main" macio chip appear first */ + if (macio_chips[0].type == macio_gatwick + && macio_chips[1].type == macio_heathrow) { + struct macio_chip temp = macio_chips[0]; + macio_chips[0] = macio_chips[1]; + macio_chips[1] = temp; + } + if (macio_chips[0].type == macio_ohareII + && macio_chips[1].type == macio_ohare) { + struct macio_chip temp = macio_chips[0]; + macio_chips[0] = macio_chips[1]; + macio_chips[1] = temp; + } + macio_chips[0].lbus.index = 0; + macio_chips[1].lbus.index = 1; + + return (macio_chips[0].of_node == NULL) ? -ENODEV : 0; +} + +static void __init +initial_serial_shutdown(struct device_node* np) +{ + int len; + struct slot_names_prop { + int count; + char name[1]; + } *slots; + char *conn; + int port_type = PMAC_SCC_ASYNC; + int modem = 0; + + slots = (struct slot_names_prop *)get_property(np, "slot-names", &len); + conn = get_property(np, "AAPL,connector", &len); + if (conn && (strcmp(conn, "infrared") == 0)) + port_type = PMAC_SCC_IRDA; + else if (device_is_compatible(np, "cobalt")) + modem = 1; + else if (slots && slots->count > 0) { + if (strcmp(slots->name, "IrDA") == 0) + port_type = PMAC_SCC_IRDA; + else if (strcmp(slots->name, "Modem") == 0) + modem = 1; + } + if (modem) + pmac_call_feature(PMAC_FTR_MODEM_ENABLE, np, 0, 0); + pmac_call_feature(PMAC_FTR_SCC_ENABLE, np, port_type, 0); +} + +static void __init +set_initial_features(void) +{ + struct device_node* np; + + /* That hack appears to be necessary for some StarMax motherboards + * but I'm not too sure it was audited for side-effects on other + * ohare based machines... + * Since I still have difficulties figuring the right way to + * differenciate them all and since that hack was there for a long + * time, I'll keep it around + */ + if (macio_chips[0].type == macio_ohare && !find_devices("via-pmu")) { + struct macio_chip* macio = &macio_chips[0]; + MACIO_OUT32(OHARE_FCR, STARMAX_FEATURES); + } else if (macio_chips[0].type == macio_ohare) { + struct macio_chip* macio = &macio_chips[0]; + MACIO_BIS(OHARE_FCR, OH_IOBUS_ENABLE); + } else if (macio_chips[1].type == macio_ohare) { + struct macio_chip* macio = &macio_chips[1]; + MACIO_BIS(OHARE_FCR, OH_IOBUS_ENABLE); + } + +#ifdef CONFIG_POWER4 + if (macio_chips[0].type == macio_keylargo2) { +#ifndef CONFIG_SMP + /* On SMP machines running UP, we have the second CPU eating + * bus cycles. We need to take it off the bus. This is done + * from pmac_smp for SMP kernels running on one CPU + */ + np = of_find_node_by_type(NULL, "cpu"); + if (np != NULL) + np = of_find_node_by_type(np, "cpu"); + if (np != NULL) { + g5_phy_disable_cpu1(); + of_node_put(np); + } +#endif /* CONFIG_SMP */ + /* Enable GMAC for now for PCI probing. It will be disabled + * later on after PCI probe + */ + np = of_find_node_by_name(NULL, "ethernet"); + while(np) { + if (device_is_compatible(np, "K2-GMAC")) + g5_gmac_enable(np, 0, 1); + np = of_find_node_by_name(np, "ethernet"); + } + + /* Enable FW before PCI probe. Will be disabled later on + * Note: We should have a batter way to check that we are + * dealing with uninorth internal cell and not a PCI cell + * on the external PCI. The code below works though. + */ + np = of_find_node_by_name(NULL, "firewire"); + while(np) { + if (device_is_compatible(np, "pci106b,5811")) { + macio_chips[0].flags |= MACIO_FLAG_FW_SUPPORTED; + g5_fw_enable(np, 0, 1); + } + np = of_find_node_by_name(np, "firewire"); + } + } +#else /* CONFIG_POWER4 */ + + if (macio_chips[0].type == macio_keylargo || + macio_chips[0].type == macio_pangea || + macio_chips[0].type == macio_intrepid) { + /* Enable GMAC for now for PCI probing. It will be disabled + * later on after PCI probe + */ + np = of_find_node_by_name(NULL, "ethernet"); + while(np) { + if (np->parent + && device_is_compatible(np->parent, "uni-north") + && device_is_compatible(np, "gmac")) + core99_gmac_enable(np, 0, 1); + np = of_find_node_by_name(np, "ethernet"); + } + + /* Enable FW before PCI probe. Will be disabled later on + * Note: We should have a batter way to check that we are + * dealing with uninorth internal cell and not a PCI cell + * on the external PCI. The code below works though. + */ + np = of_find_node_by_name(NULL, "firewire"); + while(np) { + if (np->parent + && device_is_compatible(np->parent, "uni-north") + && (device_is_compatible(np, "pci106b,18") || + device_is_compatible(np, "pci106b,30") || + device_is_compatible(np, "pci11c1,5811"))) { + macio_chips[0].flags |= MACIO_FLAG_FW_SUPPORTED; + core99_firewire_enable(np, 0, 1); + } + np = of_find_node_by_name(np, "firewire"); + } + + /* Enable ATA-100 before PCI probe. */ + np = of_find_node_by_name(NULL, "ata-6"); + while(np) { + if (np->parent + && device_is_compatible(np->parent, "uni-north") + && device_is_compatible(np, "kauai-ata")) { + core99_ata100_enable(np, 1); + } + np = of_find_node_by_name(np, "ata-6"); + } + + /* Switch airport off */ + np = find_devices("radio"); + while(np) { + if (np && np->parent == macio_chips[0].of_node) { + macio_chips[0].flags |= MACIO_FLAG_AIRPORT_ON; + core99_airport_enable(np, 0, 0); + } + np = np->next; + } + } + + /* On all machines that support sound PM, switch sound off */ + if (macio_chips[0].of_node) + pmac_do_feature_call(PMAC_FTR_SOUND_CHIP_ENABLE, + macio_chips[0].of_node, 0, 0); + + /* While on some desktop G3s, we turn it back on */ + if (macio_chips[0].of_node && macio_chips[0].type == macio_heathrow + && (pmac_mb.model_id == PMAC_TYPE_GOSSAMER || + pmac_mb.model_id == PMAC_TYPE_SILK)) { + struct macio_chip* macio = &macio_chips[0]; + MACIO_BIS(HEATHROW_FCR, HRW_SOUND_CLK_ENABLE); + MACIO_BIC(HEATHROW_FCR, HRW_SOUND_POWER_N); + } + + /* Hack for bumping clock speed on the new PowerBooks and the + * iBook G4. This implements the "platform-do-clockspreading" OF + * property. For safety, we also check the product ID in the + * device-tree to make reasonably sure we won't set wrong values + * in the clock chip. + * + * Of course, ultimately, we have to implement a real parser for + * the platform-do-* stuff... + */ + while (machine_is_compatible("PowerBook5,2") || + machine_is_compatible("PowerBook5,3") || + machine_is_compatible("PowerBook6,2") || + machine_is_compatible("PowerBook6,3")) { + struct device_node *ui2c = of_find_node_by_type(NULL, "i2c"); + struct device_node *dt = of_find_node_by_name(NULL, "device-tree"); + u8 buffer[9]; + u32 *productID; + int i, rc, changed = 0; + + if (dt == NULL) + break; + productID = (u32 *)get_property(dt, "pid#", NULL); + if (productID == NULL) + break; + while(ui2c) { + struct device_node *p = of_get_parent(ui2c); + if (p && !strcmp(p->name, "uni-n")) + break; + ui2c = of_find_node_by_type(ui2c, "i2c"); + } + if (ui2c == NULL) + break; + DBG("Trying to bump clock speed for PID: %08x...\n", *productID); + rc = pmac_low_i2c_open(ui2c, 1); + if (rc != 0) + break; + pmac_low_i2c_setmode(ui2c, pmac_low_i2c_mode_combined); + rc = pmac_low_i2c_xfer(ui2c, 0xd2 | pmac_low_i2c_read, 0x80, buffer, 9); + DBG("read result: %d,", rc); + if (rc != 0) { + pmac_low_i2c_close(ui2c); + break; + } + for (i=0; i<9; i++) + DBG(" %02x", buffer[i]); + DBG("\n"); + + switch(*productID) { + case 0x1182: /* AlBook 12" rev 2 */ + case 0x1183: /* iBook G4 12" */ + buffer[0] = (buffer[0] & 0x8f) | 0x70; + buffer[2] = (buffer[2] & 0x7f) | 0x00; + buffer[5] = (buffer[5] & 0x80) | 0x31; + buffer[6] = (buffer[6] & 0x40) | 0xb0; + buffer[7] = (buffer[7] & 0x00) | 0xc0; + buffer[8] = (buffer[8] & 0x00) | 0x30; + changed = 1; + break; + case 0x3142: /* AlBook 15" (ATI M10) */ + case 0x3143: /* AlBook 17" (ATI M10) */ + buffer[0] = (buffer[0] & 0xaf) | 0x50; + buffer[2] = (buffer[2] & 0x7f) | 0x00; + buffer[5] = (buffer[5] & 0x80) | 0x31; + buffer[6] = (buffer[6] & 0x40) | 0xb0; + buffer[7] = (buffer[7] & 0x00) | 0xd0; + buffer[8] = (buffer[8] & 0x00) | 0x30; + changed = 1; + break; + default: + DBG("i2c-hwclock: Machine model not handled\n"); + break; + } + if (!changed) { + pmac_low_i2c_close(ui2c); + break; + } + pmac_low_i2c_setmode(ui2c, pmac_low_i2c_mode_stdsub); + rc = pmac_low_i2c_xfer(ui2c, 0xd2 | pmac_low_i2c_write, 0x80, buffer, 9); + DBG("write result: %d,", rc); + pmac_low_i2c_setmode(ui2c, pmac_low_i2c_mode_combined); + rc = pmac_low_i2c_xfer(ui2c, 0xd2 | pmac_low_i2c_read, 0x80, buffer, 9); + DBG("read result: %d,", rc); + if (rc != 0) { + pmac_low_i2c_close(ui2c); + break; + } + for (i=0; i<9; i++) + DBG(" %02x", buffer[i]); + pmac_low_i2c_close(ui2c); + break; + } + +#endif /* CONFIG_POWER4 */ + + /* On all machines, switch modem & serial ports off */ + np = find_devices("ch-a"); + while(np) { + initial_serial_shutdown(np); + np = np->next; + } + np = find_devices("ch-b"); + while(np) { + initial_serial_shutdown(np); + np = np->next; + } +} + +void __init +pmac_feature_init(void) +{ + /* Detect the UniNorth memory controller */ + probe_uninorth(); + + /* Probe mac-io controllers */ + if (probe_macios()) { + printk(KERN_WARNING "No mac-io chip found\n"); + return; + } + + /* Setup low-level i2c stuffs */ + pmac_init_low_i2c(); + + /* Probe machine type */ + if (probe_motherboard()) + printk(KERN_WARNING "Unknown PowerMac !\n"); + + /* Set some initial features (turn off some chips that will + * be later turned on) + */ + set_initial_features(); +} + +int __init +pmac_feature_late_init(void) +{ + struct device_node* np; + + /* Request some resources late */ + if (uninorth_node) + request_OF_resource(uninorth_node, 0, NULL); + np = find_devices("hammerhead"); + if (np) + request_OF_resource(np, 0, NULL); + np = find_devices("interrupt-controller"); + if (np) + request_OF_resource(np, 0, NULL); + return 0; +} + +device_initcall(pmac_feature_late_init); + +#ifdef CONFIG_POWER4 + +static void dump_HT_speeds(char *name, u32 cfg, u32 frq) +{ + int freqs[16] = { 200,300,400,500,600,800,1000,0,0,0,0,0,0,0,0,0 }; + int bits[8] = { 8,16,0,32,2,4,0,0 }; + int freq = (frq >> 8) & 0xf; + + if (freqs[freq] == 0) + printk("%s: Unknown HT link frequency %x\n", name, freq); + else + printk("%s: %d MHz on main link, (%d in / %d out) bits width\n", + name, freqs[freq], + bits[(cfg >> 28) & 0x7], bits[(cfg >> 24) & 0x7]); +} + +void __init pmac_check_ht_link(void) +{ + u32 ufreq, freq, ucfg, cfg; + struct device_node *pcix_node; + u8 px_bus, px_devfn; + struct pci_controller *px_hose; + + (void)in_be32(u3_ht + U3_HT_LINK_COMMAND); + ucfg = cfg = in_be32(u3_ht + U3_HT_LINK_CONFIG); + ufreq = freq = in_be32(u3_ht + U3_HT_LINK_FREQ); + dump_HT_speeds("U3 HyperTransport", cfg, freq); + + pcix_node = of_find_compatible_node(NULL, "pci", "pci-x"); + if (pcix_node == NULL) { + printk("No PCI-X bridge found\n"); + return; + } + if (pci_device_from_OF_node(pcix_node, &px_bus, &px_devfn) != 0) { + printk("PCI-X bridge found but not matched to pci\n"); + return; + } + px_hose = pci_find_hose_for_OF_device(pcix_node); + if (px_hose == NULL) { + printk("PCI-X bridge found but not matched to host\n"); + return; + } + early_read_config_dword(px_hose, px_bus, px_devfn, 0xc4, &cfg); + early_read_config_dword(px_hose, px_bus, px_devfn, 0xcc, &freq); + dump_HT_speeds("PCI-X HT Uplink", cfg, freq); + early_read_config_dword(px_hose, px_bus, px_devfn, 0xc8, &cfg); + early_read_config_dword(px_hose, px_bus, px_devfn, 0xd0, &freq); + dump_HT_speeds("PCI-X HT Downlink", cfg, freq); +} + +#endif /* CONFIG_POWER4 */ + +/* + * Early video resume hook + */ + +static void (*pmac_early_vresume_proc)(void *data) __pmacdata; +static void *pmac_early_vresume_data __pmacdata; + +void pmac_set_early_video_resume(void (*proc)(void *data), void *data) +{ + if (_machine != _MACH_Pmac) + return; + preempt_disable(); + pmac_early_vresume_proc = proc; + pmac_early_vresume_data = data; + preempt_enable(); +} +EXPORT_SYMBOL(pmac_set_early_video_resume); + +void __pmac pmac_call_early_video_resume(void) +{ + if (pmac_early_vresume_proc) + pmac_early_vresume_proc(pmac_early_vresume_data); +} diff --git a/arch/ppc/platforms/pmac_low_i2c.c b/arch/ppc/platforms/pmac_low_i2c.c new file mode 100644 index 00000000000..d07579f2b8b --- /dev/null +++ b/arch/ppc/platforms/pmac_low_i2c.c @@ -0,0 +1,513 @@ +/* + * arch/ppc/platforms/pmac_low_i2c.c + * + * Copyright (C) 2003 Ben. Herrenschmidt (benh@kernel.crashing.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * This file contains some low-level i2c access routines that + * need to be used by various bits of the PowerMac platform code + * at times where the real asynchronous & interrupt driven driver + * cannot be used. The API borrows some semantics from the darwin + * driver in order to ease the implementation of the platform + * properties parser + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_LOW_I2C_HOST 4 + +#if 1 +#define DBG(x...) do {\ + printk(KERN_DEBUG "KW:" x); \ + } while(0) +#else +#define DBGG(x...) +#endif + +struct low_i2c_host; + +typedef int (*low_i2c_func_t)(struct low_i2c_host *host, u8 addr, u8 sub, u8 *data, int len); + +struct low_i2c_host +{ + struct device_node *np; /* OF device node */ + struct semaphore mutex; /* Access mutex for use by i2c-keywest */ + low_i2c_func_t func; /* Access function */ + int is_open : 1; /* Poor man's access control */ + int mode; /* Current mode */ + int channel; /* Current channel */ + int num_channels; /* Number of channels */ + unsigned long base; /* For keywest-i2c, base address */ + int bsteps; /* And register stepping */ + int speed; /* And speed */ +}; + +static struct low_i2c_host low_i2c_hosts[MAX_LOW_I2C_HOST]; + +/* No locking is necessary on allocation, we are running way before + * anything can race with us + */ +static struct low_i2c_host *find_low_i2c_host(struct device_node *np) +{ + int i; + + for (i = 0; i < MAX_LOW_I2C_HOST; i++) + if (low_i2c_hosts[i].np == np) + return &low_i2c_hosts[i]; + return NULL; +} + +/* + * + * i2c-keywest implementation (UniNorth, U2, U3, Keylargo's) + * + */ + +/* + * Keywest i2c definitions borrowed from drivers/i2c/i2c-keywest.h, + * should be moved somewhere in include/asm-ppc/ + */ +/* Register indices */ +typedef enum { + reg_mode = 0, + reg_control, + reg_status, + reg_isr, + reg_ier, + reg_addr, + reg_subaddr, + reg_data +} reg_t; + + +/* Mode register */ +#define KW_I2C_MODE_100KHZ 0x00 +#define KW_I2C_MODE_50KHZ 0x01 +#define KW_I2C_MODE_25KHZ 0x02 +#define KW_I2C_MODE_DUMB 0x00 +#define KW_I2C_MODE_STANDARD 0x04 +#define KW_I2C_MODE_STANDARDSUB 0x08 +#define KW_I2C_MODE_COMBINED 0x0C +#define KW_I2C_MODE_MODE_MASK 0x0C +#define KW_I2C_MODE_CHAN_MASK 0xF0 + +/* Control register */ +#define KW_I2C_CTL_AAK 0x01 +#define KW_I2C_CTL_XADDR 0x02 +#define KW_I2C_CTL_STOP 0x04 +#define KW_I2C_CTL_START 0x08 + +/* Status register */ +#define KW_I2C_STAT_BUSY 0x01 +#define KW_I2C_STAT_LAST_AAK 0x02 +#define KW_I2C_STAT_LAST_RW 0x04 +#define KW_I2C_STAT_SDA 0x08 +#define KW_I2C_STAT_SCL 0x10 + +/* IER & ISR registers */ +#define KW_I2C_IRQ_DATA 0x01 +#define KW_I2C_IRQ_ADDR 0x02 +#define KW_I2C_IRQ_STOP 0x04 +#define KW_I2C_IRQ_START 0x08 +#define KW_I2C_IRQ_MASK 0x0F + +/* State machine states */ +enum { + state_idle, + state_addr, + state_read, + state_write, + state_stop, + state_dead +}; + +#define WRONG_STATE(name) do {\ + printk(KERN_DEBUG "KW: wrong state. Got %s, state: %s (isr: %02x)\n", \ + name, __kw_state_names[state], isr); \ + } while(0) + +static const char *__kw_state_names[] = { + "state_idle", + "state_addr", + "state_read", + "state_write", + "state_stop", + "state_dead" +}; + +static inline u8 __kw_read_reg(struct low_i2c_host *host, reg_t reg) +{ + return in_8(((volatile u8 *)host->base) + + (((unsigned)reg) << host->bsteps)); +} + +static inline void __kw_write_reg(struct low_i2c_host *host, reg_t reg, u8 val) +{ + out_8(((volatile u8 *)host->base) + + (((unsigned)reg) << host->bsteps), val); + (void)__kw_read_reg(host, reg_subaddr); +} + +#define kw_write_reg(reg, val) __kw_write_reg(host, reg, val) +#define kw_read_reg(reg) __kw_read_reg(host, reg) + + +/* Don't schedule, the g5 fan controller is too + * timing sensitive + */ +static u8 kw_wait_interrupt(struct low_i2c_host* host) +{ + int i; + u8 isr; + + for (i = 0; i < 200000; i++) { + isr = kw_read_reg(reg_isr) & KW_I2C_IRQ_MASK; + if (isr != 0) + return isr; + udelay(1); + } + return isr; +} + +static int kw_handle_interrupt(struct low_i2c_host *host, int state, int rw, int *rc, u8 **data, int *len, u8 isr) +{ + u8 ack; + + if (isr == 0) { + if (state != state_stop) { + DBG("KW: Timeout !\n"); + *rc = -EIO; + goto stop; + } + if (state == state_stop) { + ack = kw_read_reg(reg_status); + if (!(ack & KW_I2C_STAT_BUSY)) { + state = state_idle; + kw_write_reg(reg_ier, 0x00); + } + } + return state; + } + + if (isr & KW_I2C_IRQ_ADDR) { + ack = kw_read_reg(reg_status); + if (state != state_addr) { + kw_write_reg(reg_isr, KW_I2C_IRQ_ADDR); + WRONG_STATE("KW_I2C_IRQ_ADDR"); + *rc = -EIO; + goto stop; + } + if ((ack & KW_I2C_STAT_LAST_AAK) == 0) { + *rc = -ENODEV; + DBG("KW: NAK on address\n"); + return state_stop; + } else { + if (rw) { + state = state_read; + if (*len > 1) + kw_write_reg(reg_control, KW_I2C_CTL_AAK); + } else { + state = state_write; + kw_write_reg(reg_data, **data); + (*data)++; (*len)--; + } + } + kw_write_reg(reg_isr, KW_I2C_IRQ_ADDR); + } + + if (isr & KW_I2C_IRQ_DATA) { + if (state == state_read) { + **data = kw_read_reg(reg_data); + (*data)++; (*len)--; + kw_write_reg(reg_isr, KW_I2C_IRQ_DATA); + if ((*len) == 0) + state = state_stop; + else if ((*len) == 1) + kw_write_reg(reg_control, 0); + } else if (state == state_write) { + ack = kw_read_reg(reg_status); + if ((ack & KW_I2C_STAT_LAST_AAK) == 0) { + DBG("KW: nack on data write\n"); + *rc = -EIO; + goto stop; + } else if (*len) { + kw_write_reg(reg_data, **data); + (*data)++; (*len)--; + } else { + kw_write_reg(reg_control, KW_I2C_CTL_STOP); + state = state_stop; + *rc = 0; + } + kw_write_reg(reg_isr, KW_I2C_IRQ_DATA); + } else { + kw_write_reg(reg_isr, KW_I2C_IRQ_DATA); + WRONG_STATE("KW_I2C_IRQ_DATA"); + if (state != state_stop) { + *rc = -EIO; + goto stop; + } + } + } + + if (isr & KW_I2C_IRQ_STOP) { + kw_write_reg(reg_isr, KW_I2C_IRQ_STOP); + if (state != state_stop) { + WRONG_STATE("KW_I2C_IRQ_STOP"); + *rc = -EIO; + } + return state_idle; + } + + if (isr & KW_I2C_IRQ_START) + kw_write_reg(reg_isr, KW_I2C_IRQ_START); + + return state; + + stop: + kw_write_reg(reg_control, KW_I2C_CTL_STOP); + return state_stop; +} + +static int keywest_low_i2c_func(struct low_i2c_host *host, u8 addr, u8 subaddr, u8 *data, int len) +{ + u8 mode_reg = host->speed; + int state = state_addr; + int rc = 0; + + /* Setup mode & subaddress if any */ + switch(host->mode) { + case pmac_low_i2c_mode_dumb: + printk(KERN_ERR "low_i2c: Dumb mode not supported !\n"); + return -EINVAL; + case pmac_low_i2c_mode_std: + mode_reg |= KW_I2C_MODE_STANDARD; + break; + case pmac_low_i2c_mode_stdsub: + mode_reg |= KW_I2C_MODE_STANDARDSUB; + kw_write_reg(reg_subaddr, subaddr); + break; + case pmac_low_i2c_mode_combined: + mode_reg |= KW_I2C_MODE_COMBINED; + kw_write_reg(reg_subaddr, subaddr); + break; + } + + /* Setup channel & clear pending irqs */ + kw_write_reg(reg_isr, kw_read_reg(reg_isr)); + kw_write_reg(reg_mode, mode_reg | (host->channel << 4)); + kw_write_reg(reg_status, 0); + + /* Set up address and r/w bit */ + kw_write_reg(reg_addr, addr); + + /* Start sending address & disable interrupt*/ + kw_write_reg(reg_ier, 0 /*KW_I2C_IRQ_MASK*/); + kw_write_reg(reg_control, KW_I2C_CTL_XADDR); + + /* State machine, to turn into an interrupt handler */ + while(state != state_idle) { + u8 isr = kw_wait_interrupt(host); + state = kw_handle_interrupt(host, state, addr & 1, &rc, &data, &len, isr); + } + + return rc; +} + +static void keywest_low_i2c_add(struct device_node *np) +{ + struct low_i2c_host *host = find_low_i2c_host(NULL); + unsigned long *psteps, *prate, steps, aoffset = 0; + struct device_node *parent; + + if (host == NULL) { + printk(KERN_ERR "low_i2c: Can't allocate host for %s\n", + np->full_name); + return; + } + memset(host, 0, sizeof(*host)); + + init_MUTEX(&host->mutex); + host->np = of_node_get(np); + psteps = (unsigned long *)get_property(np, "AAPL,address-step", NULL); + steps = psteps ? (*psteps) : 0x10; + for (host->bsteps = 0; (steps & 0x01) == 0; host->bsteps++) + steps >>= 1; + parent = of_get_parent(np); + host->num_channels = 1; + if (parent && parent->name[0] == 'u') { + host->num_channels = 2; + aoffset = 3; + } + /* Select interface rate */ + host->speed = KW_I2C_MODE_100KHZ; + prate = (unsigned long *)get_property(np, "AAPL,i2c-rate", NULL); + if (prate) switch(*prate) { + case 100: + host->speed = KW_I2C_MODE_100KHZ; + break; + case 50: + host->speed = KW_I2C_MODE_50KHZ; + break; + case 25: + host->speed = KW_I2C_MODE_25KHZ; + break; + } + host->mode = pmac_low_i2c_mode_std; + host->base = (unsigned long)ioremap(np->addrs[0].address + aoffset, + np->addrs[0].size); + host->func = keywest_low_i2c_func; +} + +/* + * + * PMU implementation + * + */ + + +#ifdef CONFIG_ADB_PMU + +static int pmu_low_i2c_func(struct low_i2c_host *host, u8 addr, u8 sub, u8 *data, int len) +{ + // TODO + return -ENODEV; +} + +static void pmu_low_i2c_add(struct device_node *np) +{ + struct low_i2c_host *host = find_low_i2c_host(NULL); + + if (host == NULL) { + printk(KERN_ERR "low_i2c: Can't allocate host for %s\n", + np->full_name); + return; + } + memset(host, 0, sizeof(*host)); + + init_MUTEX(&host->mutex); + host->np = of_node_get(np); + host->num_channels = 3; + host->mode = pmac_low_i2c_mode_std; + host->func = pmu_low_i2c_func; +} + +#endif /* CONFIG_ADB_PMU */ + +void __init pmac_init_low_i2c(void) +{ + struct device_node *np; + + /* Probe keywest-i2c busses */ + np = of_find_compatible_node(NULL, "i2c", "keywest-i2c"); + while(np) { + keywest_low_i2c_add(np); + np = of_find_compatible_node(np, "i2c", "keywest-i2c"); + } + +#ifdef CONFIG_ADB_PMU + /* Probe PMU busses */ + np = of_find_node_by_name(NULL, "via-pmu"); + if (np) + pmu_low_i2c_add(np); +#endif /* CONFIG_ADB_PMU */ + + /* TODO: Add CUDA support as well */ +} + +int pmac_low_i2c_lock(struct device_node *np) +{ + struct low_i2c_host *host = find_low_i2c_host(np); + + if (!host) + return -ENODEV; + down(&host->mutex); + return 0; +} +EXPORT_SYMBOL(pmac_low_i2c_lock); + +int pmac_low_i2c_unlock(struct device_node *np) +{ + struct low_i2c_host *host = find_low_i2c_host(np); + + if (!host) + return -ENODEV; + up(&host->mutex); + return 0; +} +EXPORT_SYMBOL(pmac_low_i2c_unlock); + + +int pmac_low_i2c_open(struct device_node *np, int channel) +{ + struct low_i2c_host *host = find_low_i2c_host(np); + + if (!host) + return -ENODEV; + + if (channel >= host->num_channels) + return -EINVAL; + + down(&host->mutex); + host->is_open = 1; + host->channel = channel; + + return 0; +} +EXPORT_SYMBOL(pmac_low_i2c_open); + +int pmac_low_i2c_close(struct device_node *np) +{ + struct low_i2c_host *host = find_low_i2c_host(np); + + if (!host) + return -ENODEV; + + host->is_open = 0; + up(&host->mutex); + + return 0; +} +EXPORT_SYMBOL(pmac_low_i2c_close); + +int pmac_low_i2c_setmode(struct device_node *np, int mode) +{ + struct low_i2c_host *host = find_low_i2c_host(np); + + if (!host) + return -ENODEV; + WARN_ON(!host->is_open); + host->mode = mode; + + return 0; +} +EXPORT_SYMBOL(pmac_low_i2c_setmode); + +int pmac_low_i2c_xfer(struct device_node *np, u8 addrdir, u8 subaddr, u8 *data, int len) +{ + struct low_i2c_host *host = find_low_i2c_host(np); + + if (!host) + return -ENODEV; + WARN_ON(!host->is_open); + + return host->func(host, addrdir, subaddr, data, len); +} +EXPORT_SYMBOL(pmac_low_i2c_xfer); + diff --git a/arch/ppc/platforms/pmac_nvram.c b/arch/ppc/platforms/pmac_nvram.c new file mode 100644 index 00000000000..c9de6420599 --- /dev/null +++ b/arch/ppc/platforms/pmac_nvram.c @@ -0,0 +1,584 @@ +/* + * arch/ppc/platforms/pmac_nvram.c + * + * Copyright (C) 2002 Benjamin Herrenschmidt (benh@kernel.crashing.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Todo: - add support for the OF persistent properties + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +#define NVRAM_SIZE 0x2000 /* 8kB of non-volatile RAM */ + +#define CORE99_SIGNATURE 0x5a +#define CORE99_ADLER_START 0x14 + +/* On Core99, nvram is either a sharp, a micron or an AMD flash */ +#define SM_FLASH_STATUS_DONE 0x80 +#define SM_FLASH_STATUS_ERR 0x38 +#define SM_FLASH_CMD_ERASE_CONFIRM 0xd0 +#define SM_FLASH_CMD_ERASE_SETUP 0x20 +#define SM_FLASH_CMD_RESET 0xff +#define SM_FLASH_CMD_WRITE_SETUP 0x40 +#define SM_FLASH_CMD_CLEAR_STATUS 0x50 +#define SM_FLASH_CMD_READ_STATUS 0x70 + +/* CHRP NVRAM header */ +struct chrp_header { + u8 signature; + u8 cksum; + u16 len; + char name[12]; + u8 data[0]; +}; + +struct core99_header { + struct chrp_header hdr; + u32 adler; + u32 generation; + u32 reserved[2]; +}; + +/* + * Read and write the non-volatile RAM on PowerMacs and CHRP machines. + */ +static int nvram_naddrs; +static volatile unsigned char *nvram_addr; +static volatile unsigned char *nvram_data; +static int nvram_mult, is_core_99; +static int core99_bank = 0; +static int nvram_partitions[3]; +static DEFINE_SPINLOCK(nv_lock); + +extern int pmac_newworld; +extern int system_running; + +static int (*core99_write_bank)(int bank, u8* datas); +static int (*core99_erase_bank)(int bank); + +static char *nvram_image __pmacdata; + + +static unsigned char __pmac core99_nvram_read_byte(int addr) +{ + if (nvram_image == NULL) + return 0xff; + return nvram_image[addr]; +} + +static void __pmac core99_nvram_write_byte(int addr, unsigned char val) +{ + if (nvram_image == NULL) + return; + nvram_image[addr] = val; +} + + +static unsigned char __openfirmware direct_nvram_read_byte(int addr) +{ + return in_8(&nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult]); +} + +static void __openfirmware direct_nvram_write_byte(int addr, unsigned char val) +{ + out_8(&nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult], val); +} + + +static unsigned char __pmac indirect_nvram_read_byte(int addr) +{ + unsigned char val; + unsigned long flags; + + spin_lock_irqsave(&nv_lock, flags); + out_8(nvram_addr, addr >> 5); + val = in_8(&nvram_data[(addr & 0x1f) << 4]); + spin_unlock_irqrestore(&nv_lock, flags); + + return val; +} + +static void __pmac indirect_nvram_write_byte(int addr, unsigned char val) +{ + unsigned long flags; + + spin_lock_irqsave(&nv_lock, flags); + out_8(nvram_addr, addr >> 5); + out_8(&nvram_data[(addr & 0x1f) << 4], val); + spin_unlock_irqrestore(&nv_lock, flags); +} + + +#ifdef CONFIG_ADB_PMU + +static void __pmac pmu_nvram_complete(struct adb_request *req) +{ + if (req->arg) + complete((struct completion *)req->arg); +} + +static unsigned char __pmac pmu_nvram_read_byte(int addr) +{ + struct adb_request req; + DECLARE_COMPLETION(req_complete); + + req.arg = system_state == SYSTEM_RUNNING ? &req_complete : NULL; + if (pmu_request(&req, pmu_nvram_complete, 3, PMU_READ_NVRAM, + (addr >> 8) & 0xff, addr & 0xff)) + return 0xff; + if (system_state == SYSTEM_RUNNING) + wait_for_completion(&req_complete); + while (!req.complete) + pmu_poll(); + return req.reply[0]; +} + +static void __pmac pmu_nvram_write_byte(int addr, unsigned char val) +{ + struct adb_request req; + DECLARE_COMPLETION(req_complete); + + req.arg = system_state == SYSTEM_RUNNING ? &req_complete : NULL; + if (pmu_request(&req, pmu_nvram_complete, 4, PMU_WRITE_NVRAM, + (addr >> 8) & 0xff, addr & 0xff, val)) + return; + if (system_state == SYSTEM_RUNNING) + wait_for_completion(&req_complete); + while (!req.complete) + pmu_poll(); +} + +#endif /* CONFIG_ADB_PMU */ + + +static u8 __pmac chrp_checksum(struct chrp_header* hdr) +{ + u8 *ptr; + u16 sum = hdr->signature; + for (ptr = (u8 *)&hdr->len; ptr < hdr->data; ptr++) + sum += *ptr; + while (sum > 0xFF) + sum = (sum & 0xFF) + (sum>>8); + return sum; +} + +static u32 __pmac core99_calc_adler(u8 *buffer) +{ + int cnt; + u32 low, high; + + buffer += CORE99_ADLER_START; + low = 1; + high = 0; + for (cnt=0; cnt<(NVRAM_SIZE-CORE99_ADLER_START); cnt++) { + if ((cnt % 5000) == 0) { + high %= 65521UL; + high %= 65521UL; + } + low += buffer[cnt]; + high += low; + } + low %= 65521UL; + high %= 65521UL; + + return (high << 16) | low; +} + +static u32 __pmac core99_check(u8* datas) +{ + struct core99_header* hdr99 = (struct core99_header*)datas; + + if (hdr99->hdr.signature != CORE99_SIGNATURE) { + DBG("Invalid signature\n"); + return 0; + } + if (hdr99->hdr.cksum != chrp_checksum(&hdr99->hdr)) { + DBG("Invalid checksum\n"); + return 0; + } + if (hdr99->adler != core99_calc_adler(datas)) { + DBG("Invalid adler\n"); + return 0; + } + return hdr99->generation; +} + +static int __pmac sm_erase_bank(int bank) +{ + int stat, i; + unsigned long timeout; + + u8* base = (u8 *)nvram_data + core99_bank*NVRAM_SIZE; + + DBG("nvram: Sharp/Micron Erasing bank %d...\n", bank); + + out_8(base, SM_FLASH_CMD_ERASE_SETUP); + out_8(base, SM_FLASH_CMD_ERASE_CONFIRM); + timeout = 0; + do { + if (++timeout > 1000000) { + printk(KERN_ERR "nvram: Sharp/Miron flash erase timeout !\n"); + break; + } + out_8(base, SM_FLASH_CMD_READ_STATUS); + stat = in_8(base); + } while (!(stat & SM_FLASH_STATUS_DONE)); + + out_8(base, SM_FLASH_CMD_CLEAR_STATUS); + out_8(base, SM_FLASH_CMD_RESET); + + for (i=0; i 1000000) { + printk(KERN_ERR "nvram: Sharp/Micron flash write timeout !\n"); + break; + } + out_8(base, SM_FLASH_CMD_READ_STATUS); + stat = in_8(base); + } while (!(stat & SM_FLASH_STATUS_DONE)); + if (!(stat & SM_FLASH_STATUS_DONE)) + break; + } + out_8(base, SM_FLASH_CMD_CLEAR_STATUS); + out_8(base, SM_FLASH_CMD_RESET); + for (i=0; i 1000000) { + printk(KERN_ERR "nvram: AMD flash erase timeout !\n"); + break; + } + stat = in_8(base) ^ in_8(base); + } while (stat != 0); + + /* Reset */ + out_8(base, 0xf0); + udelay(1); + + for (i=0; i 1000000) { + printk(KERN_ERR "nvram: AMD flash write timeout !\n"); + break; + } + stat = in_8(base) ^ in_8(base); + } while (stat != 0); + if (stat != 0) + break; + } + + /* Reset */ + out_8(base, 0xf0); + udelay(1); + + for (i=0; iname, "common")) + nvram_partitions[pmac_nvram_OF] = offset + 0x10; + if (!strcmp(hdr->name, "APL,MacOS75")) { + nvram_partitions[pmac_nvram_XPRAM] = offset + 0x10; + nvram_partitions[pmac_nvram_NR] = offset + 0x110; + } + offset += (hdr->len * 0x10); + } while(offset < NVRAM_SIZE); + } else { + nvram_partitions[pmac_nvram_OF] = 0x1800; + nvram_partitions[pmac_nvram_XPRAM] = 0x1300; + nvram_partitions[pmac_nvram_NR] = 0x1400; + } + DBG("nvram: OF partition at 0x%x\n", nvram_partitions[pmac_nvram_OF]); + DBG("nvram: XP partition at 0x%x\n", nvram_partitions[pmac_nvram_XPRAM]); + DBG("nvram: NR partition at 0x%x\n", nvram_partitions[pmac_nvram_NR]); +} + +static void __pmac core99_nvram_sync(void) +{ + struct core99_header* hdr99; + unsigned long flags; + + if (!is_core_99 || !nvram_data || !nvram_image) + return; + + spin_lock_irqsave(&nv_lock, flags); + if (!memcmp(nvram_image, (u8*)nvram_data + core99_bank*NVRAM_SIZE, + NVRAM_SIZE)) + goto bail; + + DBG("Updating nvram...\n"); + + hdr99 = (struct core99_header*)nvram_image; + hdr99->generation++; + hdr99->hdr.signature = CORE99_SIGNATURE; + hdr99->hdr.cksum = chrp_checksum(&hdr99->hdr); + hdr99->adler = core99_calc_adler(nvram_image); + core99_bank = core99_bank ? 0 : 1; + if (core99_erase_bank) + if (core99_erase_bank(core99_bank)) { + printk("nvram: Error erasing bank %d\n", core99_bank); + goto bail; + } + if (core99_write_bank) + if (core99_write_bank(core99_bank, nvram_image)) + printk("nvram: Error writing bank %d\n", core99_bank); + bail: + spin_unlock_irqrestore(&nv_lock, flags); + +#ifdef DEBUG + mdelay(2000); +#endif +} + +void __init pmac_nvram_init(void) +{ + struct device_node *dp; + + nvram_naddrs = 0; + + dp = find_devices("nvram"); + if (dp == NULL) { + printk(KERN_ERR "Can't find NVRAM device\n"); + return; + } + nvram_naddrs = dp->n_addrs; + is_core_99 = device_is_compatible(dp, "nvram,flash"); + if (is_core_99) { + int i; + u32 gen_bank0, gen_bank1; + + if (nvram_naddrs < 1) { + printk(KERN_ERR "nvram: no address\n"); + return; + } + nvram_image = alloc_bootmem(NVRAM_SIZE); + if (nvram_image == NULL) { + printk(KERN_ERR "nvram: can't allocate ram image\n"); + return; + } + nvram_data = ioremap(dp->addrs[0].address, NVRAM_SIZE*2); + nvram_naddrs = 1; /* Make sure we get the correct case */ + + DBG("nvram: Checking bank 0...\n"); + + gen_bank0 = core99_check((u8 *)nvram_data); + gen_bank1 = core99_check((u8 *)nvram_data + NVRAM_SIZE); + core99_bank = (gen_bank0 < gen_bank1) ? 1 : 0; + + DBG("nvram: gen0=%d, gen1=%d\n", gen_bank0, gen_bank1); + DBG("nvram: Active bank is: %d\n", core99_bank); + + for (i=0; iaddrs[0].address + isa_mem_base, + dp->addrs[0].size); + nvram_mult = 1; + ppc_md.nvram_read_val = direct_nvram_read_byte; + ppc_md.nvram_write_val = direct_nvram_write_byte; + } else if (nvram_naddrs == 1) { + nvram_data = ioremap(dp->addrs[0].address, dp->addrs[0].size); + nvram_mult = (dp->addrs[0].size + NVRAM_SIZE - 1) / NVRAM_SIZE; + ppc_md.nvram_read_val = direct_nvram_read_byte; + ppc_md.nvram_write_val = direct_nvram_write_byte; + } else if (nvram_naddrs == 2) { + nvram_addr = ioremap(dp->addrs[0].address, dp->addrs[0].size); + nvram_data = ioremap(dp->addrs[1].address, dp->addrs[1].size); + ppc_md.nvram_read_val = indirect_nvram_read_byte; + ppc_md.nvram_write_val = indirect_nvram_write_byte; + } else if (nvram_naddrs == 0 && sys_ctrler == SYS_CTRLER_PMU) { +#ifdef CONFIG_ADB_PMU + nvram_naddrs = -1; + ppc_md.nvram_read_val = pmu_nvram_read_byte; + ppc_md.nvram_write_val = pmu_nvram_write_byte; +#endif /* CONFIG_ADB_PMU */ + } else { + printk(KERN_ERR "Don't know how to access NVRAM with %d addresses\n", + nvram_naddrs); + } + lookup_partitions(); +} + +int __pmac pmac_get_partition(int partition) +{ + return nvram_partitions[partition]; +} + +u8 __pmac pmac_xpram_read(int xpaddr) +{ + int offset = nvram_partitions[pmac_nvram_XPRAM]; + + if (offset < 0) + return 0xff; + + return ppc_md.nvram_read_val(xpaddr + offset); +} + +void __pmac pmac_xpram_write(int xpaddr, u8 data) +{ + int offset = nvram_partitions[pmac_nvram_XPRAM]; + + if (offset < 0) + return; + + ppc_md.nvram_write_val(xpaddr + offset, data); +} + +EXPORT_SYMBOL(pmac_get_partition); +EXPORT_SYMBOL(pmac_xpram_read); +EXPORT_SYMBOL(pmac_xpram_write); diff --git a/arch/ppc/platforms/pmac_pci.c b/arch/ppc/platforms/pmac_pci.c new file mode 100644 index 00000000000..f6ff5192406 --- /dev/null +++ b/arch/ppc/platforms/pmac_pci.c @@ -0,0 +1,1125 @@ +/* + * Support for PCI bridges found on Power Macintoshes. + * At present the "bandit" and "chaos" bridges are supported. + * Fortunately you access configuration space in the same + * way with either bridge. + * + * Copyright (C) 1997 Paul Mackerras (paulus@cs.anu.edu.au) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#undef DEBUG + +#ifdef DEBUG +#ifdef CONFIG_XMON +extern void xmon_printf(const char *fmt, ...); +#define DBG(x...) xmon_printf(x) +#else +#define DBG(x...) printk(x) +#endif +#else +#define DBG(x...) +#endif + +static int add_bridge(struct device_node *dev); +extern void pmac_check_ht_link(void); + +/* XXX Could be per-controller, but I don't think we risk anything by + * assuming we won't have both UniNorth and Bandit */ +static int has_uninorth; +#ifdef CONFIG_POWER4 +static struct pci_controller *u3_agp; +#endif /* CONFIG_POWER4 */ + +extern u8 pci_cache_line_size; +extern int pcibios_assign_bus_offset; + +struct device_node *k2_skiplist[2]; + +/* + * Magic constants for enabling cache coherency in the bandit/PSX bridge. + */ +#define BANDIT_DEVID_2 8 +#define BANDIT_REVID 3 + +#define BANDIT_DEVNUM 11 +#define BANDIT_MAGIC 0x50 +#define BANDIT_COHERENT 0x40 + +static int __init +fixup_one_level_bus_range(struct device_node *node, int higher) +{ + for (; node != 0;node = node->sibling) { + int * bus_range; + unsigned int *class_code; + int len; + + /* For PCI<->PCI bridges or CardBus bridges, we go down */ + class_code = (unsigned int *) get_property(node, "class-code", NULL); + if (!class_code || ((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI && + (*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS)) + continue; + bus_range = (int *) get_property(node, "bus-range", &len); + if (bus_range != NULL && len > 2 * sizeof(int)) { + if (bus_range[1] > higher) + higher = bus_range[1]; + } + higher = fixup_one_level_bus_range(node->child, higher); + } + return higher; +} + +/* This routine fixes the "bus-range" property of all bridges in the + * system since they tend to have their "last" member wrong on macs + * + * Note that the bus numbers manipulated here are OF bus numbers, they + * are not Linux bus numbers. + */ +static void __init +fixup_bus_range(struct device_node *bridge) +{ + int * bus_range; + int len; + + /* Lookup the "bus-range" property for the hose */ + bus_range = (int *) get_property(bridge, "bus-range", &len); + if (bus_range == NULL || len < 2 * sizeof(int)) { + printk(KERN_WARNING "Can't get bus-range for %s\n", + bridge->full_name); + return; + } + bus_range[1] = fixup_one_level_bus_range(bridge->child, bus_range[1]); +} + +/* + * Apple MacRISC (U3, UniNorth, Bandit, Chaos) PCI controllers. + * + * The "Bandit" version is present in all early PCI PowerMacs, + * and up to the first ones using Grackle. Some machines may + * have 2 bandit controllers (2 PCI busses). + * + * "Chaos" is used in some "Bandit"-type machines as a bridge + * for the separate display bus. It is accessed the same + * way as bandit, but cannot be probed for devices. It therefore + * has its own config access functions. + * + * The "UniNorth" version is present in all Core99 machines + * (iBook, G4, new IMacs, and all the recent Apple machines). + * It contains 3 controllers in one ASIC. + * + * The U3 is the bridge used on G5 machines. It contains an + * AGP bus which is dealt with the old UniNorth access routines + * and a HyperTransport bus which uses its own set of access + * functions. + */ + +#define MACRISC_CFA0(devfn, off) \ + ((1 << (unsigned long)PCI_SLOT(dev_fn)) \ + | (((unsigned long)PCI_FUNC(dev_fn)) << 8) \ + | (((unsigned long)(off)) & 0xFCUL)) + +#define MACRISC_CFA1(bus, devfn, off) \ + ((((unsigned long)(bus)) << 16) \ + |(((unsigned long)(devfn)) << 8) \ + |(((unsigned long)(off)) & 0xFCUL) \ + |1UL) + +static void volatile __iomem * __pmac +macrisc_cfg_access(struct pci_controller* hose, u8 bus, u8 dev_fn, u8 offset) +{ + unsigned int caddr; + + if (bus == hose->first_busno) { + if (dev_fn < (11 << 3)) + return NULL; + caddr = MACRISC_CFA0(dev_fn, offset); + } else + caddr = MACRISC_CFA1(bus, dev_fn, offset); + + /* Uninorth will return garbage if we don't read back the value ! */ + do { + out_le32(hose->cfg_addr, caddr); + } while (in_le32(hose->cfg_addr) != caddr); + + offset &= has_uninorth ? 0x07 : 0x03; + return hose->cfg_data + offset; +} + +static int __pmac +macrisc_read_config(struct pci_bus *bus, unsigned int devfn, int offset, + int len, u32 *val) +{ + struct pci_controller *hose = bus->sysdata; + void volatile __iomem *addr; + + addr = macrisc_cfg_access(hose, bus->number, devfn, offset); + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + /* + * Note: the caller has already checked that offset is + * suitably aligned and that len is 1, 2 or 4. + */ + switch (len) { + case 1: + *val = in_8(addr); + break; + case 2: + *val = in_le16(addr); + break; + default: + *val = in_le32(addr); + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static int __pmac +macrisc_write_config(struct pci_bus *bus, unsigned int devfn, int offset, + int len, u32 val) +{ + struct pci_controller *hose = bus->sysdata; + void volatile __iomem *addr; + + addr = macrisc_cfg_access(hose, bus->number, devfn, offset); + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + /* + * Note: the caller has already checked that offset is + * suitably aligned and that len is 1, 2 or 4. + */ + switch (len) { + case 1: + out_8(addr, val); + (void) in_8(addr); + break; + case 2: + out_le16(addr, val); + (void) in_le16(addr); + break; + default: + out_le32(addr, val); + (void) in_le32(addr); + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops macrisc_pci_ops = +{ + macrisc_read_config, + macrisc_write_config +}; + +/* + * Verifiy that a specific (bus, dev_fn) exists on chaos + */ +static int __pmac +chaos_validate_dev(struct pci_bus *bus, int devfn, int offset) +{ + struct device_node *np; + u32 *vendor, *device; + + np = pci_busdev_to_OF_node(bus, devfn); + if (np == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; + + vendor = (u32 *)get_property(np, "vendor-id", NULL); + device = (u32 *)get_property(np, "device-id", NULL); + if (vendor == NULL || device == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; + + if ((*vendor == 0x106b) && (*device == 3) && (offset >= 0x10) + && (offset != 0x14) && (offset != 0x18) && (offset <= 0x24)) + return PCIBIOS_BAD_REGISTER_NUMBER; + + return PCIBIOS_SUCCESSFUL; +} + +static int __pmac +chaos_read_config(struct pci_bus *bus, unsigned int devfn, int offset, + int len, u32 *val) +{ + int result = chaos_validate_dev(bus, devfn, offset); + if (result == PCIBIOS_BAD_REGISTER_NUMBER) + *val = ~0U; + if (result != PCIBIOS_SUCCESSFUL) + return result; + return macrisc_read_config(bus, devfn, offset, len, val); +} + +static int __pmac +chaos_write_config(struct pci_bus *bus, unsigned int devfn, int offset, + int len, u32 val) +{ + int result = chaos_validate_dev(bus, devfn, offset); + if (result != PCIBIOS_SUCCESSFUL) + return result; + return macrisc_write_config(bus, devfn, offset, len, val); +} + +static struct pci_ops chaos_pci_ops = +{ + chaos_read_config, + chaos_write_config +}; + +#ifdef CONFIG_POWER4 + +/* + * These versions of U3 HyperTransport config space access ops do not + * implement self-view of the HT host yet + */ + +#define U3_HT_CFA0(devfn, off) \ + ((((unsigned long)devfn) << 8) | offset) +#define U3_HT_CFA1(bus, devfn, off) \ + (U3_HT_CFA0(devfn, off) \ + + (((unsigned long)bus) << 16) \ + + 0x01000000UL) + +static void volatile __iomem * __pmac +u3_ht_cfg_access(struct pci_controller* hose, u8 bus, u8 devfn, u8 offset) +{ + if (bus == hose->first_busno) { + /* For now, we don't self probe U3 HT bridge */ + if (PCI_FUNC(devfn) != 0 || PCI_SLOT(devfn) > 7 || + PCI_SLOT(devfn) < 1) + return 0; + return hose->cfg_data + U3_HT_CFA0(devfn, offset); + } else + return hose->cfg_data + U3_HT_CFA1(bus, devfn, offset); +} + +static int __pmac +u3_ht_read_config(struct pci_bus *bus, unsigned int devfn, int offset, + int len, u32 *val) +{ + struct pci_controller *hose = bus->sysdata; + void volatile __iomem *addr; + int i; + + struct device_node *np = pci_busdev_to_OF_node(bus, devfn); + if (np == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; + + /* + * When a device in K2 is powered down, we die on config + * cycle accesses. Fix that here. + */ + for (i=0; i<2; i++) + if (k2_skiplist[i] == np) { + switch (len) { + case 1: + *val = 0xff; break; + case 2: + *val = 0xffff; break; + default: + *val = 0xfffffffful; break; + } + return PCIBIOS_SUCCESSFUL; + } + + addr = u3_ht_cfg_access(hose, bus->number, devfn, offset); + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + /* + * Note: the caller has already checked that offset is + * suitably aligned and that len is 1, 2 or 4. + */ + switch (len) { + case 1: + *val = in_8(addr); + break; + case 2: + *val = in_le16(addr); + break; + default: + *val = in_le32(addr); + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static int __pmac +u3_ht_write_config(struct pci_bus *bus, unsigned int devfn, int offset, + int len, u32 val) +{ + struct pci_controller *hose = bus->sysdata; + void volatile __iomem *addr; + int i; + + struct device_node *np = pci_busdev_to_OF_node(bus, devfn); + if (np == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; + /* + * When a device in K2 is powered down, we die on config + * cycle accesses. Fix that here. + */ + for (i=0; i<2; i++) + if (k2_skiplist[i] == np) + return PCIBIOS_SUCCESSFUL; + + addr = u3_ht_cfg_access(hose, bus->number, devfn, offset); + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + /* + * Note: the caller has already checked that offset is + * suitably aligned and that len is 1, 2 or 4. + */ + switch (len) { + case 1: + out_8(addr, val); + (void) in_8(addr); + break; + case 2: + out_le16(addr, val); + (void) in_le16(addr); + break; + default: + out_le32(addr, val); + (void) in_le32(addr); + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops u3_ht_pci_ops = +{ + u3_ht_read_config, + u3_ht_write_config +}; + +#endif /* CONFIG_POWER4 */ + +/* + * For a bandit bridge, turn on cache coherency if necessary. + * N.B. we could clean this up using the hose ops directly. + */ +static void __init +init_bandit(struct pci_controller *bp) +{ + unsigned int vendev, magic; + int rev; + + /* read the word at offset 0 in config space for device 11 */ + out_le32(bp->cfg_addr, (1UL << BANDIT_DEVNUM) + PCI_VENDOR_ID); + udelay(2); + vendev = in_le32(bp->cfg_data); + if (vendev == (PCI_DEVICE_ID_APPLE_BANDIT << 16) + + PCI_VENDOR_ID_APPLE) { + /* read the revision id */ + out_le32(bp->cfg_addr, + (1UL << BANDIT_DEVNUM) + PCI_REVISION_ID); + udelay(2); + rev = in_8(bp->cfg_data); + if (rev != BANDIT_REVID) + printk(KERN_WARNING + "Unknown revision %d for bandit\n", rev); + } else if (vendev != (BANDIT_DEVID_2 << 16) + PCI_VENDOR_ID_APPLE) { + printk(KERN_WARNING "bandit isn't? (%x)\n", vendev); + return; + } + + /* read the word at offset 0x50 */ + out_le32(bp->cfg_addr, (1UL << BANDIT_DEVNUM) + BANDIT_MAGIC); + udelay(2); + magic = in_le32(bp->cfg_data); + if ((magic & BANDIT_COHERENT) != 0) + return; + magic |= BANDIT_COHERENT; + udelay(2); + out_le32(bp->cfg_data, magic); + printk(KERN_INFO "Cache coherency enabled for bandit/PSX\n"); +} + + +/* + * Tweak the PCI-PCI bridge chip on the blue & white G3s. + */ +static void __init +init_p2pbridge(void) +{ + struct device_node *p2pbridge; + struct pci_controller* hose; + u8 bus, devfn; + u16 val; + + /* XXX it would be better here to identify the specific + PCI-PCI bridge chip we have. */ + if ((p2pbridge = find_devices("pci-bridge")) == 0 + || p2pbridge->parent == NULL + || strcmp(p2pbridge->parent->name, "pci") != 0) + return; + if (pci_device_from_OF_node(p2pbridge, &bus, &devfn) < 0) { + DBG("Can't find PCI infos for PCI<->PCI bridge\n"); + return; + } + /* Warning: At this point, we have not yet renumbered all busses. + * So we must use OF walking to find out hose + */ + hose = pci_find_hose_for_OF_device(p2pbridge); + if (!hose) { + DBG("Can't find hose for PCI<->PCI bridge\n"); + return; + } + if (early_read_config_word(hose, bus, devfn, + PCI_BRIDGE_CONTROL, &val) < 0) { + printk(KERN_ERR "init_p2pbridge: couldn't read bridge control\n"); + return; + } + val &= ~PCI_BRIDGE_CTL_MASTER_ABORT; + early_write_config_word(hose, bus, devfn, PCI_BRIDGE_CONTROL, val); +} + +/* + * Some Apple desktop machines have a NEC PD720100A USB2 controller + * on the motherboard. Open Firmware, on these, will disable the + * EHCI part of it so it behaves like a pair of OHCI's. This fixup + * code re-enables it ;) + */ +static void __init +fixup_nec_usb2(void) +{ + struct device_node *nec; + + for (nec = NULL; (nec = of_find_node_by_name(nec, "usb")) != NULL;) { + struct pci_controller *hose; + u32 data, *prop; + u8 bus, devfn; + + prop = (u32 *)get_property(nec, "vendor-id", NULL); + if (prop == NULL) + continue; + if (0x1033 != *prop) + continue; + prop = (u32 *)get_property(nec, "device-id", NULL); + if (prop == NULL) + continue; + if (0x0035 != *prop) + continue; + prop = (u32 *)get_property(nec, "reg", NULL); + if (prop == NULL) + continue; + devfn = (prop[0] >> 8) & 0xff; + bus = (prop[0] >> 16) & 0xff; + if (PCI_FUNC(devfn) != 0) + continue; + hose = pci_find_hose_for_OF_device(nec); + if (!hose) + continue; + early_read_config_dword(hose, bus, devfn, 0xe4, &data); + if (data & 1UL) { + printk("Found NEC PD720100A USB2 chip with disabled EHCI, fixing up...\n"); + data &= ~1UL; + early_write_config_dword(hose, bus, devfn, 0xe4, data); + early_write_config_byte(hose, bus, devfn | 2, PCI_INTERRUPT_LINE, + nec->intrs[0].line); + } + } +} + +void __init +pmac_find_bridges(void) +{ + struct device_node *np, *root; + struct device_node *ht = NULL; + + root = of_find_node_by_path("/"); + if (root == NULL) { + printk(KERN_CRIT "pmac_find_bridges: can't find root of device tree\n"); + return; + } + for (np = NULL; (np = of_get_next_child(root, np)) != NULL;) { + if (np->name == NULL) + continue; + if (strcmp(np->name, "bandit") == 0 + || strcmp(np->name, "chaos") == 0 + || strcmp(np->name, "pci") == 0) { + if (add_bridge(np) == 0) + of_node_get(np); + } + if (strcmp(np->name, "ht") == 0) { + of_node_get(np); + ht = np; + } + } + of_node_put(root); + + /* Probe HT last as it relies on the agp resources to be already + * setup + */ + if (ht && add_bridge(ht) != 0) + of_node_put(ht); + + init_p2pbridge(); + fixup_nec_usb2(); + + /* We are still having some issues with the Xserve G4, enabling + * some offset between bus number and domains for now when we + * assign all busses should help for now + */ + if (pci_assign_all_busses) + pcibios_assign_bus_offset = 0x10; + +#ifdef CONFIG_POWER4 + /* There is something wrong with DMA on U3/HT. I haven't figured out + * the details yet, but if I set the cache line size to 128 bytes like + * it should, I'm getting memory corruption caused by devices like + * sungem (even without the MWI bit set, but maybe sungem doesn't + * care). Right now, it appears that setting up a 64 bytes line size + * works properly, 64 bytes beeing the max transfer size of HT, I + * suppose this is related the way HT/PCI are hooked together. I still + * need to dive into more specs though to be really sure of what's + * going on. --BenH. + * + * Ok, apparently, it's just that HT can't do more than 64 bytes + * transactions. MWI seem to be meaningless there as well, it may + * be worth nop'ing out pci_set_mwi too though I haven't done that + * yet. + * + * Note that it's a bit different for whatever is in the AGP slot. + * For now, I don't care, but this can become a real issue, we + * should probably hook pci_set_mwi anyway to make sure it sets + * the real cache line size in there. + */ + if (machine_is_compatible("MacRISC4")) + pci_cache_line_size = 16; /* 64 bytes */ + + pmac_check_ht_link(); +#endif /* CONFIG_POWER4 */ +} + +#define GRACKLE_CFA(b, d, o) (0x80 | ((b) << 8) | ((d) << 16) \ + | (((o) & ~3) << 24)) + +#define GRACKLE_PICR1_STG 0x00000040 +#define GRACKLE_PICR1_LOOPSNOOP 0x00000010 + +/* N.B. this is called before bridges is initialized, so we can't + use grackle_pcibios_{read,write}_config_dword. */ +static inline void grackle_set_stg(struct pci_controller* bp, int enable) +{ + unsigned int val; + + out_be32(bp->cfg_addr, GRACKLE_CFA(0, 0, 0xa8)); + val = in_le32(bp->cfg_data); + val = enable? (val | GRACKLE_PICR1_STG) : + (val & ~GRACKLE_PICR1_STG); + out_be32(bp->cfg_addr, GRACKLE_CFA(0, 0, 0xa8)); + out_le32(bp->cfg_data, val); + (void)in_le32(bp->cfg_data); +} + +static inline void grackle_set_loop_snoop(struct pci_controller *bp, int enable) +{ + unsigned int val; + + out_be32(bp->cfg_addr, GRACKLE_CFA(0, 0, 0xa8)); + val = in_le32(bp->cfg_data); + val = enable? (val | GRACKLE_PICR1_LOOPSNOOP) : + (val & ~GRACKLE_PICR1_LOOPSNOOP); + out_be32(bp->cfg_addr, GRACKLE_CFA(0, 0, 0xa8)); + out_le32(bp->cfg_data, val); + (void)in_le32(bp->cfg_data); +} + +static int __init +setup_uninorth(struct pci_controller* hose, struct reg_property* addr) +{ + pci_assign_all_busses = 1; + has_uninorth = 1; + hose->ops = ¯isc_pci_ops; + hose->cfg_addr = ioremap(addr->address + 0x800000, 0x1000); + hose->cfg_data = ioremap(addr->address + 0xc00000, 0x1000); + /* We "know" that the bridge at f2000000 has the PCI slots. */ + return addr->address == 0xf2000000; +} + +static void __init +setup_bandit(struct pci_controller* hose, struct reg_property* addr) +{ + hose->ops = ¯isc_pci_ops; + hose->cfg_addr = ioremap(addr->address + 0x800000, 0x1000); + hose->cfg_data = ioremap(addr->address + 0xc00000, 0x1000); + init_bandit(hose); +} + +static void __init +setup_chaos(struct pci_controller* hose, struct reg_property* addr) +{ + /* assume a `chaos' bridge */ + hose->ops = &chaos_pci_ops; + hose->cfg_addr = ioremap(addr->address + 0x800000, 0x1000); + hose->cfg_data = ioremap(addr->address + 0xc00000, 0x1000); +} + +#ifdef CONFIG_POWER4 + +static void __init +setup_u3_agp(struct pci_controller* hose, struct reg_property* addr) +{ + /* On G5, we move AGP up to high bus number so we don't need + * to reassign bus numbers for HT. If we ever have P2P bridges + * on AGP, we'll have to move pci_assign_all_busses to the + * pci_controller structure so we enable it for AGP and not for + * HT childs. + * We hard code the address because of the different size of + * the reg address cell, we shall fix that by killing struct + * reg_property and using some accessor functions instead + */ + hose->first_busno = 0xf0; + hose->last_busno = 0xff; + has_uninorth = 1; + hose->ops = ¯isc_pci_ops; + hose->cfg_addr = ioremap(0xf0000000 + 0x800000, 0x1000); + hose->cfg_data = ioremap(0xf0000000 + 0xc00000, 0x1000); + + u3_agp = hose; +} + +static void __init +setup_u3_ht(struct pci_controller* hose, struct reg_property *addr) +{ + struct device_node *np = (struct device_node *)hose->arch_data; + int i, cur; + + hose->ops = &u3_ht_pci_ops; + + /* We hard code the address because of the different size of + * the reg address cell, we shall fix that by killing struct + * reg_property and using some accessor functions instead + */ + hose->cfg_data = ioremap(0xf2000000, 0x02000000); + + /* + * /ht node doesn't expose a "ranges" property, so we "remove" regions that + * have been allocated to AGP. So far, this version of the code doesn't assign + * any of the 0xfxxxxxxx "fine" memory regions to /ht. + * We need to fix that sooner or later by either parsing all child "ranges" + * properties or figuring out the U3 address space decoding logic and + * then read its configuration register (if any). + */ + hose->io_base_phys = 0xf4000000; + hose->io_base_virt = ioremap(hose->io_base_phys, 0x00400000); + isa_io_base = (unsigned long) hose->io_base_virt; + hose->io_resource.name = np->full_name; + hose->io_resource.start = 0; + hose->io_resource.end = 0x003fffff; + hose->io_resource.flags = IORESOURCE_IO; + hose->pci_mem_offset = 0; + hose->first_busno = 0; + hose->last_busno = 0xef; + hose->mem_resources[0].name = np->full_name; + hose->mem_resources[0].start = 0x80000000; + hose->mem_resources[0].end = 0xefffffff; + hose->mem_resources[0].flags = IORESOURCE_MEM; + + if (u3_agp == NULL) { + DBG("U3 has no AGP, using full resource range\n"); + return; + } + + /* We "remove" the AGP resources from the resources allocated to HT, that + * is we create "holes". However, that code does assumptions that so far + * happen to be true (cross fingers...), typically that resources in the + * AGP node are properly ordered + */ + cur = 0; + for (i=0; i<3; i++) { + struct resource *res = &u3_agp->mem_resources[i]; + if (res->flags != IORESOURCE_MEM) + continue; + /* We don't care about "fine" resources */ + if (res->start >= 0xf0000000) + continue; + /* Check if it's just a matter of "shrinking" us in one direction */ + if (hose->mem_resources[cur].start == res->start) { + DBG("U3/HT: shrink start of %d, %08lx -> %08lx\n", + cur, hose->mem_resources[cur].start, res->end + 1); + hose->mem_resources[cur].start = res->end + 1; + continue; + } + if (hose->mem_resources[cur].end == res->end) { + DBG("U3/HT: shrink end of %d, %08lx -> %08lx\n", + cur, hose->mem_resources[cur].end, res->start - 1); + hose->mem_resources[cur].end = res->start - 1; + continue; + } + /* No, it's not the case, we need a hole */ + if (cur == 2) { + /* not enough resources to make a hole, we drop part of the range */ + printk(KERN_WARNING "Running out of resources for /ht host !\n"); + hose->mem_resources[cur].end = res->start - 1; + continue; + } + cur++; + DBG("U3/HT: hole, %d end at %08lx, %d start at %08lx\n", + cur-1, res->start - 1, cur, res->end + 1); + hose->mem_resources[cur].name = np->full_name; + hose->mem_resources[cur].flags = IORESOURCE_MEM; + hose->mem_resources[cur].start = res->end + 1; + hose->mem_resources[cur].end = hose->mem_resources[cur-1].end; + hose->mem_resources[cur-1].end = res->start - 1; + } +} + +#endif /* CONFIG_POWER4 */ + +void __init +setup_grackle(struct pci_controller *hose) +{ + setup_indirect_pci(hose, 0xfec00000, 0xfee00000); + if (machine_is_compatible("AAPL,PowerBook1998")) + grackle_set_loop_snoop(hose, 1); +#if 0 /* Disabled for now, HW problems ??? */ + grackle_set_stg(hose, 1); +#endif +} + +/* + * We assume that if we have a G3 powermac, we have one bridge called + * "pci" (a MPC106) and no bandit or chaos bridges, and contrariwise, + * if we have one or more bandit or chaos bridges, we don't have a MPC106. + */ +static int __init +add_bridge(struct device_node *dev) +{ + int len; + struct pci_controller *hose; + struct reg_property *addr; + char* disp_name; + int *bus_range; + int primary = 1; + + DBG("Adding PCI host bridge %s\n", dev->full_name); + + addr = (struct reg_property *) get_property(dev, "reg", &len); + if (addr == NULL || len < sizeof(*addr)) { + printk(KERN_WARNING "Can't use %s: no address\n", + dev->full_name); + return -ENODEV; + } + bus_range = (int *) get_property(dev, "bus-range", &len); + if (bus_range == NULL || len < 2 * sizeof(int)) { + printk(KERN_WARNING "Can't get bus-range for %s, assume bus 0\n", + dev->full_name); + } + + hose = pcibios_alloc_controller(); + if (!hose) + return -ENOMEM; + hose->arch_data = dev; + hose->first_busno = bus_range ? bus_range[0] : 0; + hose->last_busno = bus_range ? bus_range[1] : 0xff; + + disp_name = NULL; +#ifdef CONFIG_POWER4 + if (device_is_compatible(dev, "u3-agp")) { + setup_u3_agp(hose, addr); + disp_name = "U3-AGP"; + primary = 0; + } else if (device_is_compatible(dev, "u3-ht")) { + setup_u3_ht(hose, addr); + disp_name = "U3-HT"; + primary = 1; + } else +#endif /* CONFIG_POWER4 */ + if (device_is_compatible(dev, "uni-north")) { + primary = setup_uninorth(hose, addr); + disp_name = "UniNorth"; + } else if (strcmp(dev->name, "pci") == 0) { + /* XXX assume this is a mpc106 (grackle) */ + setup_grackle(hose); + disp_name = "Grackle (MPC106)"; + } else if (strcmp(dev->name, "bandit") == 0) { + setup_bandit(hose, addr); + disp_name = "Bandit"; + } else if (strcmp(dev->name, "chaos") == 0) { + setup_chaos(hose, addr); + disp_name = "Chaos"; + primary = 0; + } + printk(KERN_INFO "Found %s PCI host bridge at 0x%08x. Firmware bus number: %d->%d\n", + disp_name, addr->address, hose->first_busno, hose->last_busno); + DBG(" ->Hose at 0x%p, cfg_addr=0x%p,cfg_data=0x%p\n", + hose, hose->cfg_addr, hose->cfg_data); + + /* Interpret the "ranges" property */ + /* This also maps the I/O region and sets isa_io/mem_base */ + pci_process_bridge_OF_ranges(hose, dev, primary); + + /* Fixup "bus-range" OF property */ + fixup_bus_range(dev); + + return 0; +} + +static void __init +pcibios_fixup_OF_interrupts(void) +{ + struct pci_dev* dev = NULL; + + /* + * Open Firmware often doesn't initialize the + * PCI_INTERRUPT_LINE config register properly, so we + * should find the device node and apply the interrupt + * obtained from the OF device-tree + */ + for_each_pci_dev(dev) { + struct device_node *node; + node = pci_device_to_OF_node(dev); + /* this is the node, see if it has interrupts */ + if (node && node->n_intrs > 0) + dev->irq = node->intrs[0].line; + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); + } +} + +void __init +pmac_pcibios_fixup(void) +{ + /* Fixup interrupts according to OF tree */ + pcibios_fixup_OF_interrupts(); +} + +int __pmac +pmac_pci_enable_device_hook(struct pci_dev *dev, int initial) +{ + struct device_node* node; + int updatecfg = 0; + int uninorth_child; + + node = pci_device_to_OF_node(dev); + + /* We don't want to enable USB controllers absent from the OF tree + * (iBook second controller) + */ + if (dev->vendor == PCI_VENDOR_ID_APPLE + && (dev->class == ((PCI_CLASS_SERIAL_USB << 8) | 0x10)) + && !node) { + printk(KERN_INFO "Apple USB OHCI %s disabled by firmware\n", + pci_name(dev)); + return -EINVAL; + } + + if (!node) + return 0; + + uninorth_child = node->parent && + device_is_compatible(node->parent, "uni-north"); + + /* Firewire & GMAC were disabled after PCI probe, the driver is + * claiming them, we must re-enable them now. + */ + if (uninorth_child && !strcmp(node->name, "firewire") && + (device_is_compatible(node, "pci106b,18") || + device_is_compatible(node, "pci106b,30") || + device_is_compatible(node, "pci11c1,5811"))) { + pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, node, 0, 1); + pmac_call_feature(PMAC_FTR_1394_ENABLE, node, 0, 1); + updatecfg = 1; + } + if (uninorth_child && !strcmp(node->name, "ethernet") && + device_is_compatible(node, "gmac")) { + pmac_call_feature(PMAC_FTR_GMAC_ENABLE, node, 0, 1); + updatecfg = 1; + } + + if (updatecfg) { + u16 cmd; + + /* + * Make sure PCI is correctly configured + * + * We use old pci_bios versions of the function since, by + * default, gmac is not powered up, and so will be absent + * from the kernel initial PCI lookup. + * + * Should be replaced by 2.4 new PCI mechanisms and really + * register the device. + */ + pci_read_config_word(dev, PCI_COMMAND, &cmd); + cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE; + pci_write_config_word(dev, PCI_COMMAND, cmd); + pci_write_config_byte(dev, PCI_LATENCY_TIMER, 16); + pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, pci_cache_line_size); + } + + return 0; +} + +/* We power down some devices after they have been probed. They'll + * be powered back on later on + */ +void __init +pmac_pcibios_after_init(void) +{ + struct device_node* nd; + +#ifdef CONFIG_BLK_DEV_IDE + struct pci_dev *dev = NULL; + + /* OF fails to initialize IDE controllers on macs + * (and maybe other machines) + * + * Ideally, this should be moved to the IDE layer, but we need + * to check specifically with Andre Hedrick how to do it cleanly + * since the common IDE code seem to care about the fact that the + * BIOS may have disabled a controller. + * + * -- BenH + */ + for_each_pci_dev(dev) { + if ((dev->class >> 16) == PCI_BASE_CLASS_STORAGE) + pci_enable_device(dev); + } +#endif /* CONFIG_BLK_DEV_IDE */ + + nd = find_devices("firewire"); + while (nd) { + if (nd->parent && (device_is_compatible(nd, "pci106b,18") || + device_is_compatible(nd, "pci106b,30") || + device_is_compatible(nd, "pci11c1,5811")) + && device_is_compatible(nd->parent, "uni-north")) { + pmac_call_feature(PMAC_FTR_1394_ENABLE, nd, 0, 0); + pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, nd, 0, 0); + } + nd = nd->next; + } + nd = find_devices("ethernet"); + while (nd) { + if (nd->parent && device_is_compatible(nd, "gmac") + && device_is_compatible(nd->parent, "uni-north")) + pmac_call_feature(PMAC_FTR_GMAC_ENABLE, nd, 0, 0); + nd = nd->next; + } +} + +void pmac_pci_fixup_cardbus(struct pci_dev* dev) +{ + if (_machine != _MACH_Pmac) + return; + /* + * Fix the interrupt routing on the various cardbus bridges + * used on powerbooks + */ + if (dev->vendor != PCI_VENDOR_ID_TI) + return; + if (dev->device == PCI_DEVICE_ID_TI_1130 || + dev->device == PCI_DEVICE_ID_TI_1131) { + u8 val; + /* Enable PCI interrupt */ + if (pci_read_config_byte(dev, 0x91, &val) == 0) + pci_write_config_byte(dev, 0x91, val | 0x30); + /* Disable ISA interrupt mode */ + if (pci_read_config_byte(dev, 0x92, &val) == 0) + pci_write_config_byte(dev, 0x92, val & ~0x06); + } + if (dev->device == PCI_DEVICE_ID_TI_1210 || + dev->device == PCI_DEVICE_ID_TI_1211 || + dev->device == PCI_DEVICE_ID_TI_1410 || + dev->device == PCI_DEVICE_ID_TI_1510) { + u8 val; + /* 0x8c == TI122X_IRQMUX, 2 says to route the INTA + signal out the MFUNC0 pin */ + if (pci_read_config_byte(dev, 0x8c, &val) == 0) + pci_write_config_byte(dev, 0x8c, (val & ~0x0f) | 2); + /* Disable ISA interrupt mode */ + if (pci_read_config_byte(dev, 0x92, &val) == 0) + pci_write_config_byte(dev, 0x92, val & ~0x06); + } +} + +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_TI, PCI_ANY_ID, pmac_pci_fixup_cardbus); + +void pmac_pci_fixup_pciata(struct pci_dev* dev) +{ + u8 progif = 0; + + /* + * On PowerMacs, we try to switch any PCI ATA controller to + * fully native mode + */ + if (_machine != _MACH_Pmac) + return; + /* Some controllers don't have the class IDE */ + if (dev->vendor == PCI_VENDOR_ID_PROMISE) + switch(dev->device) { + case PCI_DEVICE_ID_PROMISE_20246: + case PCI_DEVICE_ID_PROMISE_20262: + case PCI_DEVICE_ID_PROMISE_20263: + case PCI_DEVICE_ID_PROMISE_20265: + case PCI_DEVICE_ID_PROMISE_20267: + case PCI_DEVICE_ID_PROMISE_20268: + case PCI_DEVICE_ID_PROMISE_20269: + case PCI_DEVICE_ID_PROMISE_20270: + case PCI_DEVICE_ID_PROMISE_20271: + case PCI_DEVICE_ID_PROMISE_20275: + case PCI_DEVICE_ID_PROMISE_20276: + case PCI_DEVICE_ID_PROMISE_20277: + goto good; + } + /* Others, check PCI class */ + if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE) + return; + good: + pci_read_config_byte(dev, PCI_CLASS_PROG, &progif); + if ((progif & 5) != 5) { + printk(KERN_INFO "Forcing PCI IDE into native mode: %s\n", pci_name(dev)); + (void) pci_write_config_byte(dev, PCI_CLASS_PROG, progif|5); + if (pci_read_config_byte(dev, PCI_CLASS_PROG, &progif) || + (progif & 5) != 5) + printk(KERN_ERR "Rewrite of PROGIF failed !\n"); + } +} +DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, pmac_pci_fixup_pciata); + + +/* + * Disable second function on K2-SATA, it's broken + * and disable IO BARs on first one + */ +void __pmac pmac_pci_fixup_k2_sata(struct pci_dev* dev) +{ + int i; + u16 cmd; + + if (PCI_FUNC(dev->devfn) > 0) { + pci_read_config_word(dev, PCI_COMMAND, &cmd); + cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY); + pci_write_config_word(dev, PCI_COMMAND, cmd); + for (i = 0; i < 6; i++) { + dev->resource[i].start = dev->resource[i].end = 0; + dev->resource[i].flags = 0; + pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + 4 * i, 0); + } + } else { + pci_read_config_word(dev, PCI_COMMAND, &cmd); + cmd &= ~PCI_COMMAND_IO; + pci_write_config_word(dev, PCI_COMMAND, cmd); + for (i = 0; i < 5; i++) { + dev->resource[i].start = dev->resource[i].end = 0; + dev->resource[i].flags = 0; + pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + 4 * i, 0); + } + } +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SERVERWORKS, 0x0240, pmac_pci_fixup_k2_sata); diff --git a/arch/ppc/platforms/pmac_pic.c b/arch/ppc/platforms/pmac_pic.c new file mode 100644 index 00000000000..9f92e1bb7f3 --- /dev/null +++ b/arch/ppc/platforms/pmac_pic.c @@ -0,0 +1,689 @@ +/* + * Support for the interrupt controllers found on Power Macintosh, + * currently Apple's "Grand Central" interrupt controller in all + * it's incarnations. OpenPIC support used on newer machines is + * in a separate file + * + * Copyright (C) 1997 Paul Mackerras (paulus@cs.anu.edu.au) + * + * Maintained by Benjamin Herrenschmidt (benh@kernel.crashing.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pmac_pic.h" + +/* + * XXX this should be in xmon.h, but putting it there means xmon.h + * has to include (to get irqreturn_t), which + * causes all sorts of problems. -- paulus + */ +extern irqreturn_t xmon_irq(int, void *, struct pt_regs *); + +struct pmac_irq_hw { + unsigned int event; + unsigned int enable; + unsigned int ack; + unsigned int level; +}; + +/* Default addresses */ +static volatile struct pmac_irq_hw *pmac_irq_hw[4] __pmacdata = { + (struct pmac_irq_hw *) 0xf3000020, + (struct pmac_irq_hw *) 0xf3000010, + (struct pmac_irq_hw *) 0xf4000020, + (struct pmac_irq_hw *) 0xf4000010, +}; + +#define GC_LEVEL_MASK 0x3ff00000 +#define OHARE_LEVEL_MASK 0x1ff00000 +#define HEATHROW_LEVEL_MASK 0x1ff00000 + +static int max_irqs __pmacdata; +static int max_real_irqs __pmacdata; +static u32 level_mask[4] __pmacdata; + +static DEFINE_SPINLOCK(pmac_pic_lock __pmacdata); + + +#define GATWICK_IRQ_POOL_SIZE 10 +static struct interrupt_info gatwick_int_pool[GATWICK_IRQ_POOL_SIZE] __pmacdata; + +/* + * Mark an irq as "lost". This is only used on the pmac + * since it can lose interrupts (see pmac_set_irq_mask). + * -- Cort + */ +void __pmac +__set_lost(unsigned long irq_nr, int nokick) +{ + if (!test_and_set_bit(irq_nr, ppc_lost_interrupts)) { + atomic_inc(&ppc_n_lost_interrupts); + if (!nokick) + set_dec(1); + } +} + +static void __pmac +pmac_mask_and_ack_irq(unsigned int irq_nr) +{ + unsigned long bit = 1UL << (irq_nr & 0x1f); + int i = irq_nr >> 5; + unsigned long flags; + + if ((unsigned)irq_nr >= max_irqs) + return; + + clear_bit(irq_nr, ppc_cached_irq_mask); + if (test_and_clear_bit(irq_nr, ppc_lost_interrupts)) + atomic_dec(&ppc_n_lost_interrupts); + spin_lock_irqsave(&pmac_pic_lock, flags); + out_le32(&pmac_irq_hw[i]->enable, ppc_cached_irq_mask[i]); + out_le32(&pmac_irq_hw[i]->ack, bit); + do { + /* make sure ack gets to controller before we enable + interrupts */ + mb(); + } while((in_le32(&pmac_irq_hw[i]->enable) & bit) + != (ppc_cached_irq_mask[i] & bit)); + spin_unlock_irqrestore(&pmac_pic_lock, flags); +} + +static void __pmac pmac_set_irq_mask(unsigned int irq_nr, int nokicklost) +{ + unsigned long bit = 1UL << (irq_nr & 0x1f); + int i = irq_nr >> 5; + unsigned long flags; + + if ((unsigned)irq_nr >= max_irqs) + return; + + spin_lock_irqsave(&pmac_pic_lock, flags); + /* enable unmasked interrupts */ + out_le32(&pmac_irq_hw[i]->enable, ppc_cached_irq_mask[i]); + + do { + /* make sure mask gets to controller before we + return to user */ + mb(); + } while((in_le32(&pmac_irq_hw[i]->enable) & bit) + != (ppc_cached_irq_mask[i] & bit)); + + /* + * Unfortunately, setting the bit in the enable register + * when the device interrupt is already on *doesn't* set + * the bit in the flag register or request another interrupt. + */ + if (bit & ppc_cached_irq_mask[i] & in_le32(&pmac_irq_hw[i]->level)) + __set_lost((ulong)irq_nr, nokicklost); + spin_unlock_irqrestore(&pmac_pic_lock, flags); +} + +/* When an irq gets requested for the first client, if it's an + * edge interrupt, we clear any previous one on the controller + */ +static unsigned int __pmac pmac_startup_irq(unsigned int irq_nr) +{ + unsigned long bit = 1UL << (irq_nr & 0x1f); + int i = irq_nr >> 5; + + if ((irq_desc[irq_nr].status & IRQ_LEVEL) == 0) + out_le32(&pmac_irq_hw[i]->ack, bit); + set_bit(irq_nr, ppc_cached_irq_mask); + pmac_set_irq_mask(irq_nr, 0); + + return 0; +} + +static void __pmac pmac_mask_irq(unsigned int irq_nr) +{ + clear_bit(irq_nr, ppc_cached_irq_mask); + pmac_set_irq_mask(irq_nr, 0); + mb(); +} + +static void __pmac pmac_unmask_irq(unsigned int irq_nr) +{ + set_bit(irq_nr, ppc_cached_irq_mask); + pmac_set_irq_mask(irq_nr, 0); +} + +static void __pmac pmac_end_irq(unsigned int irq_nr) +{ + if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS)) + && irq_desc[irq_nr].action) { + set_bit(irq_nr, ppc_cached_irq_mask); + pmac_set_irq_mask(irq_nr, 1); + } +} + + +struct hw_interrupt_type pmac_pic = { + .typename = " PMAC-PIC ", + .startup = pmac_startup_irq, + .enable = pmac_unmask_irq, + .disable = pmac_mask_irq, + .ack = pmac_mask_and_ack_irq, + .end = pmac_end_irq, +}; + +struct hw_interrupt_type gatwick_pic = { + .typename = " GATWICK ", + .startup = pmac_startup_irq, + .enable = pmac_unmask_irq, + .disable = pmac_mask_irq, + .ack = pmac_mask_and_ack_irq, + .end = pmac_end_irq, +}; + +static irqreturn_t gatwick_action(int cpl, void *dev_id, struct pt_regs *regs) +{ + int irq, bits; + + for (irq = max_irqs; (irq -= 32) >= max_real_irqs; ) { + int i = irq >> 5; + bits = in_le32(&pmac_irq_hw[i]->event) | ppc_lost_interrupts[i]; + /* We must read level interrupts from the level register */ + bits |= (in_le32(&pmac_irq_hw[i]->level) & level_mask[i]); + bits &= ppc_cached_irq_mask[i]; + if (bits == 0) + continue; + irq += __ilog2(bits); + __do_IRQ(irq, regs); + return IRQ_HANDLED; + } + printk("gatwick irq not from gatwick pic\n"); + return IRQ_NONE; +} + +int +pmac_get_irq(struct pt_regs *regs) +{ + int irq; + unsigned long bits = 0; + +#ifdef CONFIG_SMP + void psurge_smp_message_recv(struct pt_regs *); + + /* IPI's are a hack on the powersurge -- Cort */ + if ( smp_processor_id() != 0 ) { + psurge_smp_message_recv(regs); + return -2; /* ignore, already handled */ + } +#endif /* CONFIG_SMP */ + for (irq = max_real_irqs; (irq -= 32) >= 0; ) { + int i = irq >> 5; + bits = in_le32(&pmac_irq_hw[i]->event) | ppc_lost_interrupts[i]; + /* We must read level interrupts from the level register */ + bits |= (in_le32(&pmac_irq_hw[i]->level) & level_mask[i]); + bits &= ppc_cached_irq_mask[i]; + if (bits == 0) + continue; + irq += __ilog2(bits); + break; + } + + return irq; +} + +/* This routine will fix some missing interrupt values in the device tree + * on the gatwick mac-io controller used by some PowerBooks + */ +static void __init +pmac_fix_gatwick_interrupts(struct device_node *gw, int irq_base) +{ + struct device_node *node; + int count; + + memset(gatwick_int_pool, 0, sizeof(gatwick_int_pool)); + node = gw->child; + count = 0; + while(node) + { + /* Fix SCC */ + if (strcasecmp(node->name, "escc") == 0) + if (node->child) { + if (node->child->n_intrs < 3) { + node->child->intrs = &gatwick_int_pool[count]; + count += 3; + } + node->child->n_intrs = 3; + node->child->intrs[0].line = 15+irq_base; + node->child->intrs[1].line = 4+irq_base; + node->child->intrs[2].line = 5+irq_base; + printk(KERN_INFO "irq: fixed SCC on second controller (%d,%d,%d)\n", + node->child->intrs[0].line, + node->child->intrs[1].line, + node->child->intrs[2].line); + } + /* Fix media-bay & left SWIM */ + if (strcasecmp(node->name, "media-bay") == 0) { + struct device_node* ya_node; + + if (node->n_intrs == 0) + node->intrs = &gatwick_int_pool[count++]; + node->n_intrs = 1; + node->intrs[0].line = 29+irq_base; + printk(KERN_INFO "irq: fixed media-bay on second controller (%d)\n", + node->intrs[0].line); + + ya_node = node->child; + while(ya_node) + { + if (strcasecmp(ya_node->name, "floppy") == 0) { + if (ya_node->n_intrs < 2) { + ya_node->intrs = &gatwick_int_pool[count]; + count += 2; + } + ya_node->n_intrs = 2; + ya_node->intrs[0].line = 19+irq_base; + ya_node->intrs[1].line = 1+irq_base; + printk(KERN_INFO "irq: fixed floppy on second controller (%d,%d)\n", + ya_node->intrs[0].line, ya_node->intrs[1].line); + } + if (strcasecmp(ya_node->name, "ata4") == 0) { + if (ya_node->n_intrs < 2) { + ya_node->intrs = &gatwick_int_pool[count]; + count += 2; + } + ya_node->n_intrs = 2; + ya_node->intrs[0].line = 14+irq_base; + ya_node->intrs[1].line = 3+irq_base; + printk(KERN_INFO "irq: fixed ide on second controller (%d,%d)\n", + ya_node->intrs[0].line, ya_node->intrs[1].line); + } + ya_node = ya_node->sibling; + } + } + node = node->sibling; + } + if (count > 10) { + printk("WARNING !! Gatwick interrupt pool overflow\n"); + printk(" GATWICK_IRQ_POOL_SIZE = %d\n", GATWICK_IRQ_POOL_SIZE); + printk(" requested = %d\n", count); + } +} + +/* + * The PowerBook 3400/2400/3500 can have a combo ethernet/modem + * card which includes an ohare chip that acts as a second interrupt + * controller. If we find this second ohare, set it up and fix the + * interrupt value in the device tree for the ethernet chip. + */ +static int __init enable_second_ohare(void) +{ + unsigned char bus, devfn; + unsigned short cmd; + unsigned long addr; + struct device_node *irqctrler = find_devices("pci106b,7"); + struct device_node *ether; + + if (irqctrler == NULL || irqctrler->n_addrs <= 0) + return -1; + addr = (unsigned long) ioremap(irqctrler->addrs[0].address, 0x40); + pmac_irq_hw[1] = (volatile struct pmac_irq_hw *)(addr + 0x20); + max_irqs = 64; + if (pci_device_from_OF_node(irqctrler, &bus, &devfn) == 0) { + struct pci_controller* hose = pci_find_hose_for_OF_device(irqctrler); + if (!hose) + printk(KERN_ERR "Can't find PCI hose for OHare2 !\n"); + else { + early_read_config_word(hose, bus, devfn, PCI_COMMAND, &cmd); + cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; + cmd &= ~PCI_COMMAND_IO; + early_write_config_word(hose, bus, devfn, PCI_COMMAND, cmd); + } + } + + /* Fix interrupt for the modem/ethernet combo controller. The number + in the device tree (27) is bogus (correct for the ethernet-only + board but not the combo ethernet/modem board). + The real interrupt is 28 on the second controller -> 28+32 = 60. + */ + ether = find_devices("pci1011,14"); + if (ether && ether->n_intrs > 0) { + ether->intrs[0].line = 60; + printk(KERN_INFO "irq: Fixed ethernet IRQ to %d\n", + ether->intrs[0].line); + } + + /* Return the interrupt number of the cascade */ + return irqctrler->intrs[0].line; +} + +#ifdef CONFIG_POWER4 +static irqreturn_t k2u3_action(int cpl, void *dev_id, struct pt_regs *regs) +{ + int irq; + + irq = openpic2_get_irq(regs); + if (irq != -1) + __do_IRQ(irq, regs); + return IRQ_HANDLED; +} + +static struct irqaction k2u3_cascade_action = { + .handler = k2u3_action, + .flags = 0, + .mask = CPU_MASK_NONE, + .name = "U3->K2 Cascade", +}; +#endif /* CONFIG_POWER4 */ + +#ifdef CONFIG_XMON +static struct irqaction xmon_action = { + .handler = xmon_irq, + .flags = 0, + .mask = CPU_MASK_NONE, + .name = "NMI - XMON" +}; +#endif + +static struct irqaction gatwick_cascade_action = { + .handler = gatwick_action, + .flags = SA_INTERRUPT, + .mask = CPU_MASK_NONE, + .name = "cascade", +}; + +void __init pmac_pic_init(void) +{ + int i; + struct device_node *irqctrler = NULL; + struct device_node *irqctrler2 = NULL; + struct device_node *np; + unsigned long addr; + int irq_cascade = -1; + + /* We first try to detect Apple's new Core99 chipset, since mac-io + * is quite different on those machines and contains an IBM MPIC2. + */ + np = find_type_devices("open-pic"); + while(np) { + if (np->parent && !strcmp(np->parent->name, "u3")) + irqctrler2 = np; + else + irqctrler = np; + np = np->next; + } + if (irqctrler != NULL) + { + if (irqctrler->n_addrs > 0) + { + unsigned char senses[128]; + + printk(KERN_INFO "PowerMac using OpenPIC irq controller at 0x%08x\n", + irqctrler->addrs[0].address); + + prom_get_irq_senses(senses, 0, 128); + OpenPIC_InitSenses = senses; + OpenPIC_NumInitSenses = 128; + ppc_md.get_irq = openpic_get_irq; + pmac_call_feature(PMAC_FTR_ENABLE_MPIC, irqctrler, 0, 0); + OpenPIC_Addr = ioremap(irqctrler->addrs[0].address, + irqctrler->addrs[0].size); + openpic_init(0); + +#ifdef CONFIG_POWER4 + if (irqctrler2 != NULL && irqctrler2->n_intrs > 0 && + irqctrler2->n_addrs > 0) { + printk(KERN_INFO "Slave OpenPIC at 0x%08x hooked on IRQ %d\n", + irqctrler2->addrs[0].address, + irqctrler2->intrs[0].line); + pmac_call_feature(PMAC_FTR_ENABLE_MPIC, irqctrler2, 0, 0); + OpenPIC2_Addr = ioremap(irqctrler2->addrs[0].address, + irqctrler2->addrs[0].size); + prom_get_irq_senses(senses, PMAC_OPENPIC2_OFFSET, + PMAC_OPENPIC2_OFFSET+128); + OpenPIC_InitSenses = senses; + OpenPIC_NumInitSenses = 128; + openpic2_init(PMAC_OPENPIC2_OFFSET); + + if (setup_irq(irqctrler2->intrs[0].line, + &k2u3_cascade_action)) + printk("Unable to get OpenPIC IRQ for cascade\n"); + } +#endif /* CONFIG_POWER4 */ + +#ifdef CONFIG_XMON + { + struct device_node* pswitch; + int nmi_irq; + + pswitch = find_devices("programmer-switch"); + if (pswitch && pswitch->n_intrs) { + nmi_irq = pswitch->intrs[0].line; + openpic_init_nmi_irq(nmi_irq); + setup_irq(nmi_irq, &xmon_action); + } + } +#endif /* CONFIG_XMON */ + return; + } + irqctrler = NULL; + } + + /* Get the level/edge settings, assume if it's not + * a Grand Central nor an OHare, then it's an Heathrow + * (or Paddington). + */ + if (find_devices("gc")) + level_mask[0] = GC_LEVEL_MASK; + else if (find_devices("ohare")) { + level_mask[0] = OHARE_LEVEL_MASK; + /* We might have a second cascaded ohare */ + level_mask[1] = OHARE_LEVEL_MASK; + } else { + level_mask[0] = HEATHROW_LEVEL_MASK; + level_mask[1] = 0; + /* We might have a second cascaded heathrow */ + level_mask[2] = HEATHROW_LEVEL_MASK; + level_mask[3] = 0; + } + + /* + * G3 powermacs and 1999 G3 PowerBooks have 64 interrupts, + * 1998 G3 Series PowerBooks have 128, + * other powermacs have 32. + * The combo ethernet/modem card for the Powerstar powerbooks + * (2400/3400/3500, ohare based) has a second ohare chip + * effectively making a total of 64. + */ + max_irqs = max_real_irqs = 32; + irqctrler = find_devices("mac-io"); + if (irqctrler) + { + max_real_irqs = 64; + if (irqctrler->next) + max_irqs = 128; + else + max_irqs = 64; + } + for ( i = 0; i < max_real_irqs ; i++ ) + irq_desc[i].handler = &pmac_pic; + + /* get addresses of first controller */ + if (irqctrler) { + if (irqctrler->n_addrs > 0) { + addr = (unsigned long) + ioremap(irqctrler->addrs[0].address, 0x40); + for (i = 0; i < 2; ++i) + pmac_irq_hw[i] = (volatile struct pmac_irq_hw*) + (addr + (2 - i) * 0x10); + } + + /* get addresses of second controller */ + irqctrler = irqctrler->next; + if (irqctrler && irqctrler->n_addrs > 0) { + addr = (unsigned long) + ioremap(irqctrler->addrs[0].address, 0x40); + for (i = 2; i < 4; ++i) + pmac_irq_hw[i] = (volatile struct pmac_irq_hw*) + (addr + (4 - i) * 0x10); + irq_cascade = irqctrler->intrs[0].line; + if (device_is_compatible(irqctrler, "gatwick")) + pmac_fix_gatwick_interrupts(irqctrler, max_real_irqs); + } + } else { + /* older powermacs have a GC (grand central) or ohare at + f3000000, with interrupt control registers at f3000020. */ + addr = (unsigned long) ioremap(0xf3000000, 0x40); + pmac_irq_hw[0] = (volatile struct pmac_irq_hw *) (addr + 0x20); + } + + /* PowerBooks 3400 and 3500 can have a second controller in a second + ohare chip, on the combo ethernet/modem card */ + if (machine_is_compatible("AAPL,3400/2400") + || machine_is_compatible("AAPL,3500")) + irq_cascade = enable_second_ohare(); + + /* disable all interrupts in all controllers */ + for (i = 0; i * 32 < max_irqs; ++i) + out_le32(&pmac_irq_hw[i]->enable, 0); + /* mark level interrupts */ + for (i = 0; i < max_irqs; i++) + if (level_mask[i >> 5] & (1UL << (i & 0x1f))) + irq_desc[i].status = IRQ_LEVEL; + + /* get interrupt line of secondary interrupt controller */ + if (irq_cascade >= 0) { + printk(KERN_INFO "irq: secondary controller on irq %d\n", + (int)irq_cascade); + for ( i = max_real_irqs ; i < max_irqs ; i++ ) + irq_desc[i].handler = &gatwick_pic; + setup_irq(irq_cascade, &gatwick_cascade_action); + } + printk("System has %d possible interrupts\n", max_irqs); + if (max_irqs != max_real_irqs) + printk(KERN_DEBUG "%d interrupts on main controller\n", + max_real_irqs); + +#ifdef CONFIG_XMON + setup_irq(20, &xmon_action); +#endif /* CONFIG_XMON */ +} + +#ifdef CONFIG_PM +/* + * These procedures are used in implementing sleep on the powerbooks. + * sleep_save_intrs() saves the states of all interrupt enables + * and disables all interrupts except for the nominated one. + * sleep_restore_intrs() restores the states of all interrupt enables. + */ +unsigned long sleep_save_mask[2]; + +/* This used to be passed by the PMU driver but that link got + * broken with the new driver model. We use this tweak for now... + */ +static int pmacpic_find_viaint(void) +{ + int viaint = -1; + +#ifdef CONFIG_ADB_PMU + struct device_node *np; + + if (pmu_get_model() != PMU_OHARE_BASED) + goto not_found; + np = of_find_node_by_name(NULL, "via-pmu"); + if (np == NULL) + goto not_found; + viaint = np->intrs[0].line; +#endif /* CONFIG_ADB_PMU */ + +not_found: + return viaint; +} + +static int pmacpic_suspend(struct sys_device *sysdev, u32 state) +{ + int viaint = pmacpic_find_viaint(); + + sleep_save_mask[0] = ppc_cached_irq_mask[0]; + sleep_save_mask[1] = ppc_cached_irq_mask[1]; + ppc_cached_irq_mask[0] = 0; + ppc_cached_irq_mask[1] = 0; + if (viaint > 0) + set_bit(viaint, ppc_cached_irq_mask); + out_le32(&pmac_irq_hw[0]->enable, ppc_cached_irq_mask[0]); + if (max_real_irqs > 32) + out_le32(&pmac_irq_hw[1]->enable, ppc_cached_irq_mask[1]); + (void)in_le32(&pmac_irq_hw[0]->event); + /* make sure mask gets to controller before we return to caller */ + mb(); + (void)in_le32(&pmac_irq_hw[0]->enable); + + return 0; +} + +static int pmacpic_resume(struct sys_device *sysdev) +{ + int i; + + out_le32(&pmac_irq_hw[0]->enable, 0); + if (max_real_irqs > 32) + out_le32(&pmac_irq_hw[1]->enable, 0); + mb(); + for (i = 0; i < max_real_irqs; ++i) + if (test_bit(i, sleep_save_mask)) + pmac_unmask_irq(i); + + return 0; +} + +#endif /* CONFIG_PM */ + +static struct sysdev_class pmacpic_sysclass = { + set_kset_name("pmac_pic"), +}; + +static struct sys_device device_pmacpic = { + .id = 0, + .cls = &pmacpic_sysclass, +}; + +static struct sysdev_driver driver_pmacpic = { +#ifdef CONFIG_PM + .suspend = &pmacpic_suspend, + .resume = &pmacpic_resume, +#endif /* CONFIG_PM */ +}; + +static int __init init_pmacpic_sysfs(void) +{ + if (max_irqs == 0) + return -ENODEV; + + printk(KERN_DEBUG "Registering pmac pic with sysfs...\n"); + sysdev_class_register(&pmacpic_sysclass); + sysdev_register(&device_pmacpic); + sysdev_driver_register(&pmacpic_sysclass, &driver_pmacpic); + return 0; +} + +subsys_initcall(init_pmacpic_sysfs); + diff --git a/arch/ppc/platforms/pmac_pic.h b/arch/ppc/platforms/pmac_pic.h new file mode 100644 index 00000000000..664103dfeef --- /dev/null +++ b/arch/ppc/platforms/pmac_pic.h @@ -0,0 +1,11 @@ +#ifndef __PPC_PLATFORMS_PMAC_PIC_H +#define __PPC_PLATFORMS_PMAC_PIC_H + +#include + +extern struct hw_interrupt_type pmac_pic; + +void pmac_pic_init(void); +int pmac_get_irq(struct pt_regs *regs); + +#endif /* __PPC_PLATFORMS_PMAC_PIC_H */ diff --git a/arch/ppc/platforms/pmac_setup.c b/arch/ppc/platforms/pmac_setup.c new file mode 100644 index 00000000000..4d324b630f4 --- /dev/null +++ b/arch/ppc/platforms/pmac_setup.c @@ -0,0 +1,745 @@ +/* + * arch/ppc/platforms/setup.c + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Adapted for Power Macintosh by Paul Mackerras + * Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au) + * + * Derived from "arch/alpha/kernel/setup.c" + * Copyright (C) 1995 Linus Torvalds + * + * Maintained by Benjamin Herrenschmidt (benh@kernel.crashing.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +/* + * bootup setup stuff.. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pmac_pic.h" +#include "mem_pieces.h" + +#undef SHOW_GATWICK_IRQS + +extern long pmac_time_init(void); +extern unsigned long pmac_get_rtc_time(void); +extern int pmac_set_rtc_time(unsigned long nowtime); +extern void pmac_read_rtc_time(void); +extern void pmac_calibrate_decr(void); +extern void pmac_pcibios_fixup(void); +extern void pmac_find_bridges(void); +extern unsigned long pmac_ide_get_base(int index); +extern void pmac_ide_init_hwif_ports(hw_regs_t *hw, + unsigned long data_port, unsigned long ctrl_port, int *irq); + +extern void pmac_nvram_update(void); +extern unsigned char pmac_nvram_read_byte(int addr); +extern void pmac_nvram_write_byte(int addr, unsigned char val); +extern int pmac_pci_enable_device_hook(struct pci_dev *dev, int initial); +extern void pmac_pcibios_after_init(void); +extern int of_show_percpuinfo(struct seq_file *m, int i); + +struct device_node *memory_node; + +unsigned char drive_info; + +int ppc_override_l2cr = 0; +int ppc_override_l2cr_value; +int has_l2cache = 0; + +static int current_root_goodness = -1; + +extern int pmac_newworld; + +#define DEFAULT_ROOT_DEVICE Root_SDA1 /* sda1 - slightly silly choice */ + +extern void zs_kgdb_hook(int tty_num); +static void ohare_init(void); +#ifdef CONFIG_BOOTX_TEXT +void pmac_progress(char *s, unsigned short hex); +#endif + +sys_ctrler_t sys_ctrler = SYS_CTRLER_UNKNOWN; + +#ifdef CONFIG_SMP +extern struct smp_ops_t psurge_smp_ops; +extern struct smp_ops_t core99_smp_ops; +#endif /* CONFIG_SMP */ + +int __pmac +pmac_show_cpuinfo(struct seq_file *m) +{ + struct device_node *np; + char *pp; + int plen; + int mbmodel = pmac_call_feature(PMAC_FTR_GET_MB_INFO, + NULL, PMAC_MB_INFO_MODEL, 0); + unsigned int mbflags = (unsigned int)pmac_call_feature(PMAC_FTR_GET_MB_INFO, + NULL, PMAC_MB_INFO_FLAGS, 0); + char* mbname; + + if (pmac_call_feature(PMAC_FTR_GET_MB_INFO, NULL, PMAC_MB_INFO_NAME, (int)&mbname) != 0) + mbname = "Unknown"; + + /* find motherboard type */ + seq_printf(m, "machine\t\t: "); + np = find_devices("device-tree"); + if (np != NULL) { + pp = (char *) get_property(np, "model", NULL); + if (pp != NULL) + seq_printf(m, "%s\n", pp); + else + seq_printf(m, "PowerMac\n"); + pp = (char *) get_property(np, "compatible", &plen); + if (pp != NULL) { + seq_printf(m, "motherboard\t:"); + while (plen > 0) { + int l = strlen(pp) + 1; + seq_printf(m, " %s", pp); + plen -= l; + pp += l; + } + seq_printf(m, "\n"); + } + } else + seq_printf(m, "PowerMac\n"); + + /* print parsed model */ + seq_printf(m, "detected as\t: %d (%s)\n", mbmodel, mbname); + seq_printf(m, "pmac flags\t: %08x\n", mbflags); + + /* find l2 cache info */ + np = find_devices("l2-cache"); + if (np == 0) + np = find_type_devices("cache"); + if (np != 0) { + unsigned int *ic = (unsigned int *) + get_property(np, "i-cache-size", NULL); + unsigned int *dc = (unsigned int *) + get_property(np, "d-cache-size", NULL); + seq_printf(m, "L2 cache\t:"); + has_l2cache = 1; + if (get_property(np, "cache-unified", NULL) != 0 && dc) { + seq_printf(m, " %dK unified", *dc / 1024); + } else { + if (ic) + seq_printf(m, " %dK instruction", *ic / 1024); + if (dc) + seq_printf(m, "%s %dK data", + (ic? " +": ""), *dc / 1024); + } + pp = get_property(np, "ram-type", NULL); + if (pp) + seq_printf(m, " %s", pp); + seq_printf(m, "\n"); + } + + /* find ram info */ + np = find_devices("memory"); + if (np != 0) { + int n; + struct reg_property *reg = (struct reg_property *) + get_property(np, "reg", &n); + + if (reg != 0) { + unsigned long total = 0; + + for (n /= sizeof(struct reg_property); n > 0; --n) + total += (reg++)->size; + seq_printf(m, "memory\t\t: %luMB\n", total >> 20); + } + } + + /* Checks "l2cr-value" property in the registry */ + np = find_devices("cpus"); + if (np == 0) + np = find_type_devices("cpu"); + if (np != 0) { + unsigned int *l2cr = (unsigned int *) + get_property(np, "l2cr-value", NULL); + if (l2cr != 0) { + seq_printf(m, "l2cr override\t: 0x%x\n", *l2cr); + } + } + + /* Indicate newworld/oldworld */ + seq_printf(m, "pmac-generation\t: %s\n", + pmac_newworld ? "NewWorld" : "OldWorld"); + + + return 0; +} + +int __openfirmware +pmac_show_percpuinfo(struct seq_file *m, int i) +{ +#ifdef CONFIG_CPU_FREQ_PMAC + extern unsigned int pmac_get_one_cpufreq(int i); + unsigned int freq = pmac_get_one_cpufreq(i); + if (freq != 0) { + seq_printf(m, "clock\t\t: %dMHz\n", freq/1000); + return 0; + } +#endif /* CONFIG_CPU_FREQ_PMAC */ + return of_show_percpuinfo(m, i); +} + +static volatile u32 *sysctrl_regs; + +void __init +pmac_setup_arch(void) +{ + struct device_node *cpu; + int *fp; + unsigned long pvr; + + pvr = PVR_VER(mfspr(SPRN_PVR)); + + /* Set loops_per_jiffy to a half-way reasonable value, + for use until calibrate_delay gets called. */ + cpu = find_type_devices("cpu"); + if (cpu != 0) { + fp = (int *) get_property(cpu, "clock-frequency", NULL); + if (fp != 0) { + if (pvr == 4 || pvr >= 8) + /* 604, G3, G4 etc. */ + loops_per_jiffy = *fp / HZ; + else + /* 601, 603, etc. */ + loops_per_jiffy = *fp / (2*HZ); + } else + loops_per_jiffy = 50000000 / HZ; + } + + /* this area has the CPU identification register + and some registers used by smp boards */ + sysctrl_regs = (volatile u32 *) ioremap(0xf8000000, 0x1000); + ohare_init(); + + /* Lookup PCI hosts */ + pmac_find_bridges(); + + /* Checks "l2cr-value" property in the registry */ + if (cpu_has_feature(CPU_FTR_L2CR)) { + struct device_node *np = find_devices("cpus"); + if (np == 0) + np = find_type_devices("cpu"); + if (np != 0) { + unsigned int *l2cr = (unsigned int *) + get_property(np, "l2cr-value", NULL); + if (l2cr != 0) { + ppc_override_l2cr = 1; + ppc_override_l2cr_value = *l2cr; + _set_L2CR(0); + _set_L2CR(ppc_override_l2cr_value); + } + } + } + + if (ppc_override_l2cr) + printk(KERN_INFO "L2CR overriden (0x%x), backside cache is %s\n", + ppc_override_l2cr_value, (ppc_override_l2cr_value & 0x80000000) + ? "enabled" : "disabled"); + +#ifdef CONFIG_KGDB + zs_kgdb_hook(0); +#endif + +#ifdef CONFIG_ADB_CUDA + find_via_cuda(); +#else + if (find_devices("via-cuda")) { + printk("WARNING ! Your machine is Cuda based but your kernel\n"); + printk(" wasn't compiled with CONFIG_ADB_CUDA option !\n"); + } +#endif +#ifdef CONFIG_ADB_PMU + find_via_pmu(); +#else + if (find_devices("via-pmu")) { + printk("WARNING ! Your machine is PMU based but your kernel\n"); + printk(" wasn't compiled with CONFIG_ADB_PMU option !\n"); + } +#endif +#ifdef CONFIG_NVRAM + pmac_nvram_init(); +#endif +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif + ROOT_DEV = DEFAULT_ROOT_DEVICE; + +#ifdef CONFIG_SMP + /* Check for Core99 */ + if (find_devices("uni-n") || find_devices("u3")) + ppc_md.smp_ops = &core99_smp_ops; + else + ppc_md.smp_ops = &psurge_smp_ops; +#endif /* CONFIG_SMP */ + + pci_create_OF_bus_map(); +} + +static void __init ohare_init(void) +{ + /* + * Turn on the L2 cache. + * We assume that we have a PSX memory controller iff + * we have an ohare I/O controller. + */ + if (find_devices("ohare") != NULL) { + if (((sysctrl_regs[2] >> 24) & 0xf) >= 3) { + if (sysctrl_regs[4] & 0x10) + sysctrl_regs[4] |= 0x04000020; + else + sysctrl_regs[4] |= 0x04000000; + if(has_l2cache) + printk(KERN_INFO "Level 2 cache enabled\n"); + } + } +} + +extern char *bootpath; +extern char *bootdevice; +void *boot_host; +int boot_target; +int boot_part; +extern dev_t boot_dev; + +#ifdef CONFIG_SCSI +void __init +note_scsi_host(struct device_node *node, void *host) +{ + int l; + char *p; + + l = strlen(node->full_name); + if (bootpath != NULL && bootdevice != NULL + && strncmp(node->full_name, bootdevice, l) == 0 + && (bootdevice[l] == '/' || bootdevice[l] == 0)) { + boot_host = host; + /* + * There's a bug in OF 1.0.5. (Why am I not surprised.) + * If you pass a path like scsi/sd@1:0 to canon, it returns + * something like /bandit@F2000000/gc@10/53c94@10000/sd@0,0 + * That is, the scsi target number doesn't get preserved. + * So we pick the target number out of bootpath and use that. + */ + p = strstr(bootpath, "/sd@"); + if (p != NULL) { + p += 4; + boot_target = simple_strtoul(p, NULL, 10); + p = strchr(p, ':'); + if (p != NULL) + boot_part = simple_strtoul(p + 1, NULL, 10); + } + } +} +#endif + +#if defined(CONFIG_BLK_DEV_IDE) && defined(CONFIG_BLK_DEV_IDE_PMAC) +static dev_t __init +find_ide_boot(void) +{ + char *p; + int n; + dev_t __init pmac_find_ide_boot(char *bootdevice, int n); + + if (bootdevice == NULL) + return 0; + p = strrchr(bootdevice, '/'); + if (p == NULL) + return 0; + n = p - bootdevice; + + return pmac_find_ide_boot(bootdevice, n); +} +#endif /* CONFIG_BLK_DEV_IDE && CONFIG_BLK_DEV_IDE_PMAC */ + +void __init +find_boot_device(void) +{ +#if defined(CONFIG_BLK_DEV_IDE) && defined(CONFIG_BLK_DEV_IDE_PMAC) + boot_dev = find_ide_boot(); +#endif +} + +static int initializing = 1; +/* TODO: Merge the suspend-to-ram with the common code !!! + * currently, this is a stub implementation for suspend-to-disk + * only + */ + +#ifdef CONFIG_SOFTWARE_SUSPEND + +static int pmac_pm_prepare(suspend_state_t state) +{ + printk(KERN_DEBUG "%s(%d)\n", __FUNCTION__, state); + + return 0; +} + +static int pmac_pm_enter(suspend_state_t state) +{ + printk(KERN_DEBUG "%s(%d)\n", __FUNCTION__, state); + + /* Giveup the lazy FPU & vec so we don't have to back them + * up from the low level code + */ + enable_kernel_fp(); + +#ifdef CONFIG_ALTIVEC + if (cur_cpu_spec[0]->cpu_features & CPU_FTR_ALTIVEC) + enable_kernel_altivec(); +#endif /* CONFIG_ALTIVEC */ + + return 0; +} + +static int pmac_pm_finish(suspend_state_t state) +{ + printk(KERN_DEBUG "%s(%d)\n", __FUNCTION__, state); + + /* Restore userland MMU context */ + set_context(current->active_mm->context, current->active_mm->pgd); + + return 0; +} + +static struct pm_ops pmac_pm_ops = { + .pm_disk_mode = PM_DISK_SHUTDOWN, + .prepare = pmac_pm_prepare, + .enter = pmac_pm_enter, + .finish = pmac_pm_finish, +}; + +#endif /* CONFIG_SOFTWARE_SUSPEND */ + +static int pmac_late_init(void) +{ + initializing = 0; +#ifdef CONFIG_SOFTWARE_SUSPEND + pm_set_ops(&pmac_pm_ops); +#endif /* CONFIG_SOFTWARE_SUSPEND */ + return 0; +} + +late_initcall(pmac_late_init); + +/* can't be __init - can be called whenever a disk is first accessed */ +void __pmac +note_bootable_part(dev_t dev, int part, int goodness) +{ + static int found_boot = 0; + char *p; + + if (!initializing) + return; + if ((goodness <= current_root_goodness) && + ROOT_DEV != DEFAULT_ROOT_DEVICE) + return; + p = strstr(saved_command_line, "root="); + if (p != NULL && (p == saved_command_line || p[-1] == ' ')) + return; + + if (!found_boot) { + find_boot_device(); + found_boot = 1; + } + if (!boot_dev || dev == boot_dev) { + ROOT_DEV = dev + part; + boot_dev = 0; + current_root_goodness = goodness; + } +} + +void __pmac +pmac_restart(char *cmd) +{ +#ifdef CONFIG_ADB_CUDA + struct adb_request req; +#endif /* CONFIG_ADB_CUDA */ + + switch (sys_ctrler) { +#ifdef CONFIG_ADB_CUDA + case SYS_CTRLER_CUDA: + cuda_request(&req, NULL, 2, CUDA_PACKET, + CUDA_RESET_SYSTEM); + for (;;) + cuda_poll(); + break; +#endif /* CONFIG_ADB_CUDA */ +#ifdef CONFIG_ADB_PMU + case SYS_CTRLER_PMU: + pmu_restart(); + break; +#endif /* CONFIG_ADB_PMU */ + default: ; + } +} + +void __pmac +pmac_power_off(void) +{ +#ifdef CONFIG_ADB_CUDA + struct adb_request req; +#endif /* CONFIG_ADB_CUDA */ + + switch (sys_ctrler) { +#ifdef CONFIG_ADB_CUDA + case SYS_CTRLER_CUDA: + cuda_request(&req, NULL, 2, CUDA_PACKET, + CUDA_POWERDOWN); + for (;;) + cuda_poll(); + break; +#endif /* CONFIG_ADB_CUDA */ +#ifdef CONFIG_ADB_PMU + case SYS_CTRLER_PMU: + pmu_shutdown(); + break; +#endif /* CONFIG_ADB_PMU */ + default: ; + } +} + +void __pmac +pmac_halt(void) +{ + pmac_power_off(); +} + +/* + * Read in a property describing some pieces of memory. + */ + +static int __init +get_mem_prop(char *name, struct mem_pieces *mp) +{ + struct reg_property *rp; + int i, s; + unsigned int *ip; + int nac = prom_n_addr_cells(memory_node); + int nsc = prom_n_size_cells(memory_node); + + ip = (unsigned int *) get_property(memory_node, name, &s); + if (ip == NULL) { + printk(KERN_ERR "error: couldn't get %s property on /memory\n", + name); + return 0; + } + s /= (nsc + nac) * 4; + rp = mp->regions; + for (i = 0; i < s; ++i, ip += nac+nsc) { + if (nac >= 2 && ip[nac-2] != 0) + continue; + rp->address = ip[nac-1]; + if (nsc >= 2 && ip[nac+nsc-2] != 0) + rp->size = ~0U; + else + rp->size = ip[nac+nsc-1]; + ++rp; + } + mp->n_regions = rp - mp->regions; + + /* Make sure the pieces are sorted. */ + mem_pieces_sort(mp); + mem_pieces_coalesce(mp); + return 1; +} + +/* + * On systems with Open Firmware, collect information about + * physical RAM and which pieces are already in use. + * At this point, we have (at least) the first 8MB mapped with a BAT. + * Our text, data, bss use something over 1MB, starting at 0. + * Open Firmware may be using 1MB at the 4MB point. + */ +unsigned long __init +pmac_find_end_of_memory(void) +{ + unsigned long a, total; + struct mem_pieces phys_mem; + + /* + * Find out where physical memory is, and check that it + * starts at 0 and is contiguous. It seems that RAM is + * always physically contiguous on Power Macintoshes. + * + * Supporting discontiguous physical memory isn't hard, + * it just makes the virtual <-> physical mapping functions + * more complicated (or else you end up wasting space + * in mem_map). + */ + memory_node = find_devices("memory"); + if (memory_node == NULL || !get_mem_prop("reg", &phys_mem) + || phys_mem.n_regions == 0) + panic("No RAM??"); + a = phys_mem.regions[0].address; + if (a != 0) + panic("RAM doesn't start at physical address 0"); + total = phys_mem.regions[0].size; + + if (phys_mem.n_regions > 1) { + printk("RAM starting at 0x%x is not contiguous\n", + phys_mem.regions[1].address); + printk("Using RAM from 0 to 0x%lx\n", total-1); + } + + return total; +} + +void __init +pmac_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + /* isa_io_base gets set in pmac_find_bridges */ + isa_mem_base = PMAC_ISA_MEM_BASE; + pci_dram_offset = PMAC_PCI_DRAM_OFFSET; + ISA_DMA_THRESHOLD = ~0L; + DMA_MODE_READ = 1; + DMA_MODE_WRITE = 2; + + ppc_md.setup_arch = pmac_setup_arch; + ppc_md.show_cpuinfo = pmac_show_cpuinfo; + ppc_md.show_percpuinfo = pmac_show_percpuinfo; + ppc_md.irq_canonicalize = NULL; + ppc_md.init_IRQ = pmac_pic_init; + ppc_md.get_irq = pmac_get_irq; /* Changed later on ... */ + + ppc_md.pcibios_fixup = pmac_pcibios_fixup; + ppc_md.pcibios_enable_device_hook = pmac_pci_enable_device_hook; + ppc_md.pcibios_after_init = pmac_pcibios_after_init; + ppc_md.phys_mem_access_prot = pci_phys_mem_access_prot; + + ppc_md.restart = pmac_restart; + ppc_md.power_off = pmac_power_off; + ppc_md.halt = pmac_halt; + + ppc_md.time_init = pmac_time_init; + ppc_md.set_rtc_time = pmac_set_rtc_time; + ppc_md.get_rtc_time = pmac_get_rtc_time; + ppc_md.calibrate_decr = pmac_calibrate_decr; + + ppc_md.find_end_of_memory = pmac_find_end_of_memory; + + ppc_md.feature_call = pmac_do_feature_call; + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +#ifdef CONFIG_BLK_DEV_IDE_PMAC + ppc_ide_md.ide_init_hwif = pmac_ide_init_hwif_ports; + ppc_ide_md.default_io_base = pmac_ide_get_base; +#endif /* CONFIG_BLK_DEV_IDE_PMAC */ +#endif /* defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) */ + +#ifdef CONFIG_BOOTX_TEXT + ppc_md.progress = pmac_progress; +#endif /* CONFIG_BOOTX_TEXT */ + + if (ppc_md.progress) ppc_md.progress("pmac_init(): exit", 0); + +} + +#ifdef CONFIG_BOOTX_TEXT +void __init +pmac_progress(char *s, unsigned short hex) +{ + if (boot_text_mapped) { + btext_drawstring(s); + btext_drawchar('\n'); + } +} +#endif /* CONFIG_BOOTX_TEXT */ + +static int __init +pmac_declare_of_platform_devices(void) +{ + struct device_node *np; + + np = find_devices("uni-n"); + if (np) { + for (np = np->child; np != NULL; np = np->sibling) + if (strncmp(np->name, "i2c", 3) == 0) { + of_platform_device_create(np, "uni-n-i2c"); + break; + } + } + np = find_devices("u3"); + if (np) { + for (np = np->child; np != NULL; np = np->sibling) + if (strncmp(np->name, "i2c", 3) == 0) { + of_platform_device_create(np, "u3-i2c"); + break; + } + } + + np = find_devices("valkyrie"); + if (np) + of_platform_device_create(np, "valkyrie"); + np = find_devices("platinum"); + if (np) + of_platform_device_create(np, "platinum"); + + return 0; +} + +device_initcall(pmac_declare_of_platform_devices); diff --git a/arch/ppc/platforms/pmac_sleep.S b/arch/ppc/platforms/pmac_sleep.S new file mode 100644 index 00000000000..3139b6766ad --- /dev/null +++ b/arch/ppc/platforms/pmac_sleep.S @@ -0,0 +1,390 @@ +/* + * This file contains sleep low-level functions for PowerBook G3. + * Copyright (C) 1999 Benjamin Herrenschmidt (benh@kernel.crashing.org) + * and Paul Mackerras (paulus@samba.org). + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAGIC 0x4c617273 /* 'Lars' */ + +/* + * Structure for storing CPU registers on the stack. + */ +#define SL_SP 0 +#define SL_PC 4 +#define SL_MSR 8 +#define SL_SDR1 0xc +#define SL_SPRG0 0x10 /* 4 sprg's */ +#define SL_DBAT0 0x20 +#define SL_IBAT0 0x28 +#define SL_DBAT1 0x30 +#define SL_IBAT1 0x38 +#define SL_DBAT2 0x40 +#define SL_IBAT2 0x48 +#define SL_DBAT3 0x50 +#define SL_IBAT3 0x58 +#define SL_TB 0x60 +#define SL_R2 0x68 +#define SL_CR 0x6c +#define SL_R12 0x70 /* r12 to r31 */ +#define SL_SIZE (SL_R12 + 80) + + .section .text + .align 5 + +#if defined(CONFIG_PMAC_PBOOK) || defined(CONFIG_CPU_FREQ_PMAC) + +/* This gets called by via-pmu.c late during the sleep process. + * The PMU was already send the sleep command and will shut us down + * soon. We need to save all that is needed and setup the wakeup + * vector that will be called by the ROM on wakeup + */ +_GLOBAL(low_sleep_handler) +#ifndef CONFIG_6xx + blr +#else + mflr r0 + stw r0,4(r1) + stwu r1,-SL_SIZE(r1) + mfcr r0 + stw r0,SL_CR(r1) + stw r2,SL_R2(r1) + stmw r12,SL_R12(r1) + + /* Save MSR & SDR1 */ + mfmsr r4 + stw r4,SL_MSR(r1) + mfsdr1 r4 + stw r4,SL_SDR1(r1) + + /* Get a stable timebase and save it */ +1: mftbu r4 + stw r4,SL_TB(r1) + mftb r5 + stw r5,SL_TB+4(r1) + mftbu r3 + cmpw r3,r4 + bne 1b + + /* Save SPRGs */ + mfsprg r4,0 + stw r4,SL_SPRG0(r1) + mfsprg r4,1 + stw r4,SL_SPRG0+4(r1) + mfsprg r4,2 + stw r4,SL_SPRG0+8(r1) + mfsprg r4,3 + stw r4,SL_SPRG0+12(r1) + + /* Save BATs */ + mfdbatu r4,0 + stw r4,SL_DBAT0(r1) + mfdbatl r4,0 + stw r4,SL_DBAT0+4(r1) + mfdbatu r4,1 + stw r4,SL_DBAT1(r1) + mfdbatl r4,1 + stw r4,SL_DBAT1+4(r1) + mfdbatu r4,2 + stw r4,SL_DBAT2(r1) + mfdbatl r4,2 + stw r4,SL_DBAT2+4(r1) + mfdbatu r4,3 + stw r4,SL_DBAT3(r1) + mfdbatl r4,3 + stw r4,SL_DBAT3+4(r1) + mfibatu r4,0 + stw r4,SL_IBAT0(r1) + mfibatl r4,0 + stw r4,SL_IBAT0+4(r1) + mfibatu r4,1 + stw r4,SL_IBAT1(r1) + mfibatl r4,1 + stw r4,SL_IBAT1+4(r1) + mfibatu r4,2 + stw r4,SL_IBAT2(r1) + mfibatl r4,2 + stw r4,SL_IBAT2+4(r1) + mfibatu r4,3 + stw r4,SL_IBAT3(r1) + mfibatl r4,3 + stw r4,SL_IBAT3+4(r1) + + /* Backup various CPU config stuffs */ + bl __save_cpu_setup + + /* The ROM can wake us up via 2 different vectors: + * - On wallstreet & lombard, we must write a magic + * value 'Lars' at address 4 and a pointer to a + * memory location containing the PC to resume from + * at address 0. + * - On Core99, we must store the wakeup vector at + * address 0x80 and eventually it's parameters + * at address 0x84. I've have some trouble with those + * parameters however and I no longer use them. + */ + lis r5,grackle_wake_up@ha + addi r5,r5,grackle_wake_up@l + tophys(r5,r5) + stw r5,SL_PC(r1) + lis r4,KERNELBASE@h + tophys(r5,r1) + addi r5,r5,SL_PC + lis r6,MAGIC@ha + addi r6,r6,MAGIC@l + stw r5,0(r4) + stw r6,4(r4) + /* Setup stuffs at 0x80-0x84 for Core99 */ + lis r3,core99_wake_up@ha + addi r3,r3,core99_wake_up@l + tophys(r3,r3) + stw r3,0x80(r4) + stw r5,0x84(r4) + /* Store a pointer to our backup storage into + * a kernel global + */ + lis r3,sleep_storage@ha + addi r3,r3,sleep_storage@l + stw r5,0(r3) + + /* Flush & disable all caches */ + bl flush_disable_caches + + /* Turn off data relocation. */ + mfmsr r3 /* Save MSR in r7 */ + rlwinm r3,r3,0,28,26 /* Turn off DR bit */ + sync + mtmsr r3 + isync + +BEGIN_FTR_SECTION + /* Flush any pending L2 data prefetches to work around HW bug */ + sync + lis r3,0xfff0 + lwz r0,0(r3) /* perform cache-inhibited load to ROM */ + sync /* (caches are disabled at this point) */ +END_FTR_SECTION_IFSET(CPU_FTR_SPEC7450) + +/* + * Set the HID0 and MSR for sleep. + */ + mfspr r2,SPRN_HID0 + rlwinm r2,r2,0,10,7 /* clear doze, nap */ + oris r2,r2,HID0_SLEEP@h + sync + isync + mtspr SPRN_HID0,r2 + sync + +/* This loop puts us back to sleep in case we have a spurrious + * wakeup so that the host bridge properly stays asleep. The + * CPU will be turned off, either after a known time (about 1 + * second) on wallstreet & lombard, or as soon as the CPU enters + * SLEEP mode on core99 + */ + mfmsr r2 + oris r2,r2,MSR_POW@h +1: sync + mtmsr r2 + isync + b 1b + +/* + * Here is the resume code. + */ + + +/* + * Core99 machines resume here + * r4 has the physical address of SL_PC(sp) (unused) + */ +_GLOBAL(core99_wake_up) + /* Make sure HID0 no longer contains any sleep bit and that data cache + * is disabled + */ + mfspr r3,SPRN_HID0 + rlwinm r3,r3,0,11,7 /* clear SLEEP, NAP, DOZE bits */ + rlwinm 3,r3,0,18,15 /* clear DCE, ICE */ + mtspr SPRN_HID0,r3 + sync + isync + + /* sanitize MSR */ + mfmsr r3 + ori r3,r3,MSR_EE|MSR_IP + xori r3,r3,MSR_EE|MSR_IP + sync + isync + mtmsr r3 + sync + isync + + /* Recover sleep storage */ + lis r3,sleep_storage@ha + addi r3,r3,sleep_storage@l + tophys(r3,r3) + lwz r1,0(r3) + + /* Pass thru to older resume code ... */ +/* + * Here is the resume code for older machines. + * r1 has the physical address of SL_PC(sp). + */ + +grackle_wake_up: + + /* Restore the kernel's segment registers before + * we do any r1 memory access as we are not sure they + * are in a sane state above the first 256Mb region + */ + li r0,16 /* load up segment register values */ + mtctr r0 /* for context 0 */ + lis r3,0x2000 /* Ku = 1, VSID = 0 */ + li r4,0 +3: mtsrin r3,r4 + addi r3,r3,0x111 /* increment VSID */ + addis r4,r4,0x1000 /* address of next segment */ + bdnz 3b + sync + isync + + subi r1,r1,SL_PC + + /* Restore various CPU config stuffs */ + bl __restore_cpu_setup + + /* Invalidate & enable L1 cache, we don't care about + * whatever the ROM may have tried to write to memory + */ + bl __inval_enable_L1 + + /* Restore the BATs, and SDR1. Then we can turn on the MMU. */ + lwz r4,SL_SDR1(r1) + mtsdr1 r4 + lwz r4,SL_SPRG0(r1) + mtsprg 0,r4 + lwz r4,SL_SPRG0+4(r1) + mtsprg 1,r4 + lwz r4,SL_SPRG0+8(r1) + mtsprg 2,r4 + lwz r4,SL_SPRG0+12(r1) + mtsprg 3,r4 + + lwz r4,SL_DBAT0(r1) + mtdbatu 0,r4 + lwz r4,SL_DBAT0+4(r1) + mtdbatl 0,r4 + lwz r4,SL_DBAT1(r1) + mtdbatu 1,r4 + lwz r4,SL_DBAT1+4(r1) + mtdbatl 1,r4 + lwz r4,SL_DBAT2(r1) + mtdbatu 2,r4 + lwz r4,SL_DBAT2+4(r1) + mtdbatl 2,r4 + lwz r4,SL_DBAT3(r1) + mtdbatu 3,r4 + lwz r4,SL_DBAT3+4(r1) + mtdbatl 3,r4 + lwz r4,SL_IBAT0(r1) + mtibatu 0,r4 + lwz r4,SL_IBAT0+4(r1) + mtibatl 0,r4 + lwz r4,SL_IBAT1(r1) + mtibatu 1,r4 + lwz r4,SL_IBAT1+4(r1) + mtibatl 1,r4 + lwz r4,SL_IBAT2(r1) + mtibatu 2,r4 + lwz r4,SL_IBAT2+4(r1) + mtibatl 2,r4 + lwz r4,SL_IBAT3(r1) + mtibatu 3,r4 + lwz r4,SL_IBAT3+4(r1) + mtibatl 3,r4 + +BEGIN_FTR_SECTION + li r4,0 + mtspr SPRN_DBAT4U,r4 + mtspr SPRN_DBAT4L,r4 + mtspr SPRN_DBAT5U,r4 + mtspr SPRN_DBAT5L,r4 + mtspr SPRN_DBAT6U,r4 + mtspr SPRN_DBAT6L,r4 + mtspr SPRN_DBAT7U,r4 + mtspr SPRN_DBAT7L,r4 + mtspr SPRN_IBAT4U,r4 + mtspr SPRN_IBAT4L,r4 + mtspr SPRN_IBAT5U,r4 + mtspr SPRN_IBAT5L,r4 + mtspr SPRN_IBAT6U,r4 + mtspr SPRN_IBAT6L,r4 + mtspr SPRN_IBAT7U,r4 + mtspr SPRN_IBAT7L,r4 +END_FTR_SECTION_IFSET(CPU_FTR_HAS_HIGH_BATS) + + /* Flush all TLBs */ + lis r4,0x1000 +1: addic. r4,r4,-0x1000 + tlbie r4 + blt 1b + sync + + /* restore the MSR and turn on the MMU */ + lwz r3,SL_MSR(r1) + bl turn_on_mmu + + /* get back the stack pointer */ + tovirt(r1,r1) + + /* Restore TB */ + li r3,0 + mttbl r3 + lwz r3,SL_TB(r1) + lwz r4,SL_TB+4(r1) + mttbu r3 + mttbl r4 + + /* Restore the callee-saved registers and return */ + lwz r0,SL_CR(r1) + mtcr r0 + lwz r2,SL_R2(r1) + lmw r12,SL_R12(r1) + addi r1,r1,SL_SIZE + lwz r0,4(r1) + mtlr r0 + blr + +turn_on_mmu: + mflr r4 + tovirt(r4,r4) + mtsrr0 r4 + mtsrr1 r3 + sync + isync + rfi + +#endif /* defined(CONFIG_PMAC_PBOOK) || defined(CONFIG_CPU_FREQ) */ + + .section .data + .balign L1_CACHE_LINE_SIZE +sleep_storage: + .long 0 + .balign L1_CACHE_LINE_SIZE, 0 + +#endif /* CONFIG_6xx */ + .section .text diff --git a/arch/ppc/platforms/pmac_smp.c b/arch/ppc/platforms/pmac_smp.c new file mode 100644 index 00000000000..2b88745576a --- /dev/null +++ b/arch/ppc/platforms/pmac_smp.c @@ -0,0 +1,640 @@ +/* + * SMP support for power macintosh. + * + * We support both the old "powersurge" SMP architecture + * and the current Core99 (G4 PowerMac) machines. + * + * Note that we don't support the very first rev. of + * Apple/DayStar 2 CPUs board, the one with the funky + * watchdog. Hopefully, none of these should be there except + * maybe internally to Apple. I should probably still add some + * code to detect this card though and disable SMP. --BenH. + * + * Support Macintosh G4 SMP by Troy Benjegerdes (hozer@drgw.net) + * and Ben Herrenschmidt . + * + * Support for DayStar quad CPU cards + * Copyright (C) XLR8, Inc. 1994-2000 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Powersurge (old powermac SMP) support. + */ + +extern void __secondary_start_psurge(void); +extern void __secondary_start_psurge2(void); /* Temporary horrible hack */ +extern void __secondary_start_psurge3(void); /* Temporary horrible hack */ + +/* Addresses for powersurge registers */ +#define HAMMERHEAD_BASE 0xf8000000 +#define HHEAD_CONFIG 0x90 +#define HHEAD_SEC_INTR 0xc0 + +/* register for interrupting the primary processor on the powersurge */ +/* N.B. this is actually the ethernet ROM! */ +#define PSURGE_PRI_INTR 0xf3019000 + +/* register for storing the start address for the secondary processor */ +/* N.B. this is the PCI config space address register for the 1st bridge */ +#define PSURGE_START 0xf2800000 + +/* Daystar/XLR8 4-CPU card */ +#define PSURGE_QUAD_REG_ADDR 0xf8800000 + +#define PSURGE_QUAD_IRQ_SET 0 +#define PSURGE_QUAD_IRQ_CLR 1 +#define PSURGE_QUAD_IRQ_PRIMARY 2 +#define PSURGE_QUAD_CKSTOP_CTL 3 +#define PSURGE_QUAD_PRIMARY_ARB 4 +#define PSURGE_QUAD_BOARD_ID 6 +#define PSURGE_QUAD_WHICH_CPU 7 +#define PSURGE_QUAD_CKSTOP_RDBK 8 +#define PSURGE_QUAD_RESET_CTL 11 + +#define PSURGE_QUAD_OUT(r, v) (out_8(quad_base + ((r) << 4) + 4, (v))) +#define PSURGE_QUAD_IN(r) (in_8(quad_base + ((r) << 4) + 4) & 0x0f) +#define PSURGE_QUAD_BIS(r, v) (PSURGE_QUAD_OUT((r), PSURGE_QUAD_IN(r) | (v))) +#define PSURGE_QUAD_BIC(r, v) (PSURGE_QUAD_OUT((r), PSURGE_QUAD_IN(r) & ~(v))) + +/* virtual addresses for the above */ +static volatile u8 *hhead_base; +static volatile u8 *quad_base; +static volatile u32 *psurge_pri_intr; +static volatile u8 *psurge_sec_intr; +static volatile u32 *psurge_start; + +/* values for psurge_type */ +#define PSURGE_NONE -1 +#define PSURGE_DUAL 0 +#define PSURGE_QUAD_OKEE 1 +#define PSURGE_QUAD_COTTON 2 +#define PSURGE_QUAD_ICEGRASS 3 + +/* what sort of powersurge board we have */ +static int psurge_type = PSURGE_NONE; + +/* L2 and L3 cache settings to pass from CPU0 to CPU1 */ +volatile static long int core99_l2_cache; +volatile static long int core99_l3_cache; + +/* Timebase freeze GPIO */ +static unsigned int core99_tb_gpio; + +/* Sync flag for HW tb sync */ +static volatile int sec_tb_reset = 0; + +static void __init core99_init_caches(int cpu) +{ + if (!cpu_has_feature(CPU_FTR_L2CR)) + return; + + if (cpu == 0) { + core99_l2_cache = _get_L2CR(); + printk("CPU0: L2CR is %lx\n", core99_l2_cache); + } else { + printk("CPU%d: L2CR was %lx\n", cpu, _get_L2CR()); + _set_L2CR(0); + _set_L2CR(core99_l2_cache); + printk("CPU%d: L2CR set to %lx\n", cpu, core99_l2_cache); + } + + if (!cpu_has_feature(CPU_FTR_L3CR)) + return; + + if (cpu == 0){ + core99_l3_cache = _get_L3CR(); + printk("CPU0: L3CR is %lx\n", core99_l3_cache); + } else { + printk("CPU%d: L3CR was %lx\n", cpu, _get_L3CR()); + _set_L3CR(0); + _set_L3CR(core99_l3_cache); + printk("CPU%d: L3CR set to %lx\n", cpu, core99_l3_cache); + } +} + +/* + * Set and clear IPIs for powersurge. + */ +static inline void psurge_set_ipi(int cpu) +{ + if (psurge_type == PSURGE_NONE) + return; + if (cpu == 0) + in_be32(psurge_pri_intr); + else if (psurge_type == PSURGE_DUAL) + out_8(psurge_sec_intr, 0); + else + PSURGE_QUAD_OUT(PSURGE_QUAD_IRQ_SET, 1 << cpu); +} + +static inline void psurge_clr_ipi(int cpu) +{ + if (cpu > 0) { + switch(psurge_type) { + case PSURGE_DUAL: + out_8(psurge_sec_intr, ~0); + case PSURGE_NONE: + break; + default: + PSURGE_QUAD_OUT(PSURGE_QUAD_IRQ_CLR, 1 << cpu); + } + } +} + +/* + * On powersurge (old SMP powermac architecture) we don't have + * separate IPIs for separate messages like openpic does. Instead + * we have a bitmap for each processor, where a 1 bit means that + * the corresponding message is pending for that processor. + * Ideally each cpu's entry would be in a different cache line. + * -- paulus. + */ +static unsigned long psurge_smp_message[NR_CPUS]; + +void __pmac psurge_smp_message_recv(struct pt_regs *regs) +{ + int cpu = smp_processor_id(); + int msg; + + /* clear interrupt */ + psurge_clr_ipi(cpu); + + if (num_online_cpus() < 2) + return; + + /* make sure there is a message there */ + for (msg = 0; msg < 4; msg++) + if (test_and_clear_bit(msg, &psurge_smp_message[cpu])) + smp_message_recv(msg, regs); +} + +irqreturn_t __pmac psurge_primary_intr(int irq, void *d, struct pt_regs *regs) +{ + psurge_smp_message_recv(regs); + return IRQ_HANDLED; +} + +static void __pmac smp_psurge_message_pass(int target, int msg, unsigned long data, + int wait) +{ + int i; + + if (num_online_cpus() < 2) + return; + + for (i = 0; i < NR_CPUS; i++) { + if (!cpu_online(i)) + continue; + if (target == MSG_ALL + || (target == MSG_ALL_BUT_SELF && i != smp_processor_id()) + || target == i) { + set_bit(msg, &psurge_smp_message[i]); + psurge_set_ipi(i); + } + } +} + +/* + * Determine a quad card presence. We read the board ID register, we + * force the data bus to change to something else, and we read it again. + * It it's stable, then the register probably exist (ugh !) + */ +static int __init psurge_quad_probe(void) +{ + int type; + unsigned int i; + + type = PSURGE_QUAD_IN(PSURGE_QUAD_BOARD_ID); + if (type < PSURGE_QUAD_OKEE || type > PSURGE_QUAD_ICEGRASS + || type != PSURGE_QUAD_IN(PSURGE_QUAD_BOARD_ID)) + return PSURGE_DUAL; + + /* looks OK, try a slightly more rigorous test */ + /* bogus is not necessarily cacheline-aligned, + though I don't suppose that really matters. -- paulus */ + for (i = 0; i < 100; i++) { + volatile u32 bogus[8]; + bogus[(0+i)%8] = 0x00000000; + bogus[(1+i)%8] = 0x55555555; + bogus[(2+i)%8] = 0xFFFFFFFF; + bogus[(3+i)%8] = 0xAAAAAAAA; + bogus[(4+i)%8] = 0x33333333; + bogus[(5+i)%8] = 0xCCCCCCCC; + bogus[(6+i)%8] = 0xCCCCCCCC; + bogus[(7+i)%8] = 0x33333333; + wmb(); + asm volatile("dcbf 0,%0" : : "r" (bogus) : "memory"); + mb(); + if (type != PSURGE_QUAD_IN(PSURGE_QUAD_BOARD_ID)) + return PSURGE_DUAL; + } + return type; +} + +static void __init psurge_quad_init(void) +{ + int procbits; + + if (ppc_md.progress) ppc_md.progress("psurge_quad_init", 0x351); + procbits = ~PSURGE_QUAD_IN(PSURGE_QUAD_WHICH_CPU); + if (psurge_type == PSURGE_QUAD_ICEGRASS) + PSURGE_QUAD_BIS(PSURGE_QUAD_RESET_CTL, procbits); + else + PSURGE_QUAD_BIC(PSURGE_QUAD_CKSTOP_CTL, procbits); + mdelay(33); + out_8(psurge_sec_intr, ~0); + PSURGE_QUAD_OUT(PSURGE_QUAD_IRQ_CLR, procbits); + PSURGE_QUAD_BIS(PSURGE_QUAD_RESET_CTL, procbits); + if (psurge_type != PSURGE_QUAD_ICEGRASS) + PSURGE_QUAD_BIS(PSURGE_QUAD_CKSTOP_CTL, procbits); + PSURGE_QUAD_BIC(PSURGE_QUAD_PRIMARY_ARB, procbits); + mdelay(33); + PSURGE_QUAD_BIC(PSURGE_QUAD_RESET_CTL, procbits); + mdelay(33); + PSURGE_QUAD_BIS(PSURGE_QUAD_PRIMARY_ARB, procbits); + mdelay(33); +} + +static int __init smp_psurge_probe(void) +{ + int i, ncpus; + + /* We don't do SMP on the PPC601 -- paulus */ + if (PVR_VER(mfspr(SPRN_PVR)) == 1) + return 1; + + /* + * The powersurge cpu board can be used in the generation + * of powermacs that have a socket for an upgradeable cpu card, + * including the 7500, 8500, 9500, 9600. + * The device tree doesn't tell you if you have 2 cpus because + * OF doesn't know anything about the 2nd processor. + * Instead we look for magic bits in magic registers, + * in the hammerhead memory controller in the case of the + * dual-cpu powersurge board. -- paulus. + */ + if (find_devices("hammerhead") == NULL) + return 1; + + hhead_base = ioremap(HAMMERHEAD_BASE, 0x800); + quad_base = ioremap(PSURGE_QUAD_REG_ADDR, 1024); + psurge_sec_intr = hhead_base + HHEAD_SEC_INTR; + + psurge_type = psurge_quad_probe(); + if (psurge_type != PSURGE_DUAL) { + psurge_quad_init(); + /* All released cards using this HW design have 4 CPUs */ + ncpus = 4; + } else { + iounmap((void *) quad_base); + if ((in_8(hhead_base + HHEAD_CONFIG) & 0x02) == 0) { + /* not a dual-cpu card */ + iounmap((void *) hhead_base); + psurge_type = PSURGE_NONE; + return 1; + } + ncpus = 2; + } + + psurge_start = ioremap(PSURGE_START, 4); + psurge_pri_intr = ioremap(PSURGE_PRI_INTR, 4); + + /* this is not actually strictly necessary -- paulus. */ + for (i = 1; i < ncpus; ++i) + smp_hw_index[i] = i; + + if (ppc_md.progress) ppc_md.progress("smp_psurge_probe - done", 0x352); + + return ncpus; +} + +static void __init smp_psurge_kick_cpu(int nr) +{ + void (*start)(void) = __secondary_start_psurge; + unsigned long a; + + /* may need to flush here if secondary bats aren't setup */ + for (a = KERNELBASE; a < KERNELBASE + 0x800000; a += 32) + asm volatile("dcbf 0,%0" : : "r" (a) : "memory"); + asm volatile("sync"); + + if (ppc_md.progress) ppc_md.progress("smp_psurge_kick_cpu", 0x353); + + /* setup entry point of secondary processor */ + switch (nr) { + case 2: + start = __secondary_start_psurge2; + break; + case 3: + start = __secondary_start_psurge3; + break; + } + + out_be32(psurge_start, __pa(start)); + mb(); + + psurge_set_ipi(nr); + udelay(10); + psurge_clr_ipi(nr); + + if (ppc_md.progress) ppc_md.progress("smp_psurge_kick_cpu - done", 0x354); +} + +/* + * With the dual-cpu powersurge board, the decrementers and timebases + * of both cpus are frozen after the secondary cpu is started up, + * until we give the secondary cpu another interrupt. This routine + * uses this to get the timebases synchronized. + * -- paulus. + */ +static void __init psurge_dual_sync_tb(int cpu_nr) +{ + int t; + + set_dec(tb_ticks_per_jiffy); + set_tb(0, 0); + last_jiffy_stamp(cpu_nr) = 0; + + if (cpu_nr > 0) { + mb(); + sec_tb_reset = 1; + return; + } + + /* wait for the secondary to have reset its TB before proceeding */ + for (t = 10000000; t > 0 && !sec_tb_reset; --t) + ; + + /* now interrupt the secondary, starting both TBs */ + psurge_set_ipi(1); + + smp_tb_synchronized = 1; +} + +static struct irqaction psurge_irqaction = { + .handler = psurge_primary_intr, + .flags = SA_INTERRUPT, + .mask = CPU_MASK_NONE, + .name = "primary IPI", +}; + +static void __init smp_psurge_setup_cpu(int cpu_nr) +{ + + if (cpu_nr == 0) { + /* If we failed to start the second CPU, we should still + * send it an IPI to start the timebase & DEC or we might + * have them stuck. + */ + if (num_online_cpus() < 2) { + if (psurge_type == PSURGE_DUAL) + psurge_set_ipi(1); + return; + } + /* reset the entry point so if we get another intr we won't + * try to startup again */ + out_be32(psurge_start, 0x100); + if (setup_irq(30, &psurge_irqaction)) + printk(KERN_ERR "Couldn't get primary IPI interrupt"); + } + + if (psurge_type == PSURGE_DUAL) + psurge_dual_sync_tb(cpu_nr); +} + +void __init smp_psurge_take_timebase(void) +{ + /* Dummy implementation */ +} + +void __init smp_psurge_give_timebase(void) +{ + /* Dummy implementation */ +} + +static int __init smp_core99_probe(void) +{ +#ifdef CONFIG_6xx + extern int powersave_nap; +#endif + struct device_node *cpus, *firstcpu; + int i, ncpus = 0, boot_cpu = -1; + u32 *tbprop; + + if (ppc_md.progress) ppc_md.progress("smp_core99_probe", 0x345); + cpus = firstcpu = find_type_devices("cpu"); + while(cpus != NULL) { + u32 *regprop = (u32 *)get_property(cpus, "reg", NULL); + char *stateprop = (char *)get_property(cpus, "state", NULL); + if (regprop != NULL && stateprop != NULL && + !strncmp(stateprop, "running", 7)) + boot_cpu = *regprop; + ++ncpus; + cpus = cpus->next; + } + if (boot_cpu == -1) + printk(KERN_WARNING "Couldn't detect boot CPU !\n"); + if (boot_cpu != 0) + printk(KERN_WARNING "Boot CPU is %d, unsupported setup !\n", boot_cpu); + + if (machine_is_compatible("MacRISC4")) { + extern struct smp_ops_t core99_smp_ops; + + core99_smp_ops.take_timebase = smp_generic_take_timebase; + core99_smp_ops.give_timebase = smp_generic_give_timebase; + } else { + if (firstcpu != NULL) + tbprop = (u32 *)get_property(firstcpu, "timebase-enable", NULL); + if (tbprop) + core99_tb_gpio = *tbprop; + else + core99_tb_gpio = KL_GPIO_TB_ENABLE; + } + + if (ncpus > 1) { + openpic_request_IPIs(); + for (i = 1; i < ncpus; ++i) + smp_hw_index[i] = i; +#ifdef CONFIG_6xx + powersave_nap = 0; +#endif + core99_init_caches(0); + } + + return ncpus; +} + +static void __init smp_core99_kick_cpu(int nr) +{ + unsigned long save_vector, new_vector; + unsigned long flags; + + volatile unsigned long *vector + = ((volatile unsigned long *)(KERNELBASE+0x100)); + if (nr < 1 || nr > 3) + return; + if (ppc_md.progress) ppc_md.progress("smp_core99_kick_cpu", 0x346); + + local_irq_save(flags); + local_irq_disable(); + + /* Save reset vector */ + save_vector = *vector; + + /* Setup fake reset vector that does + * b __secondary_start_psurge - KERNELBASE + */ + switch(nr) { + case 1: + new_vector = (unsigned long)__secondary_start_psurge; + break; + case 2: + new_vector = (unsigned long)__secondary_start_psurge2; + break; + case 3: + new_vector = (unsigned long)__secondary_start_psurge3; + break; + } + *vector = 0x48000002 + new_vector - KERNELBASE; + + /* flush data cache and inval instruction cache */ + flush_icache_range((unsigned long) vector, (unsigned long) vector + 4); + + /* Put some life in our friend */ + pmac_call_feature(PMAC_FTR_RESET_CPU, NULL, nr, 0); + + /* FIXME: We wait a bit for the CPU to take the exception, I should + * instead wait for the entry code to set something for me. Well, + * ideally, all that crap will be done in prom.c and the CPU left + * in a RAM-based wait loop like CHRP. + */ + mdelay(1); + + /* Restore our exception vector */ + *vector = save_vector; + flush_icache_range((unsigned long) vector, (unsigned long) vector + 4); + + local_irq_restore(flags); + if (ppc_md.progress) ppc_md.progress("smp_core99_kick_cpu done", 0x347); +} + +static void __init smp_core99_setup_cpu(int cpu_nr) +{ + /* Setup L2/L3 */ + if (cpu_nr != 0) + core99_init_caches(cpu_nr); + + /* Setup openpic */ + do_openpic_setup_cpu(); + + if (cpu_nr == 0) { +#ifdef CONFIG_POWER4 + extern void g5_phy_disable_cpu1(void); + + /* If we didn't start the second CPU, we must take + * it off the bus + */ + if (machine_is_compatible("MacRISC4") && + num_online_cpus() < 2) + g5_phy_disable_cpu1(); +#endif /* CONFIG_POWER4 */ + if (ppc_md.progress) ppc_md.progress("core99_setup_cpu 0 done", 0x349); + } +} + +void __init smp_core99_take_timebase(void) +{ + /* Secondary processor "takes" the timebase by freezing + * it, resetting its local TB and telling CPU 0 to go on + */ + pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, core99_tb_gpio, 4); + pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, core99_tb_gpio, 0); + mb(); + + set_dec(tb_ticks_per_jiffy); + set_tb(0, 0); + last_jiffy_stamp(smp_processor_id()) = 0; + + mb(); + sec_tb_reset = 1; +} + +void __init smp_core99_give_timebase(void) +{ + unsigned int t; + + /* Primary processor waits for secondary to have frozen + * the timebase, resets local TB, and kick timebase again + */ + /* wait for the secondary to have reset its TB before proceeding */ + for (t = 1000; t > 0 && !sec_tb_reset; --t) + udelay(1000); + if (t == 0) + printk(KERN_WARNING "Timeout waiting sync on second CPU\n"); + + set_dec(tb_ticks_per_jiffy); + set_tb(0, 0); + last_jiffy_stamp(smp_processor_id()) = 0; + mb(); + + /* Now, restart the timebase by leaving the GPIO to an open collector */ + pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, core99_tb_gpio, 0); + pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, core99_tb_gpio, 0); + + smp_tb_synchronized = 1; +} + + +/* PowerSurge-style Macs */ +struct smp_ops_t psurge_smp_ops __pmacdata = { + .message_pass = smp_psurge_message_pass, + .probe = smp_psurge_probe, + .kick_cpu = smp_psurge_kick_cpu, + .setup_cpu = smp_psurge_setup_cpu, + .give_timebase = smp_psurge_give_timebase, + .take_timebase = smp_psurge_take_timebase, +}; + +/* Core99 Macs (dual G4s) */ +struct smp_ops_t core99_smp_ops __pmacdata = { + .message_pass = smp_openpic_message_pass, + .probe = smp_core99_probe, + .kick_cpu = smp_core99_kick_cpu, + .setup_cpu = smp_core99_setup_cpu, + .give_timebase = smp_core99_give_timebase, + .take_timebase = smp_core99_take_timebase, +}; diff --git a/arch/ppc/platforms/pmac_time.c b/arch/ppc/platforms/pmac_time.c new file mode 100644 index 00000000000..09636546f44 --- /dev/null +++ b/arch/ppc/platforms/pmac_time.c @@ -0,0 +1,292 @@ +/* + * Support for periodic interrupts (100 per second) and for getting + * the current time from the RTC on Power Macintoshes. + * + * We use the decrementer register for our periodic interrupts. + * + * Paul Mackerras August 1996. + * Copyright (C) 1996 Paul Mackerras. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Apparently the RTC stores seconds since 1 Jan 1904 */ +#define RTC_OFFSET 2082844800 + +/* + * Calibrate the decrementer frequency with the VIA timer 1. + */ +#define VIA_TIMER_FREQ_6 4700000 /* time 1 frequency * 6 */ + +/* VIA registers */ +#define RS 0x200 /* skip between registers */ +#define T1CL (4*RS) /* Timer 1 ctr/latch (low 8 bits) */ +#define T1CH (5*RS) /* Timer 1 counter (high 8 bits) */ +#define T1LL (6*RS) /* Timer 1 latch (low 8 bits) */ +#define T1LH (7*RS) /* Timer 1 latch (high 8 bits) */ +#define ACR (11*RS) /* Auxiliary control register */ +#define IFR (13*RS) /* Interrupt flag register */ + +/* Bits in ACR */ +#define T1MODE 0xc0 /* Timer 1 mode */ +#define T1MODE_CONT 0x40 /* continuous interrupts */ + +/* Bits in IFR and IER */ +#define T1_INT 0x40 /* Timer 1 interrupt */ + +extern struct timezone sys_tz; + +long __init +pmac_time_init(void) +{ +#ifdef CONFIG_NVRAM + s32 delta = 0; + int dst; + + delta = ((s32)pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0x9)) << 16; + delta |= ((s32)pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0xa)) << 8; + delta |= pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0xb); + if (delta & 0x00800000UL) + delta |= 0xFF000000UL; + dst = ((pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0x8) & 0x80) != 0); + printk("GMT Delta read from XPRAM: %d minutes, DST: %s\n", delta/60, + dst ? "on" : "off"); + return delta; +#else + return 0; +#endif +} + +unsigned long __pmac +pmac_get_rtc_time(void) +{ +#if defined(CONFIG_ADB_CUDA) || defined(CONFIG_ADB_PMU) + struct adb_request req; + unsigned long now; +#endif + + /* Get the time from the RTC */ + switch (sys_ctrler) { +#ifdef CONFIG_ADB_CUDA + case SYS_CTRLER_CUDA: + if (cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_GET_TIME) < 0) + return 0; + while (!req.complete) + cuda_poll(); + if (req.reply_len != 7) + printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n", + req.reply_len); + now = (req.reply[3] << 24) + (req.reply[4] << 16) + + (req.reply[5] << 8) + req.reply[6]; + return now - RTC_OFFSET; +#endif /* CONFIG_ADB_CUDA */ +#ifdef CONFIG_ADB_PMU + case SYS_CTRLER_PMU: + if (pmu_request(&req, NULL, 1, PMU_READ_RTC) < 0) + return 0; + while (!req.complete) + pmu_poll(); + if (req.reply_len != 4) + printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n", + req.reply_len); + now = (req.reply[0] << 24) + (req.reply[1] << 16) + + (req.reply[2] << 8) + req.reply[3]; + return now - RTC_OFFSET; +#endif /* CONFIG_ADB_PMU */ + default: ; + } + return 0; +} + +int __pmac +pmac_set_rtc_time(unsigned long nowtime) +{ +#if defined(CONFIG_ADB_CUDA) || defined(CONFIG_ADB_PMU) + struct adb_request req; +#endif + + nowtime += RTC_OFFSET; + + switch (sys_ctrler) { +#ifdef CONFIG_ADB_CUDA + case SYS_CTRLER_CUDA: + if (cuda_request(&req, NULL, 6, CUDA_PACKET, CUDA_SET_TIME, + nowtime >> 24, nowtime >> 16, nowtime >> 8, nowtime) < 0) + return 0; + while (!req.complete) + cuda_poll(); + if ((req.reply_len != 3) && (req.reply_len != 7)) + printk(KERN_ERR "pmac_set_rtc_time: got %d byte reply\n", + req.reply_len); + return 1; +#endif /* CONFIG_ADB_CUDA */ +#ifdef CONFIG_ADB_PMU + case SYS_CTRLER_PMU: + if (pmu_request(&req, NULL, 5, PMU_SET_RTC, + nowtime >> 24, nowtime >> 16, nowtime >> 8, nowtime) < 0) + return 0; + while (!req.complete) + pmu_poll(); + if (req.reply_len != 0) + printk(KERN_ERR "pmac_set_rtc_time: got %d byte reply\n", + req.reply_len); + return 1; +#endif /* CONFIG_ADB_PMU */ + default: + return 0; + } +} + +/* + * Calibrate the decrementer register using VIA timer 1. + * This is used both on powermacs and CHRP machines. + */ +int __init +via_calibrate_decr(void) +{ + struct device_node *vias; + volatile unsigned char *via; + int count = VIA_TIMER_FREQ_6 / 100; + unsigned int dstart, dend; + + vias = find_devices("via-cuda"); + if (vias == 0) + vias = find_devices("via-pmu"); + if (vias == 0) + vias = find_devices("via"); + if (vias == 0 || vias->n_addrs == 0) + return 0; + via = (volatile unsigned char *) + ioremap(vias->addrs[0].address, vias->addrs[0].size); + + /* set timer 1 for continuous interrupts */ + out_8(&via[ACR], (via[ACR] & ~T1MODE) | T1MODE_CONT); + /* set the counter to a small value */ + out_8(&via[T1CH], 2); + /* set the latch to `count' */ + out_8(&via[T1LL], count); + out_8(&via[T1LH], count >> 8); + /* wait until it hits 0 */ + while ((in_8(&via[IFR]) & T1_INT) == 0) + ; + dstart = get_dec(); + /* clear the interrupt & wait until it hits 0 again */ + in_8(&via[T1CL]); + while ((in_8(&via[IFR]) & T1_INT) == 0) + ; + dend = get_dec(); + + tb_ticks_per_jiffy = (dstart - dend) / (6 * (HZ/100)); + tb_to_us = mulhwu_scale_factor(dstart - dend, 60000); + + printk(KERN_INFO "via_calibrate_decr: ticks per jiffy = %u (%u ticks)\n", + tb_ticks_per_jiffy, dstart - dend); + + iounmap((void*)via); + + return 1; +} + +#ifdef CONFIG_PMAC_PBOOK +/* + * Reset the time after a sleep. + */ +static int __pmac +time_sleep_notify(struct pmu_sleep_notifier *self, int when) +{ + static unsigned long time_diff; + unsigned long flags; + unsigned long seq; + + switch (when) { + case PBOOK_SLEEP_NOW: + do { + seq = read_seqbegin_irqsave(&xtime_lock, flags); + time_diff = xtime.tv_sec - pmac_get_rtc_time(); + } while (read_seqretry_irqrestore(&xtime_lock, seq, flags)); + break; + case PBOOK_WAKE: + write_seqlock_irqsave(&xtime_lock, flags); + xtime.tv_sec = pmac_get_rtc_time() + time_diff; + xtime.tv_nsec = 0; + last_rtc_update = xtime.tv_sec; + write_sequnlock_irqrestore(&xtime_lock, flags); + break; + } + return PBOOK_SLEEP_OK; +} + +static struct pmu_sleep_notifier time_sleep_notifier __pmacdata = { + time_sleep_notify, SLEEP_LEVEL_MISC, +}; +#endif /* CONFIG_PMAC_PBOOK */ + +/* + * Query the OF and get the decr frequency. + * This was taken from the pmac time_init() when merging the prep/pmac + * time functions. + */ +void __init +pmac_calibrate_decr(void) +{ + struct device_node *cpu; + unsigned int freq, *fp; + +#ifdef CONFIG_PMAC_PBOOK + pmu_register_sleep_notifier(&time_sleep_notifier); +#endif /* CONFIG_PMAC_PBOOK */ + + /* We assume MacRISC2 machines have correct device-tree + * calibration. That's better since the VIA itself seems + * to be slightly off. --BenH + */ + if (!machine_is_compatible("MacRISC2") && + !machine_is_compatible("MacRISC3") && + !machine_is_compatible("MacRISC4")) + if (via_calibrate_decr()) + return; + + /* Special case: QuickSilver G4s seem to have a badly calibrated + * timebase-frequency in OF, VIA is much better on these. We should + * probably implement calibration based on the KL timer on these + * machines anyway... -BenH + */ + if (machine_is_compatible("PowerMac3,5")) + if (via_calibrate_decr()) + return; + /* + * The cpu node should have a timebase-frequency property + * to tell us the rate at which the decrementer counts. + */ + cpu = find_type_devices("cpu"); + if (cpu == 0) + panic("can't find cpu node in time_init"); + fp = (unsigned int *) get_property(cpu, "timebase-frequency", NULL); + if (fp == 0) + panic("can't get cpu timebase frequency"); + freq = *fp; + printk("time_init: decrementer frequency = %u.%.6u MHz\n", + freq/1000000, freq%1000000); + tb_ticks_per_jiffy = freq / HZ; + tb_to_us = mulhwu_scale_factor(freq, 1000000); +} diff --git a/arch/ppc/platforms/powerpmc250.c b/arch/ppc/platforms/powerpmc250.c new file mode 100644 index 00000000000..0abe15159e6 --- /dev/null +++ b/arch/ppc/platforms/powerpmc250.c @@ -0,0 +1,383 @@ +/* + * arch/ppc/platforms/powerpmc250.c + * + * Board setup routines for Force PowerPMC-250 Processor PMC + * + * Author: Troy Benjegerdes + * Borrowed heavily from prpmc750_*.c by + * Matt Porter + * + * 2001 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern void powerpmc250_find_bridges(void); +extern unsigned long loops_per_jiffy; + +static u_char powerpmc250_openpic_initsenses[] __initdata = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, /* PMC INTA (also MPC107 output interrupt INTA) */ + 1, /* PMC INTB (also I82559 Ethernet controller) */ + 1, /* PMC INTC */ + 1, /* PMC INTD */ + 0, /* DUART interrupt (active high) */ +}; + +static int +powerpmc250_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m,"machine\t\t: Force PowerPMC250\n"); + + return 0; +} + +static void __init +powerpmc250_setup_arch(void) +{ + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_jiffy = 50000000/HZ; + + /* Lookup PCI host bridges */ + powerpmc250_find_bridges(); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = Root_NFS; +#else + ROOT_DEV = Root_SDA2; +#endif + + printk("Force PowerPMC250 port (C) 2001 MontaVista Software, Inc. (source@mvista.com)\n"); +} + +#if 0 +/* + * Compute the PrPMC750's bus speed using the baud clock as a + * reference. + */ +unsigned long __init powerpmc250_get_bus_speed(void) +{ + unsigned long tbl_start, tbl_end; + unsigned long current_state, old_state, bus_speed; + unsigned char lcr, dll, dlm; + int baud_divisor, count; + + /* Read the UART's baud clock divisor */ + lcr = readb(PRPMC750_SERIAL_0_LCR); + writeb(lcr | UART_LCR_DLAB, PRPMC750_SERIAL_0_LCR); + dll = readb(PRPMC750_SERIAL_0_DLL); + dlm = readb(PRPMC750_SERIAL_0_DLM); + writeb(lcr & ~UART_LCR_DLAB, PRPMC750_SERIAL_0_LCR); + baud_divisor = (dlm << 8) | dll; + + /* + * Use the baud clock divisor and base baud clock + * to determine the baud rate and use that as + * the number of baud clock edges we use for + * the time base sample. Make it half the baud + * rate. + */ + count = PRPMC750_BASE_BAUD / (baud_divisor * 16); + + /* Find the first edge of the baud clock */ + old_state = readb(PRPMC750_STATUS_REG) & PRPMC750_BAUDOUT_MASK; + do { + current_state = readb(PRPMC750_STATUS_REG) & + PRPMC750_BAUDOUT_MASK; + } while(old_state == current_state); + + old_state = current_state; + + /* Get the starting time base value */ + tbl_start = get_tbl(); + + /* + * Loop until we have found a number of edges equal + * to half the count (half the baud rate) + */ + do { + do { + current_state = readb(PRPMC750_STATUS_REG) & + PRPMC750_BAUDOUT_MASK; + } while(old_state == current_state); + old_state = current_state; + } while (--count); + + /* Get the ending time base value */ + tbl_end = get_tbl(); + + /* Compute bus speed */ + bus_speed = (tbl_end-tbl_start)*128; + + return bus_speed; +} +#endif + +static void __init +powerpmc250_calibrate_decr(void) +{ + unsigned long freq; + int divisor = 4; + + //freq = powerpmc250_get_bus_speed(); +#warning hardcoded bus freq + freq = 100000000; + + tb_ticks_per_jiffy = freq / (HZ * divisor); + tb_to_us = mulhwu_scale_factor(freq/divisor, 1000000); +} + +static void +powerpmc250_restart(char *cmd) +{ + local_irq_disable(); + /* Hard reset */ + writeb(0x11, 0xfe000332); + while(1); +} + +static void +powerpmc250_halt(void) +{ + local_irq_disable(); + while (1); +} + +static void +powerpmc250_power_off(void) +{ + powerpmc250_halt(); +} + +static void __init +powerpmc250_init_IRQ(void) +{ + + OpenPIC_InitSenses = powerpmc250_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof(powerpmc250_openpic_initsenses); + mpc10x_set_openpic(); +} + +/* + * Set BAT 3 to map 0xf0000000 to end of physical memory space. + */ +static __inline__ void +powerpmc250_set_bat(void) +{ + unsigned long bat3u, bat3l; + static int mapping_set = 0; + + if (!mapping_set) + { + __asm__ __volatile__( + " lis %0,0xf000\n \ + ori %1,%0,0x002a\n \ + ori %0,%0,0x1ffe\n \ + mtspr 0x21e,%0\n \ + mtspr 0x21f,%1\n \ + isync\n \ + sync " + : "=r" (bat3u), "=r" (bat3l)); + + mapping_set = 1; + } + return; +} + +static unsigned long __init +powerpmc250_find_end_of_memory(void) +{ + /* Cover I/O space with a BAT */ + /* yuck, better hope your ram size is a power of 2 -- paulus */ + powerpmc250_set_bat(); + + return mpc10x_get_mem_size(MPC10X_MEM_MAP_B); +} + +static void __init +powerpmc250_map_io(void) +{ + io_block_mapping(0xfe000000, 0xfe000000, 0x02000000, _PAGE_IO); +} + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + +#ifdef CONFIG_BLK_DEV_INITRD + if ( r4 ) + { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } +#endif + + /* Copy cmd_line parameters */ + if ( r6) + { + *(char *)(r7 + KERNELBASE) = 0; + strcpy(cmd_line, (char *)(r6 + KERNELBASE)); + } + + isa_io_base = MPC10X_MAPB_ISA_IO_BASE; + isa_mem_base = MPC10X_MAPB_ISA_MEM_BASE; + pci_dram_offset = MPC10X_MAPB_DRAM_OFFSET; + + ppc_md.setup_arch = powerpmc250_setup_arch; + ppc_md.show_cpuinfo = powerpmc250_show_cpuinfo; + ppc_md.init_IRQ = powerpmc250_init_IRQ; + ppc_md.get_irq = openpic_get_irq; + + ppc_md.find_end_of_memory = powerpmc250_find_end_of_memory; + ppc_md.setup_io_mappings = powerpmc250_map_io; + + ppc_md.restart = powerpmc250_restart; + ppc_md.power_off = powerpmc250_power_off; + ppc_md.halt = powerpmc250_halt; + + /* PowerPMC250 has no timekeeper part */ + ppc_md.time_init = NULL; + ppc_md.get_rtc_time = NULL; + ppc_md.set_rtc_time = NULL; + ppc_md.calibrate_decr = powerpmc250_calibrate_decr; +} + + +/* + * (This used to be arch/ppc/platforms/powerpmc250_pci.c) + * + * PCI support for Force PowerPMC250 + * + */ + +#undef DEBUG +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif /* DEBUG */ + +static inline int __init +powerpmc250_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {17, 0, 0, 0}, /* Device 11 - 82559 */ + {0, 0, 0, 0}, /* 12 */ + {0, 0, 0, 0}, /* 13 */ + {0, 0, 0, 0}, /* 14 */ + {0, 0, 0, 0}, /* 15 */ + {16, 17, 18, 19}, /* Device 16 - PMC A1?? */ + }; + const long min_idsel = 11, max_idsel = 16, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +}; + +static int +powerpmc250_exclude_device(u_char bus, u_char devfn) +{ + /* + * While doing PCI Scan the MPC107 will 'detect' itself as + * device on the PCI Bus, will create an incorrect response and + * later will respond incorrectly to Configuration read coming + * from another device. + * + * The work around is that when doing a PCI Scan one + * should skip its own device number in the scan. + * + * The top IDsel is AD13 and the middle is AD14. + * + * -- Note from force + */ + + if ((bus == 0) && (PCI_SLOT(devfn) == 13 || PCI_SLOT(devfn) == 14)) { + return PCIBIOS_DEVICE_NOT_FOUND; + } + else { + return PCIBIOS_SUCCESSFUL; + } +} + +void __init +powerpmc250_find_bridges(void) +{ + struct pci_controller* hose; + + hose = pcibios_alloc_controller(); + if (!hose){ + printk("Can't allocate PCI 'hose' structure!!!\n"); + return; + } + + hose->first_busno = 0; + hose->last_busno = 0xff; + + if (mpc10x_bridge_init(hose, + MPC10X_MEM_MAP_B, + MPC10X_MEM_MAP_B, + MPC10X_MAPB_EUMB_BASE) == 0) { + + hose->mem_resources[0].end = 0xffffffff; + + hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); + + /* ppc_md.pcibios_fixup = pcore_pcibios_fixup; */ + ppc_md.pci_swizzle = common_swizzle; + + ppc_md.pci_exclude_device = powerpmc250_exclude_device; + ppc_md.pci_map_irq = powerpmc250_map_irq; + } else { + if (ppc_md.progress) + ppc_md.progress("Bridge init failed", 0x100); + printk("Host bridge init failed\n"); + } + +} diff --git a/arch/ppc/platforms/powerpmc250.h b/arch/ppc/platforms/powerpmc250.h new file mode 100644 index 00000000000..41a6dc88191 --- /dev/null +++ b/arch/ppc/platforms/powerpmc250.h @@ -0,0 +1,52 @@ +/* + * include/asm-ppc/platforms/powerpmc250.h + * + * Definitions for Force PowerPMC-250 board support + * + * Author: Troy Benjegerdes + * + * Borrowed heavily from prpmc750.h by Matt Porter + * + * 2001-2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifndef __ASMPPC_POWERPMC250_H +#define __ASMPPC_POWERPMC250_H + +#define POWERPMC250_PCI_CONFIG_ADDR 0x80000cf8 +#define POWERPMC250_PCI_CONFIG_DATA 0x80000cfc + +#define POWERPMC250_PCI_PHY_MEM_BASE 0xc0000000 +#define POWERPMC250_PCI_MEM_BASE 0xf0000000 +#define POWERPMC250_PCI_IO_BASE 0x80000000 + +#define POWERPMC250_ISA_IO_BASE POWERPMC250_PCI_IO_BASE +#define POWERPMC250_ISA_MEM_BASE POWERPMC250_PCI_MEM_BASE +#define POWERPMC250_PCI_MEM_OFFSET POWERPMC250_PCI_PHY_MEM_BASE + +#define POWERPMC250_SYS_MEM_BASE 0x80000000 + +#define POWERPMC250_HAWK_SMC_BASE 0xfef80000 + +#define POWERPMC250_BASE_BAUD 12288000 +#define POWERPMC250_SERIAL 0xff000000 +#define POWERPMC250_SERIAL_IRQ 20 + +/* UART Defines. */ +#define RS_TABLE_SIZE 1 + +#define BASE_BAUD (POWERPMC250_BASE_BAUD / 16) + +#define STD_COM_FLAGS ASYNC_BOOT_AUTOCONF + +#define SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, POWERPMC250_SERIAL, POWERPMC250_SERIAL_IRQ, \ + STD_COM_FLAGS, /* ttyS0 */ \ + iomem_base: (u8 *)POWERPMC250_SERIAL, \ + iomem_reg_shift: 0, \ + io_type: SERIAL_IO_MEM } + +#endif /* __ASMPPC_POWERPMC250_H */ diff --git a/arch/ppc/platforms/pplus.c b/arch/ppc/platforms/pplus.c new file mode 100644 index 00000000000..65705c91179 --- /dev/null +++ b/arch/ppc/platforms/pplus.c @@ -0,0 +1,917 @@ +/* + * arch/ppc/platforms/pplus.c + * + * Board and PCI setup routines for MCG PowerPlus + * + * Author: Randy Vinson + * + * Derived from original PowerPlus PReP work by + * Cort Dougan, Johnnie Peters, Matt Porter, and + * Troy Benjegerdes. + * + * 2001-2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pplus.h" + +#undef DUMP_DBATS + +TODC_ALLOC(); + +extern void pplus_setup_hose(void); +extern void pplus_set_VIA_IDE_native(void); + +extern unsigned long loops_per_jiffy; +unsigned char *Motherboard_map_name; + +/* Tables for known hardware */ + +/* Motorola Mesquite */ +static inline int +mesquite_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * MPIC interrupts for various IDSEL values (MPIC IRQ0 = + * Linux IRQ16 (to leave room for ISA IRQs at 0-15). + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {18, 0, 0, 0}, /* IDSEL 14 - Enet 0 */ + { 0, 0, 0, 0}, /* IDSEL 15 - unused */ + {19, 19, 19, 19}, /* IDSEL 16 - PMC Slot 1 */ + { 0, 0, 0, 0}, /* IDSEL 17 - unused */ + { 0, 0, 0, 0}, /* IDSEL 18 - unused */ + { 0, 0, 0, 0}, /* IDSEL 19 - unused */ + {24, 25, 26, 27}, /* IDSEL 20 - P2P bridge (to cPCI 1) */ + { 0, 0, 0, 0}, /* IDSEL 21 - unused */ + {28, 29, 30, 31} /* IDSEL 22 - P2P bridge (to cPCI 2) */ + }; + + const long min_idsel = 14, max_idsel = 22, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +} + +/* Motorola Sitka */ +static inline int +sitka_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * MPIC interrupts for various IDSEL values (MPIC IRQ0 = + * Linux IRQ16 (to leave room for ISA IRQs at 0-15). + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {18, 0, 0, 0}, /* IDSEL 14 - Enet 0 */ + { 0, 0, 0, 0}, /* IDSEL 15 - unused */ + {25, 26, 27, 28}, /* IDSEL 16 - PMC Slot 1 */ + {28, 25, 26, 27}, /* IDSEL 17 - PMC Slot 2 */ + { 0, 0, 0, 0}, /* IDSEL 18 - unused */ + { 0, 0, 0, 0}, /* IDSEL 19 - unused */ + {20, 0, 0, 0} /* IDSEL 20 - P2P bridge (to cPCI) */ + }; + + const long min_idsel = 14, max_idsel = 20, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +} + +/* Motorola MTX */ +static inline int +MTX_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * MPIC interrupts for various IDSEL values (MPIC IRQ0 = + * Linux IRQ16 (to leave room for ISA IRQs at 0-15). + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {19, 0, 0, 0}, /* IDSEL 12 - SCSI */ + { 0, 0, 0, 0}, /* IDSEL 13 - unused */ + {18, 0, 0, 0}, /* IDSEL 14 - Enet */ + { 0, 0, 0, 0}, /* IDSEL 15 - unused */ + {25, 26, 27, 28}, /* IDSEL 16 - PMC Slot 1 */ + {26, 27, 28, 25}, /* IDSEL 17 - PMC Slot 2 */ + {27, 28, 25, 26} /* IDSEL 18 - PCI Slot 3 */ + }; + + const long min_idsel = 12, max_idsel = 18, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +} + +/* Motorola MTX Plus */ +/* Secondary bus interrupt routing is not supported yet */ +static inline int +MTXplus_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * MPIC interrupts for various IDSEL values (MPIC IRQ0 = + * Linux IRQ16 (to leave room for ISA IRQs at 0-15). + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {19, 0, 0, 0}, /* IDSEL 12 - SCSI */ + { 0, 0, 0, 0}, /* IDSEL 13 - unused */ + {18, 0, 0, 0}, /* IDSEL 14 - Enet 1 */ + { 0, 0, 0, 0}, /* IDSEL 15 - unused */ + {25, 26, 27, 28}, /* IDSEL 16 - PCI Slot 1P */ + {26, 27, 28, 25}, /* IDSEL 17 - PCI Slot 2P */ + {27, 28, 25, 26}, /* IDSEL 18 - PCI Slot 3P */ + {26, 0, 0, 0}, /* IDSEL 19 - Enet 2 */ + { 0, 0, 0, 0} /* IDSEL 20 - P2P Bridge */ + }; + + const long min_idsel = 12, max_idsel = 20, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +} + +static inline int +Genesis2_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + /* 2600 + * Raven 31 + * ISA 11 + * SCSI 12 - IRQ3 + * Univ 13 + * eth 14 - IRQ2 + * VGA 15 - IRQ4 + * PMC1 16 - IRQ9,10,11,12 = PMC1 A-D + * PMC2 17 - IRQ12,9,10,11 = A-D + * SCSI2 18 - IRQ11 + * eth2 19 - IRQ10 + * PCIX 20 - IRQ9,10,11,12 = PCI A-D + */ + + /* 2400 + * Hawk 31 + * ISA 11 + * Univ 13 + * eth 14 - IRQ2 + * PMC1 16 - IRQ9,10,11,12 = PMC A-D + * PMC2 17 - IRQ12,9,10,11 = PMC A-D + * PCIX 20 - IRQ9,10,11,12 = PMC A-D + */ + + /* 2300 + * Raven 31 + * ISA 11 + * Univ 13 + * eth 14 - IRQ2 + * PMC1 16 - 9,10,11,12 = A-D + * PMC2 17 - 9,10,11,12 = B,C,D,A + */ + + static char pci_irq_table[][4] = + /* + * MPIC interrupts for various IDSEL values (MPIC IRQ0 = + * Linux IRQ16 (to leave room for ISA IRQs at 0-15). + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {19, 0, 0, 0}, /* IDSEL 12 - SCSI */ + { 0, 0, 0, 0}, /* IDSEL 13 - Universe PCI - VME */ + {18, 0, 0, 0}, /* IDSEL 14 - Enet 1 */ + { 0, 0, 0, 0}, /* IDSEL 15 - unused */ + {25, 26, 27, 28}, /* IDSEL 16 - PCI/PMC Slot 1P */ + {28, 25, 26, 27}, /* IDSEL 17 - PCI/PMC Slot 2P */ + {27, 28, 25, 26}, /* IDSEL 18 - PCI Slot 3P */ + {26, 0, 0, 0}, /* IDSEL 19 - Enet 2 */ + {25, 26, 27, 28} /* IDSEL 20 - P2P Bridge */ + }; + + const long min_idsel = 12, max_idsel = 20, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +} + +#define MOTOROLA_CPUTYPE_REG 0x800 +#define MOTOROLA_BASETYPE_REG 0x803 +#define MPIC_RAVEN_ID 0x48010000 +#define MPIC_HAWK_ID 0x48030000 +#define MOT_PROC2_BIT 0x800 + +static u_char pplus_openpic_initsenses[] __initdata = { + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* MVME2600_INT_SIO */ + (IRQ_SENSE_EDGE | IRQ_POLARITY_NEGATIVE),/*MVME2600_INT_FALCN_ECC_ERR */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),/*MVME2600_INT_PCI_ETHERNET */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_PCI_SCSI */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),/*MVME2600_INT_PCI_GRAPHICS */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_PCI_VME0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_PCI_VME1 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_PCI_VME2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_PCI_VME3 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_PCI_INTA */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_PCI_INTB */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_PCI_INTC */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_PCI_INTD */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_LM_SIG0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_LM_SIG1 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), +}; + +int mot_entry = -1; +int prep_keybd_present = 1; +int mot_multi = 0; + +struct brd_info { + /* 0x100 mask assumes for Raven and Hawk boards that the level/edge + * are set */ + int cpu_type; + /* 0x200 if this board has a Hawk chip. */ + int base_type; + /* or'ed with 0x80 if this board should be checked for multi CPU */ + int max_cpu; + const char *name; + int (*map_irq) (struct pci_dev *, unsigned char, unsigned char); +}; +struct brd_info mot_info[] = { + {0x300, 0x00, 0x00, "MVME 2400", Genesis2_map_irq}, + {0x1E0, 0xE0, 0x00, "Mesquite cPCI (MCP750)", mesquite_map_irq}, + {0x1E0, 0xE1, 0x00, "Sitka cPCI (MCPN750)", sitka_map_irq}, + {0x1E0, 0xE2, 0x00, "Mesquite cPCI (MCP750) w/ HAC", mesquite_map_irq}, + {0x1E0, 0xF6, 0x80, "MTX Plus", MTXplus_map_irq}, + {0x1E0, 0xF6, 0x81, "Dual MTX Plus", MTXplus_map_irq}, + {0x1E0, 0xF7, 0x80, "MTX wo/ Parallel Port", MTX_map_irq}, + {0x1E0, 0xF7, 0x81, "Dual MTX wo/ Parallel Port", MTX_map_irq}, + {0x1E0, 0xF8, 0x80, "MTX w/ Parallel Port", MTX_map_irq}, + {0x1E0, 0xF8, 0x81, "Dual MTX w/ Parallel Port", MTX_map_irq}, + {0x1E0, 0xF9, 0x00, "MVME 2300", Genesis2_map_irq}, + {0x1E0, 0xFA, 0x00, "MVME 2300SC/2600", Genesis2_map_irq}, + {0x1E0, 0xFB, 0x00, "MVME 2600 with MVME712M", Genesis2_map_irq}, + {0x1E0, 0xFC, 0x00, "MVME 2600/2700 with MVME761", Genesis2_map_irq}, + {0x1E0, 0xFD, 0x80, "MVME 3600 with MVME712M", Genesis2_map_irq}, + {0x1E0, 0xFD, 0x81, "MVME 4600 with MVME712M", Genesis2_map_irq}, + {0x1E0, 0xFE, 0x80, "MVME 3600 with MVME761", Genesis2_map_irq}, + {0x1E0, 0xFE, 0x81, "MVME 4600 with MVME761", Genesis2_map_irq}, + {0x000, 0x00, 0x00, "", NULL} +}; + +void __init pplus_set_board_type(void) +{ + unsigned char cpu_type; + unsigned char base_mod; + int entry; + unsigned short devid; + unsigned long *ProcInfo = NULL; + + cpu_type = inb(MOTOROLA_CPUTYPE_REG) & 0xF0; + base_mod = inb(MOTOROLA_BASETYPE_REG); + early_read_config_word(0, 0, 0, PCI_VENDOR_ID, &devid); + + for (entry = 0; mot_info[entry].cpu_type != 0; entry++) { + /* Check for Hawk chip */ + if (mot_info[entry].cpu_type & 0x200) { + if (devid != PCI_DEVICE_ID_MOTOROLA_HAWK) + continue; + } else { + /* store the system config register for later use. */ + ProcInfo = + (unsigned long *)ioremap(PPLUS_SYS_CONFIG_REG, 4); + + /* Check non hawk boards */ + if ((mot_info[entry].cpu_type & 0xff) != cpu_type) + continue; + + if (mot_info[entry].base_type == 0) { + mot_entry = entry; + break; + } + + if (mot_info[entry].base_type != base_mod) + continue; + } + + if (!(mot_info[entry].max_cpu & 0x80)) { + mot_entry = entry; + break; + } + + /* processor 1 not present and max processor zero indicated */ + if ((*ProcInfo & MOT_PROC2_BIT) + && !(mot_info[entry].max_cpu & 0x7f)) { + mot_entry = entry; + break; + } + + /* processor 1 present and max processor zero indicated */ + if (!(*ProcInfo & MOT_PROC2_BIT) + && (mot_info[entry].max_cpu & 0x7f)) { + mot_entry = entry; + break; + } + + /* Indicate to system if this is a multiprocessor board */ + if (!(*ProcInfo & MOT_PROC2_BIT)) + mot_multi = 1; + } + + if (mot_entry == -1) + /* No particular cpu type found - assume Mesquite (MCP750) */ + mot_entry = 1; + + Motherboard_map_name = (unsigned char *)mot_info[mot_entry].name; + ppc_md.pci_map_irq = mot_info[mot_entry].map_irq; +} +void __init pplus_pib_init(void) +{ + unsigned char reg; + unsigned short short_reg; + + struct pci_dev *dev = NULL; + + /* + * Perform specific configuration for the Via Tech or + * or Winbond PCI-ISA-Bridge part. + */ + if ((dev = pci_get_device(PCI_VENDOR_ID_VIA, + PCI_DEVICE_ID_VIA_82C586_1, dev))) { + /* + * PPCBUG does not set the enable bits + * for the IDE device. Force them on here. + */ + pci_read_config_byte(dev, 0x40, ®); + + reg |= 0x03; /* IDE: Chip Enable Bits */ + pci_write_config_byte(dev, 0x40, reg); + } + + if ((dev = pci_get_device(PCI_VENDOR_ID_VIA, + PCI_DEVICE_ID_VIA_82C586_2, + dev)) && (dev->devfn = 0x5a)) { + /* Force correct USB interrupt */ + dev->irq = 11; + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); + } + + if ((dev = pci_get_device(PCI_VENDOR_ID_WINBOND, + PCI_DEVICE_ID_WINBOND_83C553, dev))) { + /* Clear PCI Interrupt Routing Control Register. */ + short_reg = 0x0000; + pci_write_config_word(dev, 0x44, short_reg); + /* Route IDE interrupts to IRQ 14 */ + reg = 0xEE; + pci_write_config_byte(dev, 0x43, reg); + } + + if ((dev = pci_get_device(PCI_VENDOR_ID_WINBOND, + PCI_DEVICE_ID_WINBOND_82C105, dev))) { + /* + * Disable LEGIRQ mode so PCI INTS are routed + * directly to the 8259 and enable both channels + */ + pci_write_config_dword(dev, 0x40, 0x10ff0033); + + /* Force correct IDE interrupt */ + dev->irq = 14; + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); + } + pci_dev_put(dev); +} + +void __init pplus_set_VIA_IDE_legacy(void) +{ + unsigned short vend, dev; + + early_read_config_word(0, 0, PCI_DEVFN(0xb, 1), PCI_VENDOR_ID, &vend); + early_read_config_word(0, 0, PCI_DEVFN(0xb, 1), PCI_DEVICE_ID, &dev); + + if ((vend == PCI_VENDOR_ID_VIA) && + (dev == PCI_DEVICE_ID_VIA_82C586_1)) { + unsigned char temp; + + /* put back original "standard" port base addresses */ + early_write_config_dword(0, 0, PCI_DEVFN(0xb, 1), + PCI_BASE_ADDRESS_0, 0x1f1); + early_write_config_dword(0, 0, PCI_DEVFN(0xb, 1), + PCI_BASE_ADDRESS_1, 0x3f5); + early_write_config_dword(0, 0, PCI_DEVFN(0xb, 1), + PCI_BASE_ADDRESS_2, 0x171); + early_write_config_dword(0, 0, PCI_DEVFN(0xb, 1), + PCI_BASE_ADDRESS_3, 0x375); + early_write_config_dword(0, 0, PCI_DEVFN(0xb, 1), + PCI_BASE_ADDRESS_4, 0xcc01); + + /* put into legacy mode */ + early_read_config_byte(0, 0, PCI_DEVFN(0xb, 1), PCI_CLASS_PROG, + &temp); + temp &= ~0x05; + early_write_config_byte(0, 0, PCI_DEVFN(0xb, 1), PCI_CLASS_PROG, + temp); + } +} + +void pplus_set_VIA_IDE_native(void) +{ + unsigned short vend, dev; + + early_read_config_word(0, 0, PCI_DEVFN(0xb, 1), PCI_VENDOR_ID, &vend); + early_read_config_word(0, 0, PCI_DEVFN(0xb, 1), PCI_DEVICE_ID, &dev); + + if ((vend == PCI_VENDOR_ID_VIA) && + (dev == PCI_DEVICE_ID_VIA_82C586_1)) { + unsigned char temp; + + /* put into native mode */ + early_read_config_byte(0, 0, PCI_DEVFN(0xb, 1), PCI_CLASS_PROG, + &temp); + temp |= 0x05; + early_write_config_byte(0, 0, PCI_DEVFN(0xb, 1), PCI_CLASS_PROG, + temp); + } +} + +void __init pplus_pcibios_fixup(void) +{ + + unsigned char reg; + unsigned short devid; + unsigned char base_mod; + + printk(KERN_INFO "Setting PCI interrupts for a \"%s\"\n", + Motherboard_map_name); + + /* Setup the Winbond or Via PIB */ + pplus_pib_init(); + + /* Set up floppy in PS/2 mode */ + outb(0x09, SIO_CONFIG_RA); + reg = inb(SIO_CONFIG_RD); + reg = (reg & 0x3F) | 0x40; + outb(reg, SIO_CONFIG_RD); + outb(reg, SIO_CONFIG_RD); /* Have to write twice to change! */ + + /* This is a hack. If this is a 2300 or 2400 mot board then there is + * no keyboard controller and we have to indicate that. + */ + + early_read_config_word(0, 0, 0, PCI_VENDOR_ID, &devid); + base_mod = inb(MOTOROLA_BASETYPE_REG); + if ((devid == PCI_DEVICE_ID_MOTOROLA_HAWK) || + (base_mod == 0xF9) || (base_mod == 0xFA) || (base_mod == 0xE1)) + prep_keybd_present = 0; +} + +void __init pplus_find_bridges(void) +{ + struct pci_controller *hose; + + hose = pcibios_alloc_controller(); + if (!hose) + return; + + hose->first_busno = 0; + hose->last_busno = 0xff; + + hose->pci_mem_offset = PREP_ISA_MEM_BASE; + hose->io_base_virt = (void *)PREP_ISA_IO_BASE; + + pci_init_resource(&hose->io_resource, PPLUS_PCI_IO_START, + PPLUS_PCI_IO_END, IORESOURCE_IO, "PCI host bridge"); + pci_init_resource(&hose->mem_resources[0], PPLUS_PROC_PCI_MEM_START, + PPLUS_PROC_PCI_MEM_END, IORESOURCE_MEM, + "PCI host bridge"); + + hose->io_space.start = PPLUS_PCI_IO_START; + hose->io_space.end = PPLUS_PCI_IO_END; + hose->mem_space.start = PPLUS_PCI_MEM_START; + hose->mem_space.end = PPLUS_PCI_MEM_END - HAWK_MPIC_SIZE; + + if (hawk_init(hose, PPLUS_HAWK_PPC_REG_BASE, PPLUS_PROC_PCI_MEM_START, + PPLUS_PROC_PCI_MEM_END - HAWK_MPIC_SIZE, + PPLUS_PROC_PCI_IO_START, PPLUS_PROC_PCI_IO_END, + PPLUS_PROC_PCI_MEM_END - HAWK_MPIC_SIZE + 1) + != 0) { + printk(KERN_CRIT "Could not initialize host bridge\n"); + + } + + pplus_set_VIA_IDE_legacy(); + + hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); + + ppc_md.pcibios_fixup = pplus_pcibios_fixup; + ppc_md.pci_swizzle = common_swizzle; +} + +static int pplus_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "vendor\t\t: Motorola MCG\n"); + seq_printf(m, "machine\t\t: %s\n", Motherboard_map_name); + + return 0; +} + +static void __init pplus_setup_arch(void) +{ + struct pci_controller *hose; + + if (ppc_md.progress) + ppc_md.progress("pplus_setup_arch: enter", 0); + + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_jiffy = 50000000; + + if (ppc_md.progress) + ppc_md.progress("pplus_setup_arch: find_bridges", 0); + + /* Setup PCI host bridge */ + pplus_find_bridges(); + + hose = pci_bus_to_hose(0); + isa_io_base = (ulong) hose->io_base_virt; + + if (ppc_md.progress) + ppc_md.progress("pplus_setup_arch: set_board_type", 0); + + pplus_set_board_type(); + + /* Enable L2. Assume we don't need to flush -- Cort */ + *(unsigned char *)(PPLUS_L2_CONTROL_REG) |= 3; + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = Root_NFS; +#else + ROOT_DEV = Root_SDA2; +#endif + + printk(KERN_INFO "Motorola PowerPlus Platform\n"); + printk(KERN_INFO + "Port by MontaVista Software, Inc. (source@mvista.com)\n"); + +#ifdef CONFIG_VGA_CONSOLE + /* remap the VGA memory */ + vgacon_remap_base = (unsigned long)ioremap(PPLUS_ISA_MEM_BASE, + 0x08000000); + conswitchp = &vga_con; +#endif +#ifdef CONFIG_PPCBUG_NVRAM + /* Read in NVRAM data */ + init_prep_nvram(); + + /* if no bootargs, look in NVRAM */ + if (cmd_line[0] == '\0') { + char *bootargs; + bootargs = prep_nvram_get_var("bootargs"); + if (bootargs != NULL) { + strcpy(cmd_line, bootargs); + /* again.. */ + strcpy(saved_command_line, cmd_line); + } + } +#endif + if (ppc_md.progress) + ppc_md.progress("pplus_setup_arch: exit", 0); +} + +static void pplus_restart(char *cmd) +{ + unsigned long i = 10000; + + local_irq_disable(); + + /* set VIA IDE controller into native mode */ + pplus_set_VIA_IDE_native(); + + /* set exception prefix high - to the prom */ + _nmask_and_or_msr(0, MSR_IP); + + /* make sure bit 0 (reset) is a 0 */ + outb(inb(0x92) & ~1L, 0x92); + /* signal a reset to system control port A - soft reset */ + outb(inb(0x92) | 1, 0x92); + + while (i != 0) + i++; + panic("restart failed\n"); +} + +static void pplus_halt(void) +{ + /* set exception prefix high - to the prom */ + _nmask_and_or_msr(MSR_EE, MSR_IP); + + /* make sure bit 0 (reset) is a 0 */ + outb(inb(0x92) & ~1L, 0x92); + /* signal a reset to system control port A - soft reset */ + outb(inb(0x92) | 1, 0x92); + + while (1) ; + /* + * Not reached + */ +} + +static void pplus_power_off(void) +{ + pplus_halt(); +} + +static unsigned int pplus_irq_canonicalize(u_int irq) +{ + if (irq == 2) + return 9; + else + return irq; +} + +static void __init pplus_init_IRQ(void) +{ + int i; + + if (ppc_md.progress) + ppc_md.progress("init_irq: enter", 0); + + OpenPIC_InitSenses = pplus_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof(pplus_openpic_initsenses); + + if (OpenPIC_Addr != NULL) { + + openpic_set_sources(0, 16, OpenPIC_Addr + 0x10000); + openpic_init(NUM_8259_INTERRUPTS); + openpic_hookup_cascade(NUM_8259_INTERRUPTS, "82c59 cascade", + i8259_irq); + ppc_md.get_irq = openpic_get_irq; + } + + for (i = 0; i < NUM_8259_INTERRUPTS; i++) + irq_desc[i].handler = &i8259_pic; + + i8259_init(0); + + if (ppc_md.progress) + ppc_md.progress("init_irq: exit", 0); +} + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +/* + * IDE stuff. + */ +static int pplus_ide_default_irq(unsigned long base) +{ + switch (base) { + case 0x1f0: + return 14; + case 0x170: + return 15; + default: + return 0; + } +} + +static unsigned long pplus_ide_default_io_base(int index) +{ + switch (index) { + case 0: + return 0x1f0; + case 1: + return 0x170; + default: + return 0; + } +} + +static void __init +pplus_ide_init_hwif_ports(hw_regs_t * hw, unsigned long data_port, + unsigned long ctrl_port, int *irq) +{ + unsigned long reg = data_port; + int i; + + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { + hw->io_ports[i] = reg; + reg += 1; + } + + if (ctrl_port) + hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port; + else + hw->io_ports[IDE_CONTROL_OFFSET] = + hw->io_ports[IDE_DATA_OFFSET] + 0x206; + + if (irq != NULL) + *irq = pplus_ide_default_irq(data_port); +} +#endif + +#ifdef CONFIG_SMP +/* PowerPlus (MTX) support */ +static int __init smp_pplus_probe(void) +{ + extern int mot_multi; + + if (mot_multi) { + openpic_request_IPIs(); + smp_hw_index[1] = 1; + return 2; + } + + return 1; +} + +static void __init smp_pplus_kick_cpu(int nr) +{ + *(unsigned long *)KERNELBASE = nr; + asm volatile ("dcbf 0,%0"::"r" (KERNELBASE):"memory"); + printk(KERN_INFO "CPU1 reset, waiting\n"); +} + +static void __init smp_pplus_setup_cpu(int cpu_nr) +{ + if (OpenPIC_Addr) + do_openpic_setup_cpu(); +} + +static struct smp_ops_t pplus_smp_ops = { + smp_openpic_message_pass, + smp_pplus_probe, + smp_pplus_kick_cpu, + smp_pplus_setup_cpu, + .give_timebase = smp_generic_give_timebase, + .take_timebase = smp_generic_take_timebase, +}; +#endif /* CONFIG_SMP */ + +#ifdef DUMP_DBATS +static void print_dbat(int idx, u32 bat) +{ + + char str[64]; + + sprintf(str, "DBAT%c%c = 0x%08x\n", + (char)((idx - DBAT0U) / 2) + '0', (idx & 1) ? 'L' : 'U', bat); + ppc_md.progress(str, 0); +} + +#define DUMP_DBAT(x) \ + do { \ + u32 __temp = mfspr(x);\ + print_dbat(x, __temp); \ + } while (0) + +static void dump_dbats(void) +{ + if (ppc_md.progress) { + DUMP_DBAT(DBAT0U); + DUMP_DBAT(DBAT0L); + DUMP_DBAT(DBAT1U); + DUMP_DBAT(DBAT1L); + DUMP_DBAT(DBAT2U); + DUMP_DBAT(DBAT2L); + DUMP_DBAT(DBAT3U); + DUMP_DBAT(DBAT3L); + } +} +#endif + +static unsigned long __init pplus_find_end_of_memory(void) +{ + unsigned long total; + + if (ppc_md.progress) + ppc_md.progress("pplus_find_end_of_memory", 0); + +#ifdef DUMP_DBATS + dump_dbats(); +#endif + + total = hawk_get_mem_size(PPLUS_HAWK_SMC_BASE); + return (total); +} + +static void __init pplus_map_io(void) +{ + io_block_mapping(PPLUS_ISA_IO_BASE, PPLUS_ISA_IO_BASE, 0x10000000, + _PAGE_IO); + io_block_mapping(0xfef80000, 0xfef80000, 0x00080000, _PAGE_IO); +} + +static void __init pplus_init2(void) +{ +#ifdef CONFIG_NVRAM + request_region(PREP_NVRAM_AS0, 0x8, "nvram"); +#endif + request_region(0x20, 0x20, "pic1"); + request_region(0xa0, 0x20, "pic2"); + request_region(0x00, 0x20, "dma1"); + request_region(0x40, 0x20, "timer"); + request_region(0x80, 0x10, "dma page reg"); + request_region(0xc0, 0x20, "dma2"); +} + +/* + * Set BAT 2 to access 0x8000000 so progress messages will work and set BAT 3 + * to 0xf0000000 to access Falcon/Raven or Hawk registers + */ +static __inline__ void pplus_set_bat(void) +{ + /* wait for all outstanding memory accesses to complete */ + mb(); + + /* setup DBATs */ + mtspr(SPRN_DBAT2U, 0x80001ffe); + mtspr(SPRN_DBAT2L, 0x8000002a); + mtspr(SPRN_DBAT3U, 0xf0001ffe); + mtspr(SPRN_DBAT3L, 0xf000002a); + + /* wait for updates */ + mb(); +} + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + + /* Map in board regs, etc. */ + pplus_set_bat(); + + isa_io_base = PREP_ISA_IO_BASE; + isa_mem_base = PREP_ISA_MEM_BASE; + pci_dram_offset = PREP_PCI_DRAM_OFFSET; + ISA_DMA_THRESHOLD = 0x00ffffff; + DMA_MODE_READ = 0x44; + DMA_MODE_WRITE = 0x48; + + ppc_md.setup_arch = pplus_setup_arch; + ppc_md.show_cpuinfo = pplus_show_cpuinfo; + ppc_md.irq_canonicalize = pplus_irq_canonicalize; + ppc_md.init_IRQ = pplus_init_IRQ; + /* this gets changed later on if we have an OpenPIC -- Cort */ + ppc_md.get_irq = i8259_irq; + ppc_md.init = pplus_init2; + + ppc_md.restart = pplus_restart; + ppc_md.power_off = pplus_power_off; + ppc_md.halt = pplus_halt; + + TODC_INIT(TODC_TYPE_MK48T59, PREP_NVRAM_AS0, PREP_NVRAM_AS1, + PREP_NVRAM_DATA, 8); + + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.calibrate_decr = todc_calibrate_decr; + ppc_md.nvram_read_val = todc_m48txx_read_val; + ppc_md.nvram_write_val = todc_m48txx_write_val; + + ppc_md.find_end_of_memory = pplus_find_end_of_memory; + ppc_md.setup_io_mappings = pplus_map_io; + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) + ppc_ide_md.default_irq = pplus_ide_default_irq; + ppc_ide_md.default_io_base = pplus_ide_default_io_base; + ppc_ide_md.ide_init_hwif = pplus_ide_init_hwif_ports; +#endif + +#ifdef CONFIG_SERIAL_TEXT_DEBUG + ppc_md.progress = gen550_progress; +#endif /* CONFIG_SERIAL_TEXT_DEBUG */ +#ifdef CONFIG_KGDB + ppc_md.kgdb_map_scc = gen550_kgdb_map_scc; +#endif +#ifdef CONFIG_SMP + ppc_md.smp_ops = &pplus_smp_ops; +#endif /* CONFIG_SMP */ +} diff --git a/arch/ppc/platforms/pplus.h b/arch/ppc/platforms/pplus.h new file mode 100644 index 00000000000..90f0cb2d409 --- /dev/null +++ b/arch/ppc/platforms/pplus.h @@ -0,0 +1,67 @@ +/* + * arch/ppc/platforms/pplus.h + * + * Definitions for Motorola MCG Falcon/Raven & HAWK North Bridge & Memory ctlr. + * + * Author: Mark A. Greerinclude/asm-ppc/hawk.h + * mgreer@mvista.com + * + * Modified by Randy Vinson (rvinson@mvista.com) + * + * 2001-2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifndef __PPC_PPLUS_H +#define __PPC_PPLUS_H + +#include + +/* + * Due to limiations imposed by legacy hardware (primaryily IDE controllers), + * the PPLUS boards operate using a PReP address map. + * + * From Processor (physical) -> PCI: + * PCI Mem Space: 0xc0000000 - 0xfe000000 -> 0x00000000 - 0x3e000000 (768 MB) + * PCI I/O Space: 0x80000000 - 0x90000000 -> 0x00000000 - 0x10000000 (256 MB) + * Note: Must skip 0xfe000000-0xfe400000 for CONFIG_HIGHMEM/PKMAP area + * + * From PCI -> Processor (physical): + * System Memory: 0x80000000 -> 0x00000000 + */ + +#define PPLUS_ISA_MEM_BASE PREP_ISA_MEM_BASE +#define PPLUS_ISA_IO_BASE PREP_ISA_IO_BASE + +/* PCI Memory space mapping info */ +#define PPLUS_PCI_MEM_SIZE 0x30000000U +#define PPLUS_PROC_PCI_MEM_START PPLUS_ISA_MEM_BASE +#define PPLUS_PROC_PCI_MEM_END (PPLUS_PROC_PCI_MEM_START + \ + PPLUS_PCI_MEM_SIZE - 1) +#define PPLUS_PCI_MEM_START 0x00000000U +#define PPLUS_PCI_MEM_END (PPLUS_PCI_MEM_START + \ + PPLUS_PCI_MEM_SIZE - 1) + +/* PCI I/O space mapping info */ +#define PPLUS_PCI_IO_SIZE 0x10000000U +#define PPLUS_PROC_PCI_IO_START PPLUS_ISA_IO_BASE +#define PPLUS_PROC_PCI_IO_END (PPLUS_PROC_PCI_IO_START + \ + PPLUS_PCI_IO_SIZE - 1) +#define PPLUS_PCI_IO_START 0x00000000U +#define PPLUS_PCI_IO_END (PPLUS_PCI_IO_START + \ + PPLUS_PCI_IO_SIZE - 1) +/* System memory mapping info */ +#define PPLUS_PCI_DRAM_OFFSET PREP_PCI_DRAM_OFFSET +#define PPLUS_PCI_PHY_MEM_OFFSET (PPLUS_ISA_MEM_BASE-PPLUS_PCI_MEM_START) + +/* Define base addresses for important sets of registers */ +#define PPLUS_HAWK_SMC_BASE 0xfef80000U +#define PPLUS_HAWK_PPC_REG_BASE 0xfeff0000U +#define PPLUS_SYS_CONFIG_REG 0xfef80400U +#define PPLUS_L2_CONTROL_REG 0x8000081cU + +#define PPLUS_VGA_MEM_BASE 0xf0000000U + +#endif /* __PPC_PPLUS_H */ diff --git a/arch/ppc/platforms/pq2ads.c b/arch/ppc/platforms/pq2ads.c new file mode 100644 index 00000000000..6a1475c1e12 --- /dev/null +++ b/arch/ppc/platforms/pq2ads.c @@ -0,0 +1,26 @@ +/* + * arch/ppc/platforms/pq2ads.c + * + * PQ2ADS platform support + * + * Author: Kumar Gala + * Derived from: est8260_setup.c by Allen Curtis + * + * Copyright 2004 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include + +#include + +void __init +m82xx_board_setup(void) +{ + /* Enable the 2nd UART port */ + *(volatile uint *)(BCSR_ADDR + 4) &= ~BCSR1_RS232_EN2; +} diff --git a/arch/ppc/platforms/pq2ads.h b/arch/ppc/platforms/pq2ads.h new file mode 100644 index 00000000000..cf5e5dd06d6 --- /dev/null +++ b/arch/ppc/platforms/pq2ads.h @@ -0,0 +1,96 @@ +/* + * A collection of structures, addresses, and values associated with + * the Motorola MPC8260ADS/MPC8266ADS-PCI boards. + * Copied from the RPX-Classic and SBS8260 stuff. + * + * Copyright (c) 2001 Dan Malek (dan@mvista.com) + */ +#ifdef __KERNEL__ +#ifndef __MACH_ADS8260_DEFS +#define __MACH_ADS8260_DEFS + +#include + +#include + +/* Memory map is configured by the PROM startup. + * We just map a few things we need. The CSR is actually 4 byte-wide + * registers that can be accessed as 8-, 16-, or 32-bit values. + */ +#define CPM_MAP_ADDR ((uint)0xf0000000) +#define BCSR_ADDR ((uint)0xf4500000) +#define BCSR_SIZE ((uint)(32 * 1024)) + +#define BOOTROM_RESTART_ADDR ((uint)0xff000104) + +/* For our show_cpuinfo hooks. */ +#define CPUINFO_VENDOR "Motorola" +#define CPUINFO_MACHINE "PQ2 ADS PowerPC" + +/* The ADS8260 has 16, 32-bit wide control/status registers, accessed + * only on word boundaries. + * Not all are used (yet), or are interesting to us (yet). + */ + +/* Things of interest in the CSR. +*/ +#define BCSR0_LED0 ((uint)0x02000000) /* 0 == on */ +#define BCSR0_LED1 ((uint)0x01000000) /* 0 == on */ +#define BCSR1_FETHIEN ((uint)0x08000000) /* 0 == enable */ +#define BCSR1_FETH_RST ((uint)0x04000000) /* 0 == reset */ +#define BCSR1_RS232_EN1 ((uint)0x02000000) /* 0 == enable */ +#define BCSR1_RS232_EN2 ((uint)0x01000000) /* 0 == enable */ +#define BCSR3_FETHIEN2 ((uint)0x10000000) /* 0 == enable */ +#define BCSR3_FETH2_RST ((uint)0x80000000) /* 0 == reset */ + +#define PHY_INTERRUPT SIU_INT_IRQ7 + +#ifdef CONFIG_PCI +/* PCI interrupt controller */ +#define PCI_INT_STAT_REG 0xF8200000 +#define PCI_INT_MASK_REG 0xF8200004 +#define PIRQA (NR_SIU_INTS + 0) +#define PIRQB (NR_SIU_INTS + 1) +#define PIRQC (NR_SIU_INTS + 2) +#define PIRQD (NR_SIU_INTS + 3) + +/* + * PCI memory map definitions for MPC8266ADS-PCI. + * + * processor view + * local address PCI address target + * 0x80000000-0x9FFFFFFF 0x80000000-0x9FFFFFFF PCI mem with prefetch + * 0xA0000000-0xBFFFFFFF 0xA0000000-0xBFFFFFFF PCI mem w/o prefetch + * 0xF4000000-0xF7FFFFFF 0x00000000-0x03FFFFFF PCI IO + * + * PCI master view + * local address PCI address target + * 0x00000000-0x1FFFFFFF 0x00000000-0x1FFFFFFF MPC8266 local memory + */ + +/* window for a PCI master to access MPC8266 memory */ +#define PCI_SLV_MEM_LOCAL 0x00000000 /* Local base */ +#define PCI_SLV_MEM_BUS 0x00000000 /* PCI base */ + +/* window for the processor to access PCI memory with prefetching */ +#define PCI_MSTR_MEM_LOCAL 0x80000000 /* Local base */ +#define PCI_MSTR_MEM_BUS 0x80000000 /* PCI base */ +#define PCI_MSTR_MEM_SIZE 0x20000000 /* 512MB */ + +/* window for the processor to access PCI memory without prefetching */ +#define PCI_MSTR_MEMIO_LOCAL 0xA0000000 /* Local base */ +#define PCI_MSTR_MEMIO_BUS 0xA0000000 /* PCI base */ +#define PCI_MSTR_MEMIO_SIZE 0x20000000 /* 512MB */ + +/* window for the processor to access PCI I/O */ +#define PCI_MSTR_IO_LOCAL 0xF4000000 /* Local base */ +#define PCI_MSTR_IO_BUS 0x00000000 /* PCI base */ +#define PCI_MSTR_IO_SIZE 0x04000000 /* 64MB */ + +#define _IO_BASE PCI_MSTR_IO_LOCAL +#define _ISA_MEM_BASE PCI_MSTR_MEMIO_LOCAL +#define PCI_DRAM_OFFSET PCI_SLV_MEM_BUS +#endif /* CONFIG_PCI */ + +#endif /* __MACH_ADS8260_DEFS */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/prep_pci.c b/arch/ppc/platforms/prep_pci.c new file mode 100644 index 00000000000..8cd80eb447b --- /dev/null +++ b/arch/ppc/platforms/prep_pci.c @@ -0,0 +1,1336 @@ +/* + * PReP pci functions. + * Originally by Gary Thomas + * rewritten and updated by Cort Dougan (cort@cs.nmt.edu) + * + * The motherboard routes/maps will disappear shortly. -- Cort + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern void (*setup_ibm_pci)(char *irq_lo, char *irq_hi); + +/* Which PCI interrupt line does a given device [slot] use? */ +/* Note: This really should be two dimensional based in slot/pin used */ +static unsigned char *Motherboard_map; +unsigned char *Motherboard_map_name; + +/* How is the 82378 PIRQ mapping setup? */ +static unsigned char *Motherboard_routes; + +static void (*Motherboard_non0)(struct pci_dev *); + +static void Powerplus_Map_Non0(struct pci_dev *); + +/* Used for Motorola to store system config register */ +static unsigned long *ProcInfo; + +/* Tables for known hardware */ + +/* Motorola PowerStackII - Utah */ +static char Utah_pci_IRQ_map[23] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 5, /* Slot 2 - SCSI - NCR825A */ + 0, /* Slot 3 - unused */ + 3, /* Slot 4 - Ethernet - DEC2114x */ + 0, /* Slot 5 - unused */ + 2, /* Slot 6 - PCI Card slot #1 */ + 3, /* Slot 7 - PCI Card slot #2 */ + 5, /* Slot 8 - PCI Card slot #3 */ + 5, /* Slot 9 - PCI Bridge */ + /* added here in case we ever support PCI bridges */ + /* Secondary PCI bus cards are at slot-9,6 & slot-9,7 */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 5, /* Slot 12 - SCSI - NCR825A */ + 0, /* Slot 13 - unused */ + 3, /* Slot 14 - enet */ + 0, /* Slot 15 - unused */ + 2, /* Slot 16 - unused */ + 3, /* Slot 17 - unused */ + 5, /* Slot 18 - unused */ + 0, /* Slot 19 - unused */ + 0, /* Slot 20 - unused */ + 0, /* Slot 21 - unused */ + 0, /* Slot 22 - unused */ +}; + +static char Utah_pci_IRQ_routes[] __prepdata = +{ + 0, /* Line 0 - Unused */ + 9, /* Line 1 */ + 10, /* Line 2 */ + 11, /* Line 3 */ + 14, /* Line 4 */ + 15, /* Line 5 */ +}; + +/* Motorola PowerStackII - Omaha */ +/* no integrated SCSI or ethernet */ +static char Omaha_pci_IRQ_map[23] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 3, /* Slot 2 - Winbond EIDE */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 1, /* Slot 6 - PCI slot 1 */ + 2, /* Slot 7 - PCI slot 2 */ + 3, /* Slot 8 - PCI slot 3 */ + 4, /* Slot 9 - PCI slot 4 */ /* needs indirect access */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 0, /* Slot 12 - unused */ + 0, /* Slot 13 - unused */ + 0, /* Slot 14 - unused */ + 0, /* Slot 15 - unused */ + 1, /* Slot 16 - PCI slot 1 */ + 2, /* Slot 17 - PCI slot 2 */ + 3, /* Slot 18 - PCI slot 3 */ + 4, /* Slot 19 - PCI slot 4 */ /* needs indirect access */ + 0, + 0, + 0, +}; + +static char Omaha_pci_IRQ_routes[] __prepdata = +{ + 0, /* Line 0 - Unused */ + 9, /* Line 1 */ + 11, /* Line 2 */ + 14, /* Line 3 */ + 15 /* Line 4 */ +}; + +/* Motorola PowerStack */ +static char Blackhawk_pci_IRQ_map[19] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 3, /* Slot 12 - SCSI */ + 0, /* Slot 13 - unused */ + 1, /* Slot 14 - Ethernet */ + 0, /* Slot 15 - unused */ + 1, /* Slot P7 */ + 2, /* Slot P6 */ + 3, /* Slot P5 */ +}; + +static char Blackhawk_pci_IRQ_routes[] __prepdata = +{ + 0, /* Line 0 - Unused */ + 9, /* Line 1 */ + 11, /* Line 2 */ + 15, /* Line 3 */ + 15 /* Line 4 */ +}; + +/* Motorola Mesquite */ +static char Mesquite_pci_IRQ_map[23] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 0, /* Slot 12 - unused */ + 0, /* Slot 13 - unused */ + 2, /* Slot 14 - Ethernet */ + 0, /* Slot 15 - unused */ + 3, /* Slot 16 - PMC */ + 0, /* Slot 17 - unused */ + 0, /* Slot 18 - unused */ + 0, /* Slot 19 - unused */ + 0, /* Slot 20 - unused */ + 0, /* Slot 21 - unused */ + 0, /* Slot 22 - unused */ +}; + +/* Motorola Sitka */ +static char Sitka_pci_IRQ_map[21] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 0, /* Slot 12 - unused */ + 0, /* Slot 13 - unused */ + 2, /* Slot 14 - Ethernet */ + 0, /* Slot 15 - unused */ + 9, /* Slot 16 - PMC 1 */ + 12, /* Slot 17 - PMC 2 */ + 0, /* Slot 18 - unused */ + 0, /* Slot 19 - unused */ + 4, /* Slot 20 - NT P2P bridge */ +}; + +/* Motorola MTX */ +static char MTX_pci_IRQ_map[23] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 3, /* Slot 12 - SCSI */ + 0, /* Slot 13 - unused */ + 2, /* Slot 14 - Ethernet */ + 0, /* Slot 15 - unused */ + 9, /* Slot 16 - PCI/PMC slot 1 */ + 10, /* Slot 17 - PCI/PMC slot 2 */ + 11, /* Slot 18 - PCI slot 3 */ + 0, /* Slot 19 - unused */ + 0, /* Slot 20 - unused */ + 0, /* Slot 21 - unused */ + 0, /* Slot 22 - unused */ +}; + +/* Motorola MTX Plus */ +/* Secondary bus interrupt routing is not supported yet */ +static char MTXplus_pci_IRQ_map[23] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 3, /* Slot 12 - SCSI */ + 0, /* Slot 13 - unused */ + 2, /* Slot 14 - Ethernet 1 */ + 0, /* Slot 15 - unused */ + 9, /* Slot 16 - PCI slot 1P */ + 10, /* Slot 17 - PCI slot 2P */ + 11, /* Slot 18 - PCI slot 3P */ + 10, /* Slot 19 - Ethernet 2 */ + 0, /* Slot 20 - P2P Bridge */ + 0, /* Slot 21 - unused */ + 0, /* Slot 22 - unused */ +}; + +static char Raven_pci_IRQ_routes[] __prepdata = +{ + 0, /* This is a dummy structure */ +}; + +/* Motorola MVME16xx */ +static char Genesis_pci_IRQ_map[16] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 3, /* Slot 12 - SCSI */ + 0, /* Slot 13 - unused */ + 1, /* Slot 14 - Ethernet */ + 0, /* Slot 15 - unused */ +}; + +static char Genesis_pci_IRQ_routes[] __prepdata = +{ + 0, /* Line 0 - Unused */ + 10, /* Line 1 */ + 11, /* Line 2 */ + 14, /* Line 3 */ + 15 /* Line 4 */ +}; + +static char Genesis2_pci_IRQ_map[23] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - IDE */ + 3, /* Slot 12 - SCSI */ + 5, /* Slot 13 - Universe PCI - VME Bridge */ + 2, /* Slot 14 - Ethernet */ + 0, /* Slot 15 - unused */ + 9, /* Slot 16 - PMC 1 */ + 12, /* Slot 17 - pci */ + 11, /* Slot 18 - pci */ + 10, /* Slot 19 - pci */ + 0, /* Slot 20 - pci */ + 0, /* Slot 21 - unused */ + 0, /* Slot 22 - unused */ +}; + +/* Motorola Series-E */ +static char Comet_pci_IRQ_map[23] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 3, /* Slot 12 - SCSI */ + 0, /* Slot 13 - unused */ + 1, /* Slot 14 - Ethernet */ + 0, /* Slot 15 - unused */ + 1, /* Slot 16 - PCI slot 1 */ + 2, /* Slot 17 - PCI slot 2 */ + 3, /* Slot 18 - PCI slot 3 */ + 4, /* Slot 19 - PCI bridge */ + 0, + 0, + 0, +}; + +static char Comet_pci_IRQ_routes[] __prepdata = +{ + 0, /* Line 0 - Unused */ + 10, /* Line 1 */ + 11, /* Line 2 */ + 14, /* Line 3 */ + 15 /* Line 4 */ +}; + +/* Motorola Series-EX */ +static char Comet2_pci_IRQ_map[23] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 3, /* Slot 2 - SCSI - NCR825A */ + 0, /* Slot 3 - unused */ + 1, /* Slot 4 - Ethernet - DEC2104X */ + 0, /* Slot 5 - unused */ + 1, /* Slot 6 - PCI slot 1 */ + 2, /* Slot 7 - PCI slot 2 */ + 3, /* Slot 8 - PCI slot 3 */ + 4, /* Slot 9 - PCI bridge */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 3, /* Slot 12 - SCSI - NCR825A */ + 0, /* Slot 13 - unused */ + 1, /* Slot 14 - Ethernet - DEC2104X */ + 0, /* Slot 15 - unused */ + 1, /* Slot 16 - PCI slot 1 */ + 2, /* Slot 17 - PCI slot 2 */ + 3, /* Slot 18 - PCI slot 3 */ + 4, /* Slot 19 - PCI bridge */ + 0, + 0, + 0, +}; + +static char Comet2_pci_IRQ_routes[] __prepdata = +{ + 0, /* Line 0 - Unused */ + 10, /* Line 1 */ + 11, /* Line 2 */ + 14, /* Line 3 */ + 15, /* Line 4 */ +}; + +/* + * ibm 830 (and 850?). + * This is actually based on the Carolina motherboard + * -- Cort + */ +static char ibm8xx_pci_IRQ_map[23] __prepdata = { + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - FireCoral */ + 4, /* Slot 12 - Ethernet PCIINTD# */ + 2, /* Slot 13 - PCI Slot #2 */ + 2, /* Slot 14 - S3 Video PCIINTD# */ + 0, /* Slot 15 - onboard SCSI (INDI) [1] */ + 3, /* Slot 16 - NCR58C810 RS6000 Only PCIINTC# */ + 0, /* Slot 17 - unused */ + 2, /* Slot 18 - PCI Slot 2 PCIINTx# (See below) */ + 0, /* Slot 19 - unused */ + 0, /* Slot 20 - unused */ + 0, /* Slot 21 - unused */ + 2, /* Slot 22 - PCI slot 1 PCIINTx# (See below) */ +}; + +static char ibm8xx_pci_IRQ_routes[] __prepdata = { + 0, /* Line 0 - unused */ + 15, /* Line 1 */ + 15, /* Line 2 */ + 15, /* Line 3 */ + 15, /* Line 4 */ +}; + +/* + * a 6015 ibm board + * -- Cort + */ +static char ibm6015_pci_IRQ_map[23] __prepdata = { + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - */ + 1, /* Slot 12 - SCSI */ + 2, /* Slot 13 - */ + 2, /* Slot 14 - */ + 1, /* Slot 15 - */ + 1, /* Slot 16 - */ + 0, /* Slot 17 - */ + 2, /* Slot 18 - */ + 0, /* Slot 19 - */ + 0, /* Slot 20 - */ + 0, /* Slot 21 - */ + 2, /* Slot 22 - */ +}; + +static char ibm6015_pci_IRQ_routes[] __prepdata = { + 0, /* Line 0 - unused */ + 13, /* Line 1 */ + 15, /* Line 2 */ + 15, /* Line 3 */ + 15, /* Line 4 */ +}; + + +/* IBM Nobis and Thinkpad 850 */ +static char Nobis_pci_IRQ_map[23] __prepdata ={ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 3, /* Slot 12 - SCSI */ + 0, /* Slot 13 - unused */ + 0, /* Slot 14 - unused */ + 0, /* Slot 15 - unused */ +}; + +static char Nobis_pci_IRQ_routes[] __prepdata = { + 0, /* Line 0 - Unused */ + 13, /* Line 1 */ + 13, /* Line 2 */ + 13, /* Line 3 */ + 13 /* Line 4 */ +}; + +/* + * IBM RS/6000 43p/140 -- paulus + * XXX we should get all this from the residual data + */ +static char ibm43p_pci_IRQ_map[23] __prepdata = { + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - FireCoral ISA bridge */ + 6, /* Slot 12 - Ethernet */ + 0, /* Slot 13 - openpic */ + 0, /* Slot 14 - unused */ + 0, /* Slot 15 - unused */ + 7, /* Slot 16 - NCR58C825a onboard scsi */ + 0, /* Slot 17 - unused */ + 2, /* Slot 18 - PCI Slot 2 PCIINTx# (See below) */ + 0, /* Slot 19 - unused */ + 0, /* Slot 20 - unused */ + 0, /* Slot 21 - unused */ + 1, /* Slot 22 - PCI slot 1 PCIINTx# (See below) */ +}; + +static char ibm43p_pci_IRQ_routes[] __prepdata = { + 0, /* Line 0 - unused */ + 15, /* Line 1 */ + 15, /* Line 2 */ + 15, /* Line 3 */ + 15, /* Line 4 */ +}; + +/* Motorola PowerPlus architecture PCI IRQ tables */ +/* Interrupt line values for INTA-D on primary/secondary MPIC inputs */ + +struct powerplus_irq_list +{ + unsigned char primary[4]; /* INT A-D */ + unsigned char secondary[4]; /* INT A-D */ +}; + +/* + * For standard PowerPlus boards, bus 0 PCI INTs A-D are routed to + * OpenPIC inputs 9-12. PCI INTs A-D from the on board P2P bridge + * are routed to OpenPIC inputs 5-8. These values are offset by + * 16 in the table to reflect the Linux kernel interrupt value. + */ +struct powerplus_irq_list Powerplus_pci_IRQ_list __prepdata = +{ + {25, 26, 27, 28}, + {21, 22, 23, 24} +}; + +/* + * For the MCP750 (system slot board), cPCI INTs A-D are routed to + * OpenPIC inputs 8-11 and the PMC INTs A-D are routed to OpenPIC + * input 3. On a hot swap MCP750, the companion card PCI INTs A-D + * are routed to OpenPIC inputs 12-15. These values are offset by + * 16 in the table to reflect the Linux kernel interrupt value. + */ +struct powerplus_irq_list Mesquite_pci_IRQ_list __prepdata = +{ + {24, 25, 26, 27}, + {28, 29, 30, 31} +}; + +/* + * This table represents the standard PCI swizzle defined in the + * PCI bus specification. + */ +static unsigned char prep_pci_intpins[4][4] __prepdata = +{ + { 1, 2, 3, 4}, /* Buses 0, 4, 8, ... */ + { 2, 3, 4, 1}, /* Buses 1, 5, 9, ... */ + { 3, 4, 1, 2}, /* Buses 2, 6, 10 ... */ + { 4, 1, 2, 3}, /* Buses 3, 7, 11 ... */ +}; + +/* We have to turn on LEVEL mode for changed IRQ's */ +/* All PCI IRQ's need to be level mode, so this should be something + * other than hard-coded as well... IRQ's are individually mappable + * to either edge or level. + */ + +/* + * 8259 edge/level control definitions + */ +#define ISA8259_M_ELCR 0x4d0 +#define ISA8259_S_ELCR 0x4d1 + +#define ELCRS_INT15_LVL 0x80 +#define ELCRS_INT14_LVL 0x40 +#define ELCRS_INT12_LVL 0x10 +#define ELCRS_INT11_LVL 0x08 +#define ELCRS_INT10_LVL 0x04 +#define ELCRS_INT9_LVL 0x02 +#define ELCRS_INT8_LVL 0x01 +#define ELCRM_INT7_LVL 0x80 +#define ELCRM_INT5_LVL 0x20 + +#if 0 +/* + * PCI config space access. + */ +#define CFGADDR(dev) ((1<<(dev>>3)) | ((dev&7)<<8)) +#define DEVNO(dev) (dev>>3) + +#define MIN_DEVNR 11 +#define MAX_DEVNR 22 + +static int __prep +prep_read_config(struct pci_bus *bus, unsigned int devfn, int offset, + int len, u32 *val) +{ + struct pci_controller *hose = bus->sysdata; + volatile void __iomem *cfg_data; + + if (bus->number != 0 || DEVNO(devfn) < MIN_DEVNR + || DEVNO(devfn) > MAX_DEVNR) + return PCIBIOS_DEVICE_NOT_FOUND; + + /* + * Note: the caller has already checked that offset is + * suitably aligned and that len is 1, 2 or 4. + */ + cfg_data = hose->cfg_data + CFGADDR(devfn) + offset; + switch (len) { + case 1: + *val = in_8(cfg_data); + break; + case 2: + *val = in_le16(cfg_data); + break; + default: + *val = in_le32(cfg_data); + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static int __prep +prep_write_config(struct pci_bus *bus, unsigned int devfn, int offset, + int len, u32 val) +{ + struct pci_controller *hose = bus->sysdata; + volatile void __iomem *cfg_data; + + if (bus->number != 0 || DEVNO(devfn) < MIN_DEVNR + || DEVNO(devfn) > MAX_DEVNR) + return PCIBIOS_DEVICE_NOT_FOUND; + + /* + * Note: the caller has already checked that offset is + * suitably aligned and that len is 1, 2 or 4. + */ + cfg_data = hose->cfg_data + CFGADDR(devfn) + offset; + switch (len) { + case 1: + out_8(cfg_data, val); + break; + case 2: + out_le16(cfg_data, val); + break; + default: + out_le32(cfg_data, val); + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops prep_pci_ops = +{ + prep_read_config, + prep_write_config +}; +#endif + +#define MOTOROLA_CPUTYPE_REG 0x800 +#define MOTOROLA_BASETYPE_REG 0x803 +#define MPIC_RAVEN_ID 0x48010000 +#define MPIC_HAWK_ID 0x48030000 +#define MOT_PROC2_BIT 0x800 + +static u_char prep_openpic_initsenses[] __initdata = { + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* MVME2600_INT_SIO */ + (IRQ_SENSE_EDGE | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_FALCN_ECC_ERR */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_PCI_ETHERNET */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_PCI_SCSI */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_PCI_GRAPHICS */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_PCI_VME0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_PCI_VME1 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_PCI_VME2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_PCI_VME3 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_PCI_INTA */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_PCI_INTB */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_PCI_INTC */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_PCI_INTD */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_LM_SIG0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_LM_SIG1 */ +}; + +#define MOT_RAVEN_PRESENT 0x1 +#define MOT_HAWK_PRESENT 0x2 + +int mot_entry = -1; +int prep_keybd_present = 1; +int MotMPIC; +int mot_multi; + +int __init +raven_init(void) +{ + unsigned int devid; + unsigned int pci_membase; + unsigned char base_mod; + + /* Check to see if the Raven chip exists. */ + if ( _prep_type != _PREP_Motorola) { + OpenPIC_Addr = NULL; + return 0; + } + + /* Check to see if this board is a type that might have a Raven. */ + if ((inb(MOTOROLA_CPUTYPE_REG) & 0xF0) != 0xE0) { + OpenPIC_Addr = NULL; + return 0; + } + + /* Check the first PCI device to see if it is a Raven. */ + early_read_config_dword(NULL, 0, 0, PCI_VENDOR_ID, &devid); + + switch (devid & 0xffff0000) { + case MPIC_RAVEN_ID: + MotMPIC = MOT_RAVEN_PRESENT; + break; + case MPIC_HAWK_ID: + MotMPIC = MOT_HAWK_PRESENT; + break; + default: + OpenPIC_Addr = NULL; + return 0; + } + + + /* Read the memory base register. */ + early_read_config_dword(NULL, 0, 0, PCI_BASE_ADDRESS_1, &pci_membase); + + if (pci_membase == 0) { + OpenPIC_Addr = NULL; + return 0; + } + + /* Map the Raven MPIC registers to virtual memory. */ + OpenPIC_Addr = ioremap(pci_membase+0xC0000000, 0x22000); + + OpenPIC_InitSenses = prep_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof(prep_openpic_initsenses); + + ppc_md.get_irq = openpic_get_irq; + + /* If raven is present on Motorola store the system config register + * for later use. + */ + ProcInfo = (unsigned long *)ioremap(0xfef80400, 4); + + /* Indicate to system if this is a multiprocessor board */ + if (!(*ProcInfo & MOT_PROC2_BIT)) { + mot_multi = 1; + } + + /* This is a hack. If this is a 2300 or 2400 mot board then there is + * no keyboard controller and we have to indicate that. + */ + base_mod = inb(MOTOROLA_BASETYPE_REG); + if ((MotMPIC == MOT_HAWK_PRESENT) || (base_mod == 0xF9) || + (base_mod == 0xFA) || (base_mod == 0xE1)) + prep_keybd_present = 0; + + return 1; +} + +struct mot_info { + int cpu_type; /* 0x100 mask assumes for Raven and Hawk boards that the level/edge are set */ + /* 0x200 if this board has a Hawk chip. */ + int base_type; + int max_cpu; /* ored with 0x80 if this board should be checked for multi CPU */ + const char *name; + unsigned char *map; + unsigned char *routes; + void (*map_non0_bus)(struct pci_dev *); /* For boards with more than bus 0 devices. */ + struct powerplus_irq_list *pci_irq_list; /* List of PCI MPIC inputs */ + unsigned char secondary_bridge_devfn; /* devfn of secondary bus transparent bridge */ +} mot_info[] __prepdata = { + {0x300, 0x00, 0x00, "MVME 2400", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF}, + {0x010, 0x00, 0x00, "Genesis", Genesis_pci_IRQ_map, Genesis_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0x00}, + {0x020, 0x00, 0x00, "Powerstack (Series E)", Comet_pci_IRQ_map, Comet_pci_IRQ_routes, NULL, NULL, 0x00}, + {0x040, 0x00, 0x00, "Blackhawk (Powerstack)", Blackhawk_pci_IRQ_map, Blackhawk_pci_IRQ_routes, NULL, NULL, 0x00}, + {0x050, 0x00, 0x00, "Omaha (PowerStack II Pro3000)", Omaha_pci_IRQ_map, Omaha_pci_IRQ_routes, NULL, NULL, 0x00}, + {0x060, 0x00, 0x00, "Utah (Powerstack II Pro4000)", Utah_pci_IRQ_map, Utah_pci_IRQ_routes, NULL, NULL, 0x00}, + {0x0A0, 0x00, 0x00, "Powerstack (Series EX)", Comet2_pci_IRQ_map, Comet2_pci_IRQ_routes, NULL, NULL, 0x00}, + {0x1E0, 0xE0, 0x00, "Mesquite cPCI (MCP750)", Mesquite_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Mesquite_pci_IRQ_list, 0xFF}, + {0x1E0, 0xE1, 0x00, "Sitka cPCI (MCPN750)", Sitka_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF}, + {0x1E0, 0xE2, 0x00, "Mesquite cPCI (MCP750) w/ HAC", Mesquite_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Mesquite_pci_IRQ_list, 0xC0}, + {0x1E0, 0xF6, 0x80, "MTX Plus", MTXplus_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xA0}, + {0x1E0, 0xF6, 0x81, "Dual MTX Plus", MTXplus_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xA0}, + {0x1E0, 0xF7, 0x80, "MTX wo/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0x00}, + {0x1E0, 0xF7, 0x81, "Dual MTX wo/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0x00}, + {0x1E0, 0xF8, 0x80, "MTX w/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0x00}, + {0x1E0, 0xF8, 0x81, "Dual MTX w/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0x00}, + {0x1E0, 0xF9, 0x00, "MVME 2300", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF}, + {0x1E0, 0xFA, 0x00, "MVME 2300SC/2600", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF}, + {0x1E0, 0xFB, 0x00, "MVME 2600 with MVME712M", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF}, + {0x1E0, 0xFC, 0x00, "MVME 2600/2700 with MVME761", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF}, + {0x1E0, 0xFD, 0x80, "MVME 3600 with MVME712M", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0x00}, + {0x1E0, 0xFD, 0x81, "MVME 4600 with MVME712M", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF}, + {0x1E0, 0xFE, 0x80, "MVME 3600 with MVME761", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF}, + {0x1E0, 0xFE, 0x81, "MVME 4600 with MVME761", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF}, + {0x1E0, 0xFF, 0x00, "MVME 1600-001 or 1600-011", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF}, + {0x000, 0x00, 0x00, "", NULL, NULL, NULL, NULL, 0x00} +}; + +void __init +ibm_prep_init(void) +{ + if (have_residual_data) { + u32 addr, real_addr, len, offset; + PPC_DEVICE *mpic; + PnP_TAG_PACKET *pkt; + + /* Use the PReP residual data to determine if an OpenPIC is + * present. If so, get the large vendor packet which will + * tell us the base address and length in memory. + * If we are successful, ioremap the memory area and set + * OpenPIC_Addr (this indicates that the OpenPIC was found). + */ + mpic = residual_find_device(-1, NULL, SystemPeripheral, + ProgrammableInterruptController, MPIC, 0); + if (!mpic) + return; + + pkt = PnP_find_large_vendor_packet(res->DevicePnPHeap + + mpic->AllocatedOffset, 9, 0); + + if (!pkt) + return; + +#define p pkt->L4_Pack.L4_Data.L4_PPCPack + if (p.PPCData[1] == 32) { + switch (p.PPCData[0]) { + case 1: offset = PREP_ISA_IO_BASE; break; + case 2: offset = PREP_ISA_MEM_BASE; break; + default: return; /* Not I/O or memory?? */ + } + } + else + return; /* Not a 32-bit address */ + + real_addr = ld_le32((unsigned int *) (p.PPCData + 4)); + if (real_addr == 0xffffffff) + return; + + /* Adjust address to be as seen by CPU */ + addr = real_addr + offset; + + len = ld_le32((unsigned int *) (p.PPCData + 12)); + if (!len) + return; +#undef p + OpenPIC_Addr = ioremap(addr, len); + ppc_md.get_irq = openpic_get_irq; + + OpenPIC_InitSenses = prep_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof(prep_openpic_initsenses); + + printk(KERN_INFO "MPIC at 0x%08x (0x%08x), length 0x%08x " + "mapped to 0x%p\n", addr, real_addr, len, OpenPIC_Addr); + } +} + +static void __init +ibm43p_pci_map_non0(struct pci_dev *dev) +{ + unsigned char intpin; + static unsigned char bridge_intrs[4] = { 3, 4, 5, 8 }; + + if (dev == NULL) + return; + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &intpin); + if (intpin < 1 || intpin > 4) + return; + intpin = (PCI_SLOT(dev->devfn) + intpin - 1) & 3; + dev->irq = openpic_to_irq(bridge_intrs[intpin]); + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); +} + +void __init +prep_residual_setup_pci(char *irq_edge_mask_lo, char *irq_edge_mask_hi) +{ + if (have_residual_data) { + Motherboard_map_name = res->VitalProductData.PrintableModel; + Motherboard_map = NULL; + Motherboard_routes = NULL; + residual_irq_mask(irq_edge_mask_lo, irq_edge_mask_hi); + } +} + +void __init +prep_sandalfoot_setup_pci(char *irq_edge_mask_lo, char *irq_edge_mask_hi) +{ + Motherboard_map_name = "IBM 6015/7020 (Sandalfoot/Sandalbow)"; + Motherboard_map = ibm6015_pci_IRQ_map; + Motherboard_routes = ibm6015_pci_IRQ_routes; + *irq_edge_mask_lo = 0x00; /* irq's 0-7 all edge-triggered */ + *irq_edge_mask_hi = 0xA0; /* irq's 13, 15 level-triggered */ +} + +void __init +prep_thinkpad_setup_pci(char *irq_edge_mask_lo, char *irq_edge_mask_hi) +{ + Motherboard_map_name = "IBM Thinkpad 850/860"; + Motherboard_map = Nobis_pci_IRQ_map; + Motherboard_routes = Nobis_pci_IRQ_routes; + *irq_edge_mask_lo = 0x00; /* irq's 0-7 all edge-triggered */ + *irq_edge_mask_hi = 0xA0; /* irq's 13, 15 level-triggered */ +} + +void __init +prep_carolina_setup_pci(char *irq_edge_mask_lo, char *irq_edge_mask_hi) +{ + Motherboard_map_name = "IBM 7248, PowerSeries 830/850 (Carolina)"; + Motherboard_map = ibm8xx_pci_IRQ_map; + Motherboard_routes = ibm8xx_pci_IRQ_routes; + *irq_edge_mask_lo = 0x00; /* irq's 0-7 all edge-triggered */ + *irq_edge_mask_hi = 0xA4; /* irq's 10, 13, 15 level-triggered */ +} + +void __init +prep_tiger1_setup_pci(char *irq_edge_mask_lo, char *irq_edge_mask_hi) +{ + Motherboard_map_name = "IBM 43P-140 (Tiger1)"; + Motherboard_map = ibm43p_pci_IRQ_map; + Motherboard_routes = ibm43p_pci_IRQ_routes; + Motherboard_non0 = ibm43p_pci_map_non0; + *irq_edge_mask_lo = 0x00; /* irq's 0-7 all edge-triggered */ + *irq_edge_mask_hi = 0xA0; /* irq's 13, 15 level-triggered */ +} + +void __init +prep_route_pci_interrupts(void) +{ + unsigned char *ibc_pirq = (unsigned char *)0x80800860; + unsigned char *ibc_pcicon = (unsigned char *)0x80800840; + int i; + + if ( _prep_type == _PREP_Motorola) + { + unsigned short irq_mode; + unsigned char cpu_type; + unsigned char base_mod; + int entry; + + cpu_type = inb(MOTOROLA_CPUTYPE_REG) & 0xF0; + base_mod = inb(MOTOROLA_BASETYPE_REG); + + for (entry = 0; mot_info[entry].cpu_type != 0; entry++) { + if (mot_info[entry].cpu_type & 0x200) { /* Check for Hawk chip */ + if (!(MotMPIC & MOT_HAWK_PRESENT)) + continue; + } else { /* Check non hawk boards */ + if ((mot_info[entry].cpu_type & 0xff) != cpu_type) + continue; + + if (mot_info[entry].base_type == 0) { + mot_entry = entry; + break; + } + + if (mot_info[entry].base_type != base_mod) + continue; + } + + if (!(mot_info[entry].max_cpu & 0x80)) { + mot_entry = entry; + break; + } + + /* processor 1 not present and max processor zero indicated */ + if ((*ProcInfo & MOT_PROC2_BIT) && !(mot_info[entry].max_cpu & 0x7f)) { + mot_entry = entry; + break; + } + + /* processor 1 present and max processor zero indicated */ + if (!(*ProcInfo & MOT_PROC2_BIT) && (mot_info[entry].max_cpu & 0x7f)) { + mot_entry = entry; + break; + } + } + + if (mot_entry == -1) /* No particular cpu type found - assume Blackhawk */ + mot_entry = 3; + + Motherboard_map_name = (unsigned char *)mot_info[mot_entry].name; + Motherboard_map = mot_info[mot_entry].map; + Motherboard_routes = mot_info[mot_entry].routes; + Motherboard_non0 = mot_info[mot_entry].map_non0_bus; + + if (!(mot_info[entry].cpu_type & 0x100)) { + /* AJF adjust level/edge control according to routes */ + irq_mode = 0; + for (i = 1; i <= 4; i++) + irq_mode |= ( 1 << Motherboard_routes[i] ); + outb( irq_mode & 0xff, 0x4d0 ); + outb( (irq_mode >> 8) & 0xff, 0x4d1 ); + } + } else if ( _prep_type == _PREP_IBM ) { + unsigned char irq_edge_mask_lo, irq_edge_mask_hi; + unsigned short irq_edge_mask; + int i; + + setup_ibm_pci(&irq_edge_mask_lo, &irq_edge_mask_hi); + + outb(inb(0x04d0)|irq_edge_mask_lo, 0x4d0); /* primary 8259 */ + outb(inb(0x04d1)|irq_edge_mask_hi, 0x4d1); /* cascaded 8259 */ + + irq_edge_mask = (irq_edge_mask_hi << 8) | irq_edge_mask_lo; + for (i = 0; i < 16; ++i, irq_edge_mask >>= 1) + if (irq_edge_mask & 1) + irq_desc[i].status |= IRQ_LEVEL; + } else { + printk("No known machine pci routing!\n"); + return; + } + + /* Set up mapping from slots */ + if (Motherboard_routes) { + for (i = 1; i <= 4; i++) + ibc_pirq[i-1] = Motherboard_routes[i]; + + /* Enable PCI interrupts */ + *ibc_pcicon |= 0x20; + } +} + +void __init +prep_pib_init(void) +{ + unsigned char reg; + unsigned short short_reg; + + struct pci_dev *dev = NULL; + + if (( _prep_type == _PREP_Motorola) && (OpenPIC_Addr)) { + /* + * Perform specific configuration for the Via Tech or + * or Winbond PCI-ISA-Bridge part. + */ + if ((dev = pci_get_device(PCI_VENDOR_ID_VIA, + PCI_DEVICE_ID_VIA_82C586_1, dev))) { + /* + * PPCBUG does not set the enable bits + * for the IDE device. Force them on here. + */ + pci_read_config_byte(dev, 0x40, ®); + + reg |= 0x03; /* IDE: Chip Enable Bits */ + pci_write_config_byte(dev, 0x40, reg); + } + if ((dev = pci_get_device(PCI_VENDOR_ID_VIA, + PCI_DEVICE_ID_VIA_82C586_2, + dev)) && (dev->devfn = 0x5a)) { + /* Force correct USB interrupt */ + dev->irq = 11; + pci_write_config_byte(dev, + PCI_INTERRUPT_LINE, + dev->irq); + } + if ((dev = pci_get_device(PCI_VENDOR_ID_WINBOND, + PCI_DEVICE_ID_WINBOND_83C553, dev))) { + /* Clear PCI Interrupt Routing Control Register. */ + short_reg = 0x0000; + pci_write_config_word(dev, 0x44, short_reg); + if (OpenPIC_Addr){ + /* Route IDE interrupts to IRQ 14 */ + reg = 0xEE; + pci_write_config_byte(dev, 0x43, reg); + } + } + pci_dev_put(dev); + } + + if ((dev = pci_get_device(PCI_VENDOR_ID_WINBOND, + PCI_DEVICE_ID_WINBOND_82C105, dev))){ + if (OpenPIC_Addr){ + /* + * Disable LEGIRQ mode so PCI INTS are routed + * directly to the 8259 and enable both channels + */ + pci_write_config_dword(dev, 0x40, 0x10ff0033); + + /* Force correct IDE interrupt */ + dev->irq = 14; + pci_write_config_byte(dev, + PCI_INTERRUPT_LINE, + dev->irq); + } else { + /* Enable LEGIRQ for PCI INT -> 8259 IRQ routing */ + pci_write_config_dword(dev, 0x40, 0x10ff08a1); + } + } + pci_dev_put(dev); +} + +static void __init +Powerplus_Map_Non0(struct pci_dev *dev) +{ + struct pci_bus *pbus; /* Parent bus structure pointer */ + struct pci_dev *tdev = dev; /* Temporary device structure */ + unsigned int devnum; /* Accumulated device number */ + unsigned char intline; /* Linux interrupt value */ + unsigned char intpin; /* PCI interrupt pin */ + + /* Check for valid PCI dev pointer */ + if (dev == NULL) return; + + /* Initialize bridge IDSEL variable */ + devnum = PCI_SLOT(tdev->devfn); + + /* Read the interrupt pin of the device and adjust for indexing */ + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &intpin); + + /* If device doesn't request an interrupt, return */ + if ( (intpin < 1) || (intpin > 4) ) + return; + + intpin--; + + /* + * Walk up to bus 0, adjusting the interrupt pin for the standard + * PCI bus swizzle. + */ + do { + intpin = (prep_pci_intpins[devnum % 4][intpin]) - 1; + pbus = tdev->bus; /* up one level */ + tdev = pbus->self; + devnum = PCI_SLOT(tdev->devfn); + } while(tdev->bus->number); + + /* Use the primary interrupt inputs by default */ + intline = mot_info[mot_entry].pci_irq_list->primary[intpin]; + + /* + * If the board has secondary interrupt inputs, walk the bus and + * note the devfn of the bridge from bus 0. If it is the same as + * the devfn of the bus bridge with secondary inputs, use those. + * Otherwise, assume it's a PMC site and get the interrupt line + * value from the interrupt routing table. + */ + if (mot_info[mot_entry].secondary_bridge_devfn) { + pbus = dev->bus; + + while (pbus->primary != 0) + pbus = pbus->parent; + + if ((pbus->self)->devfn != 0xA0) { + if ((pbus->self)->devfn == mot_info[mot_entry].secondary_bridge_devfn) + intline = mot_info[mot_entry].pci_irq_list->secondary[intpin]; + else { + if ((char *)(mot_info[mot_entry].map) == (char *)Mesquite_pci_IRQ_map) + intline = mot_info[mot_entry].map[((pbus->self)->devfn)/8] + 16; + else { + int i; + for (i=0;i<3;i++) + intpin = (prep_pci_intpins[devnum % 4][intpin]) - 1; + intline = mot_info[mot_entry].pci_irq_list->primary[intpin]; + } + } + } + } + + /* Write calculated interrupt value to header and device list */ + dev->irq = intline; + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, (u8)dev->irq); +} + +void __init +prep_pcibios_fixup(void) +{ + struct pci_dev *dev = NULL; + int irq; + int have_openpic = (OpenPIC_Addr != NULL); + + prep_route_pci_interrupts(); + + printk("Setting PCI interrupts for a \"%s\"\n", Motherboard_map_name); + + /* Iterate through all the PCI devices, setting the IRQ */ + for_each_pci_dev(dev) { + /* + * If we have residual data, then this is easy: query the + * residual data for the IRQ line allocated to the device. + * This works the same whether we have an OpenPic or not. + */ + if (have_residual_data) { + irq = residual_pcidev_irq(dev); + dev->irq = have_openpic ? openpic_to_irq(irq) : irq; + } + /* + * If we don't have residual data, then we need to use + * tables to determine the IRQ. The table organisation + * is different depending on whether there is an OpenPIC + * or not. The tables are only used for bus 0, so check + * this first. + */ + else if (dev->bus->number == 0) { + irq = Motherboard_map[PCI_SLOT(dev->devfn)]; + dev->irq = have_openpic ? openpic_to_irq(irq) + : Motherboard_routes[irq]; + } + /* + * Finally, if we don't have residual data and the bus is + * non-zero, use the callback (if provided) + */ + else { + if (Motherboard_non0 != NULL) + Motherboard_non0(dev); + + continue; + } + + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); + } + + /* Setup the Winbond or Via PIB */ + prep_pib_init(); +} + +static void __init +prep_pcibios_after_init(void) +{ +#if 0 + struct pci_dev *dev; + + /* If there is a WD 90C, reset the IO BAR to 0x0 (it started that + * way, but the PCI layer relocated it because it thought 0x0 was + * invalid for a BAR). + * If you don't do this, the card's VGA base will be +0xc0000 + * instead of 0xc0000. vgacon.c (for example) is completely unaware of + * this little quirk. + */ + dev = pci_get_device(PCI_VENDOR_ID_WD, PCI_DEVICE_ID_WD_90C, NULL); + if (dev) { + dev->resource[1].end -= dev->resource[1].start; + dev->resource[1].start = 0; + /* tell the hardware */ + pci_write_config_dword(dev, PCI_BASE_ADDRESS_1, 0x0); + pci_dev_put(dev); + } +#endif +} + +static void __init +prep_init_resource(struct resource *res, unsigned long start, + unsigned long end, int flags) +{ + res->flags = flags; + res->start = start; + res->end = end; + res->name = "PCI host bridge"; + res->parent = NULL; + res->sibling = NULL; + res->child = NULL; +} + +void __init +prep_find_bridges(void) +{ + struct pci_controller* hose; + + hose = pcibios_alloc_controller(); + if (!hose) + return; + + hose->first_busno = 0; + hose->last_busno = 0xff; + hose->pci_mem_offset = PREP_ISA_MEM_BASE; + hose->io_base_phys = PREP_ISA_IO_BASE; + hose->io_base_virt = ioremap(PREP_ISA_IO_BASE, 0x800000); + prep_init_resource(&hose->io_resource, 0, 0x007fffff, IORESOURCE_IO); + prep_init_resource(&hose->mem_resources[0], 0xc0000000, 0xfeffffff, + IORESOURCE_MEM); + setup_indirect_pci(hose, PREP_ISA_IO_BASE + 0xcf8, + PREP_ISA_IO_BASE + 0xcfc); + + printk("PReP architecture\n"); + + if (have_residual_data) { + PPC_DEVICE *hostbridge; + + hostbridge = residual_find_device(PROCESSORDEVICE, NULL, + BridgeController, PCIBridge, -1, 0); + if (hostbridge && + ((hostbridge->DeviceId.Interface == PCIBridgeIndirect) || + (hostbridge->DeviceId.Interface == PCIBridgeRS6K))) { + PnP_TAG_PACKET * pkt; + pkt = PnP_find_large_vendor_packet( + res->DevicePnPHeap+hostbridge->AllocatedOffset, + 3, 0); + if(pkt) { +#define p pkt->L4_Pack.L4_Data.L4_PPCPack + setup_indirect_pci(hose, + ld_le32((unsigned *) (p.PPCData)), + ld_le32((unsigned *) (p.PPCData+8))); +#undef p + } else + setup_indirect_pci(hose, 0x80000cf8, 0x80000cfc); + } + } + + ppc_md.pcibios_fixup = prep_pcibios_fixup; + ppc_md.pcibios_after_init = prep_pcibios_after_init; +} diff --git a/arch/ppc/platforms/prep_setup.c b/arch/ppc/platforms/prep_setup.c new file mode 100644 index 00000000000..bc926be9547 --- /dev/null +++ b/arch/ppc/platforms/prep_setup.c @@ -0,0 +1,1181 @@ +/* + * arch/ppc/platforms/setup.c + * + * Copyright (C) 1995 Linus Torvalds + * Adapted from 'alpha' version by Gary Thomas + * Modified by Cort Dougan (cort@cs.nmt.edu) + * + * Support for PReP (Motorola MTX/MVME) + * by Troy Benjegerdes (hozer@drgw.net) + */ + +/* + * bootup setup stuff.. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +TODC_ALLOC(); + +unsigned char ucSystemType; +unsigned char ucBoardRev; +unsigned char ucBoardRevMaj, ucBoardRevMin; + +extern unsigned char prep_nvram_read_val(int addr); +extern void prep_nvram_write_val(int addr, + unsigned char val); +extern unsigned char rs_nvram_read_val(int addr); +extern void rs_nvram_write_val(int addr, + unsigned char val); +extern void ibm_prep_init(void); + +extern void prep_find_bridges(void); + +int _prep_type; + +extern void prep_residual_setup_pci(char *irq_edge_mask_lo, char *irq_edge_mask_hi); +extern void prep_sandalfoot_setup_pci(char *irq_edge_mask_lo, char *irq_edge_mask_hi); +extern void prep_thinkpad_setup_pci(char *irq_edge_mask_lo, char *irq_edge_mask_hi); +extern void prep_carolina_setup_pci(char *irq_edge_mask_lo, char *irq_edge_mask_hi); +extern void prep_tiger1_setup_pci(char *irq_edge_mask_lo, char *irq_edge_mask_hi); + + +#define cached_21 (((char *)(ppc_cached_irq_mask))[3]) +#define cached_A1 (((char *)(ppc_cached_irq_mask))[2]) + +/* for the mac fs */ +dev_t boot_dev; + +#ifdef CONFIG_SOUND_CS4232 +long ppc_cs4232_dma, ppc_cs4232_dma2; +#endif + +extern PTE *Hash, *Hash_end; +extern unsigned long Hash_size, Hash_mask; +extern int probingmem; +extern unsigned long loops_per_jiffy; + +#ifdef CONFIG_SOUND_CS4232 +EXPORT_SYMBOL(ppc_cs4232_dma); +EXPORT_SYMBOL(ppc_cs4232_dma2); +#endif + +/* useful ISA ports */ +#define PREP_SYSCTL 0x81c +/* present in the IBM reference design; possibly identical in Mot boxes: */ +#define PREP_IBM_SIMM_ID 0x803 /* SIMM size: 32 or 8 MiB */ +#define PREP_IBM_SIMM_PRESENCE 0x804 +#define PREP_IBM_EQUIPMENT 0x80c +#define PREP_IBM_L2INFO 0x80d +#define PREP_IBM_PM1 0x82a /* power management register 1 */ +#define PREP_IBM_PLANAR 0x852 /* planar ID - identifies the motherboard */ +#define PREP_IBM_DISP 0x8c0 /* 4-digit LED display */ + +/* Equipment Present Register masks: */ +#define PREP_IBM_EQUIPMENT_RESERVED 0x80 +#define PREP_IBM_EQUIPMENT_SCSIFUSE 0x40 +#define PREP_IBM_EQUIPMENT_L2_COPYBACK 0x08 +#define PREP_IBM_EQUIPMENT_L2_256 0x04 +#define PREP_IBM_EQUIPMENT_CPU 0x02 +#define PREP_IBM_EQUIPMENT_L2 0x01 + +/* planar ID values: */ +/* Sandalfoot/Sandalbow (6015/7020) */ +#define PREP_IBM_SANDALFOOT 0xfc +/* Woodfield, Thinkpad 850/860 (6042/7249) */ +#define PREP_IBM_THINKPAD 0xff /* planar ID unimplemented */ +/* PowerSeries 830/850 (6050/6070) */ +#define PREP_IBM_CAROLINA_IDE_0 0xf0 +#define PREP_IBM_CAROLINA_IDE_1 0xf1 +#define PREP_IBM_CAROLINA_IDE_2 0xf2 +#define PREP_IBM_CAROLINA_IDE_3 0xf3 +/* 7248-43P */ +#define PREP_IBM_CAROLINA_SCSI_0 0xf4 +#define PREP_IBM_CAROLINA_SCSI_1 0xf5 +#define PREP_IBM_CAROLINA_SCSI_2 0xf6 +#define PREP_IBM_CAROLINA_SCSI_3 0xf7 /* missing from Carolina Tech Spec */ +/* Tiger1 (7043-140) */ +#define PREP_IBM_TIGER1_133 0xd1 +#define PREP_IBM_TIGER1_166 0xd2 +#define PREP_IBM_TIGER1_180 0xd3 +#define PREP_IBM_TIGER1_xxx 0xd4 /* unknown, but probably exists */ +#define PREP_IBM_TIGER1_333 0xd5 /* missing from Tiger Tech Spec */ + +/* setup_ibm_pci: + * set Motherboard_map_name, Motherboard_map, Motherboard_routes. + * return 8259 edge/level masks. + */ +void (*setup_ibm_pci)(char *irq_lo, char *irq_hi); + +extern char *Motherboard_map_name; /* for use in *_cpuinfo */ + +/* + * As found in the PReP reference implementation. + * Used by Thinkpad, Sandalfoot (6015/7020), and all Motorola PReP. + */ +static void __init +prep_gen_enable_l2(void) +{ + outb(inb(PREP_SYSCTL) | 0x3, PREP_SYSCTL); +} + +/* Used by Carolina and Tiger1 */ +static void __init +prep_carolina_enable_l2(void) +{ + outb(inb(PREP_SYSCTL) | 0xc0, PREP_SYSCTL); +} + +/* cpuinfo code common to all IBM PReP */ +static void __prep +prep_ibm_cpuinfo(struct seq_file *m) +{ + unsigned int equip_reg = inb(PREP_IBM_EQUIPMENT); + + seq_printf(m, "machine\t\t: PReP %s\n", Motherboard_map_name); + + seq_printf(m, "upgrade cpu\t: "); + if (equip_reg & PREP_IBM_EQUIPMENT_CPU) { + seq_printf(m, "not "); + } + seq_printf(m, "present\n"); + + /* print info about the SCSI fuse */ + seq_printf(m, "scsi fuse\t: "); + if (equip_reg & PREP_IBM_EQUIPMENT_SCSIFUSE) + seq_printf(m, "ok"); + else + seq_printf(m, "bad"); + seq_printf(m, "\n"); + + /* print info about SIMMs */ + if (have_residual_data) { + int i; + seq_printf(m, "simms\t\t: "); + for (i = 0; (res->ActualNumMemories) && (i < MAX_MEMS); i++) { + if (res->Memories[i].SIMMSize != 0) + seq_printf(m, "%d:%ldMiB ", i, + (res->Memories[i].SIMMSize > 1024) ? + res->Memories[i].SIMMSize>>20 : + res->Memories[i].SIMMSize); + } + seq_printf(m, "\n"); + } +} + +static int __prep +prep_gen_cpuinfo(struct seq_file *m) +{ + prep_ibm_cpuinfo(m); + return 0; +} + +static int __prep +prep_sandalfoot_cpuinfo(struct seq_file *m) +{ + unsigned int equip_reg = inb(PREP_IBM_EQUIPMENT); + + prep_ibm_cpuinfo(m); + + /* report amount and type of L2 cache present */ + seq_printf(m, "L2 cache\t: "); + if (equip_reg & PREP_IBM_EQUIPMENT_L2) { + seq_printf(m, "not present"); + } else { + if (equip_reg & PREP_IBM_EQUIPMENT_L2_256) + seq_printf(m, "256KiB"); + else + seq_printf(m, "unknown size"); + + if (equip_reg & PREP_IBM_EQUIPMENT_L2_COPYBACK) + seq_printf(m, ", copy-back"); + else + seq_printf(m, ", write-through"); + } + seq_printf(m, "\n"); + + return 0; +} + +static int __prep +prep_thinkpad_cpuinfo(struct seq_file *m) +{ + unsigned int equip_reg = inb(PREP_IBM_EQUIPMENT); + char *cpubus_speed, *pci_speed; + + prep_ibm_cpuinfo(m); + + /* report amount and type of L2 cache present */ + seq_printf(m, "l2 cache\t: "); + if ((equip_reg & 0x1) == 0) { + switch ((equip_reg & 0xc) >> 2) { + case 0x0: + seq_printf(m, "128KiB look-aside 2-way write-through\n"); + break; + case 0x1: + seq_printf(m, "512KiB look-aside direct-mapped write-back\n"); + break; + case 0x2: + seq_printf(m, "256KiB look-aside 2-way write-through\n"); + break; + case 0x3: + seq_printf(m, "256KiB look-aside direct-mapped write-back\n"); + break; + } + } else { + seq_printf(m, "not present\n"); + } + + /* report bus speeds because we can */ + if ((equip_reg & 0x80) == 0) { + switch ((equip_reg & 0x30) >> 4) { + case 0x1: + cpubus_speed = "50"; + pci_speed = "25"; + break; + case 0x3: + cpubus_speed = "66"; + pci_speed = "33"; + break; + default: + cpubus_speed = "unknown"; + pci_speed = "unknown"; + break; + } + } else { + switch ((equip_reg & 0x30) >> 4) { + case 0x1: + cpubus_speed = "25"; + pci_speed = "25"; + break; + case 0x2: + cpubus_speed = "60"; + pci_speed = "30"; + break; + case 0x3: + cpubus_speed = "33"; + pci_speed = "33"; + break; + default: + cpubus_speed = "unknown"; + pci_speed = "unknown"; + break; + } + } + seq_printf(m, "60x bus\t\t: %sMHz\n", cpubus_speed); + seq_printf(m, "pci bus\t\t: %sMHz\n", pci_speed); + + return 0; +} + +static int __prep +prep_carolina_cpuinfo(struct seq_file *m) +{ + unsigned int equip_reg = inb(PREP_IBM_EQUIPMENT); + + prep_ibm_cpuinfo(m); + + /* report amount and type of L2 cache present */ + seq_printf(m, "l2 cache\t: "); + if ((equip_reg & 0x1) == 0) { + unsigned int l2_reg = inb(PREP_IBM_L2INFO); + + /* L2 size */ + if ((l2_reg & 0x60) == 0) + seq_printf(m, "256KiB"); + else if ((l2_reg & 0x60) == 0x20) + seq_printf(m, "512KiB"); + else + seq_printf(m, "unknown size"); + + /* L2 type */ + if ((l2_reg & 0x3) == 0) + seq_printf(m, ", async"); + else if ((l2_reg & 0x3) == 1) + seq_printf(m, ", sync"); + else + seq_printf(m, ", unknown type"); + + seq_printf(m, "\n"); + } else { + seq_printf(m, "not present\n"); + } + + return 0; +} + +static int __prep +prep_tiger1_cpuinfo(struct seq_file *m) +{ + unsigned int l2_reg = inb(PREP_IBM_L2INFO); + + prep_ibm_cpuinfo(m); + + /* report amount and type of L2 cache present */ + seq_printf(m, "l2 cache\t: "); + if ((l2_reg & 0xf) == 0xf) { + seq_printf(m, "not present\n"); + } else { + if (l2_reg & 0x8) + seq_printf(m, "async, "); + else + seq_printf(m, "sync burst, "); + + if (l2_reg & 0x4) + seq_printf(m, "parity, "); + else + seq_printf(m, "no parity, "); + + switch (l2_reg & 0x3) { + case 0x0: + seq_printf(m, "256KiB\n"); + break; + case 0x1: + seq_printf(m, "512KiB\n"); + break; + case 0x2: + seq_printf(m, "1MiB\n"); + break; + default: + seq_printf(m, "unknown size\n"); + break; + } + } + + return 0; +} + + +/* Used by all Motorola PReP */ +static int __prep +prep_mot_cpuinfo(struct seq_file *m) +{ + unsigned int cachew = *((unsigned char *)CACHECRBA); + + seq_printf(m, "machine\t\t: PReP %s\n", Motherboard_map_name); + + /* report amount and type of L2 cache present */ + seq_printf(m, "l2 cache\t: "); + switch (cachew & L2CACHE_MASK) { + case L2CACHE_512KB: + seq_printf(m, "512KiB"); + break; + case L2CACHE_256KB: + seq_printf(m, "256KiB"); + break; + case L2CACHE_1MB: + seq_printf(m, "1MiB"); + break; + case L2CACHE_NONE: + seq_printf(m, "none\n"); + goto no_l2; + break; + default: + seq_printf(m, "%x\n", cachew); + } + + seq_printf(m, ", parity %s", + (cachew & L2CACHE_PARITY)? "enabled" : "disabled"); + + seq_printf(m, " SRAM:"); + + switch ( ((cachew & 0xf0) >> 4) & ~(0x3) ) { + case 1: seq_printf(m, "synchronous, parity, flow-through\n"); + break; + case 2: seq_printf(m, "asynchronous, no parity\n"); + break; + case 3: seq_printf(m, "asynchronous, parity\n"); + break; + default:seq_printf(m, "synchronous, pipelined, no parity\n"); + break; + } + +no_l2: + /* print info about SIMMs */ + if (have_residual_data) { + int i; + seq_printf(m, "simms\t\t: "); + for (i = 0; (res->ActualNumMemories) && (i < MAX_MEMS); i++) { + if (res->Memories[i].SIMMSize != 0) + seq_printf(m, "%d:%ldM ", i, + (res->Memories[i].SIMMSize > 1024) ? + res->Memories[i].SIMMSize>>20 : + res->Memories[i].SIMMSize); + } + seq_printf(m, "\n"); + } + + return 0; +} + +static void __prep +prep_restart(char *cmd) +{ +#define PREP_SP92 0x92 /* Special Port 92 */ + local_irq_disable(); /* no interrupts */ + + /* set exception prefix high - to the prom */ + _nmask_and_or_msr(0, MSR_IP); + + /* make sure bit 0 (reset) is a 0 */ + outb( inb(PREP_SP92) & ~1L , PREP_SP92); + /* signal a reset to system control port A - soft reset */ + outb( inb(PREP_SP92) | 1 , PREP_SP92); + + while ( 1 ) ; + /* not reached */ +#undef PREP_SP92 +} + +static void __prep +prep_halt(void) +{ + local_irq_disable(); /* no interrupts */ + + /* set exception prefix high - to the prom */ + _nmask_and_or_msr(0, MSR_IP); + + while ( 1 ) ; + /* not reached */ +} + +/* Carrera is the power manager in the Thinkpads. Unfortunately not much is + * known about it, so we can't power down. + */ +static void __prep +prep_carrera_poweroff(void) +{ + prep_halt(); +} + +/* + * On most IBM PReP's, power management is handled by a Signetics 87c750 + * behind the Utah component on the ISA bus. To access the 750 you must write + * a series of nibbles to port 0x82a (decoded by the Utah). This is described + * somewhat in the IBM Carolina Technical Specification. + * -Hollis + */ +static void __prep +utah_sig87c750_setbit(unsigned int bytenum, unsigned int bitnum, int value) +{ + /* + * byte1: 0 0 0 1 0 d a5 a4 + * byte2: 0 0 0 1 a3 a2 a1 a0 + * + * d = the bit's value, enabled or disabled + * (a5 a4 a3) = the byte number, minus 20 + * (a2 a1 a0) = the bit number + * + * example: set the 5th bit of byte 21 (21.5) + * a5 a4 a3 = 001 (byte 1) + * a2 a1 a0 = 101 (bit 5) + * + * byte1 = 0001 0100 (0x14) + * byte2 = 0001 1101 (0x1d) + */ + unsigned char byte1=0x10, byte2=0x10; + + /* the 750's '20.0' is accessed as '0.0' through Utah (which adds 20) */ + bytenum -= 20; + + byte1 |= (!!value) << 2; /* set d */ + byte1 |= (bytenum >> 1) & 0x3; /* set a5, a4 */ + + byte2 |= (bytenum & 0x1) << 3; /* set a3 */ + byte2 |= bitnum & 0x7; /* set a2, a1, a0 */ + + outb(byte1, PREP_IBM_PM1); /* first nibble */ + mb(); + udelay(100); /* important: let controller recover */ + + outb(byte2, PREP_IBM_PM1); /* second nibble */ + mb(); + udelay(100); /* important: let controller recover */ +} + +static void __prep +prep_sig750_poweroff(void) +{ + /* tweak the power manager found in most IBM PRePs (except Thinkpads) */ + + local_irq_disable(); + /* set exception prefix high - to the prom */ + _nmask_and_or_msr(0, MSR_IP); + + utah_sig87c750_setbit(21, 5, 1); /* set bit 21.5, "PMEXEC_OFF" */ + + while (1) ; + /* not reached */ +} + +static int __prep +prep_show_percpuinfo(struct seq_file *m, int i) +{ + /* PREP's without residual data will give incorrect values here */ + seq_printf(m, "clock\t\t: "); + if (have_residual_data) + seq_printf(m, "%ldMHz\n", + (res->VitalProductData.ProcessorHz > 1024) ? + res->VitalProductData.ProcessorHz / 1000000 : + res->VitalProductData.ProcessorHz); + else + seq_printf(m, "???\n"); + + return 0; +} + +#ifdef CONFIG_SOUND_CS4232 +static long __init masktoint(unsigned int i) +{ + int t = -1; + while (i >> ++t) + ; + return (t-1); +} + +/* + * ppc_cs4232_dma and ppc_cs4232_dma2 are used in include/asm/dma.h + * to distinguish sound dma-channels from others. This is because + * blocksize on 16 bit dma-channels 5,6,7 is 128k, but + * the cs4232.c uses 64k like on 8 bit dma-channels 0,1,2,3 + */ + +static void __init prep_init_sound(void) +{ + PPC_DEVICE *audiodevice = NULL; + + /* + * Get the needed resource informations from residual data. + * + */ + if (have_residual_data) + audiodevice = residual_find_device(~0, NULL, + MultimediaController, AudioController, -1, 0); + + if (audiodevice != NULL) { + PnP_TAG_PACKET *pkt; + + pkt = PnP_find_packet((unsigned char *)&res->DevicePnPHeap[audiodevice->AllocatedOffset], + S5_Packet, 0); + if (pkt != NULL) + ppc_cs4232_dma = masktoint(pkt->S5_Pack.DMAMask); + pkt = PnP_find_packet((unsigned char*)&res->DevicePnPHeap[audiodevice->AllocatedOffset], + S5_Packet, 1); + if (pkt != NULL) + ppc_cs4232_dma2 = masktoint(pkt->S5_Pack.DMAMask); + } + + /* + * These are the PReP specs' defaults for the cs4231. We use these + * as fallback incase we don't have residual data. + * At least the IBM Thinkpad 850 with IDE DMA Channels at 6 and 7 + * will use the other values. + */ + if (audiodevice == NULL) { + switch (_prep_type) { + case _PREP_IBM: + ppc_cs4232_dma = 1; + ppc_cs4232_dma2 = -1; + break; + default: + ppc_cs4232_dma = 6; + ppc_cs4232_dma2 = 7; + } + } + + /* + * Find a way to push these informations to the cs4232 driver + * Give it out with printk, when not in cmd_line? + * Append it to cmd_line and saved_command_line? + * Format is cs4232=io,irq,dma,dma2 + */ +} +#endif /* CONFIG_SOUND_CS4232 */ + +/* + * Fill out screen_info according to the residual data. This allows us to use + * at least vesafb. + */ +static void __init +prep_init_vesa(void) +{ +#if (defined(CONFIG_FB_VGA16) || defined(CONFIG_FB_VGA16_MODULE) || \ + defined(CONFIG_FB_VESA)) + PPC_DEVICE *vgadev = NULL; + + if (have_residual_data) + vgadev = residual_find_device(~0, NULL, DisplayController, + SVGAController, -1, 0); + + if (vgadev != NULL) { + PnP_TAG_PACKET *pkt; + + pkt = PnP_find_large_vendor_packet( + (unsigned char *)&res->DevicePnPHeap[vgadev->AllocatedOffset], + 0x04, 0); /* 0x04 = Display Tag */ + if (pkt != NULL) { + unsigned char *ptr = (unsigned char *)pkt; + + if (ptr[4]) { + /* graphics mode */ + screen_info.orig_video_isVGA = VIDEO_TYPE_VLFB; + + screen_info.lfb_depth = ptr[4] * 8; + + screen_info.lfb_width = swab16(*(short *)(ptr+6)); + screen_info.lfb_height = swab16(*(short *)(ptr+8)); + screen_info.lfb_linelength = swab16(*(short *)(ptr+10)); + + screen_info.lfb_base = swab32(*(long *)(ptr+12)); + screen_info.lfb_size = swab32(*(long *)(ptr+20)) / 65536; + } + } + } +#endif +} + +/* + * Set DBAT 2 to access 0x80000000 so early progress messages will work + */ +static __inline__ void +prep_set_bat(void) +{ + /* wait for all outstanding memory access to complete */ + mb(); + + /* setup DBATs */ + mtspr(SPRN_DBAT2U, 0x80001ffe); + mtspr(SPRN_DBAT2L, 0x8000002a); + + /* wait for updates */ + mb(); +} + +/* + * IBM 3-digit status LED + */ +static unsigned int ibm_statusled_base __prepdata; + +static void __prep +ibm_statusled_progress(char *s, unsigned short hex); + +static int __prep +ibm_statusled_panic(struct notifier_block *dummy1, unsigned long dummy2, + void * dummy3) +{ + ibm_statusled_progress(NULL, 0x505); /* SOS */ + return NOTIFY_DONE; +} + +static struct notifier_block ibm_statusled_block __prepdata = { + ibm_statusled_panic, + NULL, + INT_MAX /* try to do it first */ +}; + +static void __prep +ibm_statusled_progress(char *s, unsigned short hex) +{ + static int notifier_installed; + /* + * Progress uses 4 digits and we have only 3. So, we map 0xffff to + * 0xfff for display switch off. Out of range values are mapped to + * 0xeff, as I'm told 0xf00 and above are reserved for hardware codes. + * Install the panic notifier when the display is first switched off. + */ + if (hex == 0xffff) { + hex = 0xfff; + if (!notifier_installed) { + ++notifier_installed; + notifier_chain_register(&panic_notifier_list, + &ibm_statusled_block); + } + } + else + if (hex > 0xfff) + hex = 0xeff; + + mb(); + outw(hex, ibm_statusled_base); +} + +static void __init +ibm_statusled_init(void) +{ + /* + * The IBM 3-digit LED display is specified in the residual data + * as an operator panel device, type "System Status LED". Find + * that device and determine its address. We validate all the + * other parameters on the off-chance another, similar device + * exists. + */ + if (have_residual_data) { + PPC_DEVICE *led; + PnP_TAG_PACKET *pkt; + + led = residual_find_device(~0, NULL, SystemPeripheral, + OperatorPanel, SystemStatusLED, 0); + if (!led) + return; + + pkt = PnP_find_packet((unsigned char *) + &res->DevicePnPHeap[led->AllocatedOffset], S8_Packet, 0); + if (!pkt) + return; + + if (pkt->S8_Pack.IOInfo != ISAAddr16bit) + return; + if (*(unsigned short *)pkt->S8_Pack.RangeMin != + *(unsigned short *)pkt->S8_Pack.RangeMax) + return; + if (pkt->S8_Pack.IOAlign != 2) + return; + if (pkt->S8_Pack.IONum != 2) + return; + + ibm_statusled_base = ld_le16((unsigned short *) + (pkt->S8_Pack.RangeMin)); + ppc_md.progress = ibm_statusled_progress; + } +} + +static void __init +prep_setup_arch(void) +{ + unsigned char reg; + int is_ide=0; + + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_jiffy = 50000000; + + /* Lookup PCI host bridges */ + prep_find_bridges(); + + /* Set up floppy in PS/2 mode */ + outb(0x09, SIO_CONFIG_RA); + reg = inb(SIO_CONFIG_RD); + reg = (reg & 0x3F) | 0x40; + outb(reg, SIO_CONFIG_RD); + outb(reg, SIO_CONFIG_RD); /* Have to write twice to change! */ + + switch ( _prep_type ) + { + case _PREP_IBM: + reg = inb(PREP_IBM_PLANAR); + printk(KERN_INFO "IBM planar ID: %02x", reg); + switch (reg) { + case PREP_IBM_SANDALFOOT: + prep_gen_enable_l2(); + setup_ibm_pci = prep_sandalfoot_setup_pci; + ppc_md.power_off = prep_sig750_poweroff; + ppc_md.show_cpuinfo = prep_sandalfoot_cpuinfo; + break; + case PREP_IBM_THINKPAD: + prep_gen_enable_l2(); + setup_ibm_pci = prep_thinkpad_setup_pci; + ppc_md.power_off = prep_carrera_poweroff; + ppc_md.show_cpuinfo = prep_thinkpad_cpuinfo; + break; + default: + if (have_residual_data) { + prep_gen_enable_l2(); + setup_ibm_pci = prep_residual_setup_pci; + ppc_md.power_off = prep_halt; + ppc_md.show_cpuinfo = prep_gen_cpuinfo; + break; + } + else + printk(" - unknown! Assuming Carolina"); + /* fall through */ + case PREP_IBM_CAROLINA_IDE_0: + case PREP_IBM_CAROLINA_IDE_1: + case PREP_IBM_CAROLINA_IDE_2: + case PREP_IBM_CAROLINA_IDE_3: + is_ide = 1; + case PREP_IBM_CAROLINA_SCSI_0: + case PREP_IBM_CAROLINA_SCSI_1: + case PREP_IBM_CAROLINA_SCSI_2: + case PREP_IBM_CAROLINA_SCSI_3: + prep_carolina_enable_l2(); + setup_ibm_pci = prep_carolina_setup_pci; + ppc_md.power_off = prep_sig750_poweroff; + ppc_md.show_cpuinfo = prep_carolina_cpuinfo; + break; + case PREP_IBM_TIGER1_133: + case PREP_IBM_TIGER1_166: + case PREP_IBM_TIGER1_180: + case PREP_IBM_TIGER1_xxx: + case PREP_IBM_TIGER1_333: + prep_carolina_enable_l2(); + setup_ibm_pci = prep_tiger1_setup_pci; + ppc_md.power_off = prep_sig750_poweroff; + ppc_md.show_cpuinfo = prep_tiger1_cpuinfo; + break; + } + printk("\n"); + + /* default root device */ + if (is_ide) + ROOT_DEV = MKDEV(IDE0_MAJOR, 3); + else + ROOT_DEV = MKDEV(SCSI_DISK0_MAJOR, 3); + + break; + case _PREP_Motorola: + prep_gen_enable_l2(); + ppc_md.power_off = prep_halt; + ppc_md.show_cpuinfo = prep_mot_cpuinfo; + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = Root_NFS; +#else + ROOT_DEV = Root_SDA2; +#endif + break; + } + + /* Read in NVRAM data */ + init_prep_nvram(); + + /* if no bootargs, look in NVRAM */ + if ( cmd_line[0] == '\0' ) { + char *bootargs; + bootargs = prep_nvram_get_var("bootargs"); + if (bootargs != NULL) { + strcpy(cmd_line, bootargs); + /* again.. */ + strcpy(saved_command_line, cmd_line); + } + } + +#ifdef CONFIG_SOUND_CS4232 + prep_init_sound(); +#endif /* CONFIG_SOUND_CS4232 */ + + prep_init_vesa(); + + switch (_prep_type) { + case _PREP_Motorola: + raven_init(); + break; + case _PREP_IBM: + ibm_prep_init(); + break; + } + +#ifdef CONFIG_VGA_CONSOLE + /* vgacon.c needs to know where we mapped IO memory in io_block_mapping() */ + vgacon_remap_base = 0xf0000000; + conswitchp = &vga_con; +#endif +} + +/* + * First, see if we can get this information from the residual data. + * This is important on some IBM PReP systems. If we cannot, we let the + * TODC code handle doing this. + */ +static void __init +prep_calibrate_decr(void) +{ + if (have_residual_data) { + unsigned long freq, divisor = 4; + + if ( res->VitalProductData.ProcessorBusHz ) { + freq = res->VitalProductData.ProcessorBusHz; + printk("time_init: decrementer frequency = %lu.%.6lu MHz\n", + (freq/divisor)/1000000, + (freq/divisor)%1000000); + tb_to_us = mulhwu_scale_factor(freq/divisor, 1000000); + tb_ticks_per_jiffy = freq / HZ / divisor; + } + } + else + todc_calibrate_decr(); +} + +static unsigned int __prep +prep_irq_canonicalize(u_int irq) +{ + if (irq == 2) + { + return 9; + } + else + { + return irq; + } +} + +static void __init +prep_init_IRQ(void) +{ + int i; + unsigned int pci_viddid, pci_did; + + if (OpenPIC_Addr != NULL) { + openpic_init(NUM_8259_INTERRUPTS); + /* We have a cascade on OpenPIC IRQ 0, Linux IRQ 16 */ + openpic_hookup_cascade(NUM_8259_INTERRUPTS, "82c59 cascade", + i8259_irq); + } + for ( i = 0 ; i < NUM_8259_INTERRUPTS ; i++ ) + irq_desc[i].handler = &i8259_pic; + + if (have_residual_data) { + i8259_init(residual_isapic_addr()); + return; + } + + /* If we have a Raven PCI bridge or a Hawk PCI bridge / Memory + * controller, we poll (as they have a different int-ack address). */ + early_read_config_dword(NULL, 0, 0, PCI_VENDOR_ID, &pci_viddid); + pci_did = (pci_viddid & 0xffff0000) >> 16; + if (((pci_viddid & 0xffff) == PCI_VENDOR_ID_MOTOROLA) + && ((pci_did == PCI_DEVICE_ID_MOTOROLA_RAVEN) + || (pci_did == PCI_DEVICE_ID_MOTOROLA_HAWK))) + i8259_init(0); + else + /* PCI interrupt ack address given in section 6.1.8 of the + * PReP specification. */ + i8259_init(MPC10X_MAPA_PCI_INTACK_ADDR); +} + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +/* + * IDE stuff. + */ +static int __prep +prep_ide_default_irq(unsigned long base) +{ + switch (base) { + case 0x1f0: return 13; + case 0x170: return 13; + case 0x1e8: return 11; + case 0x168: return 10; + case 0xfff0: return 14; /* MCP(N)750 ide0 */ + case 0xffe0: return 15; /* MCP(N)750 ide1 */ + default: return 0; + } +} + +static unsigned long __prep +prep_ide_default_io_base(int index) +{ + switch (index) { + case 0: return 0x1f0; + case 1: return 0x170; + case 2: return 0x1e8; + case 3: return 0x168; + default: + return 0; + } +} +#endif + +#ifdef CONFIG_SMP +/* PReP (MTX) support */ +static int __init +smp_prep_probe(void) +{ + extern int mot_multi; + + if (mot_multi) { + openpic_request_IPIs(); + smp_hw_index[1] = 1; + return 2; + } + + return 1; +} + +static void __init +smp_prep_kick_cpu(int nr) +{ + *(unsigned long *)KERNELBASE = nr; + asm volatile("dcbf 0,%0"::"r"(KERNELBASE):"memory"); + printk("CPU1 released, waiting\n"); +} + +static void __init +smp_prep_setup_cpu(int cpu_nr) +{ + if (OpenPIC_Addr) + do_openpic_setup_cpu(); +} + +static struct smp_ops_t prep_smp_ops __prepdata = { + smp_openpic_message_pass, + smp_prep_probe, + smp_prep_kick_cpu, + smp_prep_setup_cpu, + .give_timebase = smp_generic_give_timebase, + .take_timebase = smp_generic_take_timebase, +}; +#endif /* CONFIG_SMP */ + +/* + * Setup the bat mappings we're going to load that cover + * the io areas. RAM was mapped by mapin_ram(). + * -- Cort + */ +static void __init +prep_map_io(void) +{ + io_block_mapping(0x80000000, PREP_ISA_IO_BASE, 0x10000000, _PAGE_IO); + io_block_mapping(0xf0000000, PREP_ISA_MEM_BASE, 0x08000000, _PAGE_IO); +} + +static int __init +prep_request_io(void) +{ + if (_machine == _MACH_prep) { +#ifdef CONFIG_NVRAM + request_region(PREP_NVRAM_AS0, 0x8, "nvram"); +#endif + request_region(0x00,0x20,"dma1"); + request_region(0x40,0x20,"timer"); + request_region(0x80,0x10,"dma page reg"); + request_region(0xc0,0x20,"dma2"); + } + + return 0; +} + +device_initcall(prep_request_io); + +void __init +prep_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ +#ifdef CONFIG_PREP_RESIDUAL + /* make a copy of residual data */ + if ( r3 ) { + memcpy((void *)res,(void *)(r3+KERNELBASE), + sizeof(RESIDUAL)); + } +#endif + + isa_io_base = PREP_ISA_IO_BASE; + isa_mem_base = PREP_ISA_MEM_BASE; + pci_dram_offset = PREP_PCI_DRAM_OFFSET; + ISA_DMA_THRESHOLD = 0x00ffffff; + DMA_MODE_READ = 0x44; + DMA_MODE_WRITE = 0x48; + + /* figure out what kind of prep workstation we are */ + if (have_residual_data) { + if ( !strncmp(res->VitalProductData.PrintableModel,"IBM",3) ) + _prep_type = _PREP_IBM; + else + _prep_type = _PREP_Motorola; + } + else { + /* assume motorola if no residual (netboot?) */ + _prep_type = _PREP_Motorola; + } + +#ifdef CONFIG_PREP_RESIDUAL + /* Switch off all residual data processing if the user requests it */ + if (strstr(cmd_line, "noresidual") != NULL) + res = NULL; +#endif + + /* Initialise progress early to get maximum benefit */ + prep_set_bat(); + ibm_statusled_init(); + + ppc_md.setup_arch = prep_setup_arch; + ppc_md.show_percpuinfo = prep_show_percpuinfo; + ppc_md.show_cpuinfo = NULL; /* set in prep_setup_arch() */ + ppc_md.irq_canonicalize = prep_irq_canonicalize; + ppc_md.init_IRQ = prep_init_IRQ; + /* this gets changed later on if we have an OpenPIC -- Cort */ + ppc_md.get_irq = i8259_irq; + + ppc_md.phys_mem_access_prot = pci_phys_mem_access_prot; + + ppc_md.restart = prep_restart; + ppc_md.power_off = NULL; /* set in prep_setup_arch() */ + ppc_md.halt = prep_halt; + + ppc_md.nvram_read_val = prep_nvram_read_val; + ppc_md.nvram_write_val = prep_nvram_write_val; + + ppc_md.time_init = todc_time_init; + if (_prep_type == _PREP_IBM) { + ppc_md.rtc_read_val = todc_mc146818_read_val; + ppc_md.rtc_write_val = todc_mc146818_write_val; + TODC_INIT(TODC_TYPE_MC146818, RTC_PORT(0), NULL, RTC_PORT(1), + 8); + } else { + TODC_INIT(TODC_TYPE_MK48T59, PREP_NVRAM_AS0, PREP_NVRAM_AS1, + PREP_NVRAM_DATA, 8); + } + + ppc_md.calibrate_decr = prep_calibrate_decr; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + + ppc_md.setup_io_mappings = prep_map_io; + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) + ppc_ide_md.default_irq = prep_ide_default_irq; + ppc_ide_md.default_io_base = prep_ide_default_io_base; +#endif + +#ifdef CONFIG_SMP + ppc_md.smp_ops = &prep_smp_ops; +#endif /* CONFIG_SMP */ +} diff --git a/arch/ppc/platforms/prpmc750.c b/arch/ppc/platforms/prpmc750.c new file mode 100644 index 00000000000..c894e1ab593 --- /dev/null +++ b/arch/ppc/platforms/prpmc750.c @@ -0,0 +1,364 @@ +/* + * arch/ppc/platforms/prpmc750_setup.c + * + * Board setup routines for Motorola PrPMC750 + * + * Author: Matt Porter + * + * 2001-2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "prpmc750.h" + +extern unsigned long loops_per_jiffy; + +extern void gen550_progress(char *, unsigned short); + +static u_char prpmc750_openpic_initsenses[] __initdata = +{ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC750_INT_HOSTINT0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC750_INT_UART */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC750_INT_DEBUGINT */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC750_INT_HAWK_WDT */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC750_INT_UNUSED */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC750_INT_ABORT */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC750_INT_HOSTINT1 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC750_INT_HOSTINT2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC750_INT_HOSTINT3 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC750_INT_PMC_INTA */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC750_INT_PMC_INTB */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC750_INT_PMC_INTC */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC750_INT_PMC_INTD */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC750_INT_UNUSED */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC750_INT_UNUSED */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC750_INT_UNUSED */ +}; + +/* + * Motorola PrPMC750/PrPMC800 in PrPMCBASE or PrPMC-Carrier + * Combined irq tables. Only Base has IDSEL 14, only Carrier has 21 and 22. + */ +static inline int +prpmc_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {12, 0, 0, 0}, /* IDSEL 14 - Ethernet, base */ + {0, 0, 0, 0}, /* IDSEL 15 - unused */ + {10, 11, 12, 9}, /* IDSEL 16 - PMC A1, PMC1 */ + {10, 11, 12, 9}, /* IDSEL 17 - PrPMC-A-B, PMC2-B */ + {11, 12, 9, 10}, /* IDSEL 18 - PMC A1-B, PMC1-B */ + {0, 0, 0, 0}, /* IDSEL 19 - unused */ + {9, 10, 11, 12}, /* IDSEL 20 - P2P Bridge */ + {11, 12, 9, 10}, /* IDSEL 21 - PMC A2, carrier */ + {12, 9, 10, 11}, /* IDSEL 22 - PMC A2-B, carrier */ + }; + const long min_idsel = 14, max_idsel = 22, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +}; + +static void __init prpmc750_pcibios_fixup(void) +{ + struct pci_dev *dev; + unsigned short wtmp; + + /* + * Kludge to clean up after PPC6BUG which doesn't + * configure the CL5446 VGA card. Also the + * resource subsystem doesn't fixup the + * PCI mem resources on the CL5446. + */ + if ((dev = pci_get_device(PCI_VENDOR_ID_CIRRUS, + PCI_DEVICE_ID_CIRRUS_5446, 0))) { + dev->resource[0].start += PRPMC750_PCI_PHY_MEM_OFFSET; + dev->resource[0].end += PRPMC750_PCI_PHY_MEM_OFFSET; + pci_read_config_word(dev, PCI_COMMAND, &wtmp); + pci_write_config_word(dev, PCI_COMMAND, wtmp | 3); + /* Enable Color mode in MISC reg */ + outb(0x03, 0x3c2); + /* Select DRAM config reg */ + outb(0x0f, 0x3c4); + /* Set proper DRAM config */ + outb(0xdf, 0x3c5); + pci_dev_put(dev); + } +} + +void __init prpmc750_find_bridges(void) +{ + struct pci_controller *hose; + + hose = pcibios_alloc_controller(); + if (!hose) + return; + + hose->first_busno = 0; + hose->last_busno = 0xff; + hose->io_base_virt = (void *)PRPMC750_ISA_IO_BASE; + hose->pci_mem_offset = PRPMC750_PCI_PHY_MEM_OFFSET; + + pci_init_resource(&hose->io_resource, + PRPMC750_PCI_IO_START, + PRPMC750_PCI_IO_END, + IORESOURCE_IO, "PCI host bridge"); + + pci_init_resource(&hose->mem_resources[0], + PRPMC750_PROC_PCI_MEM_START, + PRPMC750_PROC_PCI_MEM_END, + IORESOURCE_MEM, "PCI host bridge"); + + hose->io_space.start = PRPMC750_PCI_IO_START; + hose->io_space.end = PRPMC750_PCI_IO_END; + hose->mem_space.start = PRPMC750_PCI_MEM_START; + hose->mem_space.end = PRPMC750_PCI_MEM_END - HAWK_MPIC_SIZE; + + if (hawk_init(hose, PRPMC750_HAWK_PPC_REG_BASE, + PRPMC750_PROC_PCI_MEM_START, + PRPMC750_PROC_PCI_MEM_END - HAWK_MPIC_SIZE, + PRPMC750_PROC_PCI_IO_START, PRPMC750_PROC_PCI_IO_END, + PRPMC750_PROC_PCI_MEM_END - HAWK_MPIC_SIZE + 1) + != 0) { + printk(KERN_CRIT "Could not initialize host bridge\n"); + } + + hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); + + ppc_md.pcibios_fixup = prpmc750_pcibios_fixup; + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = prpmc_map_irq; +} +static int prpmc750_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "machine\t\t: PrPMC750\n"); + + return 0; +} + +static void __init prpmc750_setup_arch(void) +{ + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_jiffy = 50000000 / HZ; + + /* Lookup PCI host bridges */ + prpmc750_find_bridges(); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = Root_NFS; +#else + ROOT_DEV = Root_SDA2; +#endif + + OpenPIC_InitSenses = prpmc750_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof(prpmc750_openpic_initsenses); + + printk(KERN_INFO "Port by MontaVista Software, Inc. " + "(source@mvista.com)\n"); +} + +/* + * Compute the PrPMC750's bus speed using the baud clock as a + * reference. + */ +static unsigned long __init prpmc750_get_bus_speed(void) +{ + unsigned long tbl_start, tbl_end; + unsigned long current_state, old_state, bus_speed; + unsigned char lcr, dll, dlm; + int baud_divisor, count; + + /* Read the UART's baud clock divisor */ + lcr = readb(PRPMC750_SERIAL_0_LCR); + writeb(lcr | UART_LCR_DLAB, PRPMC750_SERIAL_0_LCR); + dll = readb(PRPMC750_SERIAL_0_DLL); + dlm = readb(PRPMC750_SERIAL_0_DLM); + writeb(lcr & ~UART_LCR_DLAB, PRPMC750_SERIAL_0_LCR); + baud_divisor = (dlm << 8) | dll; + + /* + * Use the baud clock divisor and base baud clock + * to determine the baud rate and use that as + * the number of baud clock edges we use for + * the time base sample. Make it half the baud + * rate. + */ + count = PRPMC750_BASE_BAUD / (baud_divisor * 16); + + /* Find the first edge of the baud clock */ + old_state = readb(PRPMC750_STATUS_REG) & PRPMC750_BAUDOUT_MASK; + do { + current_state = readb(PRPMC750_STATUS_REG) & + PRPMC750_BAUDOUT_MASK; + } while (old_state == current_state); + + old_state = current_state; + + /* Get the starting time base value */ + tbl_start = get_tbl(); + + /* + * Loop until we have found a number of edges equal + * to half the count (half the baud rate) + */ + do { + do { + current_state = readb(PRPMC750_STATUS_REG) & + PRPMC750_BAUDOUT_MASK; + } while (old_state == current_state); + old_state = current_state; + } while (--count); + + /* Get the ending time base value */ + tbl_end = get_tbl(); + + /* Compute bus speed */ + bus_speed = (tbl_end - tbl_start) * 128; + + return bus_speed; +} + +static void __init prpmc750_calibrate_decr(void) +{ + unsigned long freq; + int divisor = 4; + + freq = prpmc750_get_bus_speed(); + + tb_ticks_per_jiffy = freq / (HZ * divisor); + tb_to_us = mulhwu_scale_factor(freq / divisor, 1000000); +} + +static void prpmc750_restart(char *cmd) +{ + local_irq_disable(); + writeb(PRPMC750_MODRST_MASK, PRPMC750_MODRST_REG); + while (1) ; +} + +static void prpmc750_halt(void) +{ + local_irq_disable(); + while (1) ; +} + +static void prpmc750_power_off(void) +{ + prpmc750_halt(); +} + +static void __init prpmc750_init_IRQ(void) +{ + openpic_init(0); +} + +/* + * Set BAT 3 to map 0xf0000000 to end of physical memory space. + */ +static __inline__ void prpmc750_set_bat(void) +{ + mb(); + mtspr(SPRN_DBAT1U, 0xf0001ffe); + mtspr(SPRN_DBAT1L, 0xf000002a); + mb(); +} + +/* + * We need to read the Falcon/Hawk memory controller + * to properly determine this value + */ +static unsigned long __init prpmc750_find_end_of_memory(void) +{ + /* Read the memory size from the Hawk SMC */ + return hawk_get_mem_size(PRPMC750_HAWK_SMC_BASE); +} + +static void __init prpmc750_map_io(void) +{ + io_block_mapping(PRPMC750_ISA_IO_BASE, PRPMC750_ISA_IO_BASE, + 0x10000000, _PAGE_IO); +#if 0 + io_block_mapping(0xf0000000, 0xc0000000, 0x08000000, _PAGE_IO); +#endif + io_block_mapping(0xf8000000, 0xf8000000, 0x08000000, _PAGE_IO); +} + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + + /* Cover the Hawk registers with a BAT */ + prpmc750_set_bat(); + + isa_io_base = PRPMC750_ISA_IO_BASE; + isa_mem_base = PRPMC750_ISA_MEM_BASE; + pci_dram_offset = PRPMC750_PCI_DRAM_OFFSET; + + ppc_md.setup_arch = prpmc750_setup_arch; + ppc_md.show_cpuinfo = prpmc750_show_cpuinfo; + ppc_md.init_IRQ = prpmc750_init_IRQ; + ppc_md.get_irq = openpic_get_irq; + + ppc_md.find_end_of_memory = prpmc750_find_end_of_memory; + ppc_md.setup_io_mappings = prpmc750_map_io; + + ppc_md.restart = prpmc750_restart; + ppc_md.power_off = prpmc750_power_off; + ppc_md.halt = prpmc750_halt; + + /* PrPMC750 has no timekeeper part */ + ppc_md.time_init = NULL; + ppc_md.get_rtc_time = NULL; + ppc_md.set_rtc_time = NULL; + ppc_md.calibrate_decr = prpmc750_calibrate_decr; + +#ifdef CONFIG_SERIAL_TEXT_DEBUG + ppc_md.progress = gen550_progress; +#endif /* CONFIG_SERIAL_TEXT_DEBUG */ +} diff --git a/arch/ppc/platforms/prpmc750.h b/arch/ppc/platforms/prpmc750.h new file mode 100644 index 00000000000..015b4f52c3e --- /dev/null +++ b/arch/ppc/platforms/prpmc750.h @@ -0,0 +1,95 @@ +/* + * include/asm-ppc/platforms/prpmc750.h + * + * Definitions for Motorola PrPMC750 board support + * + * Author: Matt Porter + * + * 2001-2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_PRPMC750_H__ +#define __ASM_PRPMC750_H__ + +/* + * Due to limiations imposed by legacy hardware (primaryily IDE controllers), + * the PrPMC750 carrier board operates using a PReP address map. + * + * From Processor (physical) -> PCI: + * PCI Mem Space: 0xc0000000 - 0xfe000000 -> 0x00000000 - 0x3e000000 (768 MB) + * PCI I/O Space: 0x80000000 - 0x90000000 -> 0x00000000 - 0x10000000 (256 MB) + * Note: Must skip 0xfe000000-0xfe400000 for CONFIG_HIGHMEM/PKMAP area + * + * From PCI -> Processor (physical): + * System Memory: 0x80000000 -> 0x00000000 + */ + +#define PRPMC750_ISA_IO_BASE PREP_ISA_IO_BASE +#define PRPMC750_ISA_MEM_BASE PREP_ISA_MEM_BASE + +/* PCI Memory space mapping info */ +#define PRPMC750_PCI_MEM_SIZE 0x30000000U +#define PRPMC750_PROC_PCI_MEM_START PRPMC750_ISA_MEM_BASE +#define PRPMC750_PROC_PCI_MEM_END (PRPMC750_PROC_PCI_MEM_START + \ + PRPMC750_PCI_MEM_SIZE - 1) +#define PRPMC750_PCI_MEM_START 0x00000000U +#define PRPMC750_PCI_MEM_END (PRPMC750_PCI_MEM_START + \ + PRPMC750_PCI_MEM_SIZE - 1) + +/* PCI I/O space mapping info */ +#define PRPMC750_PCI_IO_SIZE 0x10000000U +#define PRPMC750_PROC_PCI_IO_START PRPMC750_ISA_IO_BASE +#define PRPMC750_PROC_PCI_IO_END (PRPMC750_PROC_PCI_IO_START + \ + PRPMC750_PCI_IO_SIZE - 1) +#define PRPMC750_PCI_IO_START 0x00000000U +#define PRPMC750_PCI_IO_END (PRPMC750_PCI_IO_START + \ + PRPMC750_PCI_IO_SIZE - 1) + +/* System memory mapping info */ +#define PRPMC750_PCI_DRAM_OFFSET PREP_PCI_DRAM_OFFSET +#define PRPMC750_PCI_PHY_MEM_OFFSET (PRPMC750_ISA_MEM_BASE-PRPMC750_PCI_MEM_START) + +/* Register address definitions */ +#define PRPMC750_HAWK_SMC_BASE 0xfef80000U +#define PRPMC750_HAWK_PPC_REG_BASE 0xfeff0000U + +#define PRPMC750_BASE_BAUD 1843200 +#define PRPMC750_SERIAL_0 0xfef88000 +#define PRPMC750_SERIAL_0_DLL (PRPMC750_SERIAL_0 + (UART_DLL << 4)) +#define PRPMC750_SERIAL_0_DLM (PRPMC750_SERIAL_0 + (UART_DLM << 4)) +#define PRPMC750_SERIAL_0_LCR (PRPMC750_SERIAL_0 + (UART_LCR << 4)) + +#define PRPMC750_STATUS_REG 0xfef88080 +#define PRPMC750_BAUDOUT_MASK 0x02 +#define PRPMC750_MONARCH_MASK 0x01 + +#define PRPMC750_MODRST_REG 0xfef880a0 +#define PRPMC750_MODRST_MASK 0x01 + +#define PRPMC750_PIRQ_REG 0xfef880b0 +#define PRPMC750_SEL1_MASK 0x02 +#define PRPMC750_SEL0_MASK 0x01 + +#define PRPMC750_TBEN_REG 0xfef880c0 +#define PRPMC750_TBEN_MASK 0x01 + +/* UART Defines. */ +#define RS_TABLE_SIZE 4 + +/* Rate for the 1.8432 Mhz clock for the onboard serial chip */ +#define BASE_BAUD (PRPMC750_BASE_BAUD / 16) + +#define STD_COM_FLAGS ASYNC_BOOT_AUTOCONF + +#define SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, PRPMC750_SERIAL_0, 1, STD_COM_FLAGS, \ + iomem_base: (unsigned char *)PRPMC750_SERIAL_0, \ + iomem_reg_shift: 4, \ + io_type: SERIAL_IO_MEM } /* ttyS0 */ + +#endif /* __ASM_PRPMC750_H__ */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/prpmc800.c b/arch/ppc/platforms/prpmc800.c new file mode 100644 index 00000000000..8b09fa69b35 --- /dev/null +++ b/arch/ppc/platforms/prpmc800.c @@ -0,0 +1,477 @@ +/* + * arch/ppc/platforms/prpmc800.c + * + * Author: Dale Farnsworth + * + * 2001-2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "prpmc800.h" + +#define HARRIER_REVI_REG (PRPMC800_HARRIER_XCSR_BASE+HARRIER_REVI_OFF) +#define HARRIER_UCTL_REG (PRPMC800_HARRIER_XCSR_BASE+HARRIER_UCTL_OFF) +#define HARRIER_MISC_CSR_REG (PRPMC800_HARRIER_XCSR_BASE+HARRIER_MISC_CSR_OFF) +#define HARRIER_IFEVP_REG (PRPMC800_HARRIER_MPIC_BASE+HARRIER_MPIC_IFEVP_OFF) +#define HARRIER_IFEDE_REG (PRPMC800_HARRIER_MPIC_BASE+HARRIER_MPIC_IFEDE_OFF) +#define HARRIER_FEEN_REG (PRPMC800_HARRIER_XCSR_BASE+HARRIER_FEEN_OFF) +#define HARRIER_FEMA_REG (PRPMC800_HARRIER_XCSR_BASE+HARRIER_FEMA_OFF) + +#define HARRIER_VENI_REG (PRPMC800_HARRIER_XCSR_BASE + HARRIER_VENI_OFF) +#define HARRIER_MISC_CSR (PRPMC800_HARRIER_XCSR_BASE + \ + HARRIER_MISC_CSR_OFF) + +#define MONARCH (monarch != 0) +#define NON_MONARCH (monarch == 0) + +extern int mpic_init(void); +extern unsigned long loops_per_jiffy; +extern void gen550_progress(char *, unsigned short); + +static int monarch = 0; +static int found_self = 0; +static int self = 0; + +static u_char prpmc800_openpic_initsenses[] __initdata = +{ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC800_INT_HOSTINT0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC800_INT_UNUSED */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC800_INT_DEBUGINT */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC800_INT_HARRIER_WDT */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC800_INT_UNUSED */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC800_INT_UNUSED */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC800_INT_HOSTINT1 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC800_INT_HOSTINT2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC800_INT_HOSTINT3 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC800_INT_PMC_INTA */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC800_INT_PMC_INTB */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC800_INT_PMC_INTC */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC800_INT_PMC_INTD */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC800_INT_UNUSED */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC800_INT_UNUSED */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC800_INT_UNUSED */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PRPMC800_INT_HARRIER_INT (UARTS, ABORT, DMA) */ +}; + +/* + * Motorola PrPMC750/PrPMC800 in PrPMCBASE or PrPMC-Carrier + * Combined irq tables. Only Base has IDSEL 14, only Carrier has 21 and 22. + */ +static inline int +prpmc_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {12, 0, 0, 0}, /* IDSEL 14 - Ethernet, base */ + {0, 0, 0, 0}, /* IDSEL 15 - unused */ + {10, 11, 12, 9}, /* IDSEL 16 - PMC A1, PMC1 */ + {10, 11, 12, 9}, /* IDSEL 17 - PrPMC-A-B, PMC2-B */ + {11, 12, 9, 10}, /* IDSEL 18 - PMC A1-B, PMC1-B */ + {0, 0, 0, 0}, /* IDSEL 19 - unused */ + {9, 10, 11, 12}, /* IDSEL 20 - P2P Bridge */ + {11, 12, 9, 10}, /* IDSEL 21 - PMC A2, carrier */ + {12, 9, 10, 11}, /* IDSEL 22 - PMC A2-B, carrier */ + }; + const long min_idsel = 14, max_idsel = 22, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +}; + +static int +prpmc_read_config_dword(struct pci_controller *hose, u8 bus, u8 devfn, + int offset, u32 * val) +{ + /* paranoia */ + if ((hose == NULL) || + (hose->cfg_addr == NULL) || (hose->cfg_data == NULL)) + return PCIBIOS_DEVICE_NOT_FOUND; + + out_be32(hose->cfg_addr, ((offset & 0xfc) << 24) | (devfn << 16) + | ((bus - hose->bus_offset) << 8) | 0x80); + *val = in_le32((u32 *) (hose->cfg_data + (offset & 3))); + + return PCIBIOS_SUCCESSFUL; +} + +#define HARRIER_PCI_VEND_DEV_ID (PCI_VENDOR_ID_MOTOROLA | \ + (PCI_DEVICE_ID_MOTOROLA_HARRIER << 16)) +static int prpmc_self(u8 bus, u8 devfn) +{ + /* + * Harriers always view themselves as being on bus 0. If we're not + * looking at bus 0, we're not going to find ourselves. + */ + if (bus != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + else { + int result; + int val; + struct pci_controller *hose; + + hose = pci_bus_to_hose(bus); + + /* See if target device is a Harrier */ + result = prpmc_read_config_dword(hose, bus, devfn, + PCI_VENDOR_ID, &val); + if ((result != PCIBIOS_SUCCESSFUL) || + (val != HARRIER_PCI_VEND_DEV_ID)) + return PCIBIOS_DEVICE_NOT_FOUND; + + /* + * LBA bit is set if target Harrier == initiating Harrier + * (i.e. if we are reading our own PCI header). + */ + result = prpmc_read_config_dword(hose, bus, devfn, + HARRIER_LBA_OFF, &val); + if ((result != PCIBIOS_SUCCESSFUL) || + ((val & HARRIER_LBA_MSK) != HARRIER_LBA_MSK)) + return PCIBIOS_DEVICE_NOT_FOUND; + + /* It's us, save our location for later */ + self = devfn; + found_self = 1; + return PCIBIOS_SUCCESSFUL; + } +} + +static int prpmc_exclude_device(u8 bus, u8 devfn) +{ + /* + * Monarch is allowed to access all PCI devices. Non-monarch is + * only allowed to access its own Harrier. + */ + + if (MONARCH) + return PCIBIOS_SUCCESSFUL; + if (found_self) + if ((bus == 0) && (devfn == self)) + return PCIBIOS_SUCCESSFUL; + else + return PCIBIOS_DEVICE_NOT_FOUND; + else + return prpmc_self(bus, devfn); +} + +void __init prpmc800_find_bridges(void) +{ + struct pci_controller *hose; + int host_bridge; + + hose = pcibios_alloc_controller(); + if (!hose) + return; + + hose->first_busno = 0; + hose->last_busno = 0xff; + + ppc_md.pci_exclude_device = prpmc_exclude_device; + ppc_md.pcibios_fixup = NULL; + ppc_md.pcibios_fixup_bus = NULL; + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = prpmc_map_irq; + + setup_indirect_pci(hose, + PRPMC800_PCI_CONFIG_ADDR, PRPMC800_PCI_CONFIG_DATA); + + /* Get host bridge vendor/dev id */ + + host_bridge = in_be32((uint *) (HARRIER_VENI_REG)); + + if (host_bridge != HARRIER_VEND_DEV_ID) { + printk(KERN_CRIT "Host bridge 0x%x not supported\n", + host_bridge); + return; + } + + monarch = in_be32((uint *) HARRIER_MISC_CSR) & HARRIER_SYSCON; + + printk(KERN_INFO "Running as %s.\n", + MONARCH ? "Monarch" : "Non-Monarch"); + + hose->io_space.start = PRPMC800_PCI_IO_START; + hose->io_space.end = PRPMC800_PCI_IO_END; + hose->io_base_virt = (void *)PRPMC800_ISA_IO_BASE; + hose->pci_mem_offset = PRPMC800_PCI_PHY_MEM_OFFSET; + + pci_init_resource(&hose->io_resource, + PRPMC800_PCI_IO_START, PRPMC800_PCI_IO_END, + IORESOURCE_IO, "PCI host bridge"); + + if (MONARCH) { + hose->mem_space.start = PRPMC800_PCI_MEM_START; + hose->mem_space.end = PRPMC800_PCI_MEM_END; + + pci_init_resource(&hose->mem_resources[0], + PRPMC800_PCI_MEM_START, + PRPMC800_PCI_MEM_END, + IORESOURCE_MEM, "PCI host bridge"); + + if (harrier_init(hose, + PRPMC800_HARRIER_XCSR_BASE, + PRPMC800_PROC_PCI_MEM_START, + PRPMC800_PROC_PCI_MEM_END, + PRPMC800_PROC_PCI_IO_START, + PRPMC800_PROC_PCI_IO_END, + PRPMC800_HARRIER_MPIC_BASE) != 0) + printk(KERN_CRIT "Could not initialize HARRIER " + "bridge\n"); + + harrier_release_eready(PRPMC800_HARRIER_XCSR_BASE); + harrier_wait_eready(PRPMC800_HARRIER_XCSR_BASE); + hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); + + } else { + pci_init_resource(&hose->mem_resources[0], + PRPMC800_NM_PCI_MEM_START, + PRPMC800_NM_PCI_MEM_END, + IORESOURCE_MEM, "PCI host bridge"); + + hose->mem_space.start = PRPMC800_NM_PCI_MEM_START; + hose->mem_space.end = PRPMC800_NM_PCI_MEM_END; + + if (harrier_init(hose, + PRPMC800_HARRIER_XCSR_BASE, + PRPMC800_NM_PROC_PCI_MEM_START, + PRPMC800_NM_PROC_PCI_MEM_END, + PRPMC800_PROC_PCI_IO_START, + PRPMC800_PROC_PCI_IO_END, + PRPMC800_HARRIER_MPIC_BASE) != 0) + printk(KERN_CRIT "Could not initialize HARRIER " + "bridge\n"); + + harrier_setup_nonmonarch(PRPMC800_HARRIER_XCSR_BASE, + HARRIER_ITSZ_1MB); + harrier_release_eready(PRPMC800_HARRIER_XCSR_BASE); + } +} + +static int prpmc800_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "machine\t\t: PrPMC800\n"); + + return 0; +} + +static void __init prpmc800_setup_arch(void) +{ + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_jiffy = 50000000 / HZ; + + /* Lookup PCI host bridges */ + prpmc800_find_bridges(); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = Root_NFS; +#else + ROOT_DEV = Root_SDA2; +#endif + + printk(KERN_INFO "Port by MontaVista Software, Inc. " + "(source@mvista.com)\n"); +} + +/* + * Compute the PrPMC800's tbl frequency using the baud clock as a reference. + */ +static void __init prpmc800_calibrate_decr(void) +{ + unsigned long tbl_start, tbl_end; + unsigned long current_state, old_state, tb_ticks_per_second; + unsigned int count; + unsigned int harrier_revision; + + harrier_revision = readb(HARRIER_REVI_REG); + if (harrier_revision < 2) { + /* XTAL64 was broken in harrier revision 1 */ + printk(KERN_INFO "time_init: Harrier revision %d, assuming " + "100 Mhz bus\n", harrier_revision); + tb_ticks_per_second = 100000000 / 4; + tb_ticks_per_jiffy = tb_ticks_per_second / HZ; + tb_to_us = mulhwu_scale_factor(tb_ticks_per_second, 1000000); + return; + } + + /* + * The XTAL64 bit oscillates at the 1/64 the base baud clock + * Set count to XTAL64 cycles per second. Since we'll count + * half-cycles, we'll reach the count in half a second. + */ + count = PRPMC800_BASE_BAUD / 64; + + /* Find the first edge of the baud clock */ + old_state = readb(HARRIER_UCTL_REG) & HARRIER_XTAL64_MASK; + do { + current_state = readb(HARRIER_UCTL_REG) & HARRIER_XTAL64_MASK; + } while (old_state == current_state); + + old_state = current_state; + + /* Get the starting time base value */ + tbl_start = get_tbl(); + + /* + * Loop until we have found a number of edges (half-cycles) + * equal to the count (half a second) + */ + do { + do { + current_state = readb(HARRIER_UCTL_REG) & + HARRIER_XTAL64_MASK; + } while (old_state == current_state); + old_state = current_state; + } while (--count); + + /* Get the ending time base value */ + tbl_end = get_tbl(); + + /* We only counted for half a second, so double to get ticks/second */ + tb_ticks_per_second = (tbl_end - tbl_start) * 2; + tb_ticks_per_jiffy = tb_ticks_per_second / HZ; + tb_to_us = mulhwu_scale_factor(tb_ticks_per_second, 1000000); +} + +static void prpmc800_restart(char *cmd) +{ + ulong temp; + + local_irq_disable(); + temp = in_be32((uint *) HARRIER_MISC_CSR_REG); + temp |= HARRIER_RSTOUT; + out_be32((uint *) HARRIER_MISC_CSR_REG, temp); + while (1) ; +} + +static void prpmc800_halt(void) +{ + local_irq_disable(); + while (1) ; +} + +static void prpmc800_power_off(void) +{ + prpmc800_halt(); +} + +static void __init prpmc800_init_IRQ(void) +{ + OpenPIC_InitSenses = prpmc800_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof(prpmc800_openpic_initsenses); + + /* Setup external interrupt sources. */ + openpic_set_sources(0, 16, OpenPIC_Addr + 0x10000); + /* Setup internal UART interrupt source. */ + openpic_set_sources(16, 1, OpenPIC_Addr + 0x10200); + + /* Do the MPIC initialization based on the above settings. */ + openpic_init(0); + + /* enable functional exceptions for uarts and abort */ + out_8((u8 *) HARRIER_FEEN_REG, (HARRIER_FE_UA0 | HARRIER_FE_UA1)); + out_8((u8 *) HARRIER_FEMA_REG, ~(HARRIER_FE_UA0 | HARRIER_FE_UA1)); +} + +/* + * Set BAT 3 to map 0xf0000000 to end of physical memory space. + */ +static __inline__ void prpmc800_set_bat(void) +{ + mb(); + mtspr(SPRN_DBAT1U, 0xf0001ffe); + mtspr(SPRN_DBAT1L, 0xf000002a); + mb(); +} + +/* + * We need to read the Harrier memory controller + * to properly determine this value + */ +static unsigned long __init prpmc800_find_end_of_memory(void) +{ + /* Read the memory size from the Harrier XCSR */ + return harrier_get_mem_size(PRPMC800_HARRIER_XCSR_BASE); +} + +static void __init prpmc800_map_io(void) +{ + io_block_mapping(0x80000000, 0x80000000, 0x10000000, _PAGE_IO); + io_block_mapping(0xf0000000, 0xf0000000, 0x10000000, _PAGE_IO); +} + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + + prpmc800_set_bat(); + + isa_io_base = PRPMC800_ISA_IO_BASE; + isa_mem_base = PRPMC800_ISA_MEM_BASE; + pci_dram_offset = PRPMC800_PCI_DRAM_OFFSET; + + ppc_md.setup_arch = prpmc800_setup_arch; + ppc_md.show_cpuinfo = prpmc800_show_cpuinfo; + ppc_md.init_IRQ = prpmc800_init_IRQ; + ppc_md.get_irq = openpic_get_irq; + + ppc_md.find_end_of_memory = prpmc800_find_end_of_memory; + ppc_md.setup_io_mappings = prpmc800_map_io; + + ppc_md.restart = prpmc800_restart; + ppc_md.power_off = prpmc800_power_off; + ppc_md.halt = prpmc800_halt; + + /* PrPMC800 has no timekeeper part */ + ppc_md.time_init = NULL; + ppc_md.get_rtc_time = NULL; + ppc_md.set_rtc_time = NULL; + ppc_md.calibrate_decr = prpmc800_calibrate_decr; +#ifdef CONFIG_SERIAL_TEXT_DEBUG + ppc_md.progress = gen550_progress; +#else /* !CONFIG_SERIAL_TEXT_DEBUG */ + ppc_md.progress = NULL; +#endif /* CONFIG_SERIAL_TEXT_DEBUG */ +} diff --git a/arch/ppc/platforms/prpmc800.h b/arch/ppc/platforms/prpmc800.h new file mode 100644 index 00000000000..e53ec9b42a3 --- /dev/null +++ b/arch/ppc/platforms/prpmc800.h @@ -0,0 +1,82 @@ +/* + * include/asm-ppc/platforms/prpmc800.h + * + * Definitions for Motorola PrPMC800 board support + * + * Author: Dale Farnsworth + * + * 2001-2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + /* + * From Processor to PCI: + * PCI Mem Space: 0x80000000 - 0xa0000000 -> 0x80000000 - 0xa0000000 (512 MB) + * PCI I/O Space: 0xfe400000 - 0xfeef0000 -> 0x00000000 - 0x00b00000 (11 MB) + * Note: Must skip 0xfe000000-0xfe400000 for CONFIG_HIGHMEM/PKMAP area + * + * From PCI to Processor: + * System Memory: 0x00000000 -> 0x00000000 + */ + +#ifndef __ASMPPC_PRPMC800_H +#define __ASMPPC_PRPMC800_H + +#define PRPMC800_PCI_CONFIG_ADDR 0xfe000cf8 +#define PRPMC800_PCI_CONFIG_DATA 0xfe000cfc + +#define PRPMC800_PROC_PCI_IO_START 0xfe400000U +#define PRPMC800_PROC_PCI_IO_END 0xfeefffffU +#define PRPMC800_PCI_IO_START 0x00000000U +#define PRPMC800_PCI_IO_END 0x00afffffU + +#define PRPMC800_PROC_PCI_MEM_START 0x80000000U +#define PRPMC800_PROC_PCI_MEM_END 0x9fffffffU +#define PRPMC800_PCI_MEM_START 0x80000000U +#define PRPMC800_PCI_MEM_END 0x9fffffffU + +#define PRPMC800_NM_PROC_PCI_MEM_START 0x40000000U +#define PRPMC800_NM_PROC_PCI_MEM_END 0xdfffffffU +#define PRPMC800_NM_PCI_MEM_START 0x40000000U +#define PRPMC800_NM_PCI_MEM_END 0xdfffffffU + +#define PRPMC800_PCI_DRAM_OFFSET 0x00000000U +#define PRPMC800_PCI_PHY_MEM_OFFSET 0x00000000U + +#define PRPMC800_ISA_IO_BASE PRPMC800_PROC_PCI_IO_START +#define PRPMC800_ISA_MEM_BASE 0x00000000U + +#define PRPMC800_HARRIER_XCSR_BASE HARRIER_DEFAULT_XCSR_BASE +#define PRPMC800_HARRIER_MPIC_BASE 0xff000000 + +#define PRPMC800_SERIAL_1 0xfeff00c0 + +#define PRPMC800_BASE_BAUD 1843200 + +/* + * interrupt vector number and priority for harrier internal interrupt + * sources + */ +#define PRPMC800_INT_IRQ 16 +#define PRPMC800_INT_PRI 15 + +/* UART Defines. */ +#define RS_TABLE_SIZE 4 + +/* Rate for the 1.8432 Mhz clock for the onboard serial chip */ +#define BASE_BAUD (PRPMC800_BASE_BAUD / 16) + +#define STD_COM_FLAGS ASYNC_BOOT_AUTOCONF + +/* UARTS are at IRQ 16 */ +#define STD_SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, PRPMC800_SERIAL_1, 16, STD_COM_FLAGS, /* ttyS0 */\ + iomem_base: (unsigned char *)PRPMC800_SERIAL_1, \ + iomem_reg_shift: 0, \ + io_type: SERIAL_IO_MEM }, + +#define SERIAL_PORT_DFNS \ + STD_SERIAL_PORT_DFNS + +#endif /* __ASMPPC_PRPMC800_H */ diff --git a/arch/ppc/platforms/radstone_ppc7d.c b/arch/ppc/platforms/radstone_ppc7d.c new file mode 100644 index 00000000000..2a99b43737a --- /dev/null +++ b/arch/ppc/platforms/radstone_ppc7d.c @@ -0,0 +1,1452 @@ +/* + * arch/ppc/platforms/radstone_ppc7d.c + * + * Board setup routines for the Radstone PPC7D boards. + * + * Author: James Chapman + * + * Based on code done by Rabeeh Khoury - rabeeh@galileo.co.il + * Based on code done by - Mark A. Greer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/* Radstone PPC7D boards are rugged VME boards with PPC 7447A CPUs, + * Discovery-II, dual gigabit ethernet, dual PMC, USB, keyboard/mouse, + * 4 serial ports, 2 high speed serial ports (MPSCs) and optional + * SCSI / VGA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for linux/serial_core.h */ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "radstone_ppc7d.h" + +#undef DEBUG + +#define PPC7D_RST_PIN 17 /* GPP17 */ + +extern u32 mv64360_irq_base; + +static struct mv64x60_handle bh; +static int ppc7d_has_alma; + +extern void gen550_progress(char *, unsigned short); +extern void gen550_init(int, struct uart_port *); + +/* residual data */ +unsigned char __res[sizeof(bd_t)]; + +/***************************************************************************** + * Serial port code + *****************************************************************************/ + +#if defined(CONFIG_KGDB) || defined(CONFIG_SERIAL_TEXT_DEBUG) +static void __init ppc7d_early_serial_map(void) +{ +#if defined(CONFIG_SERIAL_MPSC_CONSOLE) + mv64x60_progress_init(CONFIG_MV64X60_NEW_BASE); +#elif defined(CONFIG_SERIAL_8250) + struct uart_port serial_req; + + /* Setup serial port access */ + memset(&serial_req, 0, sizeof(serial_req)); + serial_req.uartclk = UART_CLK; + serial_req.irq = 4; + serial_req.flags = STD_COM_FLAGS; + serial_req.iotype = SERIAL_IO_MEM; + serial_req.membase = (u_char *) PPC7D_SERIAL_0; + + gen550_init(0, &serial_req); + if (early_serial_setup(&serial_req) != 0) + printk(KERN_ERR "Early serial init of port 0 failed\n"); + + /* Assume early_serial_setup() doesn't modify serial_req */ + serial_req.line = 1; + serial_req.irq = 3; + serial_req.membase = (u_char *) PPC7D_SERIAL_1; + + gen550_init(1, &serial_req); + if (early_serial_setup(&serial_req) != 0) + printk(KERN_ERR "Early serial init of port 1 failed\n"); +#else +#error CONFIG_KGDB || CONFIG_SERIAL_TEXT_DEBUG has no supported CONFIG_SERIAL_XXX +#endif +} +#endif /* CONFIG_KGDB || CONFIG_SERIAL_TEXT_DEBUG */ + +/***************************************************************************** + * Low-level board support code + *****************************************************************************/ + +static unsigned long __init ppc7d_find_end_of_memory(void) +{ + bd_t *bp = (bd_t *) __res; + + if (bp->bi_memsize) + return bp->bi_memsize; + + return (256 * 1024 * 1024); +} + +static void __init ppc7d_map_io(void) +{ + /* remove temporary mapping */ + mtspr(SPRN_DBAT3U, 0x00000000); + mtspr(SPRN_DBAT3L, 0x00000000); + + io_block_mapping(0xe8000000, 0xe8000000, 0x08000000, _PAGE_IO); + io_block_mapping(0xfe000000, 0xfe000000, 0x02000000, _PAGE_IO); +} + +static void ppc7d_restart(char *cmd) +{ + u32 data; + + /* Disable GPP17 interrupt */ + data = mv64x60_read(&bh, MV64x60_GPP_INTR_MASK); + data &= ~(1 << PPC7D_RST_PIN); + mv64x60_write(&bh, MV64x60_GPP_INTR_MASK, data); + + /* Configure MPP17 as GPP */ + data = mv64x60_read(&bh, MV64x60_MPP_CNTL_2); + data &= ~(0x0000000f << 4); + mv64x60_write(&bh, MV64x60_MPP_CNTL_2, data); + + /* Enable pin GPP17 for output */ + data = mv64x60_read(&bh, MV64x60_GPP_IO_CNTL); + data |= (1 << PPC7D_RST_PIN); + mv64x60_write(&bh, MV64x60_GPP_IO_CNTL, data); + + /* Toggle GPP9 pin to reset the board */ + mv64x60_write(&bh, MV64x60_GPP_VALUE_CLR, 1 << PPC7D_RST_PIN); + mv64x60_write(&bh, MV64x60_GPP_VALUE_SET, 1 << PPC7D_RST_PIN); + + for (;;) ; /* Spin until reset happens */ + /* NOTREACHED */ +} + +static void ppc7d_power_off(void) +{ + u32 data; + + local_irq_disable(); + + /* Ensure that internal MV643XX watchdog is disabled. + * The Disco watchdog uses MPP17 on this hardware. + */ + data = mv64x60_read(&bh, MV64x60_MPP_CNTL_2); + data &= ~(0x0000000f << 4); + mv64x60_write(&bh, MV64x60_MPP_CNTL_2, data); + + data = mv64x60_read(&bh, MV64x60_WDT_WDC); + if (data & 0x80000000) { + mv64x60_write(&bh, MV64x60_WDT_WDC, 1 << 24); + mv64x60_write(&bh, MV64x60_WDT_WDC, 2 << 24); + } + + for (;;) ; /* No way to shut power off with software */ + /* NOTREACHED */ +} + +static void ppc7d_halt(void) +{ + ppc7d_power_off(); + /* NOTREACHED */ +} + +static unsigned long ppc7d_led_no_pulse; + +static int __init ppc7d_led_pulse_disable(char *str) +{ + ppc7d_led_no_pulse = 1; + return 1; +} + +/* This kernel option disables the heartbeat pulsing of a board LED */ +__setup("ledoff", ppc7d_led_pulse_disable); + +static void ppc7d_heartbeat(void) +{ + u32 data32; + u8 data8; + static int max706_wdog = 0; + + /* Unfortunately we can't access the LED control registers + * during early init because they're on the CPLD which is the + * other side of a PCI bridge which goes unreachable during + * PCI scan. So write the LEDs only if the MV64360 watchdog is + * enabled (i.e. userspace apps are running so kernel is up).. + */ + data32 = mv64x60_read(&bh, MV64x60_WDT_WDC); + if (data32 & 0x80000000) { + /* Enable MAX706 watchdog if not done already */ + if (!max706_wdog) { + outb(3, PPC7D_CPLD_RESET); + max706_wdog = 1; + } + + /* Hit the MAX706 watchdog */ + outb(0, PPC7D_CPLD_WATCHDOG_TRIG); + + /* Pulse LED DS219 if not disabled */ + if (!ppc7d_led_no_pulse) { + static int led_on = 0; + + data8 = inb(PPC7D_CPLD_LEDS); + if (led_on) + data8 &= ~PPC7D_CPLD_LEDS_DS219_MASK; + else + data8 |= PPC7D_CPLD_LEDS_DS219_MASK; + + outb(data8, PPC7D_CPLD_LEDS); + led_on = !led_on; + } + } + ppc_md.heartbeat_count = ppc_md.heartbeat_reset; +} + +static int ppc7d_show_cpuinfo(struct seq_file *m) +{ + u8 val; + u8 val1, val2; + static int flash_sizes[4] = { 64, 32, 0, 16 }; + static int flash_banks[4] = { 4, 3, 2, 1 }; + static char *pci_modes[] = { "PCI33", "PCI66", + "Unknown", "Unknown", + "PCIX33", "PCIX66", + "PCIX100", "PCIX133" + }; + + seq_printf(m, "vendor\t\t: Radstone Technology\n"); + seq_printf(m, "machine\t\t: PPC7D\n"); + + val = inb(PPC7D_CPLD_BOARD_REVISION); + val1 = (val & PPC7D_CPLD_BOARD_REVISION_NUMBER_MASK) >> 5; + val2 = (val & PPC7D_CPLD_BOARD_REVISION_LETTER_MASK); + seq_printf(m, "revision\t: %hd%c%c\n", + val1, + (val2 <= 0x18) ? 'A' + val2 : 'Y', + (val2 > 0x18) ? 'A' + (val2 - 0x19) : ' '); + + val = inb(PPC7D_CPLD_MOTHERBOARD_TYPE); + val1 = val & PPC7D_CPLD_MB_TYPE_PLL_MASK; + val2 = val & (PPC7D_CPLD_MB_TYPE_ECC_FITTED_MASK | + PPC7D_CPLD_MB_TYPE_ECC_ENABLE_MASK); + seq_printf(m, "bus speed\t: %dMHz\n", + (val1 == PPC7D_CPLD_MB_TYPE_PLL_133) ? 133 : + (val1 == PPC7D_CPLD_MB_TYPE_PLL_100) ? 100 : + (val1 == PPC7D_CPLD_MB_TYPE_PLL_64) ? 64 : 0); + + val = inb(PPC7D_CPLD_MEM_CONFIG_EXTEND); + val1 = val & PPC7D_CPLD_SDRAM_BANK_SIZE_MASK; + seq_printf(m, "SDRAM\t\t: %d%c", + (val1 == PPC7D_CPLD_SDRAM_BANK_SIZE_128M) ? 128 : + (val1 == PPC7D_CPLD_SDRAM_BANK_SIZE_256M) ? 256 : + (val1 == PPC7D_CPLD_SDRAM_BANK_SIZE_512M) ? 512 : 1, + (val1 == PPC7D_CPLD_SDRAM_BANK_SIZE_1G) ? 'G' : 'M'); + if (val2 & PPC7D_CPLD_MB_TYPE_ECC_FITTED_MASK) { + seq_printf(m, " [ECC %sabled]", + (val2 & PPC7D_CPLD_MB_TYPE_ECC_ENABLE_MASK) ? "en" : + "dis"); + } + seq_printf(m, "\n"); + + val1 = (val & PPC7D_CPLD_FLASH_DEV_SIZE_MASK); + val2 = (val & PPC7D_CPLD_FLASH_BANK_NUM_MASK) >> 2; + seq_printf(m, "FLASH\t\t: %d banks of %dM, total %dM\n", + flash_banks[val2], flash_sizes[val1], + flash_banks[val2] * flash_sizes[val1]); + + val = inb(PPC7D_CPLD_FLASH_WRITE_CNTL); + val1 = inb(PPC7D_CPLD_SW_FLASH_WRITE_PROTECT); + seq_printf(m, " write links\t: %s%s%s%s\n", + (val & PPD7D_CPLD_FLASH_CNTL_WR_LINK_MASK) ? "WRITE " : "", + (val & PPD7D_CPLD_FLASH_CNTL_BOOT_LINK_MASK) ? "BOOT " : "", + (val & PPD7D_CPLD_FLASH_CNTL_USER_LINK_MASK) ? "USER " : "", + (val & (PPD7D_CPLD_FLASH_CNTL_WR_LINK_MASK | + PPD7D_CPLD_FLASH_CNTL_BOOT_LINK_MASK | + PPD7D_CPLD_FLASH_CNTL_USER_LINK_MASK)) == + 0 ? "NONE" : ""); + seq_printf(m, " write sector h/w enables: %s%s%s%s%s\n", + (val & PPD7D_CPLD_FLASH_CNTL_RECO_WR_MASK) ? "RECOVERY " : + "", + (val & PPD7D_CPLD_FLASH_CNTL_BOOT_WR_MASK) ? "BOOT " : "", + (val & PPD7D_CPLD_FLASH_CNTL_USER_WR_MASK) ? "USER " : "", + (val1 & PPC7D_CPLD_FLASH_CNTL_NVRAM_PROT_MASK) ? "NVRAM " : + "", + (((val & + (PPD7D_CPLD_FLASH_CNTL_RECO_WR_MASK | + PPD7D_CPLD_FLASH_CNTL_BOOT_WR_MASK | + PPD7D_CPLD_FLASH_CNTL_BOOT_WR_MASK)) == 0) + && ((val1 & PPC7D_CPLD_FLASH_CNTL_NVRAM_PROT_MASK) == + 0)) ? "NONE" : ""); + val1 = + inb(PPC7D_CPLD_SW_FLASH_WRITE_PROTECT) & + (PPC7D_CPLD_SW_FLASH_WRPROT_SYSBOOT_MASK | + PPC7D_CPLD_SW_FLASH_WRPROT_USER_MASK); + seq_printf(m, " software sector enables: %s%s%s\n", + (val1 & PPC7D_CPLD_SW_FLASH_WRPROT_SYSBOOT_MASK) ? "SYSBOOT " + : "", + (val1 & PPC7D_CPLD_SW_FLASH_WRPROT_USER_MASK) ? "USER " : "", + (val1 == 0) ? "NONE " : ""); + + seq_printf(m, "Boot options\t: %s%s%s%s\n", + (val & PPC7D_CPLD_FLASH_CNTL_ALTBOOT_LINK_MASK) ? + "ALTERNATE " : "", + (val & PPC7D_CPLD_FLASH_CNTL_VMEBOOT_LINK_MASK) ? "VME " : + "", + (val & PPC7D_CPLD_FLASH_CNTL_RECBOOT_LINK_MASK) ? "RECOVERY " + : "", + ((val & + (PPC7D_CPLD_FLASH_CNTL_ALTBOOT_LINK_MASK | + PPC7D_CPLD_FLASH_CNTL_VMEBOOT_LINK_MASK | + PPC7D_CPLD_FLASH_CNTL_RECBOOT_LINK_MASK)) == + 0) ? "NONE" : ""); + + val = inb(PPC7D_CPLD_EQUIPMENT_PRESENT_1); + seq_printf(m, "Fitted modules\t: %s%s%s%s\n", + (val & PPC7D_CPLD_EQPT_PRES_1_PMC1_MASK) ? "" : "PMC1 ", + (val & PPC7D_CPLD_EQPT_PRES_1_PMC2_MASK) ? "" : "PMC2 ", + (val & PPC7D_CPLD_EQPT_PRES_1_AFIX_MASK) ? "AFIX " : "", + ((val & (PPC7D_CPLD_EQPT_PRES_1_PMC1_MASK | + PPC7D_CPLD_EQPT_PRES_1_PMC2_MASK | + PPC7D_CPLD_EQPT_PRES_1_AFIX_MASK)) == + (PPC7D_CPLD_EQPT_PRES_1_PMC1_MASK | + PPC7D_CPLD_EQPT_PRES_1_PMC2_MASK)) ? "NONE" : ""); + + if (val & PPC7D_CPLD_EQPT_PRES_1_AFIX_MASK) { + static const char *ids[] = { + "unknown", + "1553 (Dual Channel)", + "1553 (Single Channel)", + "8-bit SCSI + VGA", + "16-bit SCSI + VGA", + "1553 (Single Channel with sideband)", + "1553 (Dual Channel with sideband)", + NULL + }; + u8 id = __raw_readb((void *)PPC7D_AFIX_REG_BASE + 0x03); + seq_printf(m, "AFIX module\t: 0x%hx [%s]\n", id, + id < 7 ? ids[id] : "unknown"); + } + + val = inb(PPC7D_CPLD_PCI_CONFIG); + val1 = (val & PPC7D_CPLD_PCI_CONFIG_PCI0_MASK) >> 4; + val2 = (val & PPC7D_CPLD_PCI_CONFIG_PCI1_MASK); + seq_printf(m, "PCI#0\t\t: %s\nPCI#1\t\t: %s\n", + pci_modes[val1], pci_modes[val2]); + + val = inb(PPC7D_CPLD_EQUIPMENT_PRESENT_2); + seq_printf(m, "PMC1\t\t: %s\nPMC2\t\t: %s\n", + (val & PPC7D_CPLD_EQPT_PRES_3_PMC1_V_MASK) ? "3.3v" : "5v", + (val & PPC7D_CPLD_EQPT_PRES_3_PMC2_V_MASK) ? "3.3v" : "5v"); + seq_printf(m, "PMC power source: %s\n", + (val & PPC7D_CPLD_EQPT_PRES_3_PMC_POWER_MASK) ? "VME" : + "internal"); + + val = inb(PPC7D_CPLD_EQUIPMENT_PRESENT_4); + val2 = inb(PPC7D_CPLD_EQUIPMENT_PRESENT_2); + seq_printf(m, "Fit options\t: %s%s%s%s%s%s%s\n", + (val & PPC7D_CPLD_EQPT_PRES_4_LPT_MASK) ? "LPT " : "", + (val & PPC7D_CPLD_EQPT_PRES_4_PS2_FITTED) ? "PS2 " : "", + (val & PPC7D_CPLD_EQPT_PRES_4_USB2_FITTED) ? "USB2 " : "", + (val2 & PPC7D_CPLD_EQPT_PRES_2_UNIVERSE_MASK) ? "VME " : "", + (val2 & PPC7D_CPLD_EQPT_PRES_2_COM36_MASK) ? "COM3-6 " : "", + (val2 & PPC7D_CPLD_EQPT_PRES_2_GIGE_MASK) ? "eth0 " : "", + (val2 & PPC7D_CPLD_EQPT_PRES_2_DUALGIGE_MASK) ? "eth1 " : + ""); + + val = inb(PPC7D_CPLD_ID_LINK); + val1 = val & (PPC7D_CPLD_ID_LINK_E6_MASK | + PPC7D_CPLD_ID_LINK_E7_MASK | + PPC7D_CPLD_ID_LINK_E12_MASK | + PPC7D_CPLD_ID_LINK_E13_MASK); + + val = inb(PPC7D_CPLD_FLASH_WRITE_CNTL) & + (PPD7D_CPLD_FLASH_CNTL_WR_LINK_MASK | + PPD7D_CPLD_FLASH_CNTL_BOOT_LINK_MASK | + PPD7D_CPLD_FLASH_CNTL_USER_LINK_MASK); + + seq_printf(m, "Board links present: %s%s%s%s%s%s%s%s\n", + (val1 & PPC7D_CPLD_ID_LINK_E6_MASK) ? "E6 " : "", + (val1 & PPC7D_CPLD_ID_LINK_E7_MASK) ? "E7 " : "", + (val & PPD7D_CPLD_FLASH_CNTL_WR_LINK_MASK) ? "E9 " : "", + (val & PPD7D_CPLD_FLASH_CNTL_BOOT_LINK_MASK) ? "E10 " : "", + (val & PPD7D_CPLD_FLASH_CNTL_USER_LINK_MASK) ? "E11 " : "", + (val1 & PPC7D_CPLD_ID_LINK_E12_MASK) ? "E12 " : "", + (val1 & PPC7D_CPLD_ID_LINK_E13_MASK) ? "E13 " : "", + ((val == 0) && (val1 == 0)) ? "NONE" : ""); + + val = inb(PPC7D_CPLD_WDOG_RESETSW_MASK); + seq_printf(m, "Front panel reset switch: %sabled\n", + (val & PPC7D_CPLD_WDOG_RESETSW_MASK) ? "dis" : "en"); + + return 0; +} + +static void __init ppc7d_calibrate_decr(void) +{ + ulong freq; + + freq = 100000000 / 4; + + pr_debug("time_init: decrementer frequency = %lu.%.6lu MHz\n", + freq / 1000000, freq % 1000000); + + tb_ticks_per_jiffy = freq / HZ; + tb_to_us = mulhwu_scale_factor(freq, 1000000); +} + +/***************************************************************************** + * Interrupt stuff + *****************************************************************************/ + +static irqreturn_t ppc7d_i8259_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + u32 temp = mv64x60_read(&bh, MV64x60_GPP_INTR_CAUSE); + if (temp & (1 << 28)) { + i8259_irq(regs); + mv64x60_write(&bh, MV64x60_GPP_INTR_CAUSE, temp & (~(1 << 28))); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +/* + * Each interrupt cause is assigned an IRQ number. + * Southbridge has 16*2 (two 8259's) interrupts. + * Discovery-II has 96 interrupts (cause-hi, cause-lo, gpp x 32). + * If multiple interrupts are pending, get_irq() returns the + * lowest pending irq number first. + * + * + * IRQ # Source Trig Active + * ============================================================= + * + * Southbridge + * ----------- + * IRQ # Source Trig + * ============================================================= + * 0 ISA High Resolution Counter Edge + * 1 Keyboard Edge + * 2 Cascade From (IRQ 8-15) Edge + * 3 Com 2 (Uart 2) Edge + * 4 Com 1 (Uart 1) Edge + * 5 PCI Int D/AFIX IRQZ ID4 (2,7) Level + * 6 GPIO Level + * 7 LPT Edge + * 8 RTC Alarm Edge + * 9 PCI Int A/PMC 2/AFIX IRQW ID1 (2,0) Level + * 10 PCI Int B/PMC 1/AFIX IRQX ID2 (2,1) Level + * 11 USB2 Level + * 12 Mouse Edge + * 13 Reserved internally by Ali M1535+ + * 14 PCI Int C/VME/AFIX IRQY ID3 (2,6) Level + * 15 COM 5/6 Level + * + * 16..112 Discovery-II... + * + * MPP28 Southbridge Edge High + * + * + * Interrupts are cascaded through to the Discovery-II. + * + * PCI --- + * \ + * CPLD --> ALI1535 -------> DISCOVERY-II + * INTF MPP28 + */ +static void __init ppc7d_init_irq(void) +{ + int irq; + + pr_debug("%s\n", __FUNCTION__); + i8259_init(0); + mv64360_init_irq(); + + /* IRQ 0..15 are handled by the cascaded 8259's of the Ali1535 */ + for (irq = 0; irq < 16; irq++) { + irq_desc[irq].handler = &i8259_pic; + } + /* IRQs 5,6,9,10,11,14,15 are level sensitive */ + irq_desc[5].status |= IRQ_LEVEL; + irq_desc[6].status |= IRQ_LEVEL; + irq_desc[9].status |= IRQ_LEVEL; + irq_desc[10].status |= IRQ_LEVEL; + irq_desc[11].status |= IRQ_LEVEL; + irq_desc[14].status |= IRQ_LEVEL; + irq_desc[15].status |= IRQ_LEVEL; + + /* GPP28 is edge triggered */ + irq_desc[mv64360_irq_base + MV64x60_IRQ_GPP28].status &= ~IRQ_LEVEL; +} + +static u32 ppc7d_irq_canonicalize(u32 irq) +{ + if ((irq >= 16) && (irq < (16 + 96))) + irq -= 16; + + return irq; +} + +static int ppc7d_get_irq(struct pt_regs *regs) +{ + int irq; + + irq = mv64360_get_irq(regs); + if (irq == (mv64360_irq_base + MV64x60_IRQ_GPP28)) + irq = i8259_irq(regs); + return irq; +} + +/* + * 9 PCI Int A/PMC 2/AFIX IRQW ID1 (2,0) Level + * 10 PCI Int B/PMC 1/AFIX IRQX ID2 (2,1) Level + * 14 PCI Int C/VME/AFIX IRQY ID3 (2,6) Level + * 5 PCI Int D/AFIX IRQZ ID4 (2,7) Level + */ +static int __init ppc7d_map_irq(struct pci_dev *dev, unsigned char idsel, + unsigned char pin) +{ + static const char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {10, 14, 5, 9}, /* IDSEL 10 - PMC2 / AFIX IRQW */ + {9, 10, 14, 5}, /* IDSEL 11 - PMC1 / AFIX IRQX */ + {5, 9, 10, 14}, /* IDSEL 12 - AFIX IRQY */ + {14, 5, 9, 10}, /* IDSEL 13 - AFIX IRQZ */ + }; + const long min_idsel = 10, max_idsel = 14, irqs_per_slot = 4; + + pr_debug("%s: %04x/%04x/%x: idsel=%hx pin=%hu\n", __FUNCTION__, + dev->vendor, dev->device, PCI_FUNC(dev->devfn), idsel, pin); + + return PCI_IRQ_TABLE_LOOKUP; +} + +void __init ppc7d_intr_setup(void) +{ + u32 data; + + /* + * Define GPP 28 interrupt polarity as active high + * input signal and level triggered + */ + data = mv64x60_read(&bh, MV64x60_GPP_LEVEL_CNTL); + data &= ~(1 << 28); + mv64x60_write(&bh, MV64x60_GPP_LEVEL_CNTL, data); + data = mv64x60_read(&bh, MV64x60_GPP_IO_CNTL); + data &= ~(1 << 28); + mv64x60_write(&bh, MV64x60_GPP_IO_CNTL, data); + + /* Config GPP intr ctlr to respond to level trigger */ + data = mv64x60_read(&bh, MV64x60_COMM_ARBITER_CNTL); + data |= (1 << 10); + mv64x60_write(&bh, MV64x60_COMM_ARBITER_CNTL, data); + + /* XXXX Erranum FEr PCI-#8 */ + data = mv64x60_read(&bh, MV64x60_PCI0_CMD); + data &= ~((1 << 5) | (1 << 9)); + mv64x60_write(&bh, MV64x60_PCI0_CMD, data); + data = mv64x60_read(&bh, MV64x60_PCI1_CMD); + data &= ~((1 << 5) | (1 << 9)); + mv64x60_write(&bh, MV64x60_PCI1_CMD, data); + + /* + * Dismiss and then enable interrupt on GPP interrupt cause + * for CPU #0 + */ + mv64x60_write(&bh, MV64x60_GPP_INTR_CAUSE, ~(1 << 28)); + data = mv64x60_read(&bh, MV64x60_GPP_INTR_MASK); + data |= (1 << 28); + mv64x60_write(&bh, MV64x60_GPP_INTR_MASK, data); + + /* + * Dismiss and then enable interrupt on CPU #0 high cause reg + * BIT27 summarizes GPP interrupts 23-31 + */ + mv64x60_write(&bh, MV64360_IC_MAIN_CAUSE_HI, ~(1 << 27)); + data = mv64x60_read(&bh, MV64360_IC_CPU0_INTR_MASK_HI); + data |= (1 << 27); + mv64x60_write(&bh, MV64360_IC_CPU0_INTR_MASK_HI, data); +} + +/***************************************************************************** + * Platform device data fixup routines. + *****************************************************************************/ + +#if defined(CONFIG_SERIAL_MPSC) +static void __init ppc7d_fixup_mpsc_pdata(struct platform_device *pdev) +{ + struct mpsc_pdata *pdata; + + pdata = (struct mpsc_pdata *)pdev->dev.platform_data; + + pdata->max_idle = 40; + pdata->default_baud = PPC7D_DEFAULT_BAUD; + pdata->brg_clk_src = PPC7D_MPSC_CLK_SRC; + pdata->brg_clk_freq = PPC7D_MPSC_CLK_FREQ; + + return; +} +#endif + +#if defined(CONFIG_MV643XX_ETH) +static void __init ppc7d_fixup_eth_pdata(struct platform_device *pdev) +{ + struct mv643xx_eth_platform_data *eth_pd; + static u16 phy_addr[] = { + PPC7D_ETH0_PHY_ADDR, + PPC7D_ETH1_PHY_ADDR, + PPC7D_ETH2_PHY_ADDR, + }; + int i; + + eth_pd = pdev->dev.platform_data; + eth_pd->force_phy_addr = 1; + eth_pd->phy_addr = phy_addr[pdev->id]; + eth_pd->tx_queue_size = PPC7D_ETH_TX_QUEUE_SIZE; + eth_pd->rx_queue_size = PPC7D_ETH_RX_QUEUE_SIZE; + + /* Adjust IRQ by mv64360_irq_base */ + for (i = 0; i < pdev->num_resources; i++) { + struct resource *r = &pdev->resource[i]; + + if (r->flags & IORESOURCE_IRQ) { + r->start += mv64360_irq_base; + r->end += mv64360_irq_base; + pr_debug("%s, uses IRQ %d\n", pdev->name, + (int)r->start); + } + } + +} +#endif + +#if defined(CONFIG_I2C_MV64XXX) +static void __init +ppc7d_fixup_i2c_pdata(struct platform_device *pdev) +{ + struct mv64xxx_i2c_pdata *pdata; + int i; + + pdata = pdev->dev.platform_data; + if (pdata == NULL) { + pdata = kmalloc(sizeof(*pdata), GFP_KERNEL); + if (pdata == NULL) + return; + + memset(pdata, 0, sizeof(*pdata)); + pdev->dev.platform_data = pdata; + } + + /* divisors M=8, N=3 for 100kHz I2C from 133MHz system clock */ + pdata->freq_m = 8; + pdata->freq_n = 3; + pdata->timeout = 500; + pdata->retries = 3; + + /* Adjust IRQ by mv64360_irq_base */ + for (i = 0; i < pdev->num_resources; i++) { + struct resource *r = &pdev->resource[i]; + + if (r->flags & IORESOURCE_IRQ) { + r->start += mv64360_irq_base; + r->end += mv64360_irq_base; + pr_debug("%s, uses IRQ %d\n", pdev->name, (int) r->start); + } + } +} +#endif + +static int __init ppc7d_platform_notify(struct device *dev) +{ + static struct { + char *bus_id; + void ((*rtn) (struct platform_device * pdev)); + } dev_map[] = { +#if defined(CONFIG_SERIAL_MPSC) + { MPSC_CTLR_NAME ".0", ppc7d_fixup_mpsc_pdata }, + { MPSC_CTLR_NAME ".1", ppc7d_fixup_mpsc_pdata }, +#endif +#if defined(CONFIG_MV643XX_ETH) + { MV643XX_ETH_NAME ".0", ppc7d_fixup_eth_pdata }, + { MV643XX_ETH_NAME ".1", ppc7d_fixup_eth_pdata }, + { MV643XX_ETH_NAME ".2", ppc7d_fixup_eth_pdata }, +#endif +#if defined(CONFIG_I2C_MV64XXX) + { MV64XXX_I2C_CTLR_NAME ".0", ppc7d_fixup_i2c_pdata }, +#endif + }; + struct platform_device *pdev; + int i; + + if (dev && dev->bus_id) + for (i = 0; i < ARRAY_SIZE(dev_map); i++) + if (!strncmp(dev->bus_id, dev_map[i].bus_id, + BUS_ID_SIZE)) { + + pdev = container_of(dev, + struct platform_device, + dev); + dev_map[i].rtn(pdev); + } + + return 0; +} + +/***************************************************************************** + * PCI device fixups. + * These aren't really fixups per se. They are used to init devices as they + * are found during PCI scan. + * + * The PPC7D has an HB8 PCI-X bridge which must be set up during a PCI + * scan in order to find other devices on its secondary side. + *****************************************************************************/ + +static void __init ppc7d_fixup_hb8(struct pci_dev *dev) +{ + u16 val16; + + if (dev->bus->number == 0) { + pr_debug("PCI: HB8 init\n"); + + pci_write_config_byte(dev, 0x1c, + ((PPC7D_PCI0_IO_START_PCI_ADDR & 0xf000) + >> 8) | 0x01); + pci_write_config_byte(dev, 0x1d, + (((PPC7D_PCI0_IO_START_PCI_ADDR + + PPC7D_PCI0_IO_SIZE - + 1) & 0xf000) >> 8) | 0x01); + pci_write_config_word(dev, 0x30, + PPC7D_PCI0_IO_START_PCI_ADDR >> 16); + pci_write_config_word(dev, 0x32, + ((PPC7D_PCI0_IO_START_PCI_ADDR + + PPC7D_PCI0_IO_SIZE - + 1) >> 16) & 0xffff); + + pci_write_config_word(dev, 0x20, + PPC7D_PCI0_MEM0_START_PCI_LO_ADDR >> 16); + pci_write_config_word(dev, 0x22, + ((PPC7D_PCI0_MEM0_START_PCI_LO_ADDR + + PPC7D_PCI0_MEM0_SIZE - + 1) >> 16) & 0xffff); + pci_write_config_word(dev, 0x24, 0); + pci_write_config_word(dev, 0x26, 0); + pci_write_config_dword(dev, 0x28, 0); + pci_write_config_dword(dev, 0x2c, 0); + + pci_read_config_word(dev, 0x3e, &val16); + val16 |= ((1 << 5) | (1 << 1)); /* signal master aborts and + * SERR to primary + */ + val16 &= ~(1 << 2); /* ISA disable, so all ISA + * ports forwarded to secondary + */ + pci_write_config_word(dev, 0x3e, val16); + } +} + +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_HINT, 0x0028, ppc7d_fixup_hb8); + +/* This should perhaps be a separate driver as we're actually initializing + * the chip for this board here. It's hardly a fixup... + */ +static void __init ppc7d_fixup_ali1535(struct pci_dev *dev) +{ + pr_debug("PCI: ALI1535 init\n"); + + if (dev->bus->number == 1) { + /* Configure the ISA Port Settings */ + pci_write_config_byte(dev, 0x43, 0x00); + + /* Disable PCI Interrupt polling mode */ + pci_write_config_byte(dev, 0x45, 0x00); + + /* Multifunction pin select INTFJ -> INTF */ + pci_write_config_byte(dev, 0x78, 0x00); + + /* Set PCI INT -> IRQ Routing control in for external + * pins south bridge. + */ + pci_write_config_byte(dev, 0x48, 0x31); /* [7-4] INT B -> IRQ10 + * [3-0] INT A -> IRQ9 + */ + pci_write_config_byte(dev, 0x49, 0x5D); /* [7-4] INT D -> IRQ5 + * [3-0] INT C -> IRQ14 + */ + + /* PPC7D setup */ + /* NEC USB device on IRQ 11 (INTE) - INTF disabled */ + pci_write_config_byte(dev, 0x4A, 0x09); + + /* GPIO on IRQ 6 */ + pci_write_config_byte(dev, 0x76, 0x07); + + /* SIRQ I (COMS 5/6) use IRQ line 15. + * Positive (not subtractive) address decode. + */ + pci_write_config_byte(dev, 0x44, 0x0f); + + /* SIRQ II disabled */ + pci_write_config_byte(dev, 0x75, 0x0); + + /* On board USB and RTC disabled */ + pci_write_config_word(dev, 0x52, (1 << 14)); + pci_write_config_byte(dev, 0x74, 0x00); + + /* On board IDE disabled */ + pci_write_config_byte(dev, 0x58, 0x00); + + /* Decode 32-bit addresses */ + pci_write_config_byte(dev, 0x5b, 0); + + /* Disable docking IO */ + pci_write_config_word(dev, 0x5c, 0x0000); + + /* Disable modem, enable sound */ + pci_write_config_byte(dev, 0x77, (1 << 6)); + + /* Disable hot-docking mode */ + pci_write_config_byte(dev, 0x7d, 0x00); + } +} + +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AL, 0x1533, ppc7d_fixup_ali1535); + +static int ppc7d_pci_exclude_device(u8 bus, u8 devfn) +{ + /* Early versions of this board were fitted with IBM ALMA + * PCI-VME bridge chips. The PCI config space of these devices + * was not set up correctly and causes PCI scan problems. + */ + if ((bus == 1) && (PCI_SLOT(devfn) == 4) && ppc7d_has_alma) + return PCIBIOS_DEVICE_NOT_FOUND; + + return mv64x60_pci_exclude_device(bus, devfn); +} + +/* This hook is called when each PCI bus is probed. + */ +static void ppc7d_pci_fixup_bus(struct pci_bus *bus) +{ + pr_debug("PCI BUS %hu: %lx/%lx %lx/%lx %lx/%lx %lx/%lx\n", + bus->number, + bus->resource[0] ? bus->resource[0]->start : 0, + bus->resource[0] ? bus->resource[0]->end : 0, + bus->resource[1] ? bus->resource[1]->start : 0, + bus->resource[1] ? bus->resource[1]->end : 0, + bus->resource[2] ? bus->resource[2]->start : 0, + bus->resource[2] ? bus->resource[2]->end : 0, + bus->resource[3] ? bus->resource[3]->start : 0, + bus->resource[3] ? bus->resource[3]->end : 0); + + if ((bus->number == 1) && (bus->resource[2] != NULL)) { + /* Hide PCI window 2 of Bus 1 which is used only to + * map legacy ISA memory space. + */ + bus->resource[2]->start = 0; + bus->resource[2]->end = 0; + bus->resource[2]->flags = 0; + } +} + +/***************************************************************************** + * Board device setup code + *****************************************************************************/ + +void __init ppc7d_setup_peripherals(void) +{ + u32 val32; + + /* Set up windows for boot CS */ + mv64x60_set_32bit_window(&bh, MV64x60_CPU2BOOT_WIN, + PPC7D_BOOT_WINDOW_BASE, PPC7D_BOOT_WINDOW_SIZE, + 0); + bh.ci->enable_window_32bit(&bh, MV64x60_CPU2BOOT_WIN); + + /* Boot firmware configures the following DevCS addresses. + * DevCS0 - board control/status + * DevCS1 - test registers + * DevCS2 - AFIX port/address registers (for identifying) + * DevCS3 - FLASH + * + * We don't use DevCS0, DevCS1. + */ + val32 = mv64x60_read(&bh, MV64360_CPU_BAR_ENABLE); + val32 |= ((1 << 4) | (1 << 5)); + mv64x60_write(&bh, MV64360_CPU_BAR_ENABLE, val32); + mv64x60_write(&bh, MV64x60_CPU2DEV_0_BASE, 0); + mv64x60_write(&bh, MV64x60_CPU2DEV_0_SIZE, 0); + mv64x60_write(&bh, MV64x60_CPU2DEV_1_BASE, 0); + mv64x60_write(&bh, MV64x60_CPU2DEV_1_SIZE, 0); + + mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_2_WIN, + PPC7D_AFIX_REG_BASE, PPC7D_AFIX_REG_SIZE, 0); + bh.ci->enable_window_32bit(&bh, MV64x60_CPU2DEV_2_WIN); + + mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_3_WIN, + PPC7D_FLASH_BASE, PPC7D_FLASH_SIZE_ACTUAL, 0); + bh.ci->enable_window_32bit(&bh, MV64x60_CPU2DEV_3_WIN); + + mv64x60_set_32bit_window(&bh, MV64x60_CPU2SRAM_WIN, + PPC7D_INTERNAL_SRAM_BASE, MV64360_SRAM_SIZE, + 0); + bh.ci->enable_window_32bit(&bh, MV64x60_CPU2SRAM_WIN); + + /* Set up Enet->SRAM window */ + mv64x60_set_32bit_window(&bh, MV64x60_ENET2MEM_4_WIN, + PPC7D_INTERNAL_SRAM_BASE, MV64360_SRAM_SIZE, + 0x2); + bh.ci->enable_window_32bit(&bh, MV64x60_ENET2MEM_4_WIN); + + /* Give enet r/w access to memory region */ + val32 = mv64x60_read(&bh, MV64360_ENET2MEM_ACC_PROT_0); + val32 |= (0x3 << (4 << 1)); + mv64x60_write(&bh, MV64360_ENET2MEM_ACC_PROT_0, val32); + val32 = mv64x60_read(&bh, MV64360_ENET2MEM_ACC_PROT_1); + val32 |= (0x3 << (4 << 1)); + mv64x60_write(&bh, MV64360_ENET2MEM_ACC_PROT_1, val32); + val32 = mv64x60_read(&bh, MV64360_ENET2MEM_ACC_PROT_2); + val32 |= (0x3 << (4 << 1)); + mv64x60_write(&bh, MV64360_ENET2MEM_ACC_PROT_2, val32); + + val32 = mv64x60_read(&bh, MV64x60_TIMR_CNTR_0_3_CNTL); + val32 &= ~((1 << 0) | (1 << 8) | (1 << 16) | (1 << 24)); + mv64x60_write(&bh, MV64x60_TIMR_CNTR_0_3_CNTL, val32); + + /* Enumerate pci bus. + * + * We scan PCI#0 first (the bus with the HB8 and other + * on-board peripherals). We must configure the 64360 before + * each scan, according to the bus number assignments. Busses + * are assigned incrementally, starting at 0. PCI#0 is + * usually assigned bus#0, the secondary side of the HB8 gets + * bus#1 and PCI#1 (second PMC site) gets bus#2. However, if + * any PMC card has a PCI bridge, these bus assignments will + * change. + */ + + /* Turn off PCI retries */ + val32 = mv64x60_read(&bh, MV64x60_CPU_CONFIG); + val32 |= (1 << 17); + mv64x60_write(&bh, MV64x60_CPU_CONFIG, val32); + + /* Scan PCI#0 */ + mv64x60_set_bus(&bh, 0, 0); + bh.hose_a->first_busno = 0; + bh.hose_a->last_busno = 0xff; + bh.hose_a->last_busno = pciauto_bus_scan(bh.hose_a, 0); + printk(KERN_INFO "PCI#0: first=%d last=%d\n", + bh.hose_a->first_busno, bh.hose_a->last_busno); + + /* Scan PCI#1 */ + bh.hose_b->first_busno = bh.hose_a->last_busno + 1; + mv64x60_set_bus(&bh, 1, bh.hose_b->first_busno); + bh.hose_b->last_busno = 0xff; + bh.hose_b->last_busno = pciauto_bus_scan(bh.hose_b, + bh.hose_b->first_busno); + printk(KERN_INFO "PCI#1: first=%d last=%d\n", + bh.hose_b->first_busno, bh.hose_b->last_busno); + + /* Turn on PCI retries */ + val32 = mv64x60_read(&bh, MV64x60_CPU_CONFIG); + val32 &= ~(1 << 17); + mv64x60_write(&bh, MV64x60_CPU_CONFIG, val32); + + /* Setup interrupts */ + ppc7d_intr_setup(); +} + +static void __init ppc7d_setup_bridge(void) +{ + struct mv64x60_setup_info si; + int i; + u32 temp; + + mv64360_irq_base = 16; /* first 16 intrs are 2 x 8259's */ + + memset(&si, 0, sizeof(si)); + + si.phys_reg_base = CONFIG_MV64X60_NEW_BASE; + + si.pci_0.enable_bus = 1; + si.pci_0.pci_io.cpu_base = PPC7D_PCI0_IO_START_PROC_ADDR; + si.pci_0.pci_io.pci_base_hi = 0; + si.pci_0.pci_io.pci_base_lo = PPC7D_PCI0_IO_START_PCI_ADDR; + si.pci_0.pci_io.size = PPC7D_PCI0_IO_SIZE; + si.pci_0.pci_io.swap = MV64x60_CPU2PCI_SWAP_NONE; + si.pci_0.pci_mem[0].cpu_base = PPC7D_PCI0_MEM0_START_PROC_ADDR; + si.pci_0.pci_mem[0].pci_base_hi = PPC7D_PCI0_MEM0_START_PCI_HI_ADDR; + si.pci_0.pci_mem[0].pci_base_lo = PPC7D_PCI0_MEM0_START_PCI_LO_ADDR; + si.pci_0.pci_mem[0].size = PPC7D_PCI0_MEM0_SIZE; + si.pci_0.pci_mem[0].swap = MV64x60_CPU2PCI_SWAP_NONE; + si.pci_0.pci_mem[1].cpu_base = PPC7D_PCI0_MEM1_START_PROC_ADDR; + si.pci_0.pci_mem[1].pci_base_hi = PPC7D_PCI0_MEM1_START_PCI_HI_ADDR; + si.pci_0.pci_mem[1].pci_base_lo = PPC7D_PCI0_MEM1_START_PCI_LO_ADDR; + si.pci_0.pci_mem[1].size = PPC7D_PCI0_MEM1_SIZE; + si.pci_0.pci_mem[1].swap = MV64x60_CPU2PCI_SWAP_NONE; + si.pci_0.pci_cmd_bits = 0; + si.pci_0.latency_timer = 0x80; + + si.pci_1.enable_bus = 1; + si.pci_1.pci_io.cpu_base = PPC7D_PCI1_IO_START_PROC_ADDR; + si.pci_1.pci_io.pci_base_hi = 0; + si.pci_1.pci_io.pci_base_lo = PPC7D_PCI1_IO_START_PCI_ADDR; + si.pci_1.pci_io.size = PPC7D_PCI1_IO_SIZE; + si.pci_1.pci_io.swap = MV64x60_CPU2PCI_SWAP_NONE; + si.pci_1.pci_mem[0].cpu_base = PPC7D_PCI1_MEM0_START_PROC_ADDR; + si.pci_1.pci_mem[0].pci_base_hi = PPC7D_PCI1_MEM0_START_PCI_HI_ADDR; + si.pci_1.pci_mem[0].pci_base_lo = PPC7D_PCI1_MEM0_START_PCI_LO_ADDR; + si.pci_1.pci_mem[0].size = PPC7D_PCI1_MEM0_SIZE; + si.pci_1.pci_mem[0].swap = MV64x60_CPU2PCI_SWAP_NONE; + si.pci_1.pci_mem[1].cpu_base = PPC7D_PCI1_MEM1_START_PROC_ADDR; + si.pci_1.pci_mem[1].pci_base_hi = PPC7D_PCI1_MEM1_START_PCI_HI_ADDR; + si.pci_1.pci_mem[1].pci_base_lo = PPC7D_PCI1_MEM1_START_PCI_LO_ADDR; + si.pci_1.pci_mem[1].size = PPC7D_PCI1_MEM1_SIZE; + si.pci_1.pci_mem[1].swap = MV64x60_CPU2PCI_SWAP_NONE; + si.pci_1.pci_cmd_bits = 0; + si.pci_1.latency_timer = 0x80; + + /* Don't clear the SRAM window since we use it for debug */ + si.window_preserve_mask_32_lo = (1 << MV64x60_CPU2SRAM_WIN); + + printk(KERN_INFO "PCI: MV64360 PCI#0 IO at %x, size %x\n", + si.pci_0.pci_io.cpu_base, si.pci_0.pci_io.size); + printk(KERN_INFO "PCI: MV64360 PCI#1 IO at %x, size %x\n", + si.pci_1.pci_io.cpu_base, si.pci_1.pci_io.size); + + for (i = 0; i < MV64x60_CPU2MEM_WINDOWS; i++) { +#if defined(CONFIG_NOT_COHERENT_CACHE) + si.cpu_prot_options[i] = 0; + si.enet_options[i] = MV64360_ENET2MEM_SNOOP_NONE; + si.mpsc_options[i] = MV64360_MPSC2MEM_SNOOP_NONE; + si.idma_options[i] = MV64360_IDMA2MEM_SNOOP_NONE; + + si.pci_0.acc_cntl_options[i] = + MV64360_PCI_ACC_CNTL_SNOOP_NONE | + MV64360_PCI_ACC_CNTL_SWAP_NONE | + MV64360_PCI_ACC_CNTL_MBURST_128_BYTES | + MV64360_PCI_ACC_CNTL_RDSIZE_256_BYTES; + + si.pci_1.acc_cntl_options[i] = + MV64360_PCI_ACC_CNTL_SNOOP_NONE | + MV64360_PCI_ACC_CNTL_SWAP_NONE | + MV64360_PCI_ACC_CNTL_MBURST_128_BYTES | + MV64360_PCI_ACC_CNTL_RDSIZE_256_BYTES; +#else + si.cpu_prot_options[i] = 0; + /* All PPC7D hardware uses B0 or newer MV64360 silicon which + * does not have snoop bugs. + */ + si.enet_options[i] = MV64360_ENET2MEM_SNOOP_WB; + si.mpsc_options[i] = MV64360_MPSC2MEM_SNOOP_WB; + si.idma_options[i] = MV64360_IDMA2MEM_SNOOP_WB; + + si.pci_0.acc_cntl_options[i] = + MV64360_PCI_ACC_CNTL_SNOOP_WB | + MV64360_PCI_ACC_CNTL_SWAP_NONE | + MV64360_PCI_ACC_CNTL_MBURST_32_BYTES | + MV64360_PCI_ACC_CNTL_RDSIZE_32_BYTES; + + si.pci_1.acc_cntl_options[i] = + MV64360_PCI_ACC_CNTL_SNOOP_WB | + MV64360_PCI_ACC_CNTL_SWAP_NONE | + MV64360_PCI_ACC_CNTL_MBURST_32_BYTES | + MV64360_PCI_ACC_CNTL_RDSIZE_32_BYTES; +#endif + } + + /* Lookup PCI host bridges */ + if (mv64x60_init(&bh, &si)) + printk(KERN_ERR "MV64360 initialization failed.\n"); + + pr_debug("MV64360 regs @ %lx/%p\n", bh.p_base, bh.v_base); + + /* Enable WB Cache coherency on SRAM */ + temp = mv64x60_read(&bh, MV64360_SRAM_CONFIG); + pr_debug("SRAM_CONFIG: %x\n", temp); +#if defined(CONFIG_NOT_COHERENT_CACHE) + mv64x60_write(&bh, MV64360_SRAM_CONFIG, temp & ~0x2); +#else + mv64x60_write(&bh, MV64360_SRAM_CONFIG, temp | 0x2); +#endif + /* If system operates with internal bus arbiter (CPU master + * control bit8) clear AACK Delay bit [25] in CPU + * configuration register. + */ + temp = mv64x60_read(&bh, MV64x60_CPU_MASTER_CNTL); + if (temp & (1 << 8)) { + temp = mv64x60_read(&bh, MV64x60_CPU_CONFIG); + mv64x60_write(&bh, MV64x60_CPU_CONFIG, (temp & ~(1 << 25))); + } + + /* Data and address parity is enabled */ + temp = mv64x60_read(&bh, MV64x60_CPU_CONFIG); + mv64x60_write(&bh, MV64x60_CPU_CONFIG, + (temp | (1 << 26) | (1 << 19))); + + pci_dram_offset = 0; /* sys mem at same addr on PCI & cpu bus */ + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = ppc7d_map_irq; + ppc_md.pci_exclude_device = ppc7d_pci_exclude_device; + + mv64x60_set_bus(&bh, 0, 0); + bh.hose_a->first_busno = 0; + bh.hose_a->last_busno = 0xff; + bh.hose_a->mem_space.start = PPC7D_PCI0_MEM0_START_PCI_LO_ADDR; + bh.hose_a->mem_space.end = + PPC7D_PCI0_MEM0_START_PCI_LO_ADDR + PPC7D_PCI0_MEM0_SIZE; + + /* These will be set later, as a result of PCI0 scan */ + bh.hose_b->first_busno = 0; + bh.hose_b->last_busno = 0xff; + bh.hose_b->mem_space.start = PPC7D_PCI1_MEM0_START_PCI_LO_ADDR; + bh.hose_b->mem_space.end = + PPC7D_PCI1_MEM0_START_PCI_LO_ADDR + PPC7D_PCI1_MEM0_SIZE; + + pr_debug("MV64360: PCI#0 IO decode %08x/%08x IO remap %08x\n", + mv64x60_read(&bh, 0x48), mv64x60_read(&bh, 0x50), + mv64x60_read(&bh, 0xf0)); +} + +static void __init ppc7d_setup_arch(void) +{ + int port; + + loops_per_jiffy = 100000000 / HZ; + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = Root_NFS; +#else + ROOT_DEV = Root_HDA1; +#endif + + if ((cur_cpu_spec[0]->cpu_features & CPU_FTR_SPEC7450) || + (cur_cpu_spec[0]->cpu_features & CPU_FTR_L3CR)) + /* 745x is different. We only want to pass along enable. */ + _set_L2CR(L2CR_L2E); + else if (cur_cpu_spec[0]->cpu_features & CPU_FTR_L2CR) + /* All modules have 1MB of L2. We also assume that an + * L2 divisor of 3 will work. + */ + _set_L2CR(L2CR_L2E | L2CR_L2SIZ_1MB | L2CR_L2CLK_DIV3 + | L2CR_L2RAM_PIPE | L2CR_L2OH_1_0 | L2CR_L2DF); + + if (cur_cpu_spec[0]->cpu_features & CPU_FTR_L3CR) + /* No L3 cache */ + _set_L3CR(0); + +#ifdef CONFIG_DUMMY_CONSOLE + conswitchp = &dummy_con; +#endif + + /* Lookup PCI host bridges */ + if (ppc_md.progress) + ppc_md.progress("ppc7d_setup_arch: calling setup_bridge", 0); + + ppc7d_setup_bridge(); + ppc7d_setup_peripherals(); + + /* Disable ethernet. It might have been setup by the bootrom */ + for (port = 0; port < 3; port++) + mv64x60_write(&bh, MV643XX_ETH_RECEIVE_QUEUE_COMMAND_REG(port), + 0x0000ff00); + + /* Clear queue pointers to ensure they are all initialized, + * otherwise since queues 1-7 are unused, they have random + * pointers which look strange in register dumps. Don't bother + * with queue 0 since it will be initialized later. + */ + for (port = 0; port < 3; port++) { + mv64x60_write(&bh, + MV643XX_ETH_RX_CURRENT_QUEUE_DESC_PTR_1(port), + 0x00000000); + mv64x60_write(&bh, + MV643XX_ETH_RX_CURRENT_QUEUE_DESC_PTR_2(port), + 0x00000000); + mv64x60_write(&bh, + MV643XX_ETH_RX_CURRENT_QUEUE_DESC_PTR_3(port), + 0x00000000); + mv64x60_write(&bh, + MV643XX_ETH_RX_CURRENT_QUEUE_DESC_PTR_4(port), + 0x00000000); + mv64x60_write(&bh, + MV643XX_ETH_RX_CURRENT_QUEUE_DESC_PTR_5(port), + 0x00000000); + mv64x60_write(&bh, + MV643XX_ETH_RX_CURRENT_QUEUE_DESC_PTR_6(port), + 0x00000000); + mv64x60_write(&bh, + MV643XX_ETH_RX_CURRENT_QUEUE_DESC_PTR_7(port), + 0x00000000); + } + + printk(KERN_INFO "Radstone Technology PPC7D\n"); + if (ppc_md.progress) + ppc_md.progress("ppc7d_setup_arch: exit", 0); +} + +/* This kernel command line parameter can be used to have the target + * wait for a JTAG debugger to attach. Of course, a JTAG debugger + * with hardware breakpoint support can have the target stop at any + * location during init, but this is a convenience feature that makes + * it easier in the common case of loading the code using the ppcboot + * bootloader.. + */ +static unsigned long ppc7d_wait_debugger; + +static int __init ppc7d_waitdbg(char *str) +{ + ppc7d_wait_debugger = 1; + return 1; +} + +__setup("waitdbg", ppc7d_waitdbg); + +/* Second phase board init, called after other (architecture common) + * low-level services have been initialized. + */ +static void ppc7d_init2(void) +{ + unsigned long flags; + u32 data; + u8 data8; + + pr_debug("%s: enter\n", __FUNCTION__); + + /* Wait for debugger? */ + if (ppc7d_wait_debugger) { + printk("Waiting for debugger...\n"); + + while (readl(&ppc7d_wait_debugger)) ; + } + + /* Hook up i8259 interrupt which is connected to GPP28 */ + request_irq(mv64360_irq_base + MV64x60_IRQ_GPP28, ppc7d_i8259_intr, + SA_INTERRUPT, "I8259 (GPP28) interrupt", (void *)0); + + /* Configure MPP16 as watchdog NMI, MPP17 as watchdog WDE */ + spin_lock_irqsave(&mv64x60_lock, flags); + data = mv64x60_read(&bh, MV64x60_MPP_CNTL_2); + data &= ~(0x0000000f << 0); + data |= (0x00000004 << 0); + data &= ~(0x0000000f << 4); + data |= (0x00000004 << 4); + mv64x60_write(&bh, MV64x60_MPP_CNTL_2, data); + spin_unlock_irqrestore(&mv64x60_lock, flags); + + /* All LEDs off */ + data8 = inb(PPC7D_CPLD_LEDS); + data8 &= ~0x08; + data8 |= 0x07; + outb(data8, PPC7D_CPLD_LEDS); + + pr_debug("%s: exit\n", __FUNCTION__); +} + +/* Called from machine_init(), early, before any of the __init functions + * have run. We must init software-configurable pins before other functions + * such as interrupt controllers are initialised. + */ +void __init platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + u8 val8; + u8 rev_num; + + /* Map 0xe0000000-0xffffffff early because we need access to SRAM + * and the ISA memory space (for serial port) here. This mapping + * is redone properly in ppc7d_map_io() later. + */ + mtspr(SPRN_DBAT3U, 0xe0003fff); + mtspr(SPRN_DBAT3L, 0xe000002a); + + /* + * Zero SRAM. Note that this generates parity errors on + * internal data path in SRAM if it's first time accessing it + * after reset. + * + * We do this ASAP to avoid parity errors when reading + * uninitialized SRAM. + */ + memset((void *)PPC7D_INTERNAL_SRAM_BASE, 0, MV64360_SRAM_SIZE); + + pr_debug("platform_init: r3-r7: %lx %lx %lx %lx %lx\n", + r3, r4, r5, r6, r7); + + parse_bootinfo(find_bootinfo()); + + /* ASSUMPTION: If both r3 (bd_t pointer) and r6 (cmdline pointer) + * are non-zero, then we should use the board info from the bd_t + * structure and the cmdline pointed to by r6 instead of the + * information from birecs, if any. Otherwise, use the information + * from birecs as discovered by the preceeding call to + * parse_bootinfo(). This rule should work with both PPCBoot, which + * uses a bd_t board info structure, and the kernel boot wrapper, + * which uses birecs. + */ + if (r3 && r6) { + bd_t *bp = (bd_t *) __res; + + /* copy board info structure */ + memcpy((void *)__res, (void *)(r3 + KERNELBASE), sizeof(bd_t)); + /* copy command line */ + *(char *)(r7 + KERNELBASE) = 0; + strcpy(cmd_line, (char *)(r6 + KERNELBASE)); + + printk(KERN_INFO "Board info data:-\n"); + printk(KERN_INFO " Internal freq: %lu MHz, bus freq: %lu MHz\n", + bp->bi_intfreq, bp->bi_busfreq); + printk(KERN_INFO " Memory: %lx, size %lx\n", bp->bi_memstart, + bp->bi_memsize); + printk(KERN_INFO " Console baudrate: %lu\n", bp->bi_baudrate); + printk(KERN_INFO " Ethernet address: " + "%02x:%02x:%02x:%02x:%02x:%02x\n", + bp->bi_enetaddr[0], bp->bi_enetaddr[1], + bp->bi_enetaddr[2], bp->bi_enetaddr[3], + bp->bi_enetaddr[4], bp->bi_enetaddr[5]); + } +#ifdef CONFIG_BLK_DEV_INITRD + /* take care of initrd if we have one */ + if (r4) { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + printk(KERN_INFO "INITRD @ %lx/%lx\n", initrd_start, initrd_end); + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + /* Map in board regs, etc. */ + isa_io_base = 0xe8000000; + isa_mem_base = 0xe8000000; + pci_dram_offset = 0x00000000; + ISA_DMA_THRESHOLD = 0x00ffffff; + DMA_MODE_READ = 0x44; + DMA_MODE_WRITE = 0x48; + + ppc_md.setup_arch = ppc7d_setup_arch; + ppc_md.init = ppc7d_init2; + ppc_md.show_cpuinfo = ppc7d_show_cpuinfo; + ppc_md.irq_canonicalize = ppc7d_irq_canonicalize; + ppc_md.init_IRQ = ppc7d_init_irq; + ppc_md.get_irq = ppc7d_get_irq; + + ppc_md.restart = ppc7d_restart; + ppc_md.power_off = ppc7d_power_off; + ppc_md.halt = ppc7d_halt; + + ppc_md.find_end_of_memory = ppc7d_find_end_of_memory; + ppc_md.setup_io_mappings = ppc7d_map_io; + + ppc_md.time_init = NULL; + ppc_md.set_rtc_time = NULL; + ppc_md.get_rtc_time = NULL; + ppc_md.calibrate_decr = ppc7d_calibrate_decr; + ppc_md.nvram_read_val = NULL; + ppc_md.nvram_write_val = NULL; + + ppc_md.heartbeat = ppc7d_heartbeat; + ppc_md.heartbeat_reset = HZ; + ppc_md.heartbeat_count = ppc_md.heartbeat_reset; + + ppc_md.pcibios_fixup_bus = ppc7d_pci_fixup_bus; + +#if defined(CONFIG_SERIAL_MPSC) || defined(CONFIG_MV643XX_ETH) || \ + defined(CONFIG_I2C_MV64XXX) + platform_notify = ppc7d_platform_notify; +#endif + +#ifdef CONFIG_SERIAL_MPSC + /* On PPC7D, we must configure MPSC support via CPLD control + * registers. + */ + outb(PPC7D_CPLD_RTS_COM4_SCLK | + PPC7D_CPLD_RTS_COM56_ENABLED, PPC7D_CPLD_RTS); + outb(PPC7D_CPLD_COMS_COM3_TCLKEN | + PPC7D_CPLD_COMS_COM3_TXEN | + PPC7D_CPLD_COMS_COM4_TCLKEN | + PPC7D_CPLD_COMS_COM4_TXEN, PPC7D_CPLD_COMS); +#endif /* CONFIG_SERIAL_MPSC */ + +#if defined(CONFIG_KGDB) || defined(CONFIG_SERIAL_TEXT_DEBUG) + ppc7d_early_serial_map(); +#ifdef CONFIG_SERIAL_TEXT_DEBUG +#if defined(CONFIG_SERIAL_MPSC_CONSOLE) + ppc_md.progress = mv64x60_mpsc_progress; +#elif defined(CONFIG_SERIAL_8250) + ppc_md.progress = gen550_progress; +#else +#error CONFIG_KGDB || CONFIG_SERIAL_TEXT_DEBUG has no supported CONFIG_SERIAL_XXX +#endif /* CONFIG_SERIAL_8250 */ +#endif /* CONFIG_SERIAL_TEXT_DEBUG */ +#endif /* CONFIG_KGDB || CONFIG_SERIAL_TEXT_DEBUG */ + + /* Enable write access to user flash. This is necessary for + * flash probe. + */ + val8 = readb((void *)isa_io_base + PPC7D_CPLD_SW_FLASH_WRITE_PROTECT); + writeb(val8 | (PPC7D_CPLD_SW_FLASH_WRPROT_ENABLED & + PPC7D_CPLD_SW_FLASH_WRPROT_USER_MASK), + (void *)isa_io_base + PPC7D_CPLD_SW_FLASH_WRITE_PROTECT); + + /* Determine if this board has IBM ALMA VME devices */ + val8 = readb((void *)isa_io_base + PPC7D_CPLD_BOARD_REVISION); + rev_num = (val8 & PPC7D_CPLD_BOARD_REVISION_NUMBER_MASK) >> 5; + if (rev_num <= 1) + ppc7d_has_alma = 1; + +#ifdef DEBUG + console_printk[0] = 8; +#endif +} diff --git a/arch/ppc/platforms/radstone_ppc7d.h b/arch/ppc/platforms/radstone_ppc7d.h new file mode 100644 index 00000000000..4546fff2b0c --- /dev/null +++ b/arch/ppc/platforms/radstone_ppc7d.h @@ -0,0 +1,434 @@ +/* + * arch/ppc/platforms/radstone_ppc7d.h + * + * Board definitions for the Radstone PPC7D boards. + * + * Author: James Chapman + * + * Based on code done by Rabeeh Khoury - rabeeh@galileo.co.il + * Based on code done by - Mark A. Greer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/* + * The MV64360 has 2 PCI buses each with 1 window from the CPU bus to + * PCI I/O space and 4 windows from the CPU bus to PCI MEM space. + * We'll only use one PCI MEM window on each PCI bus. + * + * This is the CPU physical memory map (windows must be at least 1MB + * and start on a boundary that is a multiple of the window size): + * + * 0xff800000-0xffffffff - Boot window + * 0xff000000-0xff000fff - AFIX registers (DevCS2) + * 0xfef00000-0xfef0ffff - Internal MV64x60 registers + * 0xfef40000-0xfef7ffff - Internal SRAM + * 0xfef00000-0xfef0ffff - MV64360 Registers + * 0x70000000-0x7fffffff - soldered flash (DevCS3) + * 0xe8000000-0xe9ffffff - PCI I/O + * 0x80000000-0xbfffffff - PCI MEM + */ + +#ifndef __PPC_PLATFORMS_PPC7D_H +#define __PPC_PLATFORMS_PPC7D_H + +#include + +/***************************************************************************** + * CPU Physical Memory Map setup. + *****************************************************************************/ + +#define PPC7D_BOOT_WINDOW_BASE 0xff800000 +#define PPC7D_AFIX_REG_BASE 0xff000000 +#define PPC7D_INTERNAL_SRAM_BASE 0xfef40000 +#define PPC7D_FLASH_BASE 0x70000000 + +#define PPC7D_BOOT_WINDOW_SIZE_ACTUAL 0x00800000 /* 8MB */ +#define PPC7D_FLASH_SIZE_ACTUAL 0x10000000 /* 256MB */ + +#define PPC7D_BOOT_WINDOW_SIZE max(MV64360_WINDOW_SIZE_MIN, \ + PPC7D_BOOT_WINDOW_SIZE_ACTUAL) +#define PPC7D_FLASH_SIZE max(MV64360_WINDOW_SIZE_MIN, \ + PPC7D_FLASH_SIZE_ACTUAL) +#define PPC7D_AFIX_REG_SIZE max(MV64360_WINDOW_SIZE_MIN, 0xff) + + +#define PPC7D_PCI0_MEM0_START_PROC_ADDR 0x80000000UL +#define PPC7D_PCI0_MEM0_START_PCI_HI_ADDR 0x00000000UL +#define PPC7D_PCI0_MEM0_START_PCI_LO_ADDR 0x80000000UL +#define PPC7D_PCI0_MEM0_SIZE 0x20000000UL +#define PPC7D_PCI0_MEM1_START_PROC_ADDR 0xe8010000UL +#define PPC7D_PCI0_MEM1_START_PCI_HI_ADDR 0x00000000UL +#define PPC7D_PCI0_MEM1_START_PCI_LO_ADDR 0x00000000UL +#define PPC7D_PCI0_MEM1_SIZE 0x000f0000UL +#define PPC7D_PCI0_IO_START_PROC_ADDR 0xe8000000UL +#define PPC7D_PCI0_IO_START_PCI_ADDR 0x00000000UL +#define PPC7D_PCI0_IO_SIZE 0x00010000UL + +#define PPC7D_PCI1_MEM0_START_PROC_ADDR 0xa0000000UL +#define PPC7D_PCI1_MEM0_START_PCI_HI_ADDR 0x00000000UL +#define PPC7D_PCI1_MEM0_START_PCI_LO_ADDR 0xa0000000UL +#define PPC7D_PCI1_MEM0_SIZE 0x20000000UL +#define PPC7D_PCI1_MEM1_START_PROC_ADDR 0xe9800000UL +#define PPC7D_PCI1_MEM1_START_PCI_HI_ADDR 0x00000000UL +#define PPC7D_PCI1_MEM1_START_PCI_LO_ADDR 0x00000000UL +#define PPC7D_PCI1_MEM1_SIZE 0x00800000UL +#define PPC7D_PCI1_IO_START_PROC_ADDR 0xe9000000UL +#define PPC7D_PCI1_IO_START_PCI_ADDR 0x00000000UL +#define PPC7D_PCI1_IO_SIZE 0x00010000UL + +#define PPC7D_DEFAULT_BAUD 9600 +#define PPC7D_MPSC_CLK_SRC 8 /* TCLK */ +#define PPC7D_MPSC_CLK_FREQ 133333333 /* 133.3333... MHz */ + +#define PPC7D_ETH0_PHY_ADDR 8 +#define PPC7D_ETH1_PHY_ADDR 9 +#define PPC7D_ETH2_PHY_ADDR 0 + +#define PPC7D_ETH_TX_QUEUE_SIZE 400 +#define PPC7D_ETH_RX_QUEUE_SIZE 400 + +#define PPC7D_ETH_PORT_CONFIG_VALUE \ + MV64340_ETH_UNICAST_NORMAL_MODE | \ + MV64340_ETH_DEFAULT_RX_QUEUE_0 | \ + MV64340_ETH_DEFAULT_RX_ARP_QUEUE_0 | \ + MV64340_ETH_RECEIVE_BC_IF_NOT_IP_OR_ARP | \ + MV64340_ETH_RECEIVE_BC_IF_IP | \ + MV64340_ETH_RECEIVE_BC_IF_ARP | \ + MV64340_ETH_CAPTURE_TCP_FRAMES_DIS | \ + MV64340_ETH_CAPTURE_UDP_FRAMES_DIS | \ + MV64340_ETH_DEFAULT_RX_TCP_QUEUE_0 | \ + MV64340_ETH_DEFAULT_RX_UDP_QUEUE_0 | \ + MV64340_ETH_DEFAULT_RX_BPDU_QUEUE_0 + +#define PPC7D_ETH_PORT_CONFIG_EXTEND_VALUE \ + MV64340_ETH_SPAN_BPDU_PACKETS_AS_NORMAL | \ + MV64340_ETH_PARTITION_DISABLE + +#define GT_ETH_IPG_INT_RX(value) \ + ((value & 0x3fff) << 8) + +#define PPC7D_ETH_PORT_SDMA_CONFIG_VALUE \ + MV64340_ETH_RX_BURST_SIZE_4_64BIT | \ + GT_ETH_IPG_INT_RX(0) | \ + MV64340_ETH_TX_BURST_SIZE_4_64BIT + +#define PPC7D_ETH_PORT_SERIAL_CONTROL_VALUE \ + MV64340_ETH_ENABLE_AUTO_NEG_FOR_DUPLX | \ + MV64340_ETH_DISABLE_AUTO_NEG_FOR_FLOW_CTRL | \ + MV64340_ETH_ADV_SYMMETRIC_FLOW_CTRL | \ + MV64340_ETH_FORCE_FC_MODE_NO_PAUSE_DIS_TX | \ + MV64340_ETH_FORCE_BP_MODE_NO_JAM | \ + (1 << 9) | \ + MV64340_ETH_DO_NOT_FORCE_LINK_FAIL | \ + MV64340_ETH_RETRANSMIT_16_ATTEMPTS | \ + MV64340_ETH_ENABLE_AUTO_NEG_SPEED_GMII | \ + MV64340_ETH_DTE_ADV_0 | \ + MV64340_ETH_DISABLE_AUTO_NEG_BYPASS | \ + MV64340_ETH_AUTO_NEG_NO_CHANGE | \ + MV64340_ETH_MAX_RX_PACKET_9700BYTE | \ + MV64340_ETH_CLR_EXT_LOOPBACK | \ + MV64340_ETH_SET_FULL_DUPLEX_MODE | \ + MV64340_ETH_ENABLE_FLOW_CTRL_TX_RX_IN_FULL_DUPLEX + +/***************************************************************************** + * Serial defines. + *****************************************************************************/ + +#define PPC7D_SERIAL_0 0xe80003f8 +#define PPC7D_SERIAL_1 0xe80002f8 + +#define RS_TABLE_SIZE 2 + +/* Rate for the 1.8432 Mhz clock for the onboard serial chip */ +#define UART_CLK 1843200 +#define BASE_BAUD ( UART_CLK / 16 ) + +#ifdef CONFIG_SERIAL_DETECT_IRQ +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_AUTO_IRQ) +#else +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF) +#endif + +#define STD_SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, PPC7D_SERIAL_0, 4, STD_COM_FLAGS, /* ttyS0 */ \ + iomem_base: (u8 *)PPC7D_SERIAL_0, \ + io_type: SERIAL_IO_MEM, }, \ + { 0, BASE_BAUD, PPC7D_SERIAL_1, 3, STD_COM_FLAGS, /* ttyS1 */ \ + iomem_base: (u8 *)PPC7D_SERIAL_1, \ + io_type: SERIAL_IO_MEM }, + +#define SERIAL_PORT_DFNS \ + STD_SERIAL_PORT_DFNS + +/***************************************************************************** + * CPLD defines. + * + * Register map:- + * + * 0000 to 000F South Bridge DMA 1 Control + * 0020 and 0021 South Bridge Interrupt 1 Control + * 0040 to 0043 South Bridge Counter Control + * 0060 Keyboard + * 0061 South Bridge NMI Status and Control + * 0064 Keyboard + * 0071 and 0072 RTC R/W + * 0078 to 007B South Bridge BIOS Timer + * 0080 to 0090 South Bridge DMA Pages + * 00A0 and 00A1 South Bridge Interrupt 2 Control + * 00C0 to 00DE South Bridge DMA 2 Control + * 02E8 to 02EF COM6 R/W + * 02F8 to 02FF South Bridge COM2 R/W + * 03E8 to 03EF COM5 R/W + * 03F8 to 03FF South Bridge COM1 R/W + * 040A South Bridge DMA Scatter/Gather RO + * 040B DMA 1 Extended Mode WO + * 0410 to 043F South Bridge DMA Scatter/Gather + * 0481 to 048B South Bridge DMA High Pages + * 04D0 and 04D1 South Bridge Edge/Level Control + * 04D6 DMA 2 Extended Mode WO + * 0804 Memory Configuration RO + * 0806 Memory Configuration Extend RO + * 0808 SCSI Activity LED R/W + * 080C Equipment Present 1 RO + * 080E Equipment Present 2 RO + * 0810 Equipment Present 3 RO + * 0812 Equipment Present 4 RO + * 0818 Key Lock RO + * 0820 LEDS R/W + * 0824 COMs R/W + * 0826 RTS R/W + * 0828 Reset R/W + * 082C Watchdog Trig R/W + * 082E Interrupt R/W + * 0830 Interrupt Status RO + * 0832 PCI configuration RO + * 0854 Board Revision RO + * 0858 Extended ID RO + * 0864 ID Link RO + * 0866 Motherboard Type RO + * 0868 FLASH Write control RO + * 086A Software FLASH write protect R/W + * 086E FLASH Control R/W + *****************************************************************************/ + +#define PPC7D_CPLD_MEM_CONFIG 0x0804 +#define PPC7D_CPLD_MEM_CONFIG_EXTEND 0x0806 +#define PPC7D_CPLD_SCSI_ACTIVITY_LED 0x0808 +#define PPC7D_CPLD_EQUIPMENT_PRESENT_1 0x080C +#define PPC7D_CPLD_EQUIPMENT_PRESENT_2 0x080E +#define PPC7D_CPLD_EQUIPMENT_PRESENT_3 0x0810 +#define PPC7D_CPLD_EQUIPMENT_PRESENT_4 0x0812 +#define PPC7D_CPLD_KEY_LOCK 0x0818 +#define PPC7D_CPLD_LEDS 0x0820 +#define PPC7D_CPLD_COMS 0x0824 +#define PPC7D_CPLD_RTS 0x0826 +#define PPC7D_CPLD_RESET 0x0828 +#define PPC7D_CPLD_WATCHDOG_TRIG 0x082C +#define PPC7D_CPLD_INTR 0x082E +#define PPC7D_CPLD_INTR_STATUS 0x0830 +#define PPC7D_CPLD_PCI_CONFIG 0x0832 +#define PPC7D_CPLD_BOARD_REVISION 0x0854 +#define PPC7D_CPLD_EXTENDED_ID 0x0858 +#define PPC7D_CPLD_ID_LINK 0x0864 +#define PPC7D_CPLD_MOTHERBOARD_TYPE 0x0866 +#define PPC7D_CPLD_FLASH_WRITE_CNTL 0x0868 +#define PPC7D_CPLD_SW_FLASH_WRITE_PROTECT 0x086A +#define PPC7D_CPLD_FLASH_CNTL 0x086E + +/* MEMORY_CONFIG_EXTEND */ +#define PPC7D_CPLD_SDRAM_BANK_SIZE_MASK 0xc0 +#define PPC7D_CPLD_SDRAM_BANK_SIZE_128M 0 +#define PPC7D_CPLD_SDRAM_BANK_SIZE_256M 0x40 +#define PPC7D_CPLD_SDRAM_BANK_SIZE_512M 0x80 +#define PPC7D_CPLD_SDRAM_BANK_SIZE_1G 0xc0 +#define PPC7D_CPLD_FLASH_DEV_SIZE_MASK 0x03 +#define PPC7D_CPLD_FLASH_BANK_NUM_MASK 0x0c +#define PPC7D_CPLD_FLASH_DEV_SIZE_64M 0 +#define PPC7D_CPLD_FLASH_DEV_SIZE_32M 1 +#define PPC7D_CPLD_FLASH_DEV_SIZE_16M 3 +#define PPC7D_CPLD_FLASH_BANK_NUM_4 0x00 +#define PPC7D_CPLD_FLASH_BANK_NUM_3 0x04 +#define PPC7D_CPLD_FLASH_BANK_NUM_2 0x08 +#define PPC7D_CPLD_FLASH_BANK_NUM_1 0x0c + +/* SCSI_LED */ +#define PPC7D_CPLD_SCSI_ACTIVITY_LED_OFF 0 +#define PPC7D_CPLD_SCSI_ACTIVITY_LED_ON 1 + +/* EQUIPMENT_PRESENT_1 */ +#define PPC7D_CPLD_EQPT_PRES_1_FITTED 0 +#define PPC7D_CPLD_EQPT_PRES_1_PMC2_MASK (0x80 >> 2) +#define PPC7D_CPLD_EQPT_PRES_1_PMC1_MASK (0x80 >> 3) +#define PPC7D_CPLD_EQPT_PRES_1_AFIX_MASK (0x80 >> 4) + +/* EQUIPMENT_PRESENT_2 */ +#define PPC7D_CPLD_EQPT_PRES_2_FITTED !0 +#define PPC7D_CPLD_EQPT_PRES_2_UNIVERSE_MASK (0x80 >> 0) +#define PPC7D_CPLD_EQPT_PRES_2_COM36_MASK (0x80 >> 2) +#define PPC7D_CPLD_EQPT_PRES_2_GIGE_MASK (0x80 >> 3) +#define PPC7D_CPLD_EQPT_PRES_2_DUALGIGE_MASK (0x80 >> 4) + +/* EQUIPMENT_PRESENT_3 */ +#define PPC7D_CPLD_EQPT_PRES_3_PMC2_V_MASK (0x80 >> 3) +#define PPC7D_CPLD_EQPT_PRES_3_PMC2_5V (0 >> 3) +#define PPC7D_CPLD_EQPT_PRES_3_PMC2_3V (0x80 >> 3) +#define PPC7D_CPLD_EQPT_PRES_3_PMC1_V_MASK (0x80 >> 4) +#define PPC7D_CPLD_EQPT_PRES_3_PMC1_5V (0 >> 4) +#define PPC7D_CPLD_EQPT_PRES_3_PMC1_3V (0x80 >> 4) +#define PPC7D_CPLD_EQPT_PRES_3_PMC_POWER_MASK (0x80 >> 5) +#define PPC7D_CPLD_EQPT_PRES_3_PMC_POWER_INTER (0 >> 5) +#define PPC7D_CPLD_EQPT_PRES_3_PMC_POWER_VME (0x80 >> 5) + +/* EQUIPMENT_PRESENT_4 */ +#define PPC7D_CPLD_EQPT_PRES_4_LPT_MASK (0x80 >> 2) +#define PPC7D_CPLD_EQPT_PRES_4_LPT_FITTED (0x80 >> 2) +#define PPC7D_CPLD_EQPT_PRES_4_PS2_USB2_MASK (0xc0 >> 6) +#define PPC7D_CPLD_EQPT_PRES_4_PS2_FITTED (0x40 >> 6) +#define PPC7D_CPLD_EQPT_PRES_4_USB2_FITTED (0x80 >> 6) + +/* CPLD_LEDS */ +#define PPC7D_CPLD_LEDS_ON (!0) +#define PPC7D_CPLD_LEDS_OFF (0) +#define PPC7D_CPLD_LEDS_NVRAM_PAGE_MASK (0xc0 >> 2) +#define PPC7D_CPLD_LEDS_DS201_MASK (0x80 >> 4) +#define PPC7D_CPLD_LEDS_DS219_MASK (0x80 >> 5) +#define PPC7D_CPLD_LEDS_DS220_MASK (0x80 >> 6) +#define PPC7D_CPLD_LEDS_DS221_MASK (0x80 >> 7) + +/* CPLD_COMS */ +#define PPC7D_CPLD_COMS_COM3_TCLKEN (0x80 >> 0) +#define PPC7D_CPLD_COMS_COM3_RTCLKEN (0x80 >> 1) +#define PPC7D_CPLD_COMS_COM3_MODE_MASK (0x80 >> 2) +#define PPC7D_CPLD_COMS_COM3_MODE_RS232 (0) +#define PPC7D_CPLD_COMS_COM3_MODE_RS422 (0x80 >> 2) +#define PPC7D_CPLD_COMS_COM3_TXEN (0x80 >> 3) +#define PPC7D_CPLD_COMS_COM4_TCLKEN (0x80 >> 4) +#define PPC7D_CPLD_COMS_COM4_RTCLKEN (0x80 >> 5) +#define PPC7D_CPLD_COMS_COM4_MODE_MASK (0x80 >> 6) +#define PPC7D_CPLD_COMS_COM4_MODE_RS232 (0) +#define PPC7D_CPLD_COMS_COM4_MODE_RS422 (0x80 >> 6) +#define PPC7D_CPLD_COMS_COM4_TXEN (0x80 >> 7) + +/* CPLD_RTS */ +#define PPC7D_CPLD_RTS_COM36_LOOPBACK (0x80 >> 0) +#define PPC7D_CPLD_RTS_COM4_SCLK (0x80 >> 1) +#define PPC7D_CPLD_RTS_COM3_TXFUNC_MASK (0xc0 >> 2) +#define PPC7D_CPLD_RTS_COM3_TXFUNC_DISABLED (0 >> 2) +#define PPC7D_CPLD_RTS_COM3_TXFUNC_ENABLED (0x80 >> 2) +#define PPC7D_CPLD_RTS_COM3_TXFUNC_ENABLED_RTG3 (0xc0 >> 2) +#define PPC7D_CPLD_RTS_COM3_TXFUNC_ENABLED_RTG3S (0xc0 >> 2) +#define PPC7D_CPLD_RTS_COM56_MODE_MASK (0x80 >> 4) +#define PPC7D_CPLD_RTS_COM56_MODE_RS232 (0) +#define PPC7D_CPLD_RTS_COM56_MODE_RS422 (0x80 >> 4) +#define PPC7D_CPLD_RTS_COM56_ENABLE_MASK (0x80 >> 5) +#define PPC7D_CPLD_RTS_COM56_DISABLED (0) +#define PPC7D_CPLD_RTS_COM56_ENABLED (0x80 >> 5) +#define PPC7D_CPLD_RTS_COM4_TXFUNC_MASK (0xc0 >> 6) +#define PPC7D_CPLD_RTS_COM4_TXFUNC_DISABLED (0 >> 6) +#define PPC7D_CPLD_RTS_COM4_TXFUNC_ENABLED (0x80 >> 6) +#define PPC7D_CPLD_RTS_COM4_TXFUNC_ENABLED_RTG3 (0x40 >> 6) +#define PPC7D_CPLD_RTS_COM4_TXFUNC_ENABLED_RTG3S (0x40 >> 6) + +/* WATCHDOG_TRIG */ +#define PPC7D_CPLD_WDOG_CAUSE_MASK (0x80 >> 0) +#define PPC7D_CPLD_WDOG_CAUSE_NORMAL_RESET (0 >> 0) +#define PPC7D_CPLD_WDOG_CAUSE_WATCHDOG (0x80 >> 0) +#define PPC7D_CPLD_WDOG_ENABLE_MASK (0x80 >> 6) +#define PPC7D_CPLD_WDOG_ENABLE_OFF (0 >> 6) +#define PPC7D_CPLD_WDOG_ENABLE_ON (0x80 >> 6) +#define PPC7D_CPLD_WDOG_RESETSW_MASK (0x80 >> 7) +#define PPC7D_CPLD_WDOG_RESETSW_OFF (0 >> 7) +#define PPC7D_CPLD_WDOG_RESETSW_ON (0x80 >> 7) + +/* Interrupt mask and status bits */ +#define PPC7D_CPLD_INTR_TEMP_MASK (0x80 >> 0) +#define PPC7D_CPLD_INTR_HB8_MASK (0x80 >> 1) +#define PPC7D_CPLD_INTR_PHY1_MASK (0x80 >> 2) +#define PPC7D_CPLD_INTR_PHY0_MASK (0x80 >> 3) +#define PPC7D_CPLD_INTR_ISANMI_MASK (0x80 >> 5) +#define PPC7D_CPLD_INTR_CRITTEMP_MASK (0x80 >> 6) + +/* CPLD_INTR */ +#define PPC7D_CPLD_INTR_ENABLE_OFF (0) +#define PPC7D_CPLD_INTR_ENABLE_ON (!0) + +/* CPLD_INTR_STATUS */ +#define PPC7D_CPLD_INTR_STATUS_OFF (0) +#define PPC7D_CPLD_INTR_STATUS_ON (!0) + +/* CPLD_PCI_CONFIG */ +#define PPC7D_CPLD_PCI_CONFIG_PCI0_MASK 0x70 +#define PPC7D_CPLD_PCI_CONFIG_PCI0_PCI33 0x00 +#define PPC7D_CPLD_PCI_CONFIG_PCI0_PCI66 0x10 +#define PPC7D_CPLD_PCI_CONFIG_PCI0_PCIX33 0x40 +#define PPC7D_CPLD_PCI_CONFIG_PCI0_PCIX66 0x50 +#define PPC7D_CPLD_PCI_CONFIG_PCI0_PCIX100 0x60 +#define PPC7D_CPLD_PCI_CONFIG_PCI0_PCIX133 0x70 +#define PPC7D_CPLD_PCI_CONFIG_PCI1_MASK 0x07 +#define PPC7D_CPLD_PCI_CONFIG_PCI1_PCI33 0x00 +#define PPC7D_CPLD_PCI_CONFIG_PCI1_PCI66 0x01 +#define PPC7D_CPLD_PCI_CONFIG_PCI1_PCIX33 0x04 +#define PPC7D_CPLD_PCI_CONFIG_PCI1_PCIX66 0x05 +#define PPC7D_CPLD_PCI_CONFIG_PCI1_PCIX100 0x06 +#define PPC7D_CPLD_PCI_CONFIG_PCI1_PCIX133 0x07 + +/* CPLD_BOARD_REVISION */ +#define PPC7D_CPLD_BOARD_REVISION_NUMBER_MASK 0xe0 +#define PPC7D_CPLD_BOARD_REVISION_LETTER_MASK 0x1f + +/* CPLD_EXTENDED_ID */ +#define PPC7D_CPLD_EXTENDED_ID_PPC7D 0x18 + +/* CPLD_ID_LINK */ +#define PPC7D_CPLD_ID_LINK_VME64_GAP_MASK (0x80 >> 2) +#define PPC7D_CPLD_ID_LINK_VME64_GA4_MASK (0x80 >> 3) +#define PPC7D_CPLD_ID_LINK_E13_MASK (0x80 >> 4) +#define PPC7D_CPLD_ID_LINK_E12_MASK (0x80 >> 5) +#define PPC7D_CPLD_ID_LINK_E7_MASK (0x80 >> 6) +#define PPC7D_CPLD_ID_LINK_E6_MASK (0x80 >> 7) + +/* CPLD_MOTHERBOARD_TYPE */ +#define PPC7D_CPLD_MB_TYPE_ECC_ENABLE_MASK (0x80 >> 0) +#define PPC7D_CPLD_MB_TYPE_ECC_ENABLED (0x80 >> 0) +#define PPC7D_CPLD_MB_TYPE_ECC_DISABLED (0 >> 0) +#define PPC7D_CPLD_MB_TYPE_ECC_FITTED_MASK (0x80 >> 3) +#define PPC7D_CPLD_MB_TYPE_PLL_MASK 0x0c +#define PPC7D_CPLD_MB_TYPE_PLL_133 0x00 +#define PPC7D_CPLD_MB_TYPE_PLL_100 0x08 +#define PPC7D_CPLD_MB_TYPE_PLL_64 0x04 +#define PPC7D_CPLD_MB_TYPE_HW_ID_MASK 0x03 + +/* CPLD_FLASH_WRITE_CNTL */ +#define PPD7D_CPLD_FLASH_CNTL_WR_LINK_MASK (0x80 >> 0) +#define PPD7D_CPLD_FLASH_CNTL_WR_LINK_FITTED (0x80 >> 0) +#define PPD7D_CPLD_FLASH_CNTL_BOOT_LINK_MASK (0x80 >> 2) +#define PPD7D_CPLD_FLASH_CNTL_BOOT_LINK_FITTED (0x80 >> 2) +#define PPD7D_CPLD_FLASH_CNTL_USER_LINK_MASK (0x80 >> 3) +#define PPD7D_CPLD_FLASH_CNTL_USER_LINK_FITTED (0x80 >> 3) +#define PPD7D_CPLD_FLASH_CNTL_RECO_WR_MASK (0x80 >> 5) +#define PPD7D_CPLD_FLASH_CNTL_RECO_WR_ENABLED (0x80 >> 5) +#define PPD7D_CPLD_FLASH_CNTL_BOOT_WR_MASK (0x80 >> 6) +#define PPD7D_CPLD_FLASH_CNTL_BOOT_WR_ENABLED (0x80 >> 6) +#define PPD7D_CPLD_FLASH_CNTL_USER_WR_MASK (0x80 >> 7) +#define PPD7D_CPLD_FLASH_CNTL_USER_WR_ENABLED (0x80 >> 7) + +/* CPLD_SW_FLASH_WRITE_PROTECT */ +#define PPC7D_CPLD_SW_FLASH_WRPROT_ENABLED (!0) +#define PPC7D_CPLD_SW_FLASH_WRPROT_DISABLED (0) +#define PPC7D_CPLD_SW_FLASH_WRPROT_SYSBOOT_MASK (0x80 >> 6) +#define PPC7D_CPLD_SW_FLASH_WRPROT_USER_MASK (0x80 >> 7) + +/* CPLD_FLASH_WRITE_CNTL */ +#define PPC7D_CPLD_FLASH_CNTL_NVRAM_PROT_MASK (0x80 >> 0) +#define PPC7D_CPLD_FLASH_CNTL_NVRAM_DISABLED (0 >> 0) +#define PPC7D_CPLD_FLASH_CNTL_NVRAM_ENABLED (0x80 >> 0) +#define PPC7D_CPLD_FLASH_CNTL_ALTBOOT_LINK_MASK (0x80 >> 1) +#define PPC7D_CPLD_FLASH_CNTL_VMEBOOT_LINK_MASK (0x80 >> 2) +#define PPC7D_CPLD_FLASH_CNTL_RECBOOT_LINK_MASK (0x80 >> 3) + + +#endif /* __PPC_PLATFORMS_PPC7D_H */ diff --git a/arch/ppc/platforms/residual.c b/arch/ppc/platforms/residual.c new file mode 100644 index 00000000000..0f84ca60361 --- /dev/null +++ b/arch/ppc/platforms/residual.c @@ -0,0 +1,1034 @@ +/* + * Code to deal with the PReP residual data. + * + * Written by: Cort Dougan (cort@cs.nmt.edu) + * Improved _greatly_ and rewritten by Gabriel Paubert (paubert@iram.es) + * + * This file is based on the following documentation: + * + * IBM Power Personal Systems Architecture + * Residual Data + * Document Number: PPS-AR-FW0001 + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +unsigned char __res[sizeof(RESIDUAL)] __prepdata = {0,}; +RESIDUAL *res = (RESIDUAL *)&__res; + +char * PnP_BASE_TYPES[] __initdata = { + "Reserved", + "MassStorageDevice", + "NetworkInterfaceController", + "DisplayController", + "MultimediaController", + "MemoryController", + "BridgeController", + "CommunicationsDevice", + "SystemPeripheral", + "InputDevice", + "ServiceProcessor" + }; + +/* Device Sub Type Codes */ + +unsigned char * PnP_SUB_TYPES[] __initdata = { + "\001\000SCSIController", + "\001\001IDEController", + "\001\002FloppyController", + "\001\003IPIController", + "\001\200OtherMassStorageController", + "\002\000EthernetController", + "\002\001TokenRingController", + "\002\002FDDIController", + "\002\0x80OtherNetworkController", + "\003\000VGAController", + "\003\001SVGAController", + "\003\002XGAController", + "\003\200OtherDisplayController", + "\004\000VideoController", + "\004\001AudioController", + "\004\200OtherMultimediaController", + "\005\000RAM", + "\005\001FLASH", + "\005\200OtherMemoryDevice", + "\006\000HostProcessorBridge", + "\006\001ISABridge", + "\006\002EISABridge", + "\006\003MicroChannelBridge", + "\006\004PCIBridge", + "\006\005PCMCIABridge", + "\006\006VMEBridge", + "\006\200OtherBridgeDevice", + "\007\000RS232Device", + "\007\001ATCompatibleParallelPort", + "\007\200OtherCommunicationsDevice", + "\010\000ProgrammableInterruptController", + "\010\001DMAController", + "\010\002SystemTimer", + "\010\003RealTimeClock", + "\010\004L2Cache", + "\010\005NVRAM", + "\010\006PowerManagement", + "\010\007CMOS", + "\010\010OperatorPanel", + "\010\011ServiceProcessorClass1", + "\010\012ServiceProcessorClass2", + "\010\013ServiceProcessorClass3", + "\010\014GraphicAssist", + "\010\017SystemPlanar", + "\010\200OtherSystemPeripheral", + "\011\000KeyboardController", + "\011\001Digitizer", + "\011\002MouseController", + "\011\003TabletController", + "\011\0x80OtherInputController", + "\012\000GeneralMemoryController", + NULL +}; + +/* Device Interface Type Codes */ + +unsigned char * PnP_INTERFACES[] __initdata = { + "\000\000\000General", + "\001\000\000GeneralSCSI", + "\001\001\000GeneralIDE", + "\001\001\001ATACompatible", + + "\001\002\000GeneralFloppy", + "\001\002\001Compatible765", + "\001\002\002NS398_Floppy", /* NS Super I/O wired to use index + register at port 398 and data + register at port 399 */ + "\001\002\003NS26E_Floppy", /* Ports 26E and 26F */ + "\001\002\004NS15C_Floppy", /* Ports 15C and 15D */ + "\001\002\005NS2E_Floppy", /* Ports 2E and 2F */ + "\001\002\006CHRP_Floppy", /* CHRP Floppy in PR*P system */ + + "\001\003\000GeneralIPI", + + "\002\000\000GeneralEther", + "\002\001\000GeneralToken", + "\002\002\000GeneralFDDI", + + "\003\000\000GeneralVGA", + "\003\001\000GeneralSVGA", + "\003\002\000GeneralXGA", + + "\004\000\000GeneralVideo", + "\004\001\000GeneralAudio", + "\004\001\001CS4232Audio", /* CS 4232 Plug 'n Play Configured */ + + "\005\000\000GeneralRAM", + /* This one is obviously wrong ! */ + "\005\000\000PCIMemoryController", /* PCI Config Method */ + "\005\000\001RS6KMemoryController", /* RS6K Config Method */ + "\005\001\000GeneralFLASH", + + "\006\000\000GeneralHostBridge", + "\006\001\000GeneralISABridge", + "\006\002\000GeneralEISABridge", + "\006\003\000GeneralMCABridge", + /* GeneralPCIBridge = 0, */ + "\006\004\000PCIBridgeDirect", + "\006\004\001PCIBridgeIndirect", + "\006\004\002PCIBridgeRS6K", + "\006\005\000GeneralPCMCIABridge", + "\006\006\000GeneralVMEBridge", + + "\007\000\000GeneralRS232", + "\007\000\001COMx", + "\007\000\002Compatible16450", + "\007\000\003Compatible16550", + "\007\000\004NS398SerPort", /* NS Super I/O wired to use index + register at port 398 and data + register at port 399 */ + "\007\000\005NS26ESerPort", /* Ports 26E and 26F */ + "\007\000\006NS15CSerPort", /* Ports 15C and 15D */ + "\007\000\007NS2ESerPort", /* Ports 2E and 2F */ + + "\007\001\000GeneralParPort", + "\007\001\001LPTx", + "\007\001\002NS398ParPort", /* NS Super I/O wired to use index + register at port 398 and data + register at port 399 */ + "\007\001\003NS26EParPort", /* Ports 26E and 26F */ + "\007\001\004NS15CParPort", /* Ports 15C and 15D */ + "\007\001\005NS2EParPort", /* Ports 2E and 2F */ + + "\010\000\000GeneralPIC", + "\010\000\001ISA_PIC", + "\010\000\002EISA_PIC", + "\010\000\003MPIC", + "\010\000\004RS6K_PIC", + + "\010\001\000GeneralDMA", + "\010\001\001ISA_DMA", + "\010\001\002EISA_DMA", + + "\010\002\000GeneralTimer", + "\010\002\001ISA_Timer", + "\010\002\002EISA_Timer", + "\010\003\000GeneralRTC", + "\010\003\001ISA_RTC", + + "\010\004\001StoreThruOnly", + "\010\004\002StoreInEnabled", + "\010\004\003RS6KL2Cache", + + "\010\005\000IndirectNVRAM", /* Indirectly addressed */ + "\010\005\001DirectNVRAM", /* Memory Mapped */ + "\010\005\002IndirectNVRAM24", /* Indirectly addressed - 24 bit */ + + "\010\006\000GeneralPowerManagement", + "\010\006\001EPOWPowerManagement", + "\010\006\002PowerControl", // d1378 + + "\010\007\000GeneralCMOS", + + "\010\010\000GeneralOPPanel", + "\010\010\001HarddiskLight", + "\010\010\002CDROMLight", + "\010\010\003PowerLight", + "\010\010\004KeyLock", + "\010\010\005ANDisplay", /* AlphaNumeric Display */ + "\010\010\006SystemStatusLED", /* 3 digit 7 segment LED */ + "\010\010\007CHRP_SystemStatusLED", /* CHRP LEDs in PR*P system */ + + "\010\011\000GeneralServiceProcessor", + "\010\012\000GeneralServiceProcessor", + "\010\013\000GeneralServiceProcessor", + + "\010\014\001TransferData", + "\010\014\002IGMC32", + "\010\014\003IGMC64", + + "\010\017\000GeneralSystemPlanar", /* 10/5/95 */ + NULL + }; + +static const unsigned char __init *PnP_SUB_TYPE_STR(unsigned char BaseType, + unsigned char SubType) { + unsigned char ** s=PnP_SUB_TYPES; + while (*s && !((*s)[0]==BaseType + && (*s)[1]==SubType)) s++; + if (*s) return *s+2; + else return("Unknown !"); +}; + +static const unsigned char __init *PnP_INTERFACE_STR(unsigned char BaseType, + unsigned char SubType, + unsigned char Interface) { + unsigned char ** s=PnP_INTERFACES; + while (*s && !((*s)[0]==BaseType + && (*s)[1]==SubType + && (*s)[2]==Interface)) s++; + if (*s) return *s+3; + else return NULL; +}; + +static void __init printsmallvendor(PnP_TAG_PACKET *pkt, int size) { + int i, c; + char decomp[4]; +#define p pkt->S14_Pack.S14_Data.S14_PPCPack + switch(p.Type) { + case 1: + /* Decompress first 3 chars */ + c = *(unsigned short *)p.PPCData; + decomp[0]='A'-1+((c>>10)&0x1F); + decomp[1]='A'-1+((c>>5)&0x1F); + decomp[2]='A'-1+(c&0x1F); + decomp[3]=0; + printk(" Chip identification: %s%4.4X\n", + decomp, ld_le16((unsigned short *)(p.PPCData+2))); + break; + default: + printk(" Small vendor item type 0x%2.2x, data (hex): ", + p.Type); + for(i=0; iS1_Pack.Tag)) { + case PnPVersion: + printk(" PnPversion 0x%x.%x\n", + pkt->S1_Pack.Version[0], /* How to interpret version ? */ + pkt->S1_Pack.Version[1]); + break; +// case Logicaldevice: + break; +// case CompatibleDevice: + break; + case IRQFormat: +#define p pkt->S4_Pack + printk(" IRQ Mask 0x%4.4x, %s %s sensitive\n", + ld_le16((unsigned short *)p.IRQMask), + intlevel[(size>3) ? !(p.IRQInfo&0x05) : 0], + intsense[(size>3) ? !(p.IRQInfo&0x03) : 0]); +#undef p + break; + case DMAFormat: +#define p pkt->S5_Pack + printk(" DMA channel mask 0x%2.2x, info 0x%2.2x\n", + p.DMAMask, p.DMAInfo); +#undef p + break; + case StartDepFunc: + printk("Start dependent function:\n"); + break; + case EndDepFunc: + printk("End dependent function\n"); + break; + case IOPort: +#define p pkt->S8_Pack + printk(" Variable (%d decoded bits) I/O port\n" + " from 0x%4.4x to 0x%4.4x, alignment %d, %d ports\n", + p.IOInfo&ISAAddr16bit?16:10, + ld_le16((unsigned short *)p.RangeMin), + ld_le16((unsigned short *)p.RangeMax), + p.IOAlign, p.IONum); +#undef p + break; + case FixedIOPort: +#define p pkt->S9_Pack + printk(" Fixed (10 decoded bits) I/O port from %3.3x to %3.3x\n", + (p.Range[1]<<8)|p.Range[0], + ((p.Range[1]<<8)|p.Range[0])+p.IONum-1); +#undef p + break; + case Res1: + case Res2: + case Res3: + printk(" Undefined packet type %d!\n", + tag_small_item_name(pkt->S1_Pack.Tag)); + break; + case SmallVendorItem: + printsmallvendor(pkt,size); + break; + default: + printk(" Type 0x2.2x%d, size=%d\n", + pkt->S1_Pack.Tag, size); + break; + } +} + +static void __init printlargevendor(PnP_TAG_PACKET * pkt, int size) { + static const unsigned char * addrtype[] = {"I/O", "Memory", "System"}; + static const unsigned char * inttype[] = {"8259", "MPIC", "RS6k BUID %d"}; + static const unsigned char * convtype[] = {"Bus Memory", "Bus I/O", "DMA"}; + static const unsigned char * transtype[] = {"direct", "mapped", "direct-store segment"}; + static const unsigned char * L2type[] = {"WriteThru", "CopyBack"}; + static const unsigned char * L2assoc[] = {"DirectMapped", "2-way set"}; + + int i; + char tmpstr[30], *t; +#define p pkt->L4_Pack.L4_Data.L4_PPCPack + switch(p.Type) { + case 2: + printk(" %d K %s %s L2 cache, %d/%d bytes line/sector size\n", + ld_le32((unsigned int *)p.PPCData), + L2type[p.PPCData[10]-1], + L2assoc[p.PPCData[4]-1], + ld_le16((unsigned short *)p.PPCData+3), + ld_le16((unsigned short *)p.PPCData+4)); + break; + case 3: + printk(" PCI Bridge parameters\n" + " ConfigBaseAddress %0x\n" + " ConfigBaseData %0x\n" + " Bus number %d\n", + ld_le32((unsigned int *)p.PPCData), + ld_le32((unsigned int *)(p.PPCData+8)), + p.PPCData[16]); + for(i=20; iS1_Pack.Tag)) { + case LargeVendorItem: + printlargevendor(pkt, size); + break; + default: + printk(" Type 0x2.2x%d, size=%d\n", + pkt->S1_Pack.Tag, size); + break; + } +} + +static void __init printpackets(PnP_TAG_PACKET * pkt, const char * cat) +{ + if (pkt->S1_Pack.Tag== END_TAG) { + printk(" No packets describing %s resources.\n", cat); + return; + } + printk( " Packets describing %s resources:\n",cat); + do { + int size; + if (tag_type(pkt->S1_Pack.Tag)) { + size= 3 + + pkt->L1_Pack.Count0 + + pkt->L1_Pack.Count1*256; + printlargepacket(pkt, size); + } else { + size=tag_small_count(pkt->S1_Pack.Tag)+1; + printsmallpacket(pkt, size); + } + pkt = (PnP_TAG_PACKET *)((unsigned char *) pkt + size); + } while (pkt->S1_Pack.Tag != END_TAG); +} + +void __init print_residual_device_info(void) +{ + int i; + PPC_DEVICE *dev; +#define did dev->DeviceId + + /* make sure we have residual data first */ + if (!have_residual_data) + return; + + printk("Residual: %ld devices\n", res->ActualNumDevices); + for ( i = 0; + i < res->ActualNumDevices ; + i++) + { + char decomp[4], sn[20]; + const char * s; + dev = &res->Devices[i]; + s = PnP_INTERFACE_STR(did.BaseType, did.SubType, + did.Interface); + if(!s) { + sprintf(sn, "interface %d", did.Interface); + s=sn; + } + if ( did.BusId & PCIDEVICE ) + printk("PCI Device, Bus %d, DevFunc 0x%x:", + dev->BusAccess.PCIAccess.BusNumber, + dev->BusAccess.PCIAccess.DevFuncNumber); + if ( did.BusId & PNPISADEVICE ) printk("PNPISA Device:"); + if ( did.BusId & ISADEVICE ) + printk("ISA Device, Slot %d, LogicalDev %d:", + dev->BusAccess.ISAAccess.SlotNumber, + dev->BusAccess.ISAAccess.LogicalDevNumber); + if ( did.BusId & EISADEVICE ) printk("EISA Device:"); + if ( did.BusId & PROCESSORDEVICE ) + printk("ProcBus Device, Bus %d, BUID %d: ", + dev->BusAccess.ProcBusAccess.BusNumber, + dev->BusAccess.ProcBusAccess.BUID); + if ( did.BusId & PCMCIADEVICE ) printk("PCMCIA "); + if ( did.BusId & VMEDEVICE ) printk("VME "); + if ( did.BusId & MCADEVICE ) printk("MCA "); + if ( did.BusId & MXDEVICE ) printk("MX "); + /* Decompress first 3 chars */ + decomp[0]='A'-1+((did.DevId>>26)&0x1F); + decomp[1]='A'-1+((did.DevId>>21)&0x1F); + decomp[2]='A'-1+((did.DevId>>16)&0x1F); + decomp[3]=0; + printk(" %s%4.4lX, %s, %s, %s\n", + decomp, did.DevId&0xffff, + PnP_BASE_TYPES[did.BaseType], + PnP_SUB_TYPE_STR(did.BaseType,did.SubType), + s); + if ( dev->AllocatedOffset ) + printpackets( (union _PnP_TAG_PACKET *) + &res->DevicePnPHeap[dev->AllocatedOffset], + "allocated"); + if ( dev->PossibleOffset ) + printpackets( (union _PnP_TAG_PACKET *) + &res->DevicePnPHeap[dev->PossibleOffset], + "possible"); + if ( dev->CompatibleOffset ) + printpackets( (union _PnP_TAG_PACKET *) + &res->DevicePnPHeap[dev->CompatibleOffset], + "compatible"); + } +} + + +#if 0 +static void __init printVPD(void) { +#define vpd res->VitalProductData + int ps=vpd.PageSize, i, j; + static const char* Usage[]={ + "FirmwareStack", "FirmwareHeap", "FirmwareCode", "BootImage", + "Free", "Unpopulated", "ISAAddr", "PCIConfig", + "IOMemory", "SystemIO", "SystemRegs", "PCIAddr", + "UnPopSystemRom", "SystemROM", "ResumeBlock", "Other" + }; + static const unsigned char *FWMan[]={ + "IBM", "Motorola", "FirmWorks", "Bull" + }; + static const unsigned char *FWFlags[]={ + "Conventional", "OpenFirmware", "Diagnostics", "LowDebug", + "MultiBoot", "LowClient", "Hex41", "FAT", + "ISO9660", "SCSI_ID_Override", "Tape_Boot", "FW_Boot_Path" + }; + static const unsigned char *ESM[]={ + "Port92", "PCIConfigA8", "FF001030", "????????" + }; + static const unsigned char *SIOM[]={ + "Port850", "????????", "PCIConfigA8", "????????" + }; + + printk("Model: %s\n",vpd.PrintableModel); + printk("Serial: %s\n", vpd.Serial); + printk("FirmwareSupplier: %s\n", FWMan[vpd.FirmwareSupplier]); + printk("FirmwareFlags:"); + for(j=0; j<12; j++) { + if (vpd.FirmwareSupports & (1<2 ? 2 : vpd.EndianSwitchMethod]); + printk("SpreadIOMethod: %s\n", + SIOM[vpd.SpreadIOMethod>3 ? 3 : vpd.SpreadIOMethod]); + printk("Processor/Bus frequencies (Hz): %ld/%ld\n", + vpd.ProcessorHz, vpd.ProcessorBusHz); + printk("Time Base Divisor: %ld\n", vpd.TimeBaseDivisor); + printk("WordWidth, PageSize: %ld, %d\n", vpd.WordWidth, ps); + printk("Cache sector size, Lock granularity: %ld, %ld\n", + vpd.CoherenceBlockSize, vpd.GranuleSize); + for (i=0; iActualNumMemSegs; i++) { + int mask=res->Segs[i].Usage, first, j; + printk("%8.8lx-%8.8lx ", + res->Segs[i].BasePage*ps, + (res->Segs[i].PageCount+res->Segs[i].BasePage)*ps-1); + for(j=15, first=1; j>=0; j--) { + if (mask&(1<DeviceId + + /* make sure we have residual data first */ + if (!have_residual_data) + return; + printk("Residual: %ld devices\n", res->ActualNumDevices); + for ( i = 0; + i < res->ActualNumDevices ; + i++) + { + dev = &res->Devices[i]; + /* + * pci devices + */ + if ( did.BusId & PCIDEVICE ) + { + printk("PCI Device:"); + /* unknown vendor */ + if ( !strncmp( "Unknown", pci_strvendor(did.DevId>>16), 7) ) + printk(" id %08lx types %d/%d", did.DevId, + did.BaseType, did.SubType); + /* known vendor */ + else + printk(" %s %s", + pci_strvendor(did.DevId>>16), + pci_strdev(did.DevId>>16, + did.DevId&0xffff) + ); + + if ( did.BusId & PNPISADEVICE ) + { + printk(" pnp:"); + /* get pnp info on the device */ + pkt = (union _PnP_TAG_PACKET *) + &res->DevicePnPHeap[dev->AllocatedOffset]; + for (; pkt->S1_Pack.Tag != DF_END_TAG; + pkt++ ) + { + if ( (pkt->S1_Pack.Tag == S4_Packet) || + (pkt->S1_Pack.Tag == S4_Packet_flags) ) + printk(" irq %02x%02x", + pkt->S4_Pack.IRQMask[0], + pkt->S4_Pack.IRQMask[1]); + } + } + printk("\n"); + continue; + } + /* + * isa devices + */ + if ( did.BusId & ISADEVICE ) + { + printk("ISA Device: basetype: %d subtype: %d", + did.BaseType, did.SubType); + printk("\n"); + continue; + } + /* + * eisa devices + */ + if ( did.BusId & EISADEVICE ) + { + printk("EISA Device: basetype: %d subtype: %d", + did.BaseType, did.SubType); + printk("\n"); + continue; + } + /* + * proc bus devices + */ + if ( did.BusId & PROCESSORDEVICE ) + { + printk("ProcBus Device: basetype: %d subtype: %d", + did.BaseType, did.SubType); + printk("\n"); + continue; + } + /* + * pcmcia devices + */ + if ( did.BusId & PCMCIADEVICE ) + { + printk("PCMCIA Device: basetype: %d subtype: %d", + did.BaseType, did.SubType); + printk("\n"); + continue; + } + printk("Unknown bus access device: busid %lx\n", + did.BusId); + } +} +#endif + +/* Returns the device index in the residual data, + any of the search items may be set as -1 for wildcard, + DevID number field (second halfword) is big endian ! + + Examples: + - search for the Interrupt controller (8259 type), 2 methods: + 1) i8259 = residual_find_device(~0, + NULL, + SystemPeripheral, + ProgrammableInterruptController, + ISA_PIC, + 0); + 2) i8259 = residual_find_device(~0, "PNP0000", -1, -1, -1, 0) + + - search for the first two serial devices, whatever their type) + iserial1 = residual_find_device(~0,NULL, + CommunicationsDevice, + RS232Device, + -1, 0) + iserial2 = residual_find_device(~0,NULL, + CommunicationsDevice, + RS232Device, + -1, 1) + - but search for typical COM1 and COM2 is not easy due to the + fact that the interface may be anything and the name "PNP0500" or + "PNP0501". Quite bad. + +*/ + +/* devid are easier to uncompress than to compress, so to minimize bloat +in this rarely used area we unencode and compare */ + +/* in residual data number is big endian in the device table and +little endian in the heap, so we use two parameters to avoid writing +two very similar functions */ + +static int __init same_DevID(unsigned short vendor, + unsigned short Number, + char * str) +{ + static unsigned const char hexdigit[]="0123456789ABCDEF"; + if (strlen(str)!=7) return 0; + if ( ( ((vendor>>10)&0x1f)+'A'-1 == str[0]) && + ( ((vendor>>5)&0x1f)+'A'-1 == str[1]) && + ( (vendor&0x1f)+'A'-1 == str[2]) && + (hexdigit[(Number>>12)&0x0f] == str[3]) && + (hexdigit[(Number>>8)&0x0f] == str[4]) && + (hexdigit[(Number>>4)&0x0f] == str[5]) && + (hexdigit[Number&0x0f] == str[6]) ) return 1; + return 0; +} + +PPC_DEVICE __init *residual_find_device(unsigned long BusMask, + unsigned char * DevID, + int BaseType, + int SubType, + int Interface, + int n) +{ + int i; + if (!have_residual_data) return NULL; + for (i=0; iActualNumDevices; i++) { +#define Dev res->Devices[i].DeviceId + if ( (Dev.BusId&BusMask) && + (BaseType==-1 || Dev.BaseType==BaseType) && + (SubType==-1 || Dev.SubType==SubType) && + (Interface==-1 || Dev.Interface==Interface) && + (DevID==NULL || same_DevID((Dev.DevId>>16)&0xffff, + Dev.DevId&0xffff, DevID)) && + !(n--) ) return res->Devices+i; +#undef Dev + } + return NULL; +} + +PPC_DEVICE __init *residual_find_device_id(unsigned long BusMask, + unsigned short DevID, + int BaseType, + int SubType, + int Interface, + int n) +{ + int i; + if (!have_residual_data) return NULL; + for (i=0; iActualNumDevices; i++) { +#define Dev res->Devices[i].DeviceId + if ( (Dev.BusId&BusMask) && + (BaseType==-1 || Dev.BaseType==BaseType) && + (SubType==-1 || Dev.SubType==SubType) && + (Interface==-1 || Dev.Interface==Interface) && + (DevID==0xffff || (Dev.DevId&0xffff) == DevID) && + !(n--) ) return res->Devices+i; +#undef Dev + } + return NULL; +} + +static int __init +residual_scan_pcibridge(PnP_TAG_PACKET * pkt, struct pci_dev *dev) +{ + int irq = -1; + +#define data pkt->L4_Pack.L4_Data.L4_PPCPack.PPCData + if (dev->bus->number == data[16]) { + int i, size; + + size = 3 + ld_le16((u_short *) (&pkt->L4_Pack.Count0)); + for (i = 20; i < size - 4; i += 12) { + unsigned char pin; + int line_irq; + + if (dev->devfn != data[i + 1]) + continue; + + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + if (pin) { + line_irq = ld_le16((unsigned short *) + (&data[i + 4 + 2 * (pin - 1)])); + irq = (line_irq == 0xffff) ? 0 + : line_irq & 0x7fff; + } else + irq = 0; + + break; + } + } +#undef data + + return irq; +} + +int __init +residual_pcidev_irq(struct pci_dev *dev) +{ + int i = 0; + int irq = -1; + PPC_DEVICE *bridge; + + while ((bridge = residual_find_device + (-1, NULL, BridgeController, PCIBridge, -1, i++))) { + + PnP_TAG_PACKET *pkt; + if (bridge->AllocatedOffset) { + pkt = PnP_find_large_vendor_packet(res->DevicePnPHeap + + bridge->AllocatedOffset, 3, 0); + if (!pkt) + continue; + + irq = residual_scan_pcibridge(pkt, dev); + if (irq != -1) + break; + } + } + + return (irq < 0) ? 0 : irq; +} + +void __init residual_irq_mask(char *irq_edge_mask_lo, char *irq_edge_mask_hi) +{ + PPC_DEVICE *dev; + int i = 0; + unsigned short irq_mask = 0x000; /* default to edge */ + + while ((dev = residual_find_device(-1, NULL, -1, -1, -1, i++))) { + PnP_TAG_PACKET *pkt; + unsigned short mask; + int size; + int offset = dev->AllocatedOffset; + + if (!offset) + continue; + + pkt = PnP_find_packet(res->DevicePnPHeap + offset, + IRQFormat, 0); + if (!pkt) + continue; + + size = tag_small_count(pkt->S1_Pack.Tag) + 1; + mask = ld_le16((unsigned short *)pkt->S4_Pack.IRQMask); + if (size > 3 && (pkt->S4_Pack.IRQInfo & 0x0c)) + irq_mask |= mask; + } + + *irq_edge_mask_lo = irq_mask & 0xff; + *irq_edge_mask_hi = irq_mask >> 8; +} + +unsigned int __init residual_isapic_addr(void) +{ + PPC_DEVICE *isapic; + PnP_TAG_PACKET *pkt; + unsigned int addr; + + isapic = residual_find_device(~0, NULL, SystemPeripheral, + ProgrammableInterruptController, + ISA_PIC, 0); + if (!isapic) + goto unknown; + + pkt = PnP_find_large_vendor_packet(res->DevicePnPHeap + + isapic->AllocatedOffset, 9, 0); + if (!pkt) + goto unknown; + +#define p pkt->L4_Pack.L4_Data.L4_PPCPack + /* Must be 32-bit system address */ + if (!((p.PPCData[0] == 3) && (p.PPCData[1] == 32))) + goto unknown; + + /* It doesn't seem to work where length != 1 (what can I say? :-/ ) */ + if (ld_le32((unsigned int *)(p.PPCData + 12)) != 1) + goto unknown; + + addr = ld_le32((unsigned int *) (p.PPCData + 4)); +#undef p + return addr; +unknown: + return 0; +} + +PnP_TAG_PACKET *PnP_find_packet(unsigned char *p, + unsigned packet_tag, + int n) +{ + unsigned mask, masked_tag, size; + if(!p) return NULL; + if (tag_type(packet_tag)) mask=0xff; else mask=0xF8; + masked_tag = packet_tag&mask; + for(; *p != END_TAG; p+=size) { + if ((*p & mask) == masked_tag && !(n--)) + return (PnP_TAG_PACKET *) p; + if (tag_type(*p)) + size=ld_le16((unsigned short *)(p+1))+3; + else + size=tag_small_count(*p)+1; + } + return NULL; /* not found */ +} + +PnP_TAG_PACKET __init *PnP_find_small_vendor_packet(unsigned char *p, + unsigned packet_type, + int n) +{ + int next=0; + while (p) { + p = (unsigned char *) PnP_find_packet(p, 0x70, next); + if (p && p[1]==packet_type && !(n--)) + return (PnP_TAG_PACKET *) p; + next = 1; + }; + return NULL; /* not found */ +} + +PnP_TAG_PACKET __init *PnP_find_large_vendor_packet(unsigned char *p, + unsigned packet_type, + int n) +{ + int next=0; + while (p) { + p = (unsigned char *) PnP_find_packet(p, 0x84, next); + if (p && p[3]==packet_type && !(n--)) + return (PnP_TAG_PACKET *) p; + next = 1; + }; + return NULL; /* not found */ +} + +#ifdef CONFIG_PROC_PREPRESIDUAL +static int proc_prep_residual_read(char * buf, char ** start, off_t off, + int count, int *eof, void *data) +{ + int n; + + n = res->ResidualLength - off; + if (n < 0) { + *eof = 1; + n = 0; + } + else { + if (n > count) + n = count; + else + *eof = 1; + + memcpy(buf, (char *)res + off, n); + *start = buf; + } + + return n; +} + +int __init +proc_prep_residual_init(void) +{ + if (have_residual_data) + create_proc_read_entry("residual", S_IRUGO, NULL, + proc_prep_residual_read, NULL); + return 0; +} + +__initcall(proc_prep_residual_init); +#endif diff --git a/arch/ppc/platforms/rpx8260.h b/arch/ppc/platforms/rpx8260.h new file mode 100644 index 00000000000..843494a50ef --- /dev/null +++ b/arch/ppc/platforms/rpx8260.h @@ -0,0 +1,81 @@ +/* + * A collection of structures, addresses, and values associated with + * the Embedded Planet RPX6 (or RPX Super) MPC8260 board. + * Copied from the RPX-Classic and SBS8260 stuff. + * + * Copyright (c) 2001 Dan Malek + */ +#ifdef __KERNEL__ +#ifndef __ASM_PLATFORMS_RPX8260_H__ +#define __ASM_PLATFORMS_RPX8260_H__ + +/* A Board Information structure that is given to a program when + * prom starts it up. + */ +typedef struct bd_info { + unsigned int bi_memstart; /* Memory start address */ + unsigned int bi_memsize; /* Memory (end) size in bytes */ + unsigned int bi_nvsize; /* NVRAM size in bytes (can be 0) */ + unsigned int bi_intfreq; /* Internal Freq, in Hz */ + unsigned int bi_busfreq; /* Bus Freq, in MHz */ + unsigned int bi_cpmfreq; /* CPM Freq, in MHz */ + unsigned int bi_brgfreq; /* BRG Freq, in MHz */ + unsigned int bi_vco; /* VCO Out from PLL */ + unsigned int bi_baudrate; /* Default console baud rate */ + unsigned int bi_immr; /* IMMR when called from boot rom */ + unsigned char bi_enetaddr[6]; +} bd_t; + +extern bd_t m8xx_board_info; + +/* Memory map is configured by the PROM startup. + * We just map a few things we need. The CSR is actually 4 byte-wide + * registers that can be accessed as 8-, 16-, or 32-bit values. + */ +#define CPM_MAP_ADDR ((uint)0xf0000000) +#define RPX_CSR_ADDR ((uint)0xfa000000) +#define RPX_CSR_SIZE ((uint)(512 * 1024)) +#define RPX_NVRTC_ADDR ((uint)0xfa080000) +#define RPX_NVRTC_SIZE ((uint)(512 * 1024)) + +/* The RPX6 has 16, byte wide control/status registers. + * Not all are used (yet). + */ +extern volatile u_char *rpx6_csr_addr; + +/* Things of interest in the CSR. +*/ +#define BCSR0_ID_MASK ((u_char)0xf0) /* Read only */ +#define BCSR0_SWITCH_MASK ((u_char)0x0f) /* Read only */ +#define BCSR1_XCVR_SMC1 ((u_char)0x80) +#define BCSR1_XCVR_SMC2 ((u_char)0x40) +#define BCSR2_FLASH_WENABLE ((u_char)0x20) +#define BCSR2_NVRAM_ENABLE ((u_char)0x10) +#define BCSR2_ALT_IRQ2 ((u_char)0x08) +#define BCSR2_ALT_IRQ3 ((u_char)0x04) +#define BCSR2_PRST ((u_char)0x02) /* Force reset */ +#define BCSR2_ENPRST ((u_char)0x01) /* Enable POR */ +#define BCSR3_MODCLK_MASK ((u_char)0xe0) +#define BCSR3_ENCLKHDR ((u_char)0x10) +#define BCSR3_LED5 ((u_char)0x04) /* 0 == on */ +#define BCSR3_LED6 ((u_char)0x02) /* 0 == on */ +#define BCSR3_LED7 ((u_char)0x01) /* 0 == on */ +#define BCSR4_EN_PHY ((u_char)0x80) /* Enable PHY */ +#define BCSR4_EN_MII ((u_char)0x40) /* Enable PHY */ +#define BCSR4_MII_READ ((u_char)0x04) +#define BCSR4_MII_MDC ((u_char)0x02) +#define BCSR4_MII_MDIO ((u_char)0x01) +#define BCSR13_FETH_IRQMASK ((u_char)0xf0) +#define BCSR15_FETH_IRQ ((u_char)0x20) + +#define PHY_INTERRUPT SIU_INT_IRQ7 + +/* For our show_cpuinfo hooks. */ +#define CPUINFO_VENDOR "Embedded Planet" +#define CPUINFO_MACHINE "EP8260 PowerPC" + +/* Warm reset vector. */ +#define BOOTROM_RESTART_ADDR ((uint)0xfff00104) + +#endif /* __ASM_PLATFORMS_RPX8260_H__ */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/rpxclassic.h b/arch/ppc/platforms/rpxclassic.h new file mode 100644 index 00000000000..6daa109491c --- /dev/null +++ b/arch/ppc/platforms/rpxclassic.h @@ -0,0 +1,119 @@ +/* + * A collection of structures, addresses, and values associated with + * the RPCG RPX-Classic board. Copied from the RPX-Lite stuff. + * + * Copyright (c) 1998 Dan Malek (dmalek@jlc.net) + */ +#ifdef __KERNEL__ +#ifndef __MACH_RPX_DEFS +#define __MACH_RPX_DEFS + +#include + +#ifndef __ASSEMBLY__ +/* A Board Information structure that is given to a program when + * prom starts it up. + */ +typedef struct bd_info { + unsigned int bi_memstart; /* Memory start address */ + unsigned int bi_memsize; /* Memory (end) size in bytes */ + unsigned int bi_intfreq; /* Internal Freq, in Hz */ + unsigned int bi_busfreq; /* Bus Freq, in Hz */ + unsigned char bi_enetaddr[6]; + unsigned int bi_baudrate; +} bd_t; + +extern bd_t m8xx_board_info; + +/* Memory map is configured by the PROM startup. + * We just map a few things we need. The CSR is actually 4 byte-wide + * registers that can be accessed as 8-, 16-, or 32-bit values. + */ +#define PCI_ISA_IO_ADDR ((unsigned)0x80000000) +#define PCI_ISA_IO_SIZE ((uint)(512 * 1024 * 1024)) +#define PCI_ISA_MEM_ADDR ((unsigned)0xc0000000) +#define PCI_ISA_MEM_SIZE ((uint)(512 * 1024 * 1024)) +#define RPX_CSR_ADDR ((uint)0xfa400000) +#define RPX_CSR_SIZE ((uint)(4 * 1024)) +#define IMAP_ADDR ((uint)0xfa200000) +#define IMAP_SIZE ((uint)(64 * 1024)) +#define PCI_CSR_ADDR ((uint)0x80000000) +#define PCI_CSR_SIZE ((uint)(64 * 1024)) +#define PCMCIA_MEM_ADDR ((uint)0xe0000000) +#define PCMCIA_MEM_SIZE ((uint)(64 * 1024)) +#define PCMCIA_IO_ADDR ((uint)0xe4000000) +#define PCMCIA_IO_SIZE ((uint)(4 * 1024)) +#define PCMCIA_ATTRB_ADDR ((uint)0xe8000000) +#define PCMCIA_ATTRB_SIZE ((uint)(4 * 1024)) + +/* Things of interest in the CSR. +*/ +#define BCSR0_ETHEN ((uint)0x80000000) +#define BCSR0_ETHLPBK ((uint)0x40000000) +#define BCSR0_COLTESTDIS ((uint)0x20000000) +#define BCSR0_FULLDPLXDIS ((uint)0x10000000) +#define BCSR0_ENFLSHSEL ((uint)0x04000000) +#define BCSR0_FLASH_SEL ((uint)0x02000000) +#define BCSR0_ENMONXCVR ((uint)0x01000000) + +#define BCSR0_PCMCIAVOLT ((uint)0x000f0000) /* CLLF */ +#define BCSR0_PCMCIA3VOLT ((uint)0x000a0000) /* CLLF */ +#define BCSR0_PCMCIA5VOLT ((uint)0x00060000) /* CLLF */ + +#define BCSR1_IPB5SEL ((uint)0x00100000) +#define BCSR1_PCVCTL4 ((uint)0x00080000) +#define BCSR1_PCVCTL5 ((uint)0x00040000) +#define BCSR1_PCVCTL6 ((uint)0x00020000) +#define BCSR1_PCVCTL7 ((uint)0x00010000) + +#define BCSR2_EN232XCVR ((uint)0x00008000) +#define BCSR2_QSPACESEL ((uint)0x00004000) +#define BCSR2_FETHLEDMODE ((uint)0x00000800) /* CLLF */ + +#if defined(CONFIG_HTDMSOUND) +#include +#endif + +/* define IO_BASE for pcmcia, CLLF only */ +#if !defined(CONFIG_PCI) +#define _IO_BASE 0x80000000 +#define _IO_BASE_SIZE 0x1000 + +/* for pcmcia sandisk */ +#ifdef CONFIG_IDE +# define MAX_HWIFS 1 +#endif +#endif + +/* Interrupt level assignments. +*/ +#define FEC_INTERRUPT SIU_LEVEL1 /* FEC interrupt */ + + +/* CPM Ethernet through SCCx. + * + * Bits in parallel I/O port registers that have to be set/cleared + * to configure the pins for SCC1 use. + */ +#define PA_ENET_RXD ((ushort)0x0001) +#define PA_ENET_TXD ((ushort)0x0002) +#define PA_ENET_TCLK ((ushort)0x0200) +#define PA_ENET_RCLK ((ushort)0x0800) +#define PB_ENET_TENA ((uint)0x00001000) +#define PC_ENET_CLSN ((ushort)0x0010) +#define PC_ENET_RENA ((ushort)0x0020) + +/* Control bits in the SICR to route TCLK (CLK2) and RCLK (CLK4) to + * SCC1. Also, make sure GR1 (bit 24) and SC1 (bit 25) are zero. + */ +#define SICR_ENET_MASK ((uint)0x000000ff) +#define SICR_ENET_CLKRT ((uint)0x0000003d) + +/* We don't use the 8259. +*/ + +#define NR_8259_INTS 0 + +#endif /* !__ASSEMBLY__ */ +#endif /* __MACH_RPX_DEFS */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/rpxhiox.h b/arch/ppc/platforms/rpxhiox.h new file mode 100644 index 00000000000..c3fa5a65376 --- /dev/null +++ b/arch/ppc/platforms/rpxhiox.h @@ -0,0 +1,41 @@ +/* + * The Embedded Planet HIOX expansion card definitions. + * There were a few different versions of these cards, but only + * the one that escaped real production is defined here. + * + * Copyright (c) 2000 Dan Malek (dmalek@jlc.net) + */ +#ifndef __MACH_RPX_HIOX_DEFS +#define __MACH_RPX_HIOX_DEFS + +#define HIOX_CSR_ADDR ((uint)0xfac00000) +#define HIOX_CSR_SIZE ((uint)(4 * 1024)) +#define HIOX_CSR0_ADDR HIOX_CSR_ADDR +#define HIOX_CSR4_ADDR ((uint)0xfac00004) + +#define HIOX_CSR0_DEFAULT ((uint)0x380f3c00) +#define HIOX_CSR0_ENSCC2 ((uint)0x80000000) +#define HIOX_CSR0_ENSMC2 ((uint)0x04000000) +#define HIOX_CSR0_ENVDOCLK ((uint)0x02000000) +#define HIOX_CSR0_VDORST_HL ((uint)0x01000000) +#define HIOX_CSR0_RS232SEL ((uint)0x0000c000) +#define HIOX_CSR0_SCC3SEL ((uint)0x0000c000) +#define HIOX_CSR0_SMC1SEL ((uint)0x00008000) +#define HIOX_CSR0_SCC1SEL ((uint)0x00004000) +#define HIOX_CSR0_ENTOUCH ((uint)0x00000080) +#define HIOX_CSR0_PDOWN100 ((uint)0x00000060) +#define HIOX_CSR0_PDOWN10 ((uint)0x00000040) +#define HIOX_CSR0_PDOWN1 ((uint)0x00000020) +#define HIOX_CSR0_TSELSPI ((uint)0x00000010) +#define HIOX_CSR0_TIRQSTAT ((uint)0x00000008) +#define HIOX_CSR4_DEFAULT ((uint)0x00000000) +#define HIOX_CSR4_ENTIRQ2 ((uint)0x20000000) +#define HIOX_CSR4_ENTIRQ3 ((uint)0x10000000) +#define HIOX_CSR4_ENAUDIO ((uint)0x00000080) +#define HIOX_CSR4_RSTAUDIO ((uint)0x00000040) /* 0 == reset */ +#define HIOX_CSR4_AUDCLKHI ((uint)0x00000020) +#define HIOX_CSR4_AUDSPISEL ((uint)0x00000010) +#define HIOX_CSR4_AUDIRQSTAT ((uint)0x00000008) +#define HIOX_CSR4_AUDCLKSEL ((uint)0x00000007) + +#endif diff --git a/arch/ppc/platforms/rpxlite.h b/arch/ppc/platforms/rpxlite.h new file mode 100644 index 00000000000..deee5bd36aa --- /dev/null +++ b/arch/ppc/platforms/rpxlite.h @@ -0,0 +1,96 @@ +/* + * A collection of structures, addresses, and values associated with + * the RPCG RPX-Lite board. Copied from the MBX stuff. + * + * Copyright (c) 1998 Dan Malek (dmalek@jlc.net) + */ +#ifdef __KERNEL__ +#ifndef __MACH_RPX_DEFS +#define __MACH_RPX_DEFS + +#include + +#ifndef __ASSEMBLY__ +/* A Board Information structure that is given to a program when + * prom starts it up. + */ +typedef struct bd_info { + unsigned int bi_memstart; /* Memory start address */ + unsigned int bi_memsize; /* Memory (end) size in bytes */ + unsigned int bi_intfreq; /* Internal Freq, in Hz */ + unsigned int bi_busfreq; /* Bus Freq, in Hz */ + unsigned char bi_enetaddr[6]; + unsigned int bi_baudrate; +} bd_t; + +extern bd_t m8xx_board_info; + +/* Memory map is configured by the PROM startup. + * We just map a few things we need. The CSR is actually 4 byte-wide + * registers that can be accessed as 8-, 16-, or 32-bit values. + */ +#define RPX_CSR_ADDR ((uint)0xfa400000) +#define RPX_CSR_SIZE ((uint)(4 * 1024)) +#define IMAP_ADDR ((uint)0xfa200000) +#define IMAP_SIZE ((uint)(64 * 1024)) +#define PCMCIA_MEM_ADDR ((uint)0x04000000) +#define PCMCIA_MEM_SIZE ((uint)(64 * 1024)) +#define PCMCIA_IO_ADDR ((uint)0x04400000) +#define PCMCIA_IO_SIZE ((uint)(4 * 1024)) + +/* Things of interest in the CSR. +*/ +#define BCSR0_ETHEN ((uint)0x80000000) +#define BCSR0_ETHLPBK ((uint)0x40000000) +#define BCSR0_COLTESTDIS ((uint)0x20000000) +#define BCSR0_FULLDPLXDIS ((uint)0x10000000) +#define BCSR0_LEDOFF ((uint)0x08000000) +#define BCSR0_USBDISABLE ((uint)0x04000000) +#define BCSR0_USBHISPEED ((uint)0x02000000) +#define BCSR0_USBPWREN ((uint)0x01000000) +#define BCSR0_PCMCIAVOLT ((uint)0x000f0000) +#define BCSR0_PCMCIA3VOLT ((uint)0x000a0000) +#define BCSR0_PCMCIA5VOLT ((uint)0x00060000) + +#define BCSR1_IPB5SEL ((uint)0x00100000) +#define BCSR1_PCVCTL4 ((uint)0x00080000) +#define BCSR1_PCVCTL5 ((uint)0x00040000) +#define BCSR1_PCVCTL6 ((uint)0x00020000) +#define BCSR1_PCVCTL7 ((uint)0x00010000) + +#if defined(CONFIG_HTDMSOUND) +#include +#endif + +/* define IO_BASE for pcmcia */ +#define _IO_BASE 0x80000000 +#define _IO_BASE_SIZE 0x1000 + +#ifdef CONFIG_IDE +# define MAX_HWIFS 1 +#endif + +/* CPM Ethernet through SCCx. + * + * This ENET stuff is for the MPC850 with ethernet on SCC2. Some of + * this may be unique to the RPX-Lite configuration. + * Note TENA is on Port B. + */ +#define PA_ENET_RXD ((ushort)0x0004) +#define PA_ENET_TXD ((ushort)0x0008) +#define PA_ENET_TCLK ((ushort)0x0200) +#define PA_ENET_RCLK ((ushort)0x0800) +#define PB_ENET_TENA ((uint)0x00002000) +#define PC_ENET_CLSN ((ushort)0x0040) +#define PC_ENET_RENA ((ushort)0x0080) + +#define SICR_ENET_MASK ((uint)0x0000ff00) +#define SICR_ENET_CLKRT ((uint)0x00003d00) + +/* We don't use the 8259. +*/ +#define NR_8259_INTS 0 + +#endif /* !__ASSEMBLY__ */ +#endif /* __MACH_RPX_DEFS */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/sandpoint.c b/arch/ppc/platforms/sandpoint.c new file mode 100644 index 00000000000..531bfa0e451 --- /dev/null +++ b/arch/ppc/platforms/sandpoint.c @@ -0,0 +1,742 @@ +/* + * arch/ppc/platforms/sandpoint_setup.c + * + * Board setup routines for the Motorola SPS Sandpoint Test Platform. + * + * Author: Mark A. Greer + * mgreer@mvista.com + * + * 2000-2003 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +/* + * This file adds support for the Motorola SPS Sandpoint Test Platform. + * These boards have a PPMC slot for the processor so any combination + * of cpu and host bridge can be attached. This port is for an 8240 PPMC + * module from Motorola SPS and other closely related cpu/host bridge + * combinations (e.g., 750/755/7400 with MPC107 host bridge). + * The sandpoint itself has a Windbond 83c553 (PCI-ISA bridge, 2 DMA ctlrs, 2 + * cascaded 8259 interrupt ctlrs, 8254 Timer/Counter, and an IDE ctlr), a + * National 87308 (RTC, 2 UARTs, Keyboard & mouse ctlrs, and a floppy ctlr), + * and 4 PCI slots (only 2 of which are usable; the other 2 are keyed for 3.3V + * but are really 5V). + * + * The firmware on the sandpoint is called DINK (not my acronym :). This port + * depends on DINK to do some basic initialization (e.g., initialize the memory + * ctlr) and to ensure that the processor is using MAP B (CHRP map). + * + * The switch settings for the Sandpoint board MUST be as follows: + * S3: down + * S4: up + * S5: up + * S6: down + * + * 'down' is in the direction from the PCI slots towards the PPMC slot; + * 'up' is in the direction from the PPMC slot towards the PCI slots. + * Be careful, the way the sandpoint board is installed in XT chasses will + * make the directions reversed. + * + * Since Motorola listened to our suggestions for improvement, we now have + * the Sandpoint X3 board. All of the PCI slots are available, it uses + * the serial interrupt interface (just a hardware thing we need to + * configure properly). + * + * Use the default X3 switch settings. The interrupts are then: + * EPIC Source + * 0 SIOINT (8259, active low) + * 1 PCI #1 + * 2 PCI #2 + * 3 PCI #3 + * 4 PCI #4 + * 7 Winbond INTC (IDE interrupt) + * 8 Winbond INTD (IDE interrupt) + * + * + * Motorola has finally released a version of DINK32 that correctly + * (seemingly) initalizes the memory controller correctly, regardless + * of the amount of memory in the system. Once a method of determining + * what version of DINK initializes the system for us, if applicable, is + * found, we can hopefully stop hardcoding 32MB of RAM. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for linux/serial_core.h */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sandpoint.h" + +/* Set non-zero if an X2 Sandpoint detected. */ +static int sandpoint_is_x2; + +unsigned char __res[sizeof(bd_t)]; + +static void sandpoint_halt(void); +static void sandpoint_probe_type(void); + +/* + * Define all of the IRQ senses and polarities. Taken from the + * Sandpoint X3 User's manual. + */ +static u_char sandpoint_openpic_initsenses[] __initdata = { + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 0: SIOINT */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 2: PCI Slot 1 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 3: PCI Slot 2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 4: PCI Slot 3 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 5: PCI Slot 4 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 8: IDE (INT C) */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE) /* 9: IDE (INT D) */ +}; + +/* + * Motorola SPS Sandpoint interrupt routing. + */ +static inline int +x3_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + { 16, 0, 0, 0 }, /* IDSEL 11 - i8259 on Winbond */ + { 0, 0, 0, 0 }, /* IDSEL 12 - unused */ + { 18, 21, 20, 19 }, /* IDSEL 13 - PCI slot 1 */ + { 19, 18, 21, 20 }, /* IDSEL 14 - PCI slot 2 */ + { 20, 19, 18, 21 }, /* IDSEL 15 - PCI slot 3 */ + { 21, 20, 19, 18 }, /* IDSEL 16 - PCI slot 4 */ + }; + + const long min_idsel = 11, max_idsel = 16, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +} + +static inline int +x2_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + { 18, 0, 0, 0 }, /* IDSEL 11 - i8259 on Windbond */ + { 0, 0, 0, 0 }, /* IDSEL 12 - unused */ + { 16, 17, 18, 19 }, /* IDSEL 13 - PCI slot 1 */ + { 17, 18, 19, 16 }, /* IDSEL 14 - PCI slot 2 */ + { 18, 19, 16, 17 }, /* IDSEL 15 - PCI slot 3 */ + { 19, 16, 17, 18 }, /* IDSEL 16 - PCI slot 4 */ + }; + + const long min_idsel = 11, max_idsel = 16, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +} + +static void __init +sandpoint_setup_winbond_83553(struct pci_controller *hose) +{ + int devfn; + + /* + * Route IDE interrupts directly to the 8259's IRQ 14 & 15. + * We can't route the IDE interrupt to PCI INTC# or INTD# because those + * woule interfere with the PMC's INTC# and INTD# lines. + */ + /* + * Winbond Fcn 0 + */ + devfn = PCI_DEVFN(11,0); + + early_write_config_byte(hose, + 0, + devfn, + 0x43, /* IDE Interrupt Routing Control */ + 0xef); + early_write_config_word(hose, + 0, + devfn, + 0x44, /* PCI Interrupt Routing Control */ + 0x0000); + + /* Want ISA memory cycles to be forwarded to PCI bus */ + early_write_config_byte(hose, + 0, + devfn, + 0x48, /* ISA-to-PCI Addr Decoder Control */ + 0xf0); + + /* Enable Port 92. */ + early_write_config_byte(hose, + 0, + devfn, + 0x4e, /* AT System Control Register */ + 0x06); + /* + * Winbond Fcn 1 + */ + devfn = PCI_DEVFN(11,1); + + /* Put IDE controller into native mode. */ + early_write_config_byte(hose, + 0, + devfn, + 0x09, /* Programming interface Register */ + 0x8f); + + /* Init IRQ routing, enable both ports, disable fast 16 */ + early_write_config_dword(hose, + 0, + devfn, + 0x40, /* IDE Control/Status Register */ + 0x00ff0011); + return; +} + +/* On the sandpoint X2, we must avoid sending configuration cycles to + * device #12 (IDSEL addr = AD12). + */ +static int +x2_exclude_device(u_char bus, u_char devfn) +{ + if ((bus == 0) && (PCI_SLOT(devfn) == SANDPOINT_HOST_BRIDGE_IDSEL)) + return PCIBIOS_DEVICE_NOT_FOUND; + else + return PCIBIOS_SUCCESSFUL; +} + +static void __init +sandpoint_find_bridges(void) +{ + struct pci_controller *hose; + + hose = pcibios_alloc_controller(); + + if (!hose) + return; + + hose->first_busno = 0; + hose->last_busno = 0xff; + + if (mpc10x_bridge_init(hose, + MPC10X_MEM_MAP_B, + MPC10X_MEM_MAP_B, + MPC10X_MAPB_EUMB_BASE) == 0) { + + /* Do early winbond init, then scan PCI bus */ + sandpoint_setup_winbond_83553(hose); + hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); + + ppc_md.pcibios_fixup = NULL; + ppc_md.pcibios_fixup_bus = NULL; + ppc_md.pci_swizzle = common_swizzle; + if (sandpoint_is_x2) { + ppc_md.pci_map_irq = x2_map_irq; + ppc_md.pci_exclude_device = x2_exclude_device; + } else + ppc_md.pci_map_irq = x3_map_irq; + } + else { + if (ppc_md.progress) + ppc_md.progress("Bridge init failed", 0x100); + printk("Host bridge init failed\n"); + } + + return; +} + +static void __init +sandpoint_setup_arch(void) +{ + /* Probe for Sandpoint model */ + sandpoint_probe_type(); + if (sandpoint_is_x2) + epic_serial_mode = 0; + + loops_per_jiffy = 100000000 / HZ; + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = Root_NFS; +#else + ROOT_DEV = Root_HDA1; +#endif + + /* Lookup PCI host bridges */ + sandpoint_find_bridges(); + + printk(KERN_INFO "Motorola SPS Sandpoint Test Platform\n"); + printk(KERN_INFO "Port by MontaVista Software, Inc. (source@mvista.com)\n"); + + /* DINK32 12.3 and below do not correctly enable any caches. + * We will do this now with good known values. Future versions + * of DINK32 are supposed to get this correct. + */ + if (cpu_has_feature(CPU_FTR_SPEC7450)) + /* 745x is different. We only want to pass along enable. */ + _set_L2CR(L2CR_L2E); + else if (cpu_has_feature(CPU_FTR_L2CR)) + /* All modules have 1MB of L2. We also assume that an + * L2 divisor of 3 will work. + */ + _set_L2CR(L2CR_L2E | L2CR_L2SIZ_1MB | L2CR_L2CLK_DIV3 + | L2CR_L2RAM_PIPE | L2CR_L2OH_1_0 | L2CR_L2DF); +#if 0 + /* Untested right now. */ + if (cpu_has_feature(CPU_FTR_L3CR)) { + /* Magic value. */ + _set_L3CR(0x8f032000); + } +#endif +} + +#define SANDPOINT_87308_CFG_ADDR 0x15c +#define SANDPOINT_87308_CFG_DATA 0x15d + +#define SANDPOINT_87308_CFG_INB(addr, byte) { \ + outb((addr), SANDPOINT_87308_CFG_ADDR); \ + (byte) = inb(SANDPOINT_87308_CFG_DATA); \ +} + +#define SANDPOINT_87308_CFG_OUTB(addr, byte) { \ + outb((addr), SANDPOINT_87308_CFG_ADDR); \ + outb((byte), SANDPOINT_87308_CFG_DATA); \ +} + +#define SANDPOINT_87308_SELECT_DEV(dev_num) { \ + SANDPOINT_87308_CFG_OUTB(0x07, (dev_num)); \ +} + +#define SANDPOINT_87308_DEV_ENABLE(dev_num) { \ + SANDPOINT_87308_SELECT_DEV(dev_num); \ + SANDPOINT_87308_CFG_OUTB(0x30, 0x01); \ +} + +/* + * To probe the Sandpoint type, we need to check for a connection between GPIO + * pins 6 and 7 on the NS87308 SuperIO. + */ +static void __init sandpoint_probe_type(void) +{ + u8 x; + /* First, ensure that the GPIO pins are enabled. */ + SANDPOINT_87308_SELECT_DEV(0x07); /* Select GPIO logical device */ + SANDPOINT_87308_CFG_OUTB(0x60, 0x07); /* Base address 0x700 */ + SANDPOINT_87308_CFG_OUTB(0x61, 0x00); + SANDPOINT_87308_CFG_OUTB(0x30, 0x01); /* Enable */ + + /* Now, set pin 7 to output and pin 6 to input. */ + outb((inb(0x701) | 0x80) & 0xbf, 0x701); + /* Set push-pull output */ + outb(inb(0x702) | 0x80, 0x702); + /* Set pull-up on input */ + outb(inb(0x703) | 0x40, 0x703); + /* Set output high and check */ + x = inb(0x700); + outb(x | 0x80, 0x700); + x = inb(0x700); + sandpoint_is_x2 = ! (x & 0x40); + if (ppc_md.progress && sandpoint_is_x2) + ppc_md.progress("High output says X2", 0); + /* Set output low and check */ + outb(x & 0x7f, 0x700); + sandpoint_is_x2 |= inb(0x700) & 0x40; + if (ppc_md.progress && sandpoint_is_x2) + ppc_md.progress("Low output says X2", 0); + if (ppc_md.progress && ! sandpoint_is_x2) + ppc_md.progress("Sandpoint is X3", 0); +} + +/* + * Fix IDE interrupts. + */ +static int __init +sandpoint_fix_winbond_83553(void) +{ + /* Make some 8259 interrupt level sensitive */ + outb(0xe0, 0x4d0); + outb(0xde, 0x4d1); + + return 0; +} + +arch_initcall(sandpoint_fix_winbond_83553); + +/* + * Initialize the ISA devices on the Nat'l PC87308VUL SuperIO chip. + */ +static int __init +sandpoint_setup_natl_87308(void) +{ + u_char reg; + + /* + * Enable all the devices on the Super I/O chip. + */ + SANDPOINT_87308_SELECT_DEV(0x00); /* Select kbd logical device */ + SANDPOINT_87308_CFG_OUTB(0xf0, 0x00); /* Set KBC clock to 8 Mhz */ + SANDPOINT_87308_DEV_ENABLE(0x00); /* Enable keyboard */ + SANDPOINT_87308_DEV_ENABLE(0x01); /* Enable mouse */ + SANDPOINT_87308_DEV_ENABLE(0x02); /* Enable rtc */ + SANDPOINT_87308_DEV_ENABLE(0x03); /* Enable fdc (floppy) */ + SANDPOINT_87308_DEV_ENABLE(0x04); /* Enable parallel */ + SANDPOINT_87308_DEV_ENABLE(0x05); /* Enable UART 2 */ + SANDPOINT_87308_CFG_OUTB(0xf0, 0x82); /* Enable bank select regs */ + SANDPOINT_87308_DEV_ENABLE(0x06); /* Enable UART 1 */ + SANDPOINT_87308_CFG_OUTB(0xf0, 0x82); /* Enable bank select regs */ + + /* Set up floppy in PS/2 mode */ + outb(0x09, SIO_CONFIG_RA); + reg = inb(SIO_CONFIG_RD); + reg = (reg & 0x3F) | 0x40; + outb(reg, SIO_CONFIG_RD); + outb(reg, SIO_CONFIG_RD); /* Have to write twice to change! */ + + return 0; +} + +arch_initcall(sandpoint_setup_natl_87308); + +static int __init +sandpoint_request_io(void) +{ + request_region(0x00,0x20,"dma1"); + request_region(0x20,0x20,"pic1"); + request_region(0x40,0x20,"timer"); + request_region(0x80,0x10,"dma page reg"); + request_region(0xa0,0x20,"pic2"); + request_region(0xc0,0x20,"dma2"); + + return 0; +} + +arch_initcall(sandpoint_request_io); + +/* + * Interrupt setup and service. Interrrupts on the Sandpoint come + * from the four PCI slots plus the 8259 in the Winbond Super I/O (SIO). + * The 8259 is cascaded from EPIC IRQ0, IRQ1-4 map to PCI slots 1-4, + * IDE is on EPIC 7 and 8. + */ +static void __init +sandpoint_init_IRQ(void) +{ + int i; + + OpenPIC_InitSenses = sandpoint_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof(sandpoint_openpic_initsenses); + + mpc10x_set_openpic(); + openpic_hookup_cascade(sandpoint_is_x2 ? 17 : NUM_8259_INTERRUPTS, "82c59 cascade", + i8259_irq); + + /* + * openpic_init() has set up irq_desc[16-31] to be openpic + * interrupts. We need to set irq_desc[0-15] to be i8259 + * interrupts. + */ + for(i=0; i < NUM_8259_INTERRUPTS; i++) + irq_desc[i].handler = &i8259_pic; + + /* + * The EPIC allows for a read in the range of 0xFEF00000 -> + * 0xFEFFFFFF to generate a PCI interrupt-acknowledge transaction. + */ + i8259_init(0xfef00000); +} + +static u32 +sandpoint_irq_canonicalize(u32 irq) +{ + if (irq == 2) + return 9; + else + return irq; +} + +static unsigned long __init +sandpoint_find_end_of_memory(void) +{ + bd_t *bp = (bd_t *)__res; + + if (bp->bi_memsize) + return bp->bi_memsize; + + /* DINK32 13.0 correctly initalizes things, so iff you use + * this you _should_ be able to change this instead of a + * hardcoded value. */ +#if 0 + return mpc10x_get_mem_size(MPC10X_MEM_MAP_B); +#else + return 32*1024*1024; +#endif +} + +static void __init +sandpoint_map_io(void) +{ + io_block_mapping(0xfe000000, 0xfe000000, 0x02000000, _PAGE_IO); +} + +static void +sandpoint_restart(char *cmd) +{ + local_irq_disable(); + + /* Set exception prefix high - to the firmware */ + _nmask_and_or_msr(0, MSR_IP); + + /* Reset system via Port 92 */ + outb(0x00, 0x92); + outb(0x01, 0x92); + for(;;); /* Spin until reset happens */ +} + +static void +sandpoint_power_off(void) +{ + local_irq_disable(); + for(;;); /* No way to shut power off with software */ + /* NOTREACHED */ +} + +static void +sandpoint_halt(void) +{ + sandpoint_power_off(); + /* NOTREACHED */ +} + +static int +sandpoint_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "vendor\t\t: Motorola SPS\n"); + seq_printf(m, "machine\t\t: Sandpoint\n"); + + return 0; +} + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +/* + * IDE support. + */ +static int sandpoint_ide_ports_known = 0; +static unsigned long sandpoint_ide_regbase[MAX_HWIFS]; +static unsigned long sandpoint_ide_ctl_regbase[MAX_HWIFS]; +static unsigned long sandpoint_idedma_regbase; + +static void +sandpoint_ide_probe(void) +{ + struct pci_dev *pdev = pci_get_device(PCI_VENDOR_ID_WINBOND, + PCI_DEVICE_ID_WINBOND_82C105, NULL); + + if (pdev) { + sandpoint_ide_regbase[0]=pdev->resource[0].start; + sandpoint_ide_regbase[1]=pdev->resource[2].start; + sandpoint_ide_ctl_regbase[0]=pdev->resource[1].start; + sandpoint_ide_ctl_regbase[1]=pdev->resource[3].start; + sandpoint_idedma_regbase=pdev->resource[4].start; + pci_dev_put(pdev); + } + + sandpoint_ide_ports_known = 1; +} + +static int +sandpoint_ide_default_irq(unsigned long base) +{ + if (sandpoint_ide_ports_known == 0) + sandpoint_ide_probe(); + + if (base == sandpoint_ide_regbase[0]) + return SANDPOINT_IDE_INT0; + else if (base == sandpoint_ide_regbase[1]) + return SANDPOINT_IDE_INT1; + else + return 0; +} + +static unsigned long +sandpoint_ide_default_io_base(int index) +{ + if (sandpoint_ide_ports_known == 0) + sandpoint_ide_probe(); + + return sandpoint_ide_regbase[index]; +} + +static void __init +sandpoint_ide_init_hwif_ports(hw_regs_t *hw, unsigned long data_port, + unsigned long ctrl_port, int *irq) +{ + unsigned long reg = data_port; + uint alt_status_base; + int i; + + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { + hw->io_ports[i] = reg++; + } + + if (data_port == sandpoint_ide_regbase[0]) { + alt_status_base = sandpoint_ide_ctl_regbase[0] + 2; + hw->irq = 14; + } + else if (data_port == sandpoint_ide_regbase[1]) { + alt_status_base = sandpoint_ide_ctl_regbase[1] + 2; + hw->irq = 15; + } + else { + alt_status_base = 0; + hw->irq = 0; + } + + if (ctrl_port) { + hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port; + } else { + hw->io_ports[IDE_CONTROL_OFFSET] = alt_status_base; + } + + if (irq != NULL) { + *irq = hw->irq; + } +} +#endif + +/* + * Set BAT 3 to map 0xf8000000 to end of physical memory space 1-to-1. + */ +static __inline__ void +sandpoint_set_bat(void) +{ + unsigned long bat3u, bat3l; + + __asm__ __volatile__( + " lis %0,0xf800\n \ + ori %1,%0,0x002a\n \ + ori %0,%0,0x0ffe\n \ + mtspr 0x21e,%0\n \ + mtspr 0x21f,%1\n \ + isync\n \ + sync " + : "=r" (bat3u), "=r" (bat3l)); +} + +TODC_ALLOC(); + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + + /* ASSUMPTION: If both r3 (bd_t pointer) and r6 (cmdline pointer) + * are non-zero, then we should use the board info from the bd_t + * structure and the cmdline pointed to by r6 instead of the + * information from birecs, if any. Otherwise, use the information + * from birecs as discovered by the preceeding call to + * parse_bootinfo(). This rule should work with both PPCBoot, which + * uses a bd_t board info structure, and the kernel boot wrapper, + * which uses birecs. + */ + if (r3 && r6) { + /* copy board info structure */ + memcpy( (void *)__res,(void *)(r3+KERNELBASE), sizeof(bd_t) ); + /* copy command line */ + *(char *)(r7+KERNELBASE) = 0; + strcpy(cmd_line, (char *)(r6+KERNELBASE)); + } + +#ifdef CONFIG_BLK_DEV_INITRD + /* take care of initrd if we have one */ + if (r4) { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + /* Map in board regs, etc. */ + sandpoint_set_bat(); + + isa_io_base = MPC10X_MAPB_ISA_IO_BASE; + isa_mem_base = MPC10X_MAPB_ISA_MEM_BASE; + pci_dram_offset = MPC10X_MAPB_DRAM_OFFSET; + ISA_DMA_THRESHOLD = 0x00ffffff; + DMA_MODE_READ = 0x44; + DMA_MODE_WRITE = 0x48; + + ppc_md.setup_arch = sandpoint_setup_arch; + ppc_md.show_cpuinfo = sandpoint_show_cpuinfo; + ppc_md.irq_canonicalize = sandpoint_irq_canonicalize; + ppc_md.init_IRQ = sandpoint_init_IRQ; + ppc_md.get_irq = openpic_get_irq; + + ppc_md.restart = sandpoint_restart; + ppc_md.power_off = sandpoint_power_off; + ppc_md.halt = sandpoint_halt; + + ppc_md.find_end_of_memory = sandpoint_find_end_of_memory; + ppc_md.setup_io_mappings = sandpoint_map_io; + + TODC_INIT(TODC_TYPE_PC97307, 0x70, 0x00, 0x71, 8); + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.calibrate_decr = todc_calibrate_decr; + + ppc_md.nvram_read_val = todc_mc146818_read_val; + ppc_md.nvram_write_val = todc_mc146818_write_val; + +#ifdef CONFIG_KGDB + ppc_md.kgdb_map_scc = gen550_kgdb_map_scc; +#endif +#ifdef CONFIG_SERIAL_TEXT_DEBUG + ppc_md.progress = gen550_progress; +#endif + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) + ppc_ide_md.default_irq = sandpoint_ide_default_irq; + ppc_ide_md.default_io_base = sandpoint_ide_default_io_base; + ppc_ide_md.ide_init_hwif = sandpoint_ide_init_hwif_ports; +#endif +} diff --git a/arch/ppc/platforms/sandpoint.h b/arch/ppc/platforms/sandpoint.h new file mode 100644 index 00000000000..f4e982cb69d --- /dev/null +++ b/arch/ppc/platforms/sandpoint.h @@ -0,0 +1,80 @@ +/* + * arch/ppc/platforms/sandpoint.h + * + * Definitions for Motorola SPS Sandpoint Test Platform + * + * Author: Mark A. Greer + * mgreer@mvista.com + * + * 2000-2003 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +/* + * Sandpoint uses the CHRP map (Map B). + */ + +#ifndef __PPC_PLATFORMS_SANDPOINT_H +#define __PPC_PLATFORMS_SANDPOINT_H + +#include + +#if 0 +/* The Sandpoint X3 allows the IDE interrupt to be directly connected + * from the Windbond (PCI INTC or INTD) to the serial EPIC. Someday + * we should try this, but it was easier to use the existing 83c553 + * initialization than change it to route the different interrupts :-). + * -- Dan + */ +#define SANDPOINT_IDE_INT0 23 /* EPIC 7 */ +#define SANDPOINT_IDE_INT1 24 /* EPIC 8 */ +#else +#define SANDPOINT_IDE_INT0 14 /* 8259 Test */ +#define SANDPOINT_IDE_INT1 15 /* 8259 Test */ +#endif + +/* + * The sandpoint boards have processor modules that either have an 8240 or + * an MPC107 host bridge on them. These bridges have an IDSEL line that allows + * them to respond to PCI transactions as if they were a normal PCI devices. + * However, the processor on the processor side of the bridge can not reach + * out onto the PCI bus and then select the bridge or bad things will happen + * (documented in the 8240 and 107 manuals). + * Because of this, we always skip the bridge PCI device when accessing the + * PCI bus. The PCI slot that the bridge occupies is defined by the macro + * below. + */ +#define SANDPOINT_HOST_BRIDGE_IDSEL 12 + +/* + * Serial defines. + */ +#define SANDPOINT_SERIAL_0 0xfe0003f8 +#define SANDPOINT_SERIAL_1 0xfe0002f8 + +#define RS_TABLE_SIZE 2 + +/* Rate for the 1.8432 Mhz clock for the onboard serial chip */ +#define BASE_BAUD ( 1843200 / 16 ) +#define UART_CLK 1843200 + +#ifdef CONFIG_SERIAL_DETECT_IRQ +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_AUTO_IRQ) +#else +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF) +#endif + +#define STD_SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, SANDPOINT_SERIAL_0, 4, STD_COM_FLAGS, /* ttyS0 */ \ + iomem_base: (u8 *)SANDPOINT_SERIAL_0, \ + io_type: SERIAL_IO_MEM }, \ + { 0, BASE_BAUD, SANDPOINT_SERIAL_1, 3, STD_COM_FLAGS, /* ttyS1 */ \ + iomem_base: (u8 *)SANDPOINT_SERIAL_1, \ + io_type: SERIAL_IO_MEM }, + +#define SERIAL_PORT_DFNS \ + STD_SERIAL_PORT_DFNS + +#endif /* __PPC_PLATFORMS_SANDPOINT_H */ diff --git a/arch/ppc/platforms/sbc82xx.c b/arch/ppc/platforms/sbc82xx.c new file mode 100644 index 00000000000..74c9ff72c3d --- /dev/null +++ b/arch/ppc/platforms/sbc82xx.c @@ -0,0 +1,259 @@ +/* + * arch/ppc/platforms/sbc82xx.c + * + * SBC82XX platform support + * + * Author: Guy Streeter + * + * Derived from: est8260_setup.c by Allen Curtis, ONZ + * + * Copyright 2004 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static void (*callback_init_IRQ)(void); + +extern unsigned char __res[sizeof(bd_t)]; + +extern void (*late_time_init)(void); + +#ifdef CONFIG_GEN_RTC +TODC_ALLOC(); + +/* + * Timer init happens before mem_init but after paging init, so we cannot + * directly use ioremap() at that time. + * late_time_init() is call after paging init. + */ + +static void sbc82xx_time_init(void) +{ + volatile memctl_cpm2_t *mc = &cpm2_immr->im_memctl; + + /* Set up CS11 for RTC chip */ + mc->memc_br11=0; + mc->memc_or11=0xffff0836; + mc->memc_br11=SBC82xx_TODC_NVRAM_ADDR | 0x0801; + + TODC_INIT(TODC_TYPE_MK48T59, 0, 0, SBC82xx_TODC_NVRAM_ADDR, 0); + + todc_info->nvram_data = + (unsigned int)ioremap(todc_info->nvram_data, 0x2000); + BUG_ON(!todc_info->nvram_data); + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; + todc_time_init(); +} +#endif /* CONFIG_GEN_RTC */ + +static volatile char *sbc82xx_i8259_map; +static char sbc82xx_i8259_mask = 0xff; +static DEFINE_SPINLOCK(sbc82xx_i8259_lock); + +static void sbc82xx_i8259_mask_and_ack_irq(unsigned int irq_nr) +{ + unsigned long flags; + + irq_nr -= NR_SIU_INTS; + + spin_lock_irqsave(&sbc82xx_i8259_lock, flags); + sbc82xx_i8259_mask |= 1 << irq_nr; + (void) sbc82xx_i8259_map[1]; /* Dummy read */ + sbc82xx_i8259_map[1] = sbc82xx_i8259_mask; + sbc82xx_i8259_map[0] = 0x20; /* OCW2: Non-specific EOI */ + spin_unlock_irqrestore(&sbc82xx_i8259_lock, flags); +} + +static void sbc82xx_i8259_mask_irq(unsigned int irq_nr) +{ + unsigned long flags; + + irq_nr -= NR_SIU_INTS; + + spin_lock_irqsave(&sbc82xx_i8259_lock, flags); + sbc82xx_i8259_mask |= 1 << irq_nr; + sbc82xx_i8259_map[1] = sbc82xx_i8259_mask; + spin_unlock_irqrestore(&sbc82xx_i8259_lock, flags); +} + +static void sbc82xx_i8259_unmask_irq(unsigned int irq_nr) +{ + unsigned long flags; + + irq_nr -= NR_SIU_INTS; + + spin_lock_irqsave(&sbc82xx_i8259_lock, flags); + sbc82xx_i8259_mask &= ~(1 << irq_nr); + sbc82xx_i8259_map[1] = sbc82xx_i8259_mask; + spin_unlock_irqrestore(&sbc82xx_i8259_lock, flags); +} + +static void sbc82xx_i8259_end_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)) + && irq_desc[irq].action) + sbc82xx_i8259_unmask_irq(irq); +} + + +struct hw_interrupt_type sbc82xx_i8259_ic = { + .typename = " i8259 ", + .enable = sbc82xx_i8259_unmask_irq, + .disable = sbc82xx_i8259_mask_irq, + .ack = sbc82xx_i8259_mask_and_ack_irq, + .end = sbc82xx_i8259_end_irq, +}; + +static irqreturn_t sbc82xx_i8259_demux(int irq, void *dev_id, struct pt_regs *regs) +{ + spin_lock(&sbc82xx_i8259_lock); + + sbc82xx_i8259_map[0] = 0x0c; /* OCW3: Read IR register on RD# pulse */ + irq = sbc82xx_i8259_map[0] & 7; /* Read IRR */ + + if (irq == 7) { + /* Possible spurious interrupt */ + int isr; + sbc82xx_i8259_map[0] = 0x0b; /* OCW3: Read IS register on RD# pulse */ + isr = sbc82xx_i8259_map[0]; /* Read ISR */ + + if (!(isr & 0x80)) { + printk(KERN_INFO "Spurious i8259 interrupt\n"); + return IRQ_HANDLED; + } + } + __do_IRQ(NR_SIU_INTS + irq, regs); + return IRQ_HANDLED; +} + +static struct irqaction sbc82xx_i8259_irqaction = { + .handler = sbc82xx_i8259_demux, + .flags = SA_INTERRUPT, + .mask = CPU_MASK_NONE, + .name = "i8259 demux", +}; + +void __init sbc82xx_init_IRQ(void) +{ + volatile memctl_cpm2_t *mc = &cpm2_immr->im_memctl; + volatile intctl_cpm2_t *ic = &cpm2_immr->im_intctl; + int i; + + callback_init_IRQ(); + + /* u-boot doesn't always set the board up correctly */ + mc->memc_br5 = 0; + mc->memc_or5 = 0xfff00856; + mc->memc_br5 = 0x22000801; + + sbc82xx_i8259_map = ioremap(0x22008000, 2); + if (!sbc82xx_i8259_map) { + printk(KERN_CRIT "Mapping i8259 interrupt controller failed\n"); + return; + } + + /* Set up the interrupt handlers for the i8259 IRQs */ + for (i = NR_SIU_INTS; i < NR_SIU_INTS + 8; i++) { + irq_desc[i].handler = &sbc82xx_i8259_ic; + irq_desc[i].status |= IRQ_LEVEL; + } + + /* make IRQ6 level sensitive */ + ic->ic_siexr &= ~(1 << (14 - (SIU_INT_IRQ6 - SIU_INT_IRQ1))); + irq_desc[SIU_INT_IRQ6].status |= IRQ_LEVEL; + + /* Initialise the i8259 */ + sbc82xx_i8259_map[0] = 0x1b; /* ICW1: Level, no cascade, ICW4 */ + sbc82xx_i8259_map[1] = 0x00; /* ICW2: vector base */ + /* No ICW3 (no cascade) */ + sbc82xx_i8259_map[1] = 0x01; /* ICW4: 8086 mode, normal EOI */ + + sbc82xx_i8259_map[0] = 0x0b; /* OCW3: Read IS register on RD# pulse */ + + sbc82xx_i8259_map[1] = sbc82xx_i8259_mask; /* Set interrupt mask */ + + /* Request cascade IRQ */ + if (setup_irq(SIU_INT_IRQ6, &sbc82xx_i8259_irqaction)) { + printk("Installation of i8259 IRQ demultiplexer failed.\n"); + } +} + +static int sbc82xx_pci_map_irq(struct pci_dev *dev, unsigned char idsel, + unsigned char pin) +{ + static char pci_irq_table[][4] = { + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { SBC82xx_PIRQA, SBC82xx_PIRQB, SBC82xx_PIRQC, SBC82xx_PIRQD }, /* IDSEL 16 - PMC slot */ + { SBC82xx_PC_IRQA, SBC82xx_PC_IRQB, -1, -1 }, /* IDSEL 17 - CardBus */ + { SBC82xx_PIRQA, SBC82xx_PIRQB, SBC82xx_PIRQC, SBC82xx_PIRQD }, /* IDSEL 18 - PCI-X bridge */ + }; + + const long min_idsel = 16, max_idsel = 18, irqs_per_slot = 4; + + return PCI_IRQ_TABLE_LOOKUP; +} + +static void __devinit quirk_sbc8260_cardbus(struct pci_dev *pdev) +{ + uint32_t ctrl; + + if (pdev->bus->number != 0 || pdev->devfn != PCI_DEVFN(17, 0)) + return; + + printk(KERN_INFO "Setting up CardBus controller\n"); + + /* Set P2CCLK bit in System Control Register */ + pci_read_config_dword(pdev, 0x80, &ctrl); + ctrl |= (1<<27); + pci_write_config_dword(pdev, 0x80, ctrl); + + /* Set MFUNC up for PCI IRQ routing via INTA and INTB, and LEDs. */ + pci_write_config_dword(pdev, 0x8c, 0x00c01d22); + +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1420, quirk_sbc8260_cardbus); + +void __init +m82xx_board_init(void) +{ + /* u-boot may be using one of the FCC Ethernet devices. + Use the MAC address to the SCC. */ + __res[offsetof(bd_t, bi_enetaddr[5])] &= ~3; + + /* Anything special for this platform */ + callback_init_IRQ = ppc_md.init_IRQ; + + ppc_md.init_IRQ = sbc82xx_init_IRQ; + ppc_md.pci_map_irq = sbc82xx_pci_map_irq; +#ifdef CONFIG_GEN_RTC + ppc_md.time_init = NULL; + ppc_md.get_rtc_time = NULL; + ppc_md.set_rtc_time = NULL; + ppc_md.nvram_read_val = NULL; + ppc_md.nvram_write_val = NULL; + late_time_init = sbc82xx_time_init; +#endif /* CONFIG_GEN_RTC */ +} diff --git a/arch/ppc/platforms/sbc82xx.h b/arch/ppc/platforms/sbc82xx.h new file mode 100644 index 00000000000..e4042d4995f --- /dev/null +++ b/arch/ppc/platforms/sbc82xx.h @@ -0,0 +1,36 @@ +/* Board information for the SBCPowerQUICCII, which should be generic for + * all 8260 boards. The IMMR is now given to us so the hard define + * will soon be removed. All of the clock values are computed from + * the configuration SCMR and the Power-On-Reset word. + */ + +#ifndef __PPC_SBC82xx_H__ +#define __PPC_SBC82xx_H__ + +#include + +#define CPM_MAP_ADDR 0xf0000000 + +#define SBC82xx_TODC_NVRAM_ADDR 0xd0000000 + +#define SBC82xx_MACADDR_NVRAM_FCC1 0x220000c9 /* JP6B */ +#define SBC82xx_MACADDR_NVRAM_SCC1 0x220000cf /* JP6A */ +#define SBC82xx_MACADDR_NVRAM_FCC2 0x220000d5 /* JP7A */ +#define SBC82xx_MACADDR_NVRAM_FCC3 0x220000db /* JP7B */ + +/* For our show_cpuinfo hooks. */ +#define CPUINFO_VENDOR "Wind River" +#define CPUINFO_MACHINE "SBC PowerQUICC II" + +#define BOOTROM_RESTART_ADDR ((uint)0x40000104) + +#define SBC82xx_PC_IRQA (NR_SIU_INTS+0) +#define SBC82xx_PC_IRQB (NR_SIU_INTS+1) +#define SBC82xx_MPC185_IRQ (NR_SIU_INTS+2) +#define SBC82xx_ATM_IRQ (NR_SIU_INTS+3) +#define SBC82xx_PIRQA (NR_SIU_INTS+4) +#define SBC82xx_PIRQB (NR_SIU_INTS+5) +#define SBC82xx_PIRQC (NR_SIU_INTS+6) +#define SBC82xx_PIRQD (NR_SIU_INTS+7) + +#endif /* __PPC_SBC82xx_H__ */ diff --git a/arch/ppc/platforms/sbs8260.h b/arch/ppc/platforms/sbs8260.h new file mode 100644 index 00000000000..d51427a0f0d --- /dev/null +++ b/arch/ppc/platforms/sbs8260.h @@ -0,0 +1,28 @@ +#ifndef __ASSEMBLY__ +/* Board information for various SBS 8260 cards, which should be generic for + * all 8260 boards. The IMMR is now given to us so the hard define + * will soon be removed. All of the clock values are computed from + * the configuration SCMR and the Power-On-Reset word. + */ + +#define CPM_MAP_ADDR ((uint)0xfe000000) + + +/* A Board Information structure that is given to a program when + * prom starts it up. + */ +typedef struct bd_info { + unsigned int bi_memstart; /* Memory start address */ + unsigned int bi_memsize; /* Memory (end) size in bytes */ + unsigned int bi_intfreq; /* Internal Freq, in Hz */ + unsigned int bi_busfreq; /* Bus Freq, in MHz */ + unsigned int bi_cpmfreq; /* CPM Freq, in MHz */ + unsigned int bi_brgfreq; /* BRG Freq, in MHz */ + unsigned int bi_vco; /* VCO Out from PLL */ + unsigned int bi_baudrate; /* Default console baud rate */ + unsigned int bi_immr; /* IMMR when called from boot rom */ + unsigned char bi_enetaddr[6]; +} bd_t; + +extern bd_t m8xx_board_info; +#endif /* !__ASSEMBLY__ */ diff --git a/arch/ppc/platforms/spd8xx.h b/arch/ppc/platforms/spd8xx.h new file mode 100644 index 00000000000..ed48d144f41 --- /dev/null +++ b/arch/ppc/platforms/spd8xx.h @@ -0,0 +1,92 @@ +/* + * Speech Design SPD8xxTS board specific definitions + * + * Copyright (c) 2000,2001 Wolfgang Denk (wd@denx.de) + */ + +#ifdef __KERNEL__ +#ifndef __ASM_SPD8XX_H__ +#define __ASM_SPD8XX_H__ + +#include + +#include + +#ifndef __ASSEMBLY__ +#define SPD_IMMR_BASE 0xFFF00000 /* phys. addr of IMMR */ +#define SPD_IMAP_SIZE (64 * 1024) /* size of mapped area */ + +#define IMAP_ADDR SPD_IMMR_BASE /* physical base address of IMMR area */ +#define IMAP_SIZE SPD_IMAP_SIZE /* mapped size of IMMR area */ + +#define PCMCIA_MEM_ADDR ((uint)0xFE100000) +#define PCMCIA_MEM_SIZE ((uint)(64 * 1024)) + +#define IDE0_INTERRUPT 10 /* = IRQ5 */ +#define IDE1_INTERRUPT 12 /* = IRQ6 */ +#define CPM_INTERRUPT 13 /* = SIU_LEVEL6 (was: SIU_LEVEL2) */ + +/* override the default number of IDE hardware interfaces */ +#define MAX_HWIFS 2 + +/* + * Definitions for IDE0 Interface + */ +#define IDE0_BASE_OFFSET 0x0000 /* Offset in PCMCIA memory */ +#define IDE0_DATA_REG_OFFSET 0x0000 +#define IDE0_ERROR_REG_OFFSET 0x0081 +#define IDE0_NSECTOR_REG_OFFSET 0x0082 +#define IDE0_SECTOR_REG_OFFSET 0x0083 +#define IDE0_LCYL_REG_OFFSET 0x0084 +#define IDE0_HCYL_REG_OFFSET 0x0085 +#define IDE0_SELECT_REG_OFFSET 0x0086 +#define IDE0_STATUS_REG_OFFSET 0x0087 +#define IDE0_CONTROL_REG_OFFSET 0x0106 +#define IDE0_IRQ_REG_OFFSET 0x000A /* not used */ + +/* + * Definitions for IDE1 Interface + */ +#define IDE1_BASE_OFFSET 0x0C00 /* Offset in PCMCIA memory */ +#define IDE1_DATA_REG_OFFSET 0x0000 +#define IDE1_ERROR_REG_OFFSET 0x0081 +#define IDE1_NSECTOR_REG_OFFSET 0x0082 +#define IDE1_SECTOR_REG_OFFSET 0x0083 +#define IDE1_LCYL_REG_OFFSET 0x0084 +#define IDE1_HCYL_REG_OFFSET 0x0085 +#define IDE1_SELECT_REG_OFFSET 0x0086 +#define IDE1_STATUS_REG_OFFSET 0x0087 +#define IDE1_CONTROL_REG_OFFSET 0x0106 +#define IDE1_IRQ_REG_OFFSET 0x000A /* not used */ + +/* CPM Ethernet through SCCx. + * + * Bits in parallel I/O port registers that have to be set/cleared + * to configure the pins for SCC2 use. + */ +#define PA_ENET_MDC ((ushort)0x0001) /* PA 15 !!! */ +#define PA_ENET_MDIO ((ushort)0x0002) /* PA 14 !!! */ +#define PA_ENET_RXD ((ushort)0x0004) /* PA 13 */ +#define PA_ENET_TXD ((ushort)0x0008) /* PA 12 */ +#define PA_ENET_RCLK ((ushort)0x0200) /* PA 6 */ +#define PA_ENET_TCLK ((ushort)0x0400) /* PA 5 */ + +#define PB_ENET_TENA ((uint)0x00002000) /* PB 18 */ + +#define PC_ENET_CLSN ((ushort)0x0040) /* PC 9 */ +#define PC_ENET_RENA ((ushort)0x0080) /* PC 8 */ +#define PC_ENET_RESET ((ushort)0x0100) /* PC 7 !!! */ + +/* Control bits in the SICR to route TCLK (CLK3) and RCLK (CLK2) to + * SCC2. Also, make sure GR2 (bit 16) and SC2 (bit 17) are zero. + */ +#define SICR_ENET_MASK ((uint)0x0000ff00) +#define SICR_ENET_CLKRT ((uint)0x00002E00) + +/* We don't use the 8259. +*/ +#define NR_8259_INTS 0 + +#endif /* !__ASSEMBLY__ */ +#endif /* __ASM_SPD8XX_H__ */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/spruce.c b/arch/ppc/platforms/spruce.c new file mode 100644 index 00000000000..5ad70d357cb --- /dev/null +++ b/arch/ppc/platforms/spruce.c @@ -0,0 +1,325 @@ +/* + * arch/ppc/platforms/spruce.c + * + * Board and PCI setup routines for IBM Spruce + * + * Author: MontaVista Software + * + * 2000-2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "spruce.h" + +static inline int +spruce_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {23, 24, 25, 26}, /* IDSEL 1 - PCI slot 3 */ + {24, 25, 26, 23}, /* IDSEL 2 - PCI slot 2 */ + {25, 26, 23, 24}, /* IDSEL 3 - PCI slot 1 */ + {26, 23, 24, 25}, /* IDSEL 4 - PCI slot 0 */ + }; + + const long min_idsel = 1, max_idsel = 4, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +} + +static void __init +spruce_setup_hose(void) +{ + struct pci_controller *hose; + + /* Setup hose */ + hose = pcibios_alloc_controller(); + if (!hose) + return; + + hose->first_busno = 0; + hose->last_busno = 0xff; + + pci_init_resource(&hose->io_resource, + SPRUCE_PCI_LOWER_IO, + SPRUCE_PCI_UPPER_IO, + IORESOURCE_IO, + "PCI host bridge"); + + pci_init_resource(&hose->mem_resources[0], + SPRUCE_PCI_LOWER_MEM, + SPRUCE_PCI_UPPER_MEM, + IORESOURCE_MEM, + "PCI host bridge"); + + hose->io_space.start = SPRUCE_PCI_LOWER_IO; + hose->io_space.end = SPRUCE_PCI_UPPER_IO; + hose->mem_space.start = SPRUCE_PCI_LOWER_MEM; + hose->mem_space.end = SPRUCE_PCI_UPPER_MEM; + hose->io_base_virt = (void *)SPRUCE_ISA_IO_BASE; + + setup_indirect_pci(hose, + SPRUCE_PCI_CONFIG_ADDR, + SPRUCE_PCI_CONFIG_DATA); + + hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); + + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = spruce_map_irq; +} + +/* + * CPC700 PIC interrupt programming table + * + * First entry is the sensitivity (level/edge), second is the polarity. + */ +unsigned int cpc700_irq_assigns[32][2] = { + { 1, 1 }, /* IRQ 0: ECC Correctable Error - rising edge */ + { 1, 1 }, /* IRQ 1: PCI Write Mem Range - rising edge */ + { 0, 1 }, /* IRQ 2: PCI Write Command Reg - active high */ + { 0, 1 }, /* IRQ 3: UART 0 - active high */ + { 0, 1 }, /* IRQ 4: UART 1 - active high */ + { 0, 1 }, /* IRQ 5: ICC 0 - active high */ + { 0, 1 }, /* IRQ 6: ICC 1 - active high */ + { 0, 1 }, /* IRQ 7: GPT Compare 0 - active high */ + { 0, 1 }, /* IRQ 8: GPT Compare 1 - active high */ + { 0, 1 }, /* IRQ 9: GPT Compare 2 - active high */ + { 0, 1 }, /* IRQ 10: GPT Compare 3 - active high */ + { 0, 1 }, /* IRQ 11: GPT Compare 4 - active high */ + { 0, 1 }, /* IRQ 12: GPT Capture 0 - active high */ + { 0, 1 }, /* IRQ 13: GPT Capture 1 - active high */ + { 0, 1 }, /* IRQ 14: GPT Capture 2 - active high */ + { 0, 1 }, /* IRQ 15: GPT Capture 3 - active high */ + { 0, 1 }, /* IRQ 16: GPT Capture 4 - active high */ + { 0, 0 }, /* IRQ 17: Reserved */ + { 0, 0 }, /* IRQ 18: Reserved */ + { 0, 0 }, /* IRQ 19: Reserved */ + { 0, 1 }, /* IRQ 20: FPGA EXT_IRQ0 - active high */ + { 1, 1 }, /* IRQ 21: Mouse - rising edge */ + { 1, 1 }, /* IRQ 22: Keyboard - rising edge */ + { 0, 0 }, /* IRQ 23: PCI Slot 3 - active low */ + { 0, 0 }, /* IRQ 24: PCI Slot 2 - active low */ + { 0, 0 }, /* IRQ 25: PCI Slot 1 - active low */ + { 0, 0 }, /* IRQ 26: PCI Slot 0 - active low */ +}; + +static void __init +spruce_calibrate_decr(void) +{ + int freq, divisor = 4; + + /* determine processor bus speed */ + freq = SPRUCE_BUS_SPEED; + tb_ticks_per_jiffy = freq / HZ / divisor; + tb_to_us = mulhwu_scale_factor(freq/divisor, 1000000); +} + +static int +spruce_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "vendor\t\t: IBM\n"); + seq_printf(m, "machine\t\t: Spruce\n"); + + return 0; +} + +static void __init +spruce_early_serial_map(void) +{ + u32 uart_clk; + struct uart_port serial_req; + + if (SPRUCE_UARTCLK_IS_33M(readb(SPRUCE_FPGA_REG_A))) + uart_clk = SPRUCE_BAUD_33M * 16; + else + uart_clk = SPRUCE_BAUD_30M * 16; + + /* Setup serial port access */ + memset(&serial_req, 0, sizeof(serial_req)); + serial_req.uartclk = uart_clk; + serial_req.irq = UART0_INT; + serial_req.flags = ASYNC_BOOT_AUTOCONF; + serial_req.iotype = SERIAL_IO_MEM; + serial_req.membase = (u_char *)UART0_IO_BASE; + serial_req.regshift = 0; + +#if defined(CONFIG_KGDB) || defined(CONFIG_SERIAL_TEXT_DEBUG) + gen550_init(0, &serial_req); +#endif +#ifdef CONFIG_SERIAL_8250 + if (early_serial_setup(&serial_req) != 0) + printk("Early serial init of port 0 failed\n"); +#endif + + /* Assume early_serial_setup() doesn't modify serial_req */ + serial_req.line = 1; + serial_req.irq = UART1_INT; + serial_req.membase = (u_char *)UART1_IO_BASE; + +#if defined(CONFIG_KGDB) || defined(CONFIG_SERIAL_TEXT_DEBUG) + gen550_init(1, &serial_req); +#endif +#ifdef CONFIG_SERIAL_8250 + if (early_serial_setup(&serial_req) != 0) + printk("Early serial init of port 1 failed\n"); +#endif +} + +TODC_ALLOC(); + +static void __init +spruce_setup_arch(void) +{ + /* Setup TODC access */ + TODC_INIT(TODC_TYPE_DS1643, 0, 0, SPRUCE_RTC_BASE_ADDR, 8); + + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_jiffy = 50000000 / HZ; + + /* Setup PCI host bridge */ + spruce_setup_hose(); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = Root_NFS; +#else + ROOT_DEV = Root_SDA1; +#endif + + /* Identify the system */ + printk(KERN_INFO "System Identification: IBM Spruce\n"); + printk(KERN_INFO "Port by MontaVista Software, Inc. (source@mvista.com)\n"); +} + +static void +spruce_restart(char *cmd) +{ + local_irq_disable(); + + /* SRR0 has system reset vector, SRR1 has default MSR value */ + /* rfi restores MSR from SRR1 and sets the PC to the SRR0 value */ + __asm__ __volatile__ + ("\n\ + lis 3,0xfff0 \n\ + ori 3,3,0x0100 \n\ + mtspr 26,3 \n\ + li 3,0 \n\ + mtspr 27,3 \n\ + rfi \n\ + "); + for(;;); +} + +static void +spruce_power_off(void) +{ + for(;;); +} + +static void +spruce_halt(void) +{ + spruce_restart(NULL); +} + +static void __init +spruce_map_io(void) +{ + io_block_mapping(SPRUCE_PCI_IO_BASE, SPRUCE_PCI_PHY_IO_BASE, + 0x08000000, _PAGE_IO); +} + +/* + * Set BAT 3 to map 0xf8000000 to end of physical memory space 1-to-1. + */ +static __inline__ void +spruce_set_bat(void) +{ + mb(); + mtspr(SPRN_DBAT1U, 0xf8000ffe); + mtspr(SPRN_DBAT1L, 0xf800002a); + mb(); +} + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + + /* Map in board regs, etc. */ + spruce_set_bat(); + + isa_io_base = SPRUCE_ISA_IO_BASE; + pci_dram_offset = SPRUCE_PCI_SYS_MEM_BASE; + + ppc_md.setup_arch = spruce_setup_arch; + ppc_md.show_cpuinfo = spruce_show_cpuinfo; + ppc_md.init_IRQ = cpc700_init_IRQ; + ppc_md.get_irq = cpc700_get_irq; + + ppc_md.setup_io_mappings = spruce_map_io; + + ppc_md.restart = spruce_restart; + ppc_md.power_off = spruce_power_off; + ppc_md.halt = spruce_halt; + + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.calibrate_decr = spruce_calibrate_decr; + + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; + + spruce_early_serial_map(); + +#ifdef CONFIG_SERIAL_TEXT_DEBUG + ppc_md.progress = gen550_progress; +#endif /* CONFIG_SERIAL_TEXT_DEBUG */ +#ifdef CONFIG_KGDB + ppc_md.kgdb_map_scc = gen550_kgdb_map_scc; +#endif +} diff --git a/arch/ppc/platforms/spruce.h b/arch/ppc/platforms/spruce.h new file mode 100644 index 00000000000..a31ff7ee698 --- /dev/null +++ b/arch/ppc/platforms/spruce.h @@ -0,0 +1,71 @@ +/* + * include/asm-ppc/platforms/spruce.h + * + * Definitions for IBM Spruce reference board support + * + * Authors: Matt Porter and Johnnie Peters + * mporter@mvista.com + * jpeters@mvista.com + * + * 2001 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_SPRUCE_H__ +#define __ASM_SPRUCE_H__ + +#define SPRUCE_PCI_CONFIG_ADDR 0xfec00000 +#define SPRUCE_PCI_CONFIG_DATA 0xfec00004 + +#define SPRUCE_PCI_PHY_IO_BASE 0xf8000000 +#define SPRUCE_PCI_IO_BASE SPRUCE_PCI_PHY_IO_BASE + +#define SPRUCE_PCI_SYS_MEM_BASE 0x00000000 + +#define SPRUCE_PCI_LOWER_MEM 0x80000000 +#define SPRUCE_PCI_UPPER_MEM 0x9fffffff +#define SPRUCE_PCI_LOWER_IO 0x00000000 +#define SPRUCE_PCI_UPPER_IO 0x03ffffff + +#define SPRUCE_ISA_IO_BASE SPRUCE_PCI_IO_BASE + +#define SPRUCE_MEM_SIZE 0x04000000 +#define SPRUCE_BUS_SPEED 66666667 + +#define SPRUCE_NVRAM_BASE_ADDR 0xff800000 +#define SPRUCE_RTC_BASE_ADDR SPRUCE_NVRAM_BASE_ADDR + +/* + * Serial port defines + */ +#define SPRUCE_FPGA_REG_A 0xff820000 +#define SPRUCE_UARTCLK_33M 0x02 +#define SPRUCE_UARTCLK_IS_33M(reg) (reg & SPRUCE_UARTCLK_33M) + +#define UART0_IO_BASE 0xff600300 +#define UART1_IO_BASE 0xff600400 + +#define RS_TABLE_SIZE 2 + +#define SPRUCE_BAUD_33M (33000000/64) +#define SPRUCE_BAUD_30M (30000000/64) +#define BASE_BAUD SPRUCE_BAUD_33M + +#define UART0_INT 3 +#define UART1_INT 4 + +#define STD_UART_OP(num) \ + { 0, BASE_BAUD, 0, UART##num##_INT, \ + ASYNC_BOOT_AUTOCONF, \ + iomem_base: (unsigned char *) UART##num##_IO_BASE, \ + io_type: SERIAL_IO_MEM}, + +#define SERIAL_PORT_DFNS \ + STD_UART_OP(0) \ + STD_UART_OP(1) + +#endif /* __ASM_SPRUCE_H__ */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/tqm8260.h b/arch/ppc/platforms/tqm8260.h new file mode 100644 index 00000000000..c7a78a646c6 --- /dev/null +++ b/arch/ppc/platforms/tqm8260.h @@ -0,0 +1,23 @@ +/* + * TQM8260 board specific definitions + * + * Copyright (c) 2001 Wolfgang Denk (wd@denx.de) + */ + +#ifndef __TQM8260_PLATFORM +#define __TQM8260_PLATFORM + +#include + +#include + +#define CPM_MAP_ADDR ((uint)0xFFF00000) +#define PHY_INTERRUPT 25 + +/* For our show_cpuinfo hooks. */ +#define CPUINFO_VENDOR "IN2 Systems" +#define CPUINFO_MACHINE "TQM8260 PowerPC" + +#define BOOTROM_RESTART_ADDR ((uint)0x40000104) + +#endif /* __TQM8260_PLATFORM */ diff --git a/arch/ppc/platforms/tqm8260_setup.c b/arch/ppc/platforms/tqm8260_setup.c new file mode 100644 index 00000000000..a8880bfc034 --- /dev/null +++ b/arch/ppc/platforms/tqm8260_setup.c @@ -0,0 +1,44 @@ +/* + * arch/ppc/platforms/tqm8260_setup.c + * + * TQM8260 platform support + * + * Author: Allen Curtis + * Derived from: m8260_setup.c by Dan Malek, MVista + * + * Copyright 2002 Ones and Zeros, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include + +#include +#include +#include + +static int +tqm8260_set_rtc_time(unsigned long time) +{ + ((cpm2_map_t *)CPM_MAP_ADDR)->im_sit.sit_tmcnt = time; + ((cpm2_map_t *)CPM_MAP_ADDR)->im_sit.sit_tmcntsc = 0x3; + + return(0); +} + +static unsigned long +tqm8260_get_rtc_time(void) +{ + return ((cpm2_map_t *)CPM_MAP_ADDR)->im_sit.sit_tmcnt; +} + +void __init +m82xx_board_init(void) +{ + /* Anything special for this platform */ + ppc_md.set_rtc_time = tqm8260_set_rtc_time; + ppc_md.get_rtc_time = tqm8260_get_rtc_time; +} diff --git a/arch/ppc/platforms/tqm8xx.h b/arch/ppc/platforms/tqm8xx.h new file mode 100644 index 00000000000..2150dc87b18 --- /dev/null +++ b/arch/ppc/platforms/tqm8xx.h @@ -0,0 +1,179 @@ +/* + * TQM8xx(L) board specific definitions + * + * Copyright (c) 1999-2002 Wolfgang Denk (wd@denx.de) + */ + +#ifdef __KERNEL__ +#ifndef __MACH_TQM8xx_H +#define __MACH_TQM8xx_H + +#include + +#include + +#ifndef __ASSEMBLY__ +#define TQM_IMMR_BASE 0xFFF00000 /* phys. addr of IMMR */ +#define TQM_IMAP_SIZE (64 * 1024) /* size of mapped area */ + +#define IMAP_ADDR TQM_IMMR_BASE /* physical base address of IMMR area */ +#define IMAP_SIZE TQM_IMAP_SIZE /* mapped size of IMMR area */ + +/*----------------------------------------------------------------------- + * PCMCIA stuff + *----------------------------------------------------------------------- + * + */ +#define PCMCIA_MEM_SIZE ( 64 << 20 ) + +#ifndef CONFIG_KUP4K +# define MAX_HWIFS 1 /* overwrite default in include/asm-ppc/ide.h */ + +#else /* CONFIG_KUP4K */ + +# define MAX_HWIFS 2 /* overwrite default in include/asm-ppc/ide.h */ +# ifndef __ASSEMBLY__ +# include +static __inline__ void ide_led(int on) +{ + volatile immap_t *immap = (immap_t *)IMAP_ADDR; + + if (on) { + immap->im_ioport.iop_padat &= ~0x80; + } else { + immap->im_ioport.iop_padat |= 0x80; + } +} +# endif /* __ASSEMBLY__ */ +# define IDE_LED(x) ide_led((x)) +#endif /* CONFIG_KUP4K */ + +/* + * Definitions for IDE0 Interface + */ +#define IDE0_BASE_OFFSET 0 +#define IDE0_DATA_REG_OFFSET (PCMCIA_MEM_SIZE + 0x320) +#define IDE0_ERROR_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 1) +#define IDE0_NSECTOR_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 2) +#define IDE0_SECTOR_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 3) +#define IDE0_LCYL_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 4) +#define IDE0_HCYL_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 5) +#define IDE0_SELECT_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 6) +#define IDE0_STATUS_REG_OFFSET (2 * PCMCIA_MEM_SIZE + 0x320 + 7) +#define IDE0_CONTROL_REG_OFFSET 0x0106 +#define IDE0_IRQ_REG_OFFSET 0x000A /* not used */ + +/* define IO_BASE for PCMCIA */ +#define _IO_BASE 0x80000000 +#define _IO_BASE_SIZE (64<<10) + +#define FEC_INTERRUPT 9 /* = SIU_LEVEL4 */ +#define PHY_INTERRUPT 12 /* = IRQ6 */ +#define IDE0_INTERRUPT 13 + +#ifdef CONFIG_IDE +#endif + +/*----------------------------------------------------------------------- + * CPM Ethernet through SCCx. + *----------------------------------------------------------------------- + * + */ + +/*** TQM823L, TQM850L ***********************************************/ + +#if defined(CONFIG_TQM823L) || defined(CONFIG_TQM850L) +/* Bits in parallel I/O port registers that have to be set/cleared + * to configure the pins for SCC1 use. + */ +#define PA_ENET_RXD ((ushort)0x0004) /* PA 13 */ +#define PA_ENET_TXD ((ushort)0x0008) /* PA 12 */ +#define PA_ENET_RCLK ((ushort)0x0100) /* PA 7 */ +#define PA_ENET_TCLK ((ushort)0x0400) /* PA 5 */ + +#define PB_ENET_TENA ((uint)0x00002000) /* PB 18 */ + +#define PC_ENET_CLSN ((ushort)0x0040) /* PC 9 */ +#define PC_ENET_RENA ((ushort)0x0080) /* PC 8 */ + +/* Control bits in the SICR to route TCLK (CLK3) and RCLK (CLK1) to + * SCC2. Also, make sure GR2 (bit 16) and SC2 (bit 17) are zero. + */ +#define SICR_ENET_MASK ((uint)0x0000ff00) +#define SICR_ENET_CLKRT ((uint)0x00002600) +#endif /* CONFIG_TQM823L, CONFIG_TQM850L */ + +/*** TQM860L ********************************************************/ + +#ifdef CONFIG_TQM860L +/* Bits in parallel I/O port registers that have to be set/cleared + * to configure the pins for SCC1 use. + */ +#define PA_ENET_RXD ((ushort)0x0001) /* PA 15 */ +#define PA_ENET_TXD ((ushort)0x0002) /* PA 14 */ +#define PA_ENET_RCLK ((ushort)0x0100) /* PA 7 */ +#define PA_ENET_TCLK ((ushort)0x0400) /* PA 5 */ + +#define PC_ENET_TENA ((ushort)0x0001) /* PC 15 */ +#define PC_ENET_CLSN ((ushort)0x0010) /* PC 11 */ +#define PC_ENET_RENA ((ushort)0x0020) /* PC 10 */ + +/* Control bits in the SICR to route TCLK (CLK3) and RCLK (CLK1) to + * SCC1. Also, make sure GR1 (bit 24) and SC1 (bit 25) are zero. + */ +#define SICR_ENET_MASK ((uint)0x000000ff) +#define SICR_ENET_CLKRT ((uint)0x00000026) +#endif /* CONFIG_TQM860L */ + +/*** FPS850L *********************************************************/ + +#ifdef CONFIG_FPS850L +/* Bits in parallel I/O port registers that have to be set/cleared + * to configure the pins for SCC1 use. + */ +#define PA_ENET_RXD ((ushort)0x0004) /* PA 13 */ +#define PA_ENET_TXD ((ushort)0x0008) /* PA 12 */ +#define PA_ENET_RCLK ((ushort)0x0100) /* PA 7 */ +#define PA_ENET_TCLK ((ushort)0x0400) /* PA 5 */ + +#define PC_ENET_TENA ((ushort)0x0002) /* PC 14 */ +#define PC_ENET_CLSN ((ushort)0x0040) /* PC 9 */ +#define PC_ENET_RENA ((ushort)0x0080) /* PC 8 */ + +/* Control bits in the SICR to route TCLK (CLK2) and RCLK (CLK4) to + * SCC2. Also, make sure GR2 (bit 16) and SC2 (bit 17) are zero. + */ +#define SICR_ENET_MASK ((uint)0x0000ff00) +#define SICR_ENET_CLKRT ((uint)0x00002600) +#endif /* CONFIG_FPS850L */ + +/*** SM850 *********************************************************/ + +/* The SM850 Service Module uses SCC2 for IrDA and SCC3 for Ethernet */ + +#ifdef CONFIG_SM850 +#define PB_ENET_RXD ((uint)0x00000004) /* PB 29 */ +#define PB_ENET_TXD ((uint)0x00000002) /* PB 30 */ +#define PA_ENET_RCLK ((ushort)0x0100) /* PA 7 */ +#define PA_ENET_TCLK ((ushort)0x0400) /* PA 5 */ + +#define PC_ENET_LBK ((ushort)0x0008) /* PC 12 */ +#define PC_ENET_TENA ((ushort)0x0004) /* PC 13 */ + +#define PC_ENET_RENA ((ushort)0x0800) /* PC 4 */ +#define PC_ENET_CLSN ((ushort)0x0400) /* PC 5 */ + +/* Control bits in the SICR to route TCLK (CLK3) and RCLK (CLK1) to + * SCC3. Also, make sure GR3 (bit 8) and SC3 (bit 9) are zero. + */ +#define SICR_ENET_MASK ((uint)0x00FF0000) +#define SICR_ENET_CLKRT ((uint)0x00260000) +#endif /* CONFIG_SM850 */ + +/* We don't use the 8259. +*/ +#define NR_8259_INTS 0 + +#endif /* !__ASSEMBLY__ */ +#endif /* __MACH_TQM8xx_H */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/syslib/Makefile b/arch/ppc/syslib/Makefile new file mode 100644 index 00000000000..dd418ea3426 --- /dev/null +++ b/arch/ppc/syslib/Makefile @@ -0,0 +1,115 @@ +# +# Makefile for the linux kernel. +# + +CFLAGS_prom_init.o += -fPIC +CFLAGS_btext.o += -fPIC + +wdt-mpc8xx-$(CONFIG_8xx_WDT) += m8xx_wdt.o + +obj-$(CONFIG_PPCBUG_NVRAM) += prep_nvram.o +obj-$(CONFIG_PPC_OCP) += ocp.o +obj-$(CONFIG_IBM_OCP) += ibm_ocp.o +obj-$(CONFIG_44x) += ibm44x_common.o +obj-$(CONFIG_440GP) += ibm440gp_common.o +obj-$(CONFIG_440GX) += ibm440gx_common.o +obj-$(CONFIG_440SP) += ibm440gx_common.o ibm440sp_common.o +ifeq ($(CONFIG_4xx),y) +ifeq ($(CONFIG_VIRTEX_II_PRO),y) +obj-$(CONFIG_40x) += xilinx_pic.o +else +ifeq ($(CONFIG_403),y) +obj-$(CONFIG_40x) += ppc403_pic.o +else +obj-$(CONFIG_40x) += ppc4xx_pic.o +endif +endif +obj-$(CONFIG_44x) += ppc4xx_pic.o +obj-$(CONFIG_40x) += ppc4xx_setup.o +obj-$(CONFIG_GEN_RTC) += todc_time.o +obj-$(CONFIG_PPC4xx_DMA) += ppc4xx_dma.o +obj-$(CONFIG_PPC4xx_EDMA) += ppc4xx_sgdma.o +ifeq ($(CONFIG_40x),y) +obj-$(CONFIG_PCI) += indirect_pci.o pci_auto.o ppc405_pci.o +endif +endif +obj-$(CONFIG_8xx) += m8xx_setup.o ppc8xx_pic.o $(wdt-mpc8xx-y) +ifeq ($(CONFIG_8xx),y) +obj-$(CONFIG_PCI) += qspan_pci.o i8259.o +endif +obj-$(CONFIG_PPC_OF) += prom_init.o prom.o of_device.o +obj-$(CONFIG_PPC_PMAC) += open_pic.o indirect_pci.o +obj-$(CONFIG_POWER4) += open_pic2.o +obj-$(CONFIG_PPC_CHRP) += open_pic.o indirect_pci.o i8259.o +obj-$(CONFIG_PPC_PREP) += open_pic.o indirect_pci.o i8259.o todc_time.o +obj-$(CONFIG_ADIR) += i8259.o indirect_pci.o pci_auto.o \ + todc_time.o +obj-$(CONFIG_CPCI690) += todc_time.o pci_auto.o +obj-$(CONFIG_EBONY) += indirect_pci.o pci_auto.o todc_time.o +obj-$(CONFIG_EV64260) += todc_time.o pci_auto.o +obj-$(CONFIG_CHESTNUT) += mv64360_pic.o pci_auto.o +obj-$(CONFIG_GEMINI) += open_pic.o indirect_pci.o +obj-$(CONFIG_GT64260) += gt64260_pic.o +obj-$(CONFIG_K2) += i8259.o indirect_pci.o todc_time.o \ + pci_auto.o +obj-$(CONFIG_LOPEC) += i8259.o pci_auto.o todc_time.o +obj-$(CONFIG_HDPU) += pci_auto.o +obj-$(CONFIG_LUAN) += indirect_pci.o pci_auto.o todc_time.o +obj-$(CONFIG_KATANA) += pci_auto.o +obj-$(CONFIG_MCPN765) += todc_time.o indirect_pci.o pci_auto.o \ + open_pic.o i8259.o hawk_common.o +obj-$(CONFIG_MENF1) += todc_time.o i8259.o mpc10x_common.o \ + pci_auto.o indirect_pci.o +obj-$(CONFIG_MV64360) += mv64360_pic.o +obj-$(CONFIG_MV64X60) += mv64x60.o mv64x60_win.o indirect_pci.o +obj-$(CONFIG_MVME5100) += open_pic.o todc_time.o indirect_pci.o \ + pci_auto.o hawk_common.o +obj-$(CONFIG_MVME5100_IPMC761_PRESENT) += i8259.o +obj-$(CONFIG_OCOTEA) += indirect_pci.o pci_auto.o todc_time.o +obj-$(CONFIG_PAL4) += cpc700_pic.o +obj-$(CONFIG_PCORE) += todc_time.o i8259.o pci_auto.o +obj-$(CONFIG_POWERPMC250) += pci_auto.o +obj-$(CONFIG_PPLUS) += hawk_common.o open_pic.o i8259.o \ + indirect_pci.o todc_time.o pci_auto.o +obj-$(CONFIG_PRPMC750) += open_pic.o indirect_pci.o pci_auto.o \ + hawk_common.o +obj-$(CONFIG_HARRIER) += harrier.o +obj-$(CONFIG_PRPMC800) += open_pic.o indirect_pci.o pci_auto.o +obj-$(CONFIG_RADSTONE_PPC7D) += i8259.o pci_auto.o +obj-$(CONFIG_SANDPOINT) += i8259.o pci_auto.o todc_time.o +obj-$(CONFIG_SBC82xx) += todc_time.o +obj-$(CONFIG_SPRUCE) += cpc700_pic.o indirect_pci.o pci_auto.o \ + todc_time.o +obj-$(CONFIG_8260) += m8260_setup.o +obj-$(CONFIG_PCI_8260) += m8260_pci.o indirect_pci.o +obj-$(CONFIG_8260_PCI9) += m8260_pci_erratum9.o +obj-$(CONFIG_CPM2) += cpm2_common.o cpm2_pic.o +ifeq ($(CONFIG_PPC_GEN550),y) +obj-$(CONFIG_KGDB) += gen550_kgdb.o gen550_dbg.o +obj-$(CONFIG_SERIAL_TEXT_DEBUG) += gen550_dbg.o +endif +ifeq ($(CONFIG_SERIAL_MPSC_CONSOLE),y) +obj-$(CONFIG_SERIAL_TEXT_DEBUG) += mv64x60_dbg.o +endif +obj-$(CONFIG_BOOTX_TEXT) += btext.o +obj-$(CONFIG_MPC10X_BRIDGE) += mpc10x_common.o indirect_pci.o +obj-$(CONFIG_MPC10X_OPENPIC) += open_pic.o +obj-$(CONFIG_40x) += dcr.o +obj-$(CONFIG_BOOKE) += dcr.o +obj-$(CONFIG_85xx) += open_pic.o ppc85xx_common.o ppc85xx_setup.o \ + ppc_sys.o mpc85xx_sys.o \ + mpc85xx_devices.o +ifeq ($(CONFIG_85xx),y) +obj-$(CONFIG_PCI) += indirect_pci.o pci_auto.o +endif +obj-$(CONFIG_83xx) += ipic.o ppc83xx_setup.o ppc_sys.o \ + mpc83xx_sys.o mpc83xx_devices.o +ifeq ($(CONFIG_83xx),y) +obj-$(CONFIG_PCI) += indirect_pci.o pci_auto.o +endif +obj-$(CONFIG_MPC8555_CDS) += todc_time.o +obj-$(CONFIG_PPC_MPC52xx) += mpc52xx_setup.o mpc52xx_pic.o \ + mpc52xx_sys.o mpc52xx_devices.o ppc_sys.o +ifeq ($(CONFIG_PPC_MPC52xx),y) +obj-$(CONFIG_PCI) += mpc52xx_pci.o +endif diff --git a/arch/ppc/syslib/btext.c b/arch/ppc/syslib/btext.c new file mode 100644 index 00000000000..7734f683617 --- /dev/null +++ b/arch/ppc/syslib/btext.c @@ -0,0 +1,861 @@ +/* + * Procedures for drawing on the screen early on in the boot process. + * + * Benjamin Herrenschmidt + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NO_SCROLL + +#ifndef NO_SCROLL +static void scrollscreen(void); +#endif + +static void draw_byte(unsigned char c, long locX, long locY); +static void draw_byte_32(unsigned char *bits, unsigned long *base, int rb); +static void draw_byte_16(unsigned char *bits, unsigned long *base, int rb); +static void draw_byte_8(unsigned char *bits, unsigned long *base, int rb); + +static int g_loc_X; +static int g_loc_Y; +static int g_max_loc_X; +static int g_max_loc_Y; + +unsigned long disp_BAT[2] __initdata = {0, 0}; + +#define cmapsz (16*256) + +static unsigned char vga_font[cmapsz]; + +int boot_text_mapped; +int force_printk_to_btext = 0; + +boot_infos_t disp_bi; + +extern char *klimit; + +/* + * Powermac can use btext_* after boot for xmon, + * chrp only uses it during early boot. + */ +#ifdef CONFIG_XMON +#define BTEXT __pmac +#define BTDATA __pmacdata +#else +#define BTEXT __init +#define BTDATA __initdata +#endif /* CONFIG_XMON */ + +/* + * This is called only when we are booted via BootX. + */ +void __init +btext_init(boot_infos_t *bi) +{ + g_loc_X = 0; + g_loc_Y = 0; + g_max_loc_X = (bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) / 8; + g_max_loc_Y = (bi->dispDeviceRect[3] - bi->dispDeviceRect[1]) / 16; + disp_bi = *bi; + boot_text_mapped = 1; +} + +void __init +btext_welcome(void) +{ + unsigned long flags; + unsigned long pvr; + boot_infos_t* bi = &disp_bi; + + btext_drawstring("Welcome to Linux, kernel " UTS_RELEASE "\n"); + btext_drawstring("\nlinked at : 0x"); + btext_drawhex(KERNELBASE); + btext_drawstring("\nframe buffer at : 0x"); + btext_drawhex((unsigned long)bi->dispDeviceBase); + btext_drawstring(" (phys), 0x"); + btext_drawhex((unsigned long)bi->logicalDisplayBase); + btext_drawstring(" (log)"); + btext_drawstring("\nklimit : 0x"); + btext_drawhex((unsigned long)klimit); + btext_drawstring("\nMSR : 0x"); + __asm__ __volatile__ ("mfmsr %0" : "=r" (flags)); + btext_drawhex(flags); + __asm__ __volatile__ ("mfspr %0, 287" : "=r" (pvr)); + pvr >>= 16; + if (pvr > 1) { + btext_drawstring("\nHID0 : 0x"); + __asm__ __volatile__ ("mfspr %0, 1008" : "=r" (flags)); + btext_drawhex(flags); + } + if (pvr == 8 || pvr == 12 || pvr == 0x800c) { + btext_drawstring("\nICTC : 0x"); + __asm__ __volatile__ ("mfspr %0, 1019" : "=r" (flags)); + btext_drawhex(flags); + } + btext_drawstring("\n\n"); +} + +/* Calc BAT values for mapping the display and store them + * in disp_BAT. Those values are then used from head.S to map + * the display during identify_machine() and MMU_Init() + * + * The display is mapped to virtual address 0xD0000000, rather + * than 1:1, because some some CHRP machines put the frame buffer + * in the region starting at 0xC0000000 (KERNELBASE). + * This mapping is temporary and will disappear as soon as the + * setup done by MMU_Init() is applied. + * + * For now, we align the BAT and then map 8Mb on 601 and 16Mb + * on other PPCs. This may cause trouble if the framebuffer + * is really badly aligned, but I didn't encounter this case + * yet. + */ +void __init +btext_prepare_BAT(void) +{ + boot_infos_t* bi = &disp_bi; + unsigned long vaddr = KERNELBASE + 0x10000000; + unsigned long addr; + unsigned long lowbits; + + addr = (unsigned long)bi->dispDeviceBase; + if (!addr) { + boot_text_mapped = 0; + return; + } + if (PVR_VER(mfspr(SPRN_PVR)) != 1) { + /* 603, 604, G3, G4, ... */ + lowbits = addr & ~0xFF000000UL; + addr &= 0xFF000000UL; + disp_BAT[0] = vaddr | (BL_16M<<2) | 2; + disp_BAT[1] = addr | (_PAGE_NO_CACHE | _PAGE_GUARDED | BPP_RW); + } else { + /* 601 */ + lowbits = addr & ~0xFF800000UL; + addr &= 0xFF800000UL; + disp_BAT[0] = vaddr | (_PAGE_NO_CACHE | PP_RWXX) | 4; + disp_BAT[1] = addr | BL_8M | 0x40; + } + bi->logicalDisplayBase = (void *) (vaddr + lowbits); +} + +/* This function will enable the early boot text when doing OF booting. This + * way, xmon output should work too + */ +void __init +btext_setup_display(int width, int height, int depth, int pitch, + unsigned long address) +{ + boot_infos_t* bi = &disp_bi; + + g_loc_X = 0; + g_loc_Y = 0; + g_max_loc_X = width / 8; + g_max_loc_Y = height / 16; + bi->logicalDisplayBase = (unsigned char *)address; + bi->dispDeviceBase = (unsigned char *)address; + bi->dispDeviceRowBytes = pitch; + bi->dispDeviceDepth = depth; + bi->dispDeviceRect[0] = bi->dispDeviceRect[1] = 0; + bi->dispDeviceRect[2] = width; + bi->dispDeviceRect[3] = height; + boot_text_mapped = 1; +} + +/* Here's a small text engine to use during early boot + * or for debugging purposes + * + * todo: + * + * - build some kind of vgacon with it to enable early printk + * - move to a separate file + * - add a few video driver hooks to keep in sync with display + * changes. + */ + +void __openfirmware +map_boot_text(void) +{ + unsigned long base, offset, size; + boot_infos_t *bi = &disp_bi; + unsigned char *vbase; + + /* By default, we are no longer mapped */ + boot_text_mapped = 0; + if (bi->dispDeviceBase == 0) + return; + base = ((unsigned long) bi->dispDeviceBase) & 0xFFFFF000UL; + offset = ((unsigned long) bi->dispDeviceBase) - base; + size = bi->dispDeviceRowBytes * bi->dispDeviceRect[3] + offset + + bi->dispDeviceRect[0]; + vbase = ioremap(base, size); + if (vbase == 0) + return; + bi->logicalDisplayBase = vbase + offset; + boot_text_mapped = 1; +} + +/* Calc the base address of a given point (x,y) */ +static unsigned char * BTEXT +calc_base(boot_infos_t *bi, int x, int y) +{ + unsigned char *base; + + base = bi->logicalDisplayBase; + if (base == 0) + base = bi->dispDeviceBase; + base += (x + bi->dispDeviceRect[0]) * (bi->dispDeviceDepth >> 3); + base += (y + bi->dispDeviceRect[1]) * bi->dispDeviceRowBytes; + return base; +} + +/* Adjust the display to a new resolution */ +void +btext_update_display(unsigned long phys, int width, int height, + int depth, int pitch) +{ + boot_infos_t *bi = &disp_bi; + + if (bi->dispDeviceBase == 0) + return; + + /* check it's the same frame buffer (within 256MB) */ + if ((phys ^ (unsigned long)bi->dispDeviceBase) & 0xf0000000) + return; + + bi->dispDeviceBase = (__u8 *) phys; + bi->dispDeviceRect[0] = 0; + bi->dispDeviceRect[1] = 0; + bi->dispDeviceRect[2] = width; + bi->dispDeviceRect[3] = height; + bi->dispDeviceDepth = depth; + bi->dispDeviceRowBytes = pitch; + if (boot_text_mapped) { + iounmap(bi->logicalDisplayBase); + boot_text_mapped = 0; + } + map_boot_text(); + g_loc_X = 0; + g_loc_Y = 0; + g_max_loc_X = width / 8; + g_max_loc_Y = height / 16; +} + +void BTEXT btext_clearscreen(void) +{ + boot_infos_t* bi = &disp_bi; + unsigned long *base = (unsigned long *)calc_base(bi, 0, 0); + unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * + (bi->dispDeviceDepth >> 3)) >> 2; + int i,j; + + for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1]); i++) + { + unsigned long *ptr = base; + for(j=width; j; --j) + *(ptr++) = 0; + base += (bi->dispDeviceRowBytes >> 2); + } +} + +__inline__ void dcbst(const void* addr) +{ + __asm__ __volatile__ ("dcbst 0,%0" :: "r" (addr)); +} + +void BTEXT btext_flushscreen(void) +{ + boot_infos_t* bi = &disp_bi; + unsigned long *base = (unsigned long *)calc_base(bi, 0, 0); + unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * + (bi->dispDeviceDepth >> 3)) >> 2; + int i,j; + + for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1]); i++) + { + unsigned long *ptr = base; + for(j=width; j>0; j-=8) { + dcbst(ptr); + ptr += 8; + } + base += (bi->dispDeviceRowBytes >> 2); + } +} + +#ifndef NO_SCROLL +static BTEXT void +scrollscreen(void) +{ + boot_infos_t* bi = &disp_bi; + unsigned long *src = (unsigned long *)calc_base(bi,0,16); + unsigned long *dst = (unsigned long *)calc_base(bi,0,0); + unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * + (bi->dispDeviceDepth >> 3)) >> 2; + int i,j; + +#ifdef CONFIG_ADB_PMU + pmu_suspend(); /* PMU will not shut us down ! */ +#endif + for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1] - 16); i++) + { + unsigned long *src_ptr = src; + unsigned long *dst_ptr = dst; + for(j=width; j; --j) + *(dst_ptr++) = *(src_ptr++); + src += (bi->dispDeviceRowBytes >> 2); + dst += (bi->dispDeviceRowBytes >> 2); + } + for (i=0; i<16; i++) + { + unsigned long *dst_ptr = dst; + for(j=width; j; --j) + *(dst_ptr++) = 0; + dst += (bi->dispDeviceRowBytes >> 2); + } +#ifdef CONFIG_ADB_PMU + pmu_resume(); /* PMU will not shut us down ! */ +#endif +} +#endif /* ndef NO_SCROLL */ + +void BTEXT btext_drawchar(char c) +{ + int cline = 0, x; + + if (!boot_text_mapped) + return; + + switch (c) { + case '\b': + if (g_loc_X > 0) + --g_loc_X; + break; + case '\t': + g_loc_X = (g_loc_X & -8) + 8; + break; + case '\r': + g_loc_X = 0; + break; + case '\n': + g_loc_X = 0; + g_loc_Y++; + cline = 1; + break; + default: + draw_byte(c, g_loc_X++, g_loc_Y); + } + if (g_loc_X >= g_max_loc_X) { + g_loc_X = 0; + g_loc_Y++; + cline = 1; + } +#ifndef NO_SCROLL + while (g_loc_Y >= g_max_loc_Y) { + scrollscreen(); + g_loc_Y--; + } +#else + /* wrap around from bottom to top of screen so we don't + waste time scrolling each line. -- paulus. */ + if (g_loc_Y >= g_max_loc_Y) + g_loc_Y = 0; + if (cline) { + for (x = 0; x < g_max_loc_X; ++x) + draw_byte(' ', x, g_loc_Y); + } +#endif +} + +void BTEXT +btext_drawstring(const char *c) +{ + if (!boot_text_mapped) + return; + while (*c) + btext_drawchar(*c++); +} + +void BTEXT +btext_drawhex(unsigned long v) +{ + static char hex_table[] = "0123456789abcdef"; + + if (!boot_text_mapped) + return; + btext_drawchar(hex_table[(v >> 28) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 24) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 20) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 16) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 12) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 8) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 4) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 0) & 0x0000000FUL]); + btext_drawchar(' '); +} + +static void BTEXT +draw_byte(unsigned char c, long locX, long locY) +{ + boot_infos_t* bi = &disp_bi; + unsigned char *base = calc_base(bi, locX << 3, locY << 4); + unsigned char *font = &vga_font[((unsigned long)c) * 16]; + int rb = bi->dispDeviceRowBytes; + + switch(bi->dispDeviceDepth) { + case 24: + case 32: + draw_byte_32(font, (unsigned long *)base, rb); + break; + case 15: + case 16: + draw_byte_16(font, (unsigned long *)base, rb); + break; + case 8: + draw_byte_8(font, (unsigned long *)base, rb); + break; + } +} + +static unsigned long expand_bits_8[16] BTDATA = { + 0x00000000, + 0x000000ff, + 0x0000ff00, + 0x0000ffff, + 0x00ff0000, + 0x00ff00ff, + 0x00ffff00, + 0x00ffffff, + 0xff000000, + 0xff0000ff, + 0xff00ff00, + 0xff00ffff, + 0xffff0000, + 0xffff00ff, + 0xffffff00, + 0xffffffff +}; + +static unsigned long expand_bits_16[4] BTDATA = { + 0x00000000, + 0x0000ffff, + 0xffff0000, + 0xffffffff +}; + + +static void BTEXT +draw_byte_32(unsigned char *font, unsigned long *base, int rb) +{ + int l, bits; + int fg = 0xFFFFFFFFUL; + int bg = 0x00000000UL; + + for (l = 0; l < 16; ++l) + { + bits = *font++; + base[0] = (-(bits >> 7) & fg) ^ bg; + base[1] = (-((bits >> 6) & 1) & fg) ^ bg; + base[2] = (-((bits >> 5) & 1) & fg) ^ bg; + base[3] = (-((bits >> 4) & 1) & fg) ^ bg; + base[4] = (-((bits >> 3) & 1) & fg) ^ bg; + base[5] = (-((bits >> 2) & 1) & fg) ^ bg; + base[6] = (-((bits >> 1) & 1) & fg) ^ bg; + base[7] = (-(bits & 1) & fg) ^ bg; + base = (unsigned long *) ((char *)base + rb); + } +} + +static void BTEXT +draw_byte_16(unsigned char *font, unsigned long *base, int rb) +{ + int l, bits; + int fg = 0xFFFFFFFFUL; + int bg = 0x00000000UL; + unsigned long *eb = expand_bits_16; + + for (l = 0; l < 16; ++l) + { + bits = *font++; + base[0] = (eb[bits >> 6] & fg) ^ bg; + base[1] = (eb[(bits >> 4) & 3] & fg) ^ bg; + base[2] = (eb[(bits >> 2) & 3] & fg) ^ bg; + base[3] = (eb[bits & 3] & fg) ^ bg; + base = (unsigned long *) ((char *)base + rb); + } +} + +static void BTEXT +draw_byte_8(unsigned char *font, unsigned long *base, int rb) +{ + int l, bits; + int fg = 0x0F0F0F0FUL; + int bg = 0x00000000UL; + unsigned long *eb = expand_bits_8; + + for (l = 0; l < 16; ++l) + { + bits = *font++; + base[0] = (eb[bits >> 4] & fg) ^ bg; + base[1] = (eb[bits & 0xf] & fg) ^ bg; + base = (unsigned long *) ((char *)base + rb); + } +} + +static unsigned char vga_font[cmapsz] BTDATA = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, +0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xff, +0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, +0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, +0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, +0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, +0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, +0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x1e, 0x0e, +0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, +0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x63, +0x7f, 0x63, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8, +0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0e, +0x1e, 0x3e, 0xfe, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, +0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xdb, +0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00, +0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, +0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, +0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, +0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, +0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, +0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, +0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x06, 0x86, 0xc6, 0x7c, +0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, +0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, +0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, +0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, +0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, +0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, +0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0xc6, 0xc6, 0x7c, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, +0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0xc6, 0x7c, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, +0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, +0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, +0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, +0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x78, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, +0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, +0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, +0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xde, 0xde, +0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, +0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x66, 0xfc, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, +0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x6c, +0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xfe, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, +0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, +0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x0c, +0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0x66, 0xe6, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, +0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xe7, +0xff, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, +0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, +0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, +0x0c, 0x0e, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, +0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, +0xc6, 0x60, 0x38, 0x0c, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, +0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, +0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x66, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, +0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, +0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xff, 0xc3, 0x86, 0x0c, 0x18, 0x30, 0x60, 0xc1, 0xc3, 0xff, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, +0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, +0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, +0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, +0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, +0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x60, +0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, +0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xf0, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, +0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00, 0x00, 0x00, 0xe0, 0x60, +0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, +0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0xe0, 0x60, +0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xdb, +0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, +0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0x60, 0xf0, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60, +0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x30, +0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, +0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, +0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0xfe, 0xcc, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, +0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x18, +0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, +0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, +0xc2, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00, +0x00, 0x00, 0xcc, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, +0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, +0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, +0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, +0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, +0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x38, +0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x0c, 0x06, +0x3c, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, +0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, +0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, +0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x66, +0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, +0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, +0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, +0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x38, 0x00, +0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, +0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x60, 0x66, 0xfe, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b, +0x7e, 0xd8, 0xdc, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x6c, +0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00, +0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6, +0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, +0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, +0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, +0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, +0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00, +0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, +0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, +0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, +0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, +0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xe6, 0xfc, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, 0x18, +0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, 0x66, +0x7c, 0x62, 0x66, 0x6f, 0x66, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, 0x00, +0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, +0xd8, 0x70, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, +0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, +0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, +0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, +0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, +0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, +0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, +0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, +0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c, +0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc0, 0xc6, 0xc6, 0x7c, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, +0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x60, 0xce, 0x9b, 0x06, +0x0c, 0x1f, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, +0x66, 0xce, 0x96, 0x3e, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, +0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, +0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x44, 0x11, 0x44, +0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, +0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, +0x55, 0xaa, 0x55, 0xaa, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, +0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, +0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36, +0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, +0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6, +0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, +0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, +0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, +0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, +0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, +0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, +0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, +0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, +0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, +0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, +0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, +0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc6, 0xfe, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, +0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, +0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, +0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0x6c, 0xee, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66, +0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x7e, 0xdb, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, +0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, +0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, +0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, +0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x1b, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, +0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c, +0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0c, 0x0c, +0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00, +0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, +}; diff --git a/arch/ppc/syslib/cpc700.h b/arch/ppc/syslib/cpc700.h new file mode 100644 index 00000000000..f2c00253101 --- /dev/null +++ b/arch/ppc/syslib/cpc700.h @@ -0,0 +1,98 @@ +/* + * arch/ppc/syslib/cpc700.h + * + * Header file for IBM CPC700 Host Bridge, et. al. + * + * Author: Mark A. Greer + * mgreer@mvista.com + * + * 2000-2002 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +/* + * This file contains the defines and macros for the IBM CPC700 host bridge, + * memory controller, PIC, UARTs, IIC, and Timers. + */ + +#ifndef __PPC_SYSLIB_CPC700_H__ +#define __PPC_SYSLIB_CPC700_H__ + +#include +#include +#include + +/* XXX no barriers? not even any volatiles? -- paulus */ +#define CPC700_OUT_32(a,d) (*(u_int *)a = d) +#define CPC700_IN_32(a) (*(u_int *)a) + +/* + * PCI Section + */ +#define CPC700_PCI_CONFIG_ADDR 0xfec00000 +#define CPC700_PCI_CONFIG_DATA 0xfec00004 + +/* CPU -> PCI memory window 0 */ +#define CPC700_PMM0_LOCAL 0xff400000 /* CPU physical addr */ +#define CPC700_PMM0_MASK_ATTR 0xff400004 /* size and attrs */ +#define CPC700_PMM0_PCI_LOW 0xff400008 /* PCI addr, low word */ +#define CPC700_PMM0_PCI_HIGH 0xff40000c /* PCI addr, high wd */ +/* CPU -> PCI memory window 1 */ +#define CPC700_PMM1_LOCAL 0xff400010 +#define CPC700_PMM1_MASK_ATTR 0xff400014 +#define CPC700_PMM1_PCI_LOW 0xff400018 +#define CPC700_PMM1_PCI_HIGH 0xff40001c +/* CPU -> PCI memory window 2 */ +#define CPC700_PMM2_LOCAL 0xff400020 +#define CPC700_PMM2_MASK_ATTR 0xff400024 +#define CPC700_PMM2_PCI_LOW 0xff400028 +#define CPC700_PMM2_PCI_HIGH 0xff40002c +/* PCI memory -> CPU window 1 */ +#define CPC700_PTM1_MEMSIZE 0xff400030 /* window size */ +#define CPC700_PTM1_LOCAL 0xff400034 /* CPU phys addr */ +/* PCI memory -> CPU window 2 */ +#define CPC700_PTM2_MEMSIZE 0xff400038 /* size and enable */ +#define CPC700_PTM2_LOCAL 0xff40003c + +/* + * PIC Section + * + * IBM calls the CPC700's programmable interrupt controller the Universal + * Interrupt Controller or UIC. + */ + +/* + * UIC Register Addresses. + */ +#define CPC700_UIC_UICSR 0xff500880 /* Status Reg (Rd/Clr)*/ +#define CPC700_UIC_UICSRS 0xff500884 /* Status Reg (Set) */ +#define CPC700_UIC_UICER 0xff500888 /* Enable Reg */ +#define CPC700_UIC_UICCR 0xff50088c /* Critical Reg */ +#define CPC700_UIC_UICPR 0xff500890 /* Polarity Reg */ +#define CPC700_UIC_UICTR 0xff500894 /* Trigger Reg */ +#define CPC700_UIC_UICMSR 0xff500898 /* Masked Status Reg */ +#define CPC700_UIC_UICVR 0xff50089c /* Vector Reg */ +#define CPC700_UIC_UICVCR 0xff5008a0 /* Vector Config Reg */ + +#define CPC700_UIC_UICER_ENABLE 0x00000001 /* Enable an IRQ */ + +#define CPC700_UIC_UICVCR_31_HI 0x00000000 /* IRQ 31 hi priority */ +#define CPC700_UIC_UICVCR_0_HI 0x00000001 /* IRQ 0 hi priority */ +#define CPC700_UIC_UICVCR_BASE_MASK 0xfffffffc +#define CPC700_UIC_UICVCR_ORDER_MASK 0x00000001 + +/* Specify value of a bit for an IRQ. */ +#define CPC700_UIC_IRQ_BIT(i) ((0x00000001) << (31 - (i))) + +/* + * UIC Exports... + */ +extern struct hw_interrupt_type cpc700_pic; +extern unsigned int cpc700_irq_assigns[32][2]; + +extern void __init cpc700_init_IRQ(void); +extern int cpc700_get_irq(struct pt_regs *); + +#endif /* __PPC_SYSLIB_CPC700_H__ */ diff --git a/arch/ppc/syslib/cpc700_pic.c b/arch/ppc/syslib/cpc700_pic.c new file mode 100644 index 00000000000..77470980753 --- /dev/null +++ b/arch/ppc/syslib/cpc700_pic.c @@ -0,0 +1,187 @@ +/* + * arch/ppc/syslib/cpc700_pic.c + * + * Interrupt controller support for IBM Spruce + * + * Authors: Mark Greer, Matt Porter, and Johnnie Peters + * mgreer@mvista.com + * mporter@mvista.com + * jpeters@mvista.com + * + * 2001-2002 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "cpc700.h" + +static void +cpc700_unmask_irq(unsigned int irq) +{ + unsigned int tr_bits; + + /* + * IRQ 31 is largest IRQ supported. + * IRQs 17-19 are reserved. + */ + if ((irq <= 31) && ((irq < 17) || (irq > 19))) { + tr_bits = CPC700_IN_32(CPC700_UIC_UICTR); + + if ((tr_bits & (1 << (31 - irq))) == 0) { + /* level trigger interrupt, clear bit in status + * register */ + CPC700_OUT_32(CPC700_UIC_UICSR, 1 << (31 - irq)); + } + + /* Know IRQ fits in entry 0 of ppc_cached_irq_mask[] */ + ppc_cached_irq_mask[0] |= CPC700_UIC_IRQ_BIT(irq); + + CPC700_OUT_32(CPC700_UIC_UICER, ppc_cached_irq_mask[0]); + } + return; +} + +static void +cpc700_mask_irq(unsigned int irq) +{ + /* + * IRQ 31 is largest IRQ supported. + * IRQs 17-19 are reserved. + */ + if ((irq <= 31) && ((irq < 17) || (irq > 19))) { + /* Know IRQ fits in entry 0 of ppc_cached_irq_mask[] */ + ppc_cached_irq_mask[0] &= + ~CPC700_UIC_IRQ_BIT(irq); + + CPC700_OUT_32(CPC700_UIC_UICER, ppc_cached_irq_mask[0]); + } + return; +} + +static void +cpc700_mask_and_ack_irq(unsigned int irq) +{ + u_int bit; + + /* + * IRQ 31 is largest IRQ supported. + * IRQs 17-19 are reserved. + */ + if ((irq <= 31) && ((irq < 17) || (irq > 19))) { + /* Know IRQ fits in entry 0 of ppc_cached_irq_mask[] */ + bit = CPC700_UIC_IRQ_BIT(irq); + + ppc_cached_irq_mask[0] &= ~bit; + CPC700_OUT_32(CPC700_UIC_UICER, ppc_cached_irq_mask[0]); + CPC700_OUT_32(CPC700_UIC_UICSR, bit); /* Write 1 clears IRQ */ + } + return; +} + +static struct hw_interrupt_type cpc700_pic = { + "CPC700 PIC", + NULL, + NULL, + cpc700_unmask_irq, + cpc700_mask_irq, + cpc700_mask_and_ack_irq, + NULL, + NULL +}; + +__init static void +cpc700_pic_init_irq(unsigned int irq) +{ + unsigned int tmp; + + /* Set interrupt sense */ + tmp = CPC700_IN_32(CPC700_UIC_UICTR); + if (cpc700_irq_assigns[irq][0] == 0) { + tmp &= ~CPC700_UIC_IRQ_BIT(irq); + } else { + tmp |= CPC700_UIC_IRQ_BIT(irq); + } + CPC700_OUT_32(CPC700_UIC_UICTR, tmp); + + /* Set interrupt polarity */ + tmp = CPC700_IN_32(CPC700_UIC_UICPR); + if (cpc700_irq_assigns[irq][1]) { + tmp |= CPC700_UIC_IRQ_BIT(irq); + } else { + tmp &= ~CPC700_UIC_IRQ_BIT(irq); + } + CPC700_OUT_32(CPC700_UIC_UICPR, tmp); + + /* Set interrupt critical */ + tmp = CPC700_IN_32(CPC700_UIC_UICCR); + tmp |= CPC700_UIC_IRQ_BIT(irq); + CPC700_OUT_32(CPC700_UIC_UICCR, tmp); + + return; +} + +__init void +cpc700_init_IRQ(void) +{ + int i; + + ppc_cached_irq_mask[0] = 0; + CPC700_OUT_32(CPC700_UIC_UICER, 0x00000000); /* Disable all irq's */ + CPC700_OUT_32(CPC700_UIC_UICSR, 0xffffffff); /* Clear cur intrs */ + CPC700_OUT_32(CPC700_UIC_UICCR, 0xffffffff); /* Gen INT not MCP */ + CPC700_OUT_32(CPC700_UIC_UICPR, 0x00000000); /* Active low */ + CPC700_OUT_32(CPC700_UIC_UICTR, 0x00000000); /* Level Sensitive */ + CPC700_OUT_32(CPC700_UIC_UICVR, CPC700_UIC_UICVCR_0_HI); + /* IRQ 0 is highest */ + + for (i = 0; i < 17; i++) { + irq_desc[i].handler = &cpc700_pic; + cpc700_pic_init_irq(i); + } + + for (i = 20; i < 32; i++) { + irq_desc[i].handler = &cpc700_pic; + cpc700_pic_init_irq(i); + } + + return; +} + + + +/* + * Find the highest IRQ that generating an interrupt, if any. + */ +int +cpc700_get_irq(struct pt_regs *regs) +{ + int irq = 0; + u_int irq_status, irq_test = 1; + + irq_status = CPC700_IN_32(CPC700_UIC_UICMSR); + + do + { + if (irq_status & irq_test) + break; + irq++; + irq_test <<= 1; + } while (irq < NR_IRQS); + + + if (irq == NR_IRQS) + irq = 33; + + return (31 - irq); +} diff --git a/arch/ppc/syslib/cpc710.h b/arch/ppc/syslib/cpc710.h new file mode 100644 index 00000000000..cc0afd80402 --- /dev/null +++ b/arch/ppc/syslib/cpc710.h @@ -0,0 +1,83 @@ +/* + * arch/ppc/syslib/cpc710.h + * + * Definitions for the IBM CPC710 PCI Host Bridge + * + * Author: Matt Porter + * + * 2001 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifndef __PPC_PLATFORMS_CPC710_H +#define __PPC_PLATFORMS_CPC710_H + +/* General bridge and memory controller registers */ +#define PIDR 0xff000008 +#define CNFR 0xff00000c +#define RSTR 0xff000010 +#define UCTL 0xff001000 +#define MPSR 0xff001010 +#define SIOC 0xff001020 +#define ABCNTL 0xff001030 +#define SRST 0xff001040 +#define ERRC 0xff001050 +#define SESR 0xff001060 +#define SEAR 0xff001070 +#define SIOC1 0xff001090 +#define PGCHP 0xff001100 +#define GPDIR 0xff001130 +#define GPOUT 0xff001150 +#define ATAS 0xff001160 +#define AVDG 0xff001170 +#define MCCR 0xff001200 +#define MESR 0xff001220 +#define MEAR 0xff001230 +#define MCER0 0xff001300 +#define MCER1 0xff001310 +#define MCER2 0xff001320 +#define MCER3 0xff001330 +#define MCER4 0xff001340 +#define MCER5 0xff001350 +#define MCER6 0xff001360 +#define MCER7 0xff001370 + +/* + * PCI32/64 configuration registers + * Given as offsets from their + * respective physical segment BAR + */ +#define PIBAR 0x000f7800 +#define PMBAR 0x000f7810 +#define MSIZE 0x000f7f40 +#define IOSIZE 0x000f7f60 +#define SMBAR 0x000f7f80 +#define SIBAR 0x000f7fc0 +#define PSSIZE 0x000f8100 +#define PPSIZE 0x000f8110 +#define BARPS 0x000f8120 +#define BARPP 0x000f8130 +#define PSBAR 0x000f8140 +#define PPBAR 0x000f8150 +#define BPMDLK 0x000f8200 /* Bottom of Peripheral Memory Space */ +#define TPMDLK 0x000f8210 /* Top of Peripheral Memory Space */ +#define BIODLK 0x000f8220 /* Bottom of Peripheral I/O Space */ +#define TIODLK 0x000f8230 /* Top of Perioheral I/O Space */ +#define DLKCTRL 0x000f8240 /* Deadlock control */ +#define DLKDEV 0x000f8250 /* Deadlock device */ + +/* System standard configuration registers space */ +#define DCR 0xff200000 +#define DID 0xff200004 +#define BAR 0xff200018 + +/* Device specific configuration space */ +#define PCIENB 0xff201000 + +/* Configuration space registers */ +#define CPC710_BUS_NUMBER 0x40 +#define CPC710_SUB_BUS_NUMBER 0x41 + +#endif /* __PPC_PLATFORMS_CPC710_H */ diff --git a/arch/ppc/syslib/cpm2_common.c b/arch/ppc/syslib/cpm2_common.c new file mode 100644 index 00000000000..ea5e77080e8 --- /dev/null +++ b/arch/ppc/syslib/cpm2_common.c @@ -0,0 +1,198 @@ +/* + * General Purpose functions for the global management of the + * 8260 Communication Processor Module. + * Copyright (c) 1999 Dan Malek (dmalek@jlc.net) + * Copyright (c) 2000 MontaVista Software, Inc (source@mvista.com) + * 2.3.99 Updates + * + * In addition to the individual control of the communication + * channels, there are a few functions that globally affect the + * communication processor. + * + * Buffer descriptors must be allocated from the dual ported memory + * space. The allocator for that is here. When the communication + * process is reset, we reclaim the memory available. There is + * currently no deallocator for this memory. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void cpm2_dpinit(void); +cpm_cpm2_t *cpmp; /* Pointer to comm processor space */ + +/* We allocate this here because it is used almost exclusively for + * the communication processor devices. + */ +cpm2_map_t *cpm2_immr; + +#define CPM_MAP_SIZE (0x40000) /* 256k - the PQ3 reserve this amount + of space for CPM as it is larger + than on PQ2 */ + +void +cpm2_reset(void) +{ + cpm2_immr = (cpm2_map_t *)ioremap(CPM_MAP_ADDR, CPM_MAP_SIZE); + + /* Reclaim the DP memory for our use. + */ + cpm2_dpinit(); + + /* Tell everyone where the comm processor resides. + */ + cpmp = &cpm2_immr->im_cpm; +} + +/* Set a baud rate generator. This needs lots of work. There are + * eight BRGs, which can be connected to the CPM channels or output + * as clocks. The BRGs are in two different block of internal + * memory mapped space. + * The baud rate clock is the system clock divided by something. + * It was set up long ago during the initial boot phase and is + * is given to us. + * Baud rate clocks are zero-based in the driver code (as that maps + * to port numbers). Documentation uses 1-based numbering. + */ +#define BRG_INT_CLK (((bd_t *)__res)->bi_brgfreq) +#define BRG_UART_CLK (BRG_INT_CLK/16) + +/* This function is used by UARTS, or anything else that uses a 16x + * oversampled clock. + */ +void +cpm_setbrg(uint brg, uint rate) +{ + volatile uint *bp; + + /* This is good enough to get SMCs running..... + */ + if (brg < 4) { + bp = (uint *)&cpm2_immr->im_brgc1; + } + else { + bp = (uint *)&cpm2_immr->im_brgc5; + brg -= 4; + } + bp += brg; + *bp = ((BRG_UART_CLK / rate) << 1) | CPM_BRG_EN; +} + +/* This function is used to set high speed synchronous baud rate + * clocks. + */ +void +cpm2_fastbrg(uint brg, uint rate, int div16) +{ + volatile uint *bp; + + if (brg < 4) { + bp = (uint *)&cpm2_immr->im_brgc1; + } + else { + bp = (uint *)&cpm2_immr->im_brgc5; + brg -= 4; + } + bp += brg; + *bp = ((BRG_INT_CLK / rate) << 1) | CPM_BRG_EN; + if (div16) + *bp |= CPM_BRG_DIV16; +} + +/* + * dpalloc / dpfree bits. + */ +static spinlock_t cpm_dpmem_lock; +/* 16 blocks should be enough to satisfy all requests + * until the memory subsystem goes up... */ +static rh_block_t cpm_boot_dpmem_rh_block[16]; +static rh_info_t cpm_dpmem_info; + +static void cpm2_dpinit(void) +{ + spin_lock_init(&cpm_dpmem_lock); + + /* initialize the info header */ + rh_init(&cpm_dpmem_info, 1, + sizeof(cpm_boot_dpmem_rh_block) / + sizeof(cpm_boot_dpmem_rh_block[0]), + cpm_boot_dpmem_rh_block); + + /* Attach the usable dpmem area */ + /* XXX: This is actually crap. CPM_DATAONLY_BASE and + * CPM_DATAONLY_SIZE is only a subset of the available dpram. It + * varies with the processor and the microcode patches activated. + * But the following should be at least safe. + */ + rh_attach_region(&cpm_dpmem_info, (void *)CPM_DATAONLY_BASE, + CPM_DATAONLY_SIZE); +} + +/* This function returns an index into the DPRAM area. + */ +uint cpm_dpalloc(uint size, uint align) +{ + void *start; + unsigned long flags; + + spin_lock_irqsave(&cpm_dpmem_lock, flags); + cpm_dpmem_info.alignment = align; + start = rh_alloc(&cpm_dpmem_info, size, "commproc"); + spin_unlock_irqrestore(&cpm_dpmem_lock, flags); + + return (uint)start; +} +EXPORT_SYMBOL(cpm_dpalloc); + +int cpm_dpfree(uint offset) +{ + int ret; + unsigned long flags; + + spin_lock_irqsave(&cpm_dpmem_lock, flags); + ret = rh_free(&cpm_dpmem_info, (void *)offset); + spin_unlock_irqrestore(&cpm_dpmem_lock, flags); + + return ret; +} +EXPORT_SYMBOL(cpm_dpfree); + +/* not sure if this is ever needed */ +uint cpm_dpalloc_fixed(uint offset, uint size, uint align) +{ + void *start; + unsigned long flags; + + spin_lock_irqsave(&cpm_dpmem_lock, flags); + cpm_dpmem_info.alignment = align; + start = rh_alloc_fixed(&cpm_dpmem_info, (void *)offset, size, "commproc"); + spin_unlock_irqrestore(&cpm_dpmem_lock, flags); + + return (uint)start; +} +EXPORT_SYMBOL(cpm_dpalloc_fixed); + +void cpm_dpdump(void) +{ + rh_dump(&cpm_dpmem_info); +} +EXPORT_SYMBOL(cpm_dpdump); + +void *cpm_dpram_addr(uint offset) +{ + return (void *)&cpm2_immr->im_dprambase[offset]; +} +EXPORT_SYMBOL(cpm_dpram_addr); diff --git a/arch/ppc/syslib/cpm2_pic.c b/arch/ppc/syslib/cpm2_pic.c new file mode 100644 index 00000000000..954b07fc1df --- /dev/null +++ b/arch/ppc/syslib/cpm2_pic.c @@ -0,0 +1,172 @@ +/* The CPM2 internal interrupt controller. It is usually + * the only interrupt controller. + * There are two 32-bit registers (high/low) for up to 64 + * possible interrupts. + * + * Now, the fun starts.....Interrupt Numbers DO NOT MAP + * in a simple arithmetic fashion to mask or pending registers. + * That is, interrupt 4 does not map to bit position 4. + * We create two tables, indexed by vector number, to indicate + * which register to use and which bit in the register to use. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include "cpm2_pic.h" + +static u_char irq_to_siureg[] = { + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* bit numbers do not match the docs, these are precomputed so the bit for + * a given irq is (1 << irq_to_siubit[irq]) */ +static u_char irq_to_siubit[] = { + 0, 15, 14, 13, 12, 11, 10, 9, + 8, 7, 6, 5, 4, 3, 2, 1, + 2, 1, 15, 14, 13, 12, 11, 10, + 9, 8, 7, 6, 5, 4, 3, 0, + 31, 30, 29, 28, 27, 26, 25, 24, + 23, 22, 21, 20, 19, 18, 17, 16, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, +}; + +static void cpm2_mask_irq(unsigned int irq_nr) +{ + int bit, word; + volatile uint *simr; + + irq_nr -= CPM_IRQ_OFFSET; + + bit = irq_to_siubit[irq_nr]; + word = irq_to_siureg[irq_nr]; + + simr = &(cpm2_immr->im_intctl.ic_simrh); + ppc_cached_irq_mask[word] &= ~(1 << bit); + simr[word] = ppc_cached_irq_mask[word]; +} + +static void cpm2_unmask_irq(unsigned int irq_nr) +{ + int bit, word; + volatile uint *simr; + + irq_nr -= CPM_IRQ_OFFSET; + + bit = irq_to_siubit[irq_nr]; + word = irq_to_siureg[irq_nr]; + + simr = &(cpm2_immr->im_intctl.ic_simrh); + ppc_cached_irq_mask[word] |= 1 << bit; + simr[word] = ppc_cached_irq_mask[word]; +} + +static void cpm2_mask_and_ack(unsigned int irq_nr) +{ + int bit, word; + volatile uint *simr, *sipnr; + + irq_nr -= CPM_IRQ_OFFSET; + + bit = irq_to_siubit[irq_nr]; + word = irq_to_siureg[irq_nr]; + + simr = &(cpm2_immr->im_intctl.ic_simrh); + sipnr = &(cpm2_immr->im_intctl.ic_sipnrh); + ppc_cached_irq_mask[word] &= ~(1 << bit); + simr[word] = ppc_cached_irq_mask[word]; + sipnr[word] = 1 << bit; +} + +static void cpm2_end_irq(unsigned int irq_nr) +{ + int bit, word; + volatile uint *simr; + + if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS)) + && irq_desc[irq_nr].action) { + + irq_nr -= CPM_IRQ_OFFSET; + bit = irq_to_siubit[irq_nr]; + word = irq_to_siureg[irq_nr]; + + simr = &(cpm2_immr->im_intctl.ic_simrh); + ppc_cached_irq_mask[word] |= 1 << bit; + simr[word] = ppc_cached_irq_mask[word]; + } +} + +static struct hw_interrupt_type cpm2_pic = { + .typename = " CPM2 SIU ", + .enable = cpm2_unmask_irq, + .disable = cpm2_mask_irq, + .ack = cpm2_mask_and_ack, + .end = cpm2_end_irq, +}; + +int cpm2_get_irq(struct pt_regs *regs) +{ + int irq; + unsigned long bits; + + /* For CPM2, read the SIVEC register and shift the bits down + * to get the irq number. */ + bits = cpm2_immr->im_intctl.ic_sivec; + irq = bits >> 26; + + if (irq == 0) + return(-1); + return irq+CPM_IRQ_OFFSET; +} + +void cpm2_init_IRQ(void) +{ + int i; + + /* Clear the CPM IRQ controller, in case it has any bits set + * from the bootloader + */ + + /* Mask out everything */ + cpm2_immr->im_intctl.ic_simrh = 0x00000000; + cpm2_immr->im_intctl.ic_simrl = 0x00000000; + wmb(); + + /* Ack everything */ + cpm2_immr->im_intctl.ic_sipnrh = 0xffffffff; + cpm2_immr->im_intctl.ic_sipnrl = 0xffffffff; + wmb(); + + /* Dummy read of the vector */ + i = cpm2_immr->im_intctl.ic_sivec; + rmb(); + + /* Initialize the default interrupt mapping priorities, + * in case the boot rom changed something on us. + */ + cpm2_immr->im_intctl.ic_sicr = 0; + cpm2_immr->im_intctl.ic_scprrh = 0x05309770; + cpm2_immr->im_intctl.ic_scprrl = 0x05309770; + + + /* Enable chaining to OpenPIC, and make everything level + */ + for (i = 0; i < NR_CPM_INTS; i++) { + irq_desc[i+CPM_IRQ_OFFSET].handler = &cpm2_pic; + irq_desc[i+CPM_IRQ_OFFSET].status |= IRQ_LEVEL; + } +} diff --git a/arch/ppc/syslib/cpm2_pic.h b/arch/ppc/syslib/cpm2_pic.h new file mode 100644 index 00000000000..97cab8f13a1 --- /dev/null +++ b/arch/ppc/syslib/cpm2_pic.h @@ -0,0 +1,8 @@ +#ifndef _PPC_KERNEL_CPM2_H +#define _PPC_KERNEL_CPM2_H + +extern int cpm2_get_irq(struct pt_regs *regs); + +extern void cpm2_init_IRQ(void); + +#endif /* _PPC_KERNEL_CPM2_H */ diff --git a/arch/ppc/syslib/dcr.S b/arch/ppc/syslib/dcr.S new file mode 100644 index 00000000000..895f10243a4 --- /dev/null +++ b/arch/ppc/syslib/dcr.S @@ -0,0 +1,41 @@ +/* + * arch/ppc/syslib/dcr.S + * + * "Indirect" DCR access + * + * Copyright (c) 2004 Eugene Surovegin + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include + +#define DCR_ACCESS_PROLOG(table) \ + rlwinm r3,r3,4,18,27; \ + lis r5,table@h; \ + ori r5,r5,table@l; \ + add r3,r3,r5; \ + mtctr r3; \ + bctr + +_GLOBAL(__mfdcr) + DCR_ACCESS_PROLOG(__mfdcr_table) + +_GLOBAL(__mtdcr) + DCR_ACCESS_PROLOG(__mtdcr_table) + +__mfdcr_table: + mfdcr r3,0; blr +__mtdcr_table: + mtdcr 0,r4; blr + +dcr = 1 + .rept 1023 + mfdcr r3,dcr; blr + mtdcr dcr,r4; blr + dcr = dcr + 1 + .endr diff --git a/arch/ppc/syslib/gen550.h b/arch/ppc/syslib/gen550.h new file mode 100644 index 00000000000..039d249e19a --- /dev/null +++ b/arch/ppc/syslib/gen550.h @@ -0,0 +1,16 @@ +/* + * arch/ppc/syslib/gen550.h + * + * gen550 prototypes + * + * Matt Porter + * + * 2004 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +extern void gen550_progress(char *, unsigned short); +extern void gen550_init(int, struct uart_port *); +extern void gen550_kgdb_map_scc(void); diff --git a/arch/ppc/syslib/gen550_dbg.c b/arch/ppc/syslib/gen550_dbg.c new file mode 100644 index 00000000000..9ef0113c83d --- /dev/null +++ b/arch/ppc/syslib/gen550_dbg.c @@ -0,0 +1,182 @@ +/* + * arch/ppc/syslib/gen550_dbg.c + * + * A library of polled 16550 serial routines. These are intended to + * be used to support progress messages, xmon, kgdb, etc. on a + * variety of platforms. + * + * Adapted from lots of code ripped from the arch/ppc/boot/ polled + * 16550 support. + * + * Author: Matt Porter + * + * 2002-2003 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include /* For linux/serial_core.h */ +#include +#include +#include +#include +#include +#include + +#define SERIAL_BAUD 9600 + +/* SERIAL_PORT_DFNS is defined in */ +#ifndef SERIAL_PORT_DFNS +#define SERIAL_PORT_DFNS +#endif + +static struct serial_state rs_table[RS_TABLE_SIZE] = { + SERIAL_PORT_DFNS /* defined in */ +}; + +static void (*serial_outb)(unsigned long, unsigned char); +static unsigned long (*serial_inb)(unsigned long); + +static int shift; + +unsigned long direct_inb(unsigned long addr) +{ + return readb((void __iomem *)addr); +} + +void direct_outb(unsigned long addr, unsigned char val) +{ + writeb(val, (void __iomem *)addr); +} + +unsigned long io_inb(unsigned long port) +{ + return inb(port); +} + +void io_outb(unsigned long port, unsigned char val) +{ + outb(val, port); +} + +unsigned long serial_init(int chan, void *ignored) +{ + unsigned long com_port; + unsigned char lcr, dlm; + + /* We need to find out which type io we're expecting. If it's + * 'SERIAL_IO_PORT', we get an offset from the isa_io_base. + * If it's 'SERIAL_IO_MEM', we can the exact location. -- Tom */ + switch (rs_table[chan].io_type) { + case SERIAL_IO_PORT: + com_port = rs_table[chan].port; + serial_outb = io_outb; + serial_inb = io_inb; + break; + case SERIAL_IO_MEM: + com_port = (unsigned long)rs_table[chan].iomem_base; + serial_outb = direct_outb; + serial_inb = direct_inb; + break; + default: + /* We can't deal with it. */ + return -1; + } + + /* How far apart the registers are. */ + shift = rs_table[chan].iomem_reg_shift; + + /* save the LCR */ + lcr = serial_inb(com_port + (UART_LCR << shift)); + + /* Access baud rate */ + serial_outb(com_port + (UART_LCR << shift), UART_LCR_DLAB); + dlm = serial_inb(com_port + (UART_DLM << shift)); + + /* + * Test if serial port is unconfigured + * We assume that no-one uses less than 110 baud or + * less than 7 bits per character these days. + * -- paulus. + */ + if ((dlm <= 4) && (lcr & 2)) { + /* port is configured, put the old LCR back */ + serial_outb(com_port + (UART_LCR << shift), lcr); + } + else { + /* Input clock. */ + serial_outb(com_port + (UART_DLL << shift), + (rs_table[chan].baud_base / SERIAL_BAUD) & 0xFF); + serial_outb(com_port + (UART_DLM << shift), + (rs_table[chan].baud_base / SERIAL_BAUD) >> 8); + /* 8 data, 1 stop, no parity */ + serial_outb(com_port + (UART_LCR << shift), 0x03); + /* RTS/DTR */ + serial_outb(com_port + (UART_MCR << shift), 0x03); + + /* Clear & enable FIFOs */ + serial_outb(com_port + (UART_FCR << shift), 0x07); + } + + return (com_port); +} + +void +serial_putc(unsigned long com_port, unsigned char c) +{ + while ((serial_inb(com_port + (UART_LSR << shift)) & UART_LSR_THRE) == 0) + ; + serial_outb(com_port, c); +} + +unsigned char +serial_getc(unsigned long com_port) +{ + while ((serial_inb(com_port + (UART_LSR << shift)) & UART_LSR_DR) == 0) + ; + return serial_inb(com_port); +} + +int +serial_tstc(unsigned long com_port) +{ + return ((serial_inb(com_port + (UART_LSR << shift)) & UART_LSR_DR) != 0); +} + +void +serial_close(unsigned long com_port) +{ +} + +void +gen550_init(int i, struct uart_port *serial_req) +{ + rs_table[i].io_type = serial_req->iotype; + rs_table[i].port = serial_req->iobase; + rs_table[i].iomem_base = serial_req->membase; + rs_table[i].iomem_reg_shift = serial_req->regshift; + rs_table[i].baud_base = serial_req->uartclk ? serial_req->uartclk / 16 : BASE_BAUD; +} + +#ifdef CONFIG_SERIAL_TEXT_DEBUG +void +gen550_progress(char *s, unsigned short hex) +{ + volatile unsigned int progress_debugport; + volatile char c; + + progress_debugport = serial_init(0, NULL); + + serial_putc(progress_debugport, '\r'); + + while ((c = *s++) != 0) + serial_putc(progress_debugport, c); + + serial_putc(progress_debugport, '\n'); + serial_putc(progress_debugport, '\r'); +} +#endif /* CONFIG_SERIAL_TEXT_DEBUG */ diff --git a/arch/ppc/syslib/gen550_kgdb.c b/arch/ppc/syslib/gen550_kgdb.c new file mode 100644 index 00000000000..7239d5d7ddc --- /dev/null +++ b/arch/ppc/syslib/gen550_kgdb.c @@ -0,0 +1,86 @@ +/* + * arch/ppc/syslib/gen550_kgdb.c + * + * Generic 16550 kgdb support intended to be useful on a variety + * of platforms. To enable this support, it is necessary to set + * the CONFIG_GEN550 option. Any virtual mapping of the serial + * port(s) to be used can be accomplished by setting + * ppc_md.early_serial_map to a platform-specific mapping function. + * + * Adapted from ppc4xx_kgdb.c. + * + * Author: Matt Porter + * + * 2002-2004 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include + +#include + +extern unsigned long serial_init(int, void *); +extern unsigned long serial_getc(unsigned long); +extern unsigned long serial_putc(unsigned long, unsigned char); + +#if defined(CONFIG_KGDB_TTYS0) +#define KGDB_PORT 0 +#elif defined(CONFIG_KGDB_TTYS1) +#define KGDB_PORT 1 +#elif defined(CONFIG_KGDB_TTYS2) +#define KGDB_PORT 2 +#elif defined(CONFIG_KGDB_TTYS3) +#define KGDB_PORT 3 +#else +#error "invalid kgdb_tty port" +#endif + +static volatile unsigned int kgdb_debugport; + +void putDebugChar(unsigned char c) +{ + if (kgdb_debugport == 0) + kgdb_debugport = serial_init(KGDB_PORT, NULL); + + serial_putc(kgdb_debugport, c); +} + +int getDebugChar(void) +{ + if (kgdb_debugport == 0) + kgdb_debugport = serial_init(KGDB_PORT, NULL); + + return(serial_getc(kgdb_debugport)); +} + +void kgdb_interruptible(int enable) +{ + return; +} + +void putDebugString(char* str) +{ + while (*str != '\0') { + putDebugChar(*str); + str++; + } + putDebugChar('\r'); + return; +} + +/* + * Note: gen550_init() must be called already on the port we are going + * to use. + */ +void +gen550_kgdb_map_scc(void) +{ + printk(KERN_DEBUG "kgdb init\n"); + if (ppc_md.early_serial_map) + ppc_md.early_serial_map(); + kgdb_debugport = serial_init(KGDB_PORT, NULL); +} diff --git a/arch/ppc/syslib/gt64260_pic.c b/arch/ppc/syslib/gt64260_pic.c new file mode 100644 index 00000000000..44aa8738545 --- /dev/null +++ b/arch/ppc/syslib/gt64260_pic.c @@ -0,0 +1,328 @@ +/* + * arch/ppc/syslib/gt64260_pic.c + * + * Interrupt controller support for Galileo's GT64260. + * + * Author: Chris Zankel + * Modified by: Mark A. Greer + * + * Based on sources from Rabeeh Khoury / Galileo Technology + * + * 2001 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +/* + * This file contains the specific functions to support the GT64260 + * interrupt controller. + * + * The GT64260 has two main interrupt registers (high and low) that + * summarizes the interrupts generated by the units of the GT64260. + * Each bit is assigned to an interrupt number, where the low register + * are assigned from IRQ0 to IRQ31 and the high cause register + * from IRQ32 to IRQ63 + * The GPP (General Purpose Port) interrupts are assigned from IRQ64 (GPP0) + * to IRQ95 (GPP31). + * get_irq() returns the lowest interrupt number that is currently asserted. + * + * Note: + * - This driver does not initialize the GPP when used as an interrupt + * input. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define CPU_INTR_STR "gt64260 cpu interface error" +#define PCI0_INTR_STR "gt64260 pci 0 error" +#define PCI1_INTR_STR "gt64260 pci 1 error" + +/* ========================== forward declaration ========================== */ + +static void gt64260_unmask_irq(unsigned int); +static void gt64260_mask_irq(unsigned int); + +/* ========================== local declarations =========================== */ + +struct hw_interrupt_type gt64260_pic = { + .typename = " gt64260_pic ", + .enable = gt64260_unmask_irq, + .disable = gt64260_mask_irq, + .ack = gt64260_mask_irq, + .end = gt64260_unmask_irq, +}; + +u32 gt64260_irq_base = 0; /* GT64260 handles the next 96 IRQs from here */ + +static struct mv64x60_handle bh; + +/* gt64260_init_irq() + * + * This function initializes the interrupt controller. It assigns + * all interrupts from IRQ0 to IRQ95 to the gt64260 interrupt controller. + * + * Note: + * We register all GPP inputs as interrupt source, but disable them. + */ +void __init +gt64260_init_irq(void) +{ + int i; + + if (ppc_md.progress) + ppc_md.progress("gt64260_init_irq: enter", 0x0); + + bh.v_base = mv64x60_get_bridge_vbase(); + + ppc_cached_irq_mask[0] = 0; + ppc_cached_irq_mask[1] = 0x0f000000; /* Enable GPP intrs */ + ppc_cached_irq_mask[2] = 0; + + /* disable all interrupts and clear current interrupts */ + mv64x60_write(&bh, MV64x60_GPP_INTR_MASK, ppc_cached_irq_mask[2]); + mv64x60_write(&bh, MV64x60_GPP_INTR_CAUSE, 0); + mv64x60_write(&bh, GT64260_IC_CPU_INTR_MASK_LO, ppc_cached_irq_mask[0]); + mv64x60_write(&bh, GT64260_IC_CPU_INTR_MASK_HI, ppc_cached_irq_mask[1]); + + /* use the gt64260 for all (possible) interrupt sources */ + for (i = gt64260_irq_base; i < (gt64260_irq_base + 96); i++) + irq_desc[i].handler = >64260_pic; + + if (ppc_md.progress) + ppc_md.progress("gt64260_init_irq: exit", 0x0); +} + +/* + * gt64260_get_irq() + * + * This function returns the lowest interrupt number of all interrupts that + * are currently asserted. + * + * Input Variable(s): + * struct pt_regs* not used + * + * Output Variable(s): + * None. + * + * Returns: + * int or -2 (bogus interrupt) + */ +int +gt64260_get_irq(struct pt_regs *regs) +{ + int irq; + int irq_gpp; + + irq = mv64x60_read(&bh, GT64260_IC_MAIN_CAUSE_LO); + irq = __ilog2((irq & 0x3dfffffe) & ppc_cached_irq_mask[0]); + + if (irq == -1) { + irq = mv64x60_read(&bh, GT64260_IC_MAIN_CAUSE_HI); + irq = __ilog2((irq & 0x0f000db7) & ppc_cached_irq_mask[1]); + + if (irq == -1) + irq = -2; /* bogus interrupt, should never happen */ + else { + if (irq >= 24) { + irq_gpp = mv64x60_read(&bh, + MV64x60_GPP_INTR_CAUSE); + irq_gpp = __ilog2(irq_gpp & + ppc_cached_irq_mask[2]); + + if (irq_gpp == -1) + irq = -2; + else { + irq = irq_gpp + 64; + mv64x60_write(&bh, + MV64x60_GPP_INTR_CAUSE, + ~(1 << (irq - 64))); + } + } else + irq += 32; + } + } + + (void)mv64x60_read(&bh, MV64x60_GPP_INTR_CAUSE); + + if (irq < 0) + return (irq); + else + return (gt64260_irq_base + irq); +} + +/* gt64260_unmask_irq() + * + * This function enables an interrupt. + * + * Input Variable(s): + * unsigned int interrupt number (IRQ0...IRQ95). + * + * Output Variable(s): + * None. + * + * Returns: + * void + */ +static void +gt64260_unmask_irq(unsigned int irq) +{ + irq -= gt64260_irq_base; + + if (irq > 31) + if (irq > 63) /* unmask GPP irq */ + mv64x60_write(&bh, MV64x60_GPP_INTR_MASK, + ppc_cached_irq_mask[2] |= (1 << (irq - 64))); + else /* mask high interrupt register */ + mv64x60_write(&bh, GT64260_IC_CPU_INTR_MASK_HI, + ppc_cached_irq_mask[1] |= (1 << (irq - 32))); + else /* mask low interrupt register */ + mv64x60_write(&bh, GT64260_IC_CPU_INTR_MASK_LO, + ppc_cached_irq_mask[0] |= (1 << irq)); + + (void)mv64x60_read(&bh, MV64x60_GPP_INTR_MASK); + return; +} + +/* gt64260_mask_irq() + * + * This function disables the requested interrupt. + * + * Input Variable(s): + * unsigned int interrupt number (IRQ0...IRQ95). + * + * Output Variable(s): + * None. + * + * Returns: + * void + */ +static void +gt64260_mask_irq(unsigned int irq) +{ + irq -= gt64260_irq_base; + + if (irq > 31) + if (irq > 63) /* mask GPP irq */ + mv64x60_write(&bh, MV64x60_GPP_INTR_MASK, + ppc_cached_irq_mask[2] &= ~(1 << (irq - 64))); + else /* mask high interrupt register */ + mv64x60_write(&bh, GT64260_IC_CPU_INTR_MASK_HI, + ppc_cached_irq_mask[1] &= ~(1 << (irq - 32))); + else /* mask low interrupt register */ + mv64x60_write(&bh, GT64260_IC_CPU_INTR_MASK_LO, + ppc_cached_irq_mask[0] &= ~(1 << irq)); + + (void)mv64x60_read(&bh, MV64x60_GPP_INTR_MASK); + return; +} + +static irqreturn_t +gt64260_cpu_error_int_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + printk(KERN_ERR "gt64260_cpu_error_int_handler: %s 0x%08x\n", + "Error on CPU interface - Cause regiser", + mv64x60_read(&bh, MV64x60_CPU_ERR_CAUSE)); + printk(KERN_ERR "\tCPU error register dump:\n"); + printk(KERN_ERR "\tAddress low 0x%08x\n", + mv64x60_read(&bh, MV64x60_CPU_ERR_ADDR_LO)); + printk(KERN_ERR "\tAddress high 0x%08x\n", + mv64x60_read(&bh, MV64x60_CPU_ERR_ADDR_HI)); + printk(KERN_ERR "\tData low 0x%08x\n", + mv64x60_read(&bh, MV64x60_CPU_ERR_DATA_LO)); + printk(KERN_ERR "\tData high 0x%08x\n", + mv64x60_read(&bh, MV64x60_CPU_ERR_DATA_HI)); + printk(KERN_ERR "\tParity 0x%08x\n", + mv64x60_read(&bh, MV64x60_CPU_ERR_PARITY)); + mv64x60_write(&bh, MV64x60_CPU_ERR_CAUSE, 0); + return IRQ_HANDLED; +} + +static irqreturn_t +gt64260_pci_error_int_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + u32 val; + unsigned int pci_bus = (unsigned int)dev_id; + + if (pci_bus == 0) { /* Error on PCI 0 */ + val = mv64x60_read(&bh, MV64x60_PCI0_ERR_CAUSE); + printk(KERN_ERR "%s: Error in PCI %d Interface\n", + "gt64260_pci_error_int_handler", pci_bus); + printk(KERN_ERR "\tPCI %d error register dump:\n", pci_bus); + printk(KERN_ERR "\tCause register 0x%08x\n", val); + printk(KERN_ERR "\tAddress Low 0x%08x\n", + mv64x60_read(&bh, MV64x60_PCI0_ERR_ADDR_LO)); + printk(KERN_ERR "\tAddress High 0x%08x\n", + mv64x60_read(&bh, MV64x60_PCI0_ERR_ADDR_HI)); + printk(KERN_ERR "\tAttribute 0x%08x\n", + mv64x60_read(&bh, MV64x60_PCI0_ERR_DATA_LO)); + printk(KERN_ERR "\tCommand 0x%08x\n", + mv64x60_read(&bh, MV64x60_PCI0_ERR_CMD)); + mv64x60_write(&bh, MV64x60_PCI0_ERR_CAUSE, ~val); + } + if (pci_bus == 1) { /* Error on PCI 1 */ + val = mv64x60_read(&bh, MV64x60_PCI1_ERR_CAUSE); + printk(KERN_ERR "%s: Error in PCI %d Interface\n", + "gt64260_pci_error_int_handler", pci_bus); + printk(KERN_ERR "\tPCI %d error register dump:\n", pci_bus); + printk(KERN_ERR "\tCause register 0x%08x\n", val); + printk(KERN_ERR "\tAddress Low 0x%08x\n", + mv64x60_read(&bh, MV64x60_PCI1_ERR_ADDR_LO)); + printk(KERN_ERR "\tAddress High 0x%08x\n", + mv64x60_read(&bh, MV64x60_PCI1_ERR_ADDR_HI)); + printk(KERN_ERR "\tAttribute 0x%08x\n", + mv64x60_read(&bh, MV64x60_PCI1_ERR_DATA_LO)); + printk(KERN_ERR "\tCommand 0x%08x\n", + mv64x60_read(&bh, MV64x60_PCI1_ERR_CMD)); + mv64x60_write(&bh, MV64x60_PCI1_ERR_CAUSE, ~val); + } + return IRQ_HANDLED; +} + +static int __init +gt64260_register_hdlrs(void) +{ + int rc; + + /* Register CPU interface error interrupt handler */ + if ((rc = request_irq(MV64x60_IRQ_CPU_ERR, + gt64260_cpu_error_int_handler, SA_INTERRUPT, CPU_INTR_STR, 0))) + printk(KERN_WARNING "Can't register cpu error handler: %d", rc); + + mv64x60_write(&bh, MV64x60_CPU_ERR_MASK, 0); + mv64x60_write(&bh, MV64x60_CPU_ERR_MASK, 0x000000fe); + + /* Register PCI 0 error interrupt handler */ + if ((rc = request_irq(MV64360_IRQ_PCI0, gt64260_pci_error_int_handler, + SA_INTERRUPT, PCI0_INTR_STR, (void *)0))) + printk(KERN_WARNING "Can't register pci 0 error handler: %d", + rc); + + mv64x60_write(&bh, MV64x60_PCI0_ERR_MASK, 0); + mv64x60_write(&bh, MV64x60_PCI0_ERR_MASK, 0x003c0c24); + + /* Register PCI 1 error interrupt handler */ + if ((rc = request_irq(MV64360_IRQ_PCI1, gt64260_pci_error_int_handler, + SA_INTERRUPT, PCI1_INTR_STR, (void *)1))) + printk(KERN_WARNING "Can't register pci 1 error handler: %d", + rc); + + mv64x60_write(&bh, MV64x60_PCI1_ERR_MASK, 0); + mv64x60_write(&bh, MV64x60_PCI1_ERR_MASK, 0x003c0c24); + + return 0; +} + +arch_initcall(gt64260_register_hdlrs); diff --git a/arch/ppc/syslib/harrier.c b/arch/ppc/syslib/harrier.c new file mode 100644 index 00000000000..a6b3f864579 --- /dev/null +++ b/arch/ppc/syslib/harrier.c @@ -0,0 +1,302 @@ +/* + * arch/ppc/syslib/harrier.c + * + * Motorola MCG Harrier northbridge/memory controller support + * + * Author: Dale Farnsworth + * dale.farnsworth@mvista.com + * + * 2001 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* define defaults for inbound windows */ +#define HARRIER_ITAT_DEFAULT (HARRIER_ITAT_ENA | \ + HARRIER_ITAT_MEM | \ + HARRIER_ITAT_WPE | \ + HARRIER_ITAT_GBL) + +#define HARRIER_MPAT_DEFAULT (HARRIER_ITAT_ENA | \ + HARRIER_ITAT_MEM | \ + HARRIER_ITAT_WPE | \ + HARRIER_ITAT_GBL) + +/* + * Initialize the inbound window size on a non-monarch harrier. + */ +void __init harrier_setup_nonmonarch(uint ppc_reg_base, uint in0_size) +{ + u16 temps; + u32 temp; + + if (in0_size > HARRIER_ITSZ_2GB) { + printk + ("harrier_setup_nonmonarch: Invalid window size code %d\n", + in0_size); + return; + } + + /* Clear the PCI memory enable bit. If we don't, then when the + * inbound windows are enabled below, the corresponding BARs will be + * "live" and start answering to PCI memory reads from their default + * addresses (0x0), which overlap with system RAM. + */ + temps = in_le16((u16 *) (ppc_reg_base + + HARRIER_XCSR_CONFIG(PCI_COMMAND))); + temps &= ~(PCI_COMMAND_MEMORY); + out_le16((u16 *) (ppc_reg_base + HARRIER_XCSR_CONFIG(PCI_COMMAND)), + temps); + + /* Setup a non-prefetchable inbound window */ + out_le32((u32 *) (ppc_reg_base + + HARRIER_XCSR_CONFIG(HARRIER_ITSZ0_OFF)), in0_size); + + temp = in_le32((u32 *) (ppc_reg_base + + HARRIER_XCSR_CONFIG(HARRIER_ITAT0_OFF))); + temp &= ~HARRIER_ITAT_PRE; + temp |= HARRIER_ITAT_DEFAULT; + out_le32((u32 *) (ppc_reg_base + + HARRIER_XCSR_CONFIG(HARRIER_ITAT0_OFF)), temp); + + /* Enable the message passing block */ + temp = in_le32((u32 *) (ppc_reg_base + + HARRIER_XCSR_CONFIG(HARRIER_MPAT_OFF))); + temp |= HARRIER_MPAT_DEFAULT; + out_le32((u32 *) (ppc_reg_base + + HARRIER_XCSR_CONFIG(HARRIER_MPAT_OFF)), temp); +} + +void __init harrier_release_eready(uint ppc_reg_base) +{ + ulong temp; + + /* + * Set EREADY to allow the line to be pulled up after everyone is + * ready. + */ + temp = in_be32((uint *) (ppc_reg_base + HARRIER_MISC_CSR_OFF)); + temp |= HARRIER_EREADY; + out_be32((uint *) (ppc_reg_base + HARRIER_MISC_CSR_OFF), temp); +} + +void __init harrier_wait_eready(uint ppc_reg_base) +{ + ulong temp; + + /* + * Poll the ERDYS line until it goes high to indicate that all + * non-monarch PrPMCs are ready for bus enumeration (or that there are + * no PrPMCs present). + */ + + /* FIXME: Add a timeout of some kind to prevent endless waits. */ + do { + + temp = in_be32((uint *) (ppc_reg_base + HARRIER_MISC_CSR_OFF)); + + } while (!(temp & HARRIER_ERDYS)); +} + +/* + * Initialize the Motorola MCG Harrier host bridge. + * + * This means setting up the PPC bus to PCI memory and I/O space mappings, + * setting the PCI memory space address of the MPIC (mapped straight + * through), and ioremap'ing the mpic registers. + * 'OpenPIC_Addr' will be set correctly by this routine. + * This routine will not change the PCI_CONFIG_ADDR or PCI_CONFIG_DATA + * addresses and assumes that the mapping of PCI memory space back to system + * memory is set up correctly by PPCBug. + */ +int __init +harrier_init(struct pci_controller *hose, + uint ppc_reg_base, + ulong processor_pci_mem_start, + ulong processor_pci_mem_end, + ulong processor_pci_io_start, + ulong processor_pci_io_end, ulong processor_mpic_base) +{ + uint addr, offset; + + /* + * Some sanity checks... + */ + if (((processor_pci_mem_start & 0xffff0000) != processor_pci_mem_start) + || ((processor_pci_io_start & 0xffff0000) != + processor_pci_io_start)) { + printk("harrier_init: %s\n", + "PPC to PCI mappings must start on 64 KB boundaries"); + return -1; + } + + if (((processor_pci_mem_end & 0x0000ffff) != 0x0000ffff) || + ((processor_pci_io_end & 0x0000ffff) != 0x0000ffff)) { + printk("harrier_init: PPC to PCI mappings %s\n", + "must end just before a 64 KB boundaries"); + return -1; + } + + if (((processor_pci_mem_end - processor_pci_mem_start) != + (hose->mem_space.end - hose->mem_space.start)) || + ((processor_pci_io_end - processor_pci_io_start) != + (hose->io_space.end - hose->io_space.start))) { + printk("harrier_init: %s\n", + "PPC and PCI memory or I/O space sizes don't match"); + return -1; + } + + if ((processor_mpic_base & 0xfffc0000) != processor_mpic_base) { + printk("harrier_init: %s\n", + "MPIC address must start on 256 KB boundary"); + return -1; + } + + if ((pci_dram_offset & 0xffff0000) != pci_dram_offset) { + printk("harrier_init: %s\n", + "pci_dram_offset must be multiple of 64 KB"); + return -1; + } + + /* + * Program the OTAD/OTOF registers to set up the PCI Mem & I/O + * space mappings. These are the mappings going from the processor to + * the PCI bus. + * + * Note: Don't need to 'AND' start/end addresses with 0xffff0000 + * because sanity check above ensures that they are properly + * aligned. + */ + + /* Set up PPC->PCI Mem mapping */ + addr = processor_pci_mem_start | (processor_pci_mem_end >> 16); +#ifdef CONFIG_HARRIER_STORE_GATHERING + offset = (hose->mem_space.start - processor_pci_mem_start) | 0x9a; +#else + offset = (hose->mem_space.start - processor_pci_mem_start) | 0x92; +#endif + out_be32((uint *) (ppc_reg_base + HARRIER_OTAD0_OFF), addr); + out_be32((uint *) (ppc_reg_base + HARRIER_OTOF0_OFF), offset); + + /* Set up PPC->PCI I/O mapping -- Contiguous I/O space */ + addr = processor_pci_io_start | (processor_pci_io_end >> 16); + offset = (hose->io_space.start - processor_pci_io_start) | 0x80; + out_be32((uint *) (ppc_reg_base + HARRIER_OTAD1_OFF), addr); + out_be32((uint *) (ppc_reg_base + HARRIER_OTOF1_OFF), offset); + + /* Enable MPIC */ + OpenPIC_Addr = (void *)processor_mpic_base; + addr = (processor_mpic_base >> 16) | 1; + out_be16((ushort *) (ppc_reg_base + HARRIER_MBAR_OFF), addr); + out_8((u_char *) (ppc_reg_base + HARRIER_MPIC_CSR_OFF), + HARRIER_MPIC_OPI_ENABLE); + + return 0; +} + +/* + * Find the amount of RAM present. + * This assumes that PPCBug has initialized the memory controller (SMC) + * on the Harrier correctly (i.e., it does no sanity checking). + * It also assumes that the memory base registers are set to configure the + * memory as contigous starting with "RAM A BASE", "RAM B BASE", etc. + * however, RAM base registers can be skipped (e.g. A, B, C are set, + * D is skipped but E is set is okay). + */ +#define MB (1024*1024UL) + +static uint harrier_size_table[] __initdata = { + 0 * MB, /* 0 ==> 0 MB */ + 32 * MB, /* 1 ==> 32 MB */ + 64 * MB, /* 2 ==> 64 MB */ + 64 * MB, /* 3 ==> 64 MB */ + 128 * MB, /* 4 ==> 128 MB */ + 128 * MB, /* 5 ==> 128 MB */ + 128 * MB, /* 6 ==> 128 MB */ + 256 * MB, /* 7 ==> 256 MB */ + 256 * MB, /* 8 ==> 256 MB */ + 256 * MB, /* 9 ==> 256 MB */ + 512 * MB, /* a ==> 512 MB */ + 512 * MB, /* b ==> 512 MB */ + 512 * MB, /* c ==> 512 MB */ + 1024 * MB, /* d ==> 1024 MB */ + 1024 * MB, /* e ==> 1024 MB */ + 2048 * MB, /* f ==> 2048 MB */ +}; + +/* + * *** WARNING: You MUST have a BAT set up to map in the XCSR regs *** + * + * Read the memory controller's registers to determine the amount of system + * memory. Assumes that the memory controller registers are already mapped + * into virtual memory--too early to use ioremap(). + */ +unsigned long __init harrier_get_mem_size(uint xcsr_base) +{ + ulong last_addr; + int i; + uint vend_dev_id; + uint *size_table; + uint val; + uint *csrp; + uint size; + int size_table_entries; + + vend_dev_id = in_be32((uint *) xcsr_base + PCI_VENDOR_ID); + + if (((vend_dev_id & 0xffff0000) >> 16) != PCI_VENDOR_ID_MOTOROLA) { + printk("harrier_get_mem_size: %s (0x%x)\n", + "Not a Motorola Memory Controller", vend_dev_id); + return 0; + } + + vend_dev_id &= 0x0000ffff; + + if (vend_dev_id == PCI_DEVICE_ID_MOTOROLA_HARRIER) { + size_table = harrier_size_table; + size_table_entries = sizeof(harrier_size_table) / + sizeof(harrier_size_table[0]); + } else { + printk("harrier_get_mem_size: %s (0x%x)\n", + "Not a Harrier", vend_dev_id); + return 0; + } + + last_addr = 0; + + csrp = (uint *) (xcsr_base + HARRIER_SDBA_OFF); + for (i = 0; i < 8; i++) { + val = in_be32(csrp++); + + if (val & 0x100) { /* If enabled */ + size = val >> HARRIER_SDB_SIZE_SHIFT; + size &= HARRIER_SDB_SIZE_MASK; + if (size >= size_table_entries) { + break; /* Register not set correctly */ + } + size = size_table[size]; + + val &= ~(size - 1); + val += size; + + if (val > last_addr) { + last_addr = val; + } + } + } + + return last_addr; +} diff --git a/arch/ppc/syslib/hawk_common.c b/arch/ppc/syslib/hawk_common.c new file mode 100644 index 00000000000..a9911dc3a82 --- /dev/null +++ b/arch/ppc/syslib/hawk_common.c @@ -0,0 +1,319 @@ +/* + * arch/ppc/syslib/hawk_common.c + * + * Common Motorola PowerPlus Platform--really Falcon/Raven or HAWK. + * + * Author: Mark A. Greer + * mgreer@mvista.com + * + * 2001 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* + * The Falcon/Raven and HAWK has 4 sets of registers: + * 1) PPC Registers which define the mappings from PPC bus to PCI bus, + * etc. + * 2) PCI Registers which define the mappings from PCI bus to PPC bus and the + * MPIC base address. + * 3) MPIC registers. + * 4) System Memory Controller (SMC) registers. + */ + +/* + * Initialize the Motorola MCG Raven or HAWK host bridge. + * + * This means setting up the PPC bus to PCI memory and I/O space mappings, + * setting the PCI memory space address of the MPIC (mapped straight + * through), and ioremap'ing the mpic registers. + * This routine will set the PCI_CONFIG_ADDR or PCI_CONFIG_DATA + * addresses based on the PCI I/O address that is passed in. + * 'OpenPIC_Addr' will be set correctly by this routine. + */ +int __init +hawk_init(struct pci_controller *hose, + uint ppc_reg_base, + ulong processor_pci_mem_start, + ulong processor_pci_mem_end, + ulong processor_pci_io_start, + ulong processor_pci_io_end, + ulong processor_mpic_base) +{ + uint addr, offset; + + /* + * Some sanity checks... + */ + if (((processor_pci_mem_start&0xffff0000) != processor_pci_mem_start) || + ((processor_pci_io_start &0xffff0000) != processor_pci_io_start)) { + printk("hawk_init: %s\n", + "PPC to PCI mappings must start on 64 KB boundaries"); + return -1; + } + + if (((processor_pci_mem_end &0x0000ffff) != 0x0000ffff) || + ((processor_pci_io_end &0x0000ffff) != 0x0000ffff)) { + printk("hawk_init: PPC to PCI mappings %s\n", + "must end just before a 64 KB boundaries"); + return -1; + } + + if (((processor_pci_mem_end - processor_pci_mem_start) != + (hose->mem_space.end - hose->mem_space.start)) || + ((processor_pci_io_end - processor_pci_io_start) != + (hose->io_space.end - hose->io_space.start))) { + printk("hawk_init: %s\n", + "PPC and PCI memory or I/O space sizes don't match"); + return -1; + } + + if ((processor_mpic_base & 0xfffc0000) != processor_mpic_base) { + printk("hawk_init: %s\n", + "MPIC address must start on 256 MB boundary"); + return -1; + } + + if ((pci_dram_offset & 0xffff0000) != pci_dram_offset) { + printk("hawk_init: %s\n", + "pci_dram_offset must be multiple of 64 KB"); + return -1; + } + + /* + * Disable previous PPC->PCI mappings. + */ + out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSOFF0_OFF), 0x00000000); + out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSOFF1_OFF), 0x00000000); + out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSOFF2_OFF), 0x00000000); + out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSOFF3_OFF), 0x00000000); + + /* + * Program the XSADD/XSOFF registers to set up the PCI Mem & I/O + * space mappings. These are the mappings going from the processor to + * the PCI bus. + * + * Note: Don't need to 'AND' start/end addresses with 0xffff0000 + * because sanity check above ensures that they are properly + * aligned. + */ + + /* Set up PPC->PCI Mem mapping */ + addr = processor_pci_mem_start | (processor_pci_mem_end >> 16); + offset = (hose->mem_space.start - processor_pci_mem_start) | 0xd2; + out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSADD0_OFF), addr); + out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSOFF0_OFF), offset); + + /* Set up PPC->MPIC mapping on the bridge */ + addr = processor_mpic_base | + (((processor_mpic_base + HAWK_MPIC_SIZE) >> 16) - 1); + /* No write posting for this PCI Mem space */ + offset = (hose->mem_space.start - processor_pci_mem_start) | 0xc2; + + out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSADD1_OFF), addr); + out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSOFF1_OFF), offset); + + /* Set up PPC->PCI I/O mapping -- Contiguous I/O space */ + addr = processor_pci_io_start | (processor_pci_io_end >> 16); + offset = (hose->io_space.start - processor_pci_io_start) | 0xc0; + out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSADD3_OFF), addr); + out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSOFF3_OFF), offset); + + hose->io_base_virt = (void *)ioremap(processor_pci_io_start, + (processor_pci_io_end - processor_pci_io_start + 1)); + + /* + * Set up the indirect method of accessing PCI config space. + * The PCI config addr/data pair based on start addr of PCI I/O space. + */ + setup_indirect_pci(hose, + processor_pci_io_start + HAWK_PCI_CONFIG_ADDR_OFF, + processor_pci_io_start + HAWK_PCI_CONFIG_DATA_OFF); + + /* + * Disable previous PCI->PPC mappings. + */ + + /* XXXX Put in mappings from PCI bus to processor bus XXXX */ + + /* + * Disable MPIC response to PCI I/O space (BAR 0). + * Make MPIC respond to PCI Mem space at specified address. + * (BAR 1). + */ + early_write_config_dword(hose, + 0, + PCI_DEVFN(0,0), + PCI_BASE_ADDRESS_0, + 0x00000000 | 0x1); + + early_write_config_dword(hose, + 0, + PCI_DEVFN(0,0), + PCI_BASE_ADDRESS_1, + (processor_mpic_base - + processor_pci_mem_start + + hose->mem_space.start) | 0x0); + + /* Map MPIC into vitual memory */ + OpenPIC_Addr = ioremap(processor_mpic_base, HAWK_MPIC_SIZE); + + return 0; +} + +/* + * Find the amount of RAM present. + * This assumes that PPCBug has initialized the memory controller (SMC) + * on the Falcon/HAWK correctly (i.e., it does no sanity checking). + * It also assumes that the memory base registers are set to configure the + * memory as contigous starting with "RAM A BASE", "RAM B BASE", etc. + * however, RAM base registers can be skipped (e.g. A, B, C are set, + * D is skipped but E is set is okay). + */ +#define MB (1024*1024) + +static uint reg_offset_table[] __initdata = { + HAWK_SMC_RAM_A_SIZE_REG_OFF, + HAWK_SMC_RAM_B_SIZE_REG_OFF, + HAWK_SMC_RAM_C_SIZE_REG_OFF, + HAWK_SMC_RAM_D_SIZE_REG_OFF, + HAWK_SMC_RAM_E_SIZE_REG_OFF, + HAWK_SMC_RAM_F_SIZE_REG_OFF, + HAWK_SMC_RAM_G_SIZE_REG_OFF, + HAWK_SMC_RAM_H_SIZE_REG_OFF +}; + +static uint falcon_size_table[] __initdata = { + 0 * MB, /* 0 ==> 0 MB */ + 16 * MB, /* 1 ==> 16 MB */ + 32 * MB, /* 2 ==> 32 MB */ + 64 * MB, /* 3 ==> 64 MB */ + 128 * MB, /* 4 ==> 128 MB */ + 256 * MB, /* 5 ==> 256 MB */ + 1024 * MB, /* 6 ==> 1024 MB (1 GB) */ +}; + +static uint hawk_size_table[] __initdata = { + 0 * MB, /* 0 ==> 0 MB */ + 32 * MB, /* 1 ==> 32 MB */ + 64 * MB, /* 2 ==> 64 MB */ + 64 * MB, /* 3 ==> 64 MB */ + 128 * MB, /* 4 ==> 128 MB */ + 128 * MB, /* 5 ==> 128 MB */ + 128 * MB, /* 6 ==> 128 MB */ + 256 * MB, /* 7 ==> 256 MB */ + 256 * MB, /* 8 ==> 256 MB */ + 512 * MB, /* 9 ==> 512 MB */ +}; + +/* + * *** WARNING: You MUST have a BAT set up to map in the SMC regs *** + * + * Read the memory controller's registers to determine the amount of system + * memory. Assumes that the memory controller registers are already mapped + * into virtual memory--too early to use ioremap(). + */ +unsigned long __init +hawk_get_mem_size(uint smc_base) +{ + unsigned long total; + int i, size_table_entries, reg_limit; + uint vend_dev_id; + uint *size_table; + u_char val; + + + vend_dev_id = in_be32((uint *)smc_base + PCI_VENDOR_ID); + + if (((vend_dev_id & 0xffff0000) >> 16) != PCI_VENDOR_ID_MOTOROLA) { + printk("hawk_get_mem_size: %s (0x%x)\n", + "Not a Motorola Memory Controller", vend_dev_id); + return 0; + } + + vend_dev_id &= 0x0000ffff; + + if (vend_dev_id == PCI_DEVICE_ID_MOTOROLA_FALCON) { + size_table = falcon_size_table; + size_table_entries = sizeof(falcon_size_table) / + sizeof(falcon_size_table[0]); + + reg_limit = FALCON_SMC_REG_COUNT; + } + else if (vend_dev_id == PCI_DEVICE_ID_MOTOROLA_HAWK) { + size_table = hawk_size_table; + size_table_entries = sizeof(hawk_size_table) / + sizeof(hawk_size_table[0]); + reg_limit = HAWK_SMC_REG_COUNT; + } + else { + printk("hawk_get_mem_size: %s (0x%x)\n", + "Not a Falcon or HAWK", vend_dev_id); + return 0; + } + + total = 0; + + /* Check every reg because PPCBug may skip some */ + for (i=0; i +#include +#include +#include +#include + +static volatile unsigned char *pci_intack; /* RO, gives us the irq vector */ + +unsigned char cached_8259[2] = { 0xff, 0xff }; +#define cached_A1 (cached_8259[0]) +#define cached_21 (cached_8259[1]) + +static DEFINE_SPINLOCK(i8259_lock); + +int i8259_pic_irq_offset; + +/* + * Acknowledge the IRQ using either the PCI host bridge's interrupt + * acknowledge feature or poll. How i8259_init() is called determines + * which is called. It should be noted that polling is broken on some + * IBM and Motorola PReP boxes so we must use the int-ack feature on them. + */ +int +i8259_irq(struct pt_regs *regs) +{ + int irq; + + spin_lock(&i8259_lock); + + /* Either int-ack or poll for the IRQ */ + if (pci_intack) + irq = *pci_intack; + else { + /* Perform an interrupt acknowledge cycle on controller 1. */ + outb(0x0C, 0x20); /* prepare for poll */ + irq = inb(0x20) & 7; + if (irq == 2 ) { + /* + * Interrupt is cascaded so perform interrupt + * acknowledge on controller 2. + */ + outb(0x0C, 0xA0); /* prepare for poll */ + irq = (inb(0xA0) & 7) + 8; + } + } + + if (irq == 7) { + /* + * This may be a spurious interrupt. + * + * Read the interrupt status register (ISR). If the most + * significant bit is not set then there is no valid + * interrupt. + */ + if (!pci_intack) + outb(0x0B, 0x20); /* ISR register */ + if(~inb(0x20) & 0x80) + irq = -1; + } + + spin_unlock(&i8259_lock); + return irq; +} + +static void i8259_mask_and_ack_irq(unsigned int irq_nr) +{ + unsigned long flags; + + spin_lock_irqsave(&i8259_lock, flags); + if ( irq_nr >= i8259_pic_irq_offset ) + irq_nr -= i8259_pic_irq_offset; + + if (irq_nr > 7) { + cached_A1 |= 1 << (irq_nr-8); + inb(0xA1); /* DUMMY */ + outb(cached_A1,0xA1); + outb(0x20,0xA0); /* Non-specific EOI */ + outb(0x20,0x20); /* Non-specific EOI to cascade */ + } else { + cached_21 |= 1 << irq_nr; + inb(0x21); /* DUMMY */ + outb(cached_21,0x21); + outb(0x20,0x20); /* Non-specific EOI */ + } + spin_unlock_irqrestore(&i8259_lock, flags); +} + +static void i8259_set_irq_mask(int irq_nr) +{ + outb(cached_A1,0xA1); + outb(cached_21,0x21); +} + +static void i8259_mask_irq(unsigned int irq_nr) +{ + unsigned long flags; + + spin_lock_irqsave(&i8259_lock, flags); + if ( irq_nr >= i8259_pic_irq_offset ) + irq_nr -= i8259_pic_irq_offset; + if ( irq_nr < 8 ) + cached_21 |= 1 << irq_nr; + else + cached_A1 |= 1 << (irq_nr-8); + i8259_set_irq_mask(irq_nr); + spin_unlock_irqrestore(&i8259_lock, flags); +} + +static void i8259_unmask_irq(unsigned int irq_nr) +{ + unsigned long flags; + + spin_lock_irqsave(&i8259_lock, flags); + if ( irq_nr >= i8259_pic_irq_offset ) + irq_nr -= i8259_pic_irq_offset; + if ( irq_nr < 8 ) + cached_21 &= ~(1 << irq_nr); + else + cached_A1 &= ~(1 << (irq_nr-8)); + i8259_set_irq_mask(irq_nr); + spin_unlock_irqrestore(&i8259_lock, flags); +} + +static void i8259_end_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)) + && irq_desc[irq].action) + i8259_unmask_irq(irq); +} + +struct hw_interrupt_type i8259_pic = { + " i8259 ", + NULL, + NULL, + i8259_unmask_irq, + i8259_mask_irq, + i8259_mask_and_ack_irq, + i8259_end_irq, + NULL +}; + +static struct resource pic1_iores = { + .name = "8259 (master)", + .start = 0x20, + .end = 0x21, + .flags = IORESOURCE_BUSY, +}; + +static struct resource pic2_iores = { + .name = "8259 (slave)", + .start = 0xa0, + .end = 0xa1, + .flags = IORESOURCE_BUSY, +}; + +static struct resource pic_edgectrl_iores = { + .name = "8259 edge control", + .start = 0x4d0, + .end = 0x4d1, + .flags = IORESOURCE_BUSY, +}; + +static struct irqaction i8259_irqaction = { + .handler = no_action, + .flags = SA_INTERRUPT, + .mask = CPU_MASK_NONE, + .name = "82c59 secondary cascade", +}; + +/* + * i8259_init() + * intack_addr - PCI interrupt acknowledge (real) address which will return + * the active irq from the 8259 + */ +void __init +i8259_init(long intack_addr) +{ + unsigned long flags; + + spin_lock_irqsave(&i8259_lock, flags); + /* init master interrupt controller */ + outb(0x11, 0x20); /* Start init sequence */ + outb(0x00, 0x21); /* Vector base */ + outb(0x04, 0x21); /* edge tiggered, Cascade (slave) on IRQ2 */ + outb(0x01, 0x21); /* Select 8086 mode */ + + /* init slave interrupt controller */ + outb(0x11, 0xA0); /* Start init sequence */ + outb(0x08, 0xA1); /* Vector base */ + outb(0x02, 0xA1); /* edge triggered, Cascade (slave) on IRQ2 */ + outb(0x01, 0xA1); /* Select 8086 mode */ + + /* always read ISR */ + outb(0x0B, 0x20); + outb(0x0B, 0xA0); + + /* Mask all interrupts */ + outb(cached_A1, 0xA1); + outb(cached_21, 0x21); + + spin_unlock_irqrestore(&i8259_lock, flags); + + /* reserve our resources */ + setup_irq( i8259_pic_irq_offset + 2, &i8259_irqaction); + request_resource(&ioport_resource, &pic1_iores); + request_resource(&ioport_resource, &pic2_iores); + request_resource(&ioport_resource, &pic_edgectrl_iores); + + if (intack_addr != 0) + pci_intack = ioremap(intack_addr, 1); +} diff --git a/arch/ppc/syslib/ibm440gp_common.c b/arch/ppc/syslib/ibm440gp_common.c new file mode 100644 index 00000000000..0d6be2d6dd6 --- /dev/null +++ b/arch/ppc/syslib/ibm440gp_common.c @@ -0,0 +1,76 @@ +/* + * arch/ppc/syslib/ibm440gp_common.c + * + * PPC440GP system library + * + * Matt Porter + * Copyright 2002-2003 MontaVista Software Inc. + * + * Eugene Surovegin or + * Copyright (c) 2003 Zultys Technologies + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ +#include +#include +#include +#include +#include + +/* + * Calculate 440GP clocks + */ +void __init ibm440gp_get_clocks(struct ibm44x_clocks* p, + unsigned int sys_clk, + unsigned int ser_clk) +{ + u32 cpc0_sys0 = mfdcr(DCRN_CPC0_SYS0); + u32 cpc0_cr0 = mfdcr(DCRN_CPC0_CR0); + u32 opdv = ((cpc0_sys0 >> 10) & 0x3) + 1; + u32 epdv = ((cpc0_sys0 >> 8) & 0x3) + 1; + + if (cpc0_sys0 & 0x2){ + /* Bypass system PLL */ + p->cpu = p->plb = sys_clk; + } + else { + u32 fbdv, fwdva, fwdvb, m, vco; + + fbdv = (cpc0_sys0 >> 18) & 0x0f; + if (!fbdv) + fbdv = 16; + + fwdva = 8 - ((cpc0_sys0 >> 15) & 0x7); + fwdvb = 8 - ((cpc0_sys0 >> 12) & 0x7); + + /* Feedback path */ + if (cpc0_sys0 & 0x00000080){ + /* PerClk */ + m = fwdvb * opdv * epdv; + } + else { + /* CPU clock */ + m = fbdv * fwdva; + } + vco = sys_clk * m; + p->cpu = vco / fwdva; + p->plb = vco / fwdvb; + } + + p->opb = p->plb / opdv; + p->ebc = p->opb / epdv; + + if (cpc0_cr0 & 0x00400000){ + /* External UART clock */ + p->uart0 = p->uart1 = ser_clk; + } + else { + /* Internal UART clock */ + u32 uart_div = ((cpc0_cr0 >> 16) & 0x1f) + 1; + p->uart0 = p->uart1 = p->plb / uart_div; + } +} diff --git a/arch/ppc/syslib/ibm440gp_common.h b/arch/ppc/syslib/ibm440gp_common.h new file mode 100644 index 00000000000..a054d83cb1a --- /dev/null +++ b/arch/ppc/syslib/ibm440gp_common.h @@ -0,0 +1,35 @@ +/* + * arch/ppc/kernel/ibm440gp_common.h + * + * PPC440GP system library + * + * Eugene Surovegin or + * Copyright (c) 2003 Zultys Technologies + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ +#ifdef __KERNEL__ +#ifndef __PPC_SYSLIB_IBM440GP_COMMON_H +#define __PPC_SYSLIB_IBM440GP_COMMON_H + +#ifndef __ASSEMBLY__ + +#include +#include +#include + +/* + * Please, refer to the Figure 13.1 in 440GP user manual + * + * if internal UART clock is used, ser_clk is ignored + */ +void ibm440gp_get_clocks(struct ibm44x_clocks*, unsigned int sys_clk, + unsigned int ser_clk) __init; + +#endif /* __ASSEMBLY__ */ +#endif /* __PPC_SYSLIB_IBM440GP_COMMON_H */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/syslib/ibm440gx_common.c b/arch/ppc/syslib/ibm440gx_common.c new file mode 100644 index 00000000000..4ad85e0e023 --- /dev/null +++ b/arch/ppc/syslib/ibm440gx_common.c @@ -0,0 +1,270 @@ +/* + * arch/ppc/kernel/ibm440gx_common.c + * + * PPC440GX system library + * + * Eugene Surovegin or + * Copyright (c) 2003, 2004 Zultys Technologies + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ +#include +#include +#include +#include +#include +#include +#include + +/* + * Calculate 440GX clocks + */ +static inline u32 __fix_zero(u32 v, u32 def){ + return v ? v : def; +} + +void __init ibm440gx_get_clocks(struct ibm44x_clocks* p, unsigned int sys_clk, + unsigned int ser_clk) +{ + u32 pllc = CPR_READ(DCRN_CPR_PLLC); + u32 plld = CPR_READ(DCRN_CPR_PLLD); + u32 uart0 = SDR_READ(DCRN_SDR_UART0); + u32 uart1 = SDR_READ(DCRN_SDR_UART1); + + /* Dividers */ + u32 fbdv = __fix_zero((plld >> 24) & 0x1f, 32); + u32 fwdva = __fix_zero((plld >> 16) & 0xf, 16); + u32 fwdvb = __fix_zero((plld >> 8) & 7, 8); + u32 lfbdv = __fix_zero(plld & 0x3f, 64); + u32 pradv0 = __fix_zero((CPR_READ(DCRN_CPR_PRIMAD) >> 24) & 7, 8); + u32 prbdv0 = __fix_zero((CPR_READ(DCRN_CPR_PRIMBD) >> 24) & 7, 8); + u32 opbdv0 = __fix_zero((CPR_READ(DCRN_CPR_OPBD) >> 24) & 3, 4); + u32 perdv0 = __fix_zero((CPR_READ(DCRN_CPR_PERD) >> 24) & 3, 4); + + /* Input clocks for primary dividers */ + u32 clk_a, clk_b; + + if (pllc & 0x40000000){ + u32 m; + + /* Feedback path */ + switch ((pllc >> 24) & 7){ + case 0: + /* PLLOUTx */ + m = ((pllc & 0x20000000) ? fwdvb : fwdva) * lfbdv; + break; + case 1: + /* CPU */ + m = fwdva * pradv0; + break; + case 5: + /* PERClk */ + m = fwdvb * prbdv0 * opbdv0 * perdv0; + break; + default: + printk(KERN_EMERG "invalid PLL feedback source\n"); + goto bypass; + } + m *= fbdv; + p->vco = sys_clk * m; + clk_a = p->vco / fwdva; + clk_b = p->vco / fwdvb; + } + else { +bypass: + /* Bypass system PLL */ + p->vco = 0; + clk_a = clk_b = sys_clk; + } + + p->cpu = clk_a / pradv0; + p->plb = clk_b / prbdv0; + p->opb = p->plb / opbdv0; + p->ebc = p->opb / perdv0; + + /* UARTs clock */ + if (uart0 & 0x00800000) + p->uart0 = ser_clk; + else + p->uart0 = p->plb / __fix_zero(uart0 & 0xff, 256); + + if (uart1 & 0x00800000) + p->uart1 = ser_clk; + else + p->uart1 = p->plb / __fix_zero(uart1 & 0xff, 256); +} + +/* Issue L2C diagnostic command */ +static inline u32 l2c_diag(u32 addr) +{ + mtdcr(DCRN_L2C0_ADDR, addr); + mtdcr(DCRN_L2C0_CMD, L2C_CMD_DIAG); + while (!(mfdcr(DCRN_L2C0_SR) & L2C_SR_CC)) ; + return mfdcr(DCRN_L2C0_DATA); +} + +static irqreturn_t l2c_error_handler(int irq, void* dev, struct pt_regs* regs) +{ + u32 sr = mfdcr(DCRN_L2C0_SR); + if (sr & L2C_SR_CPE){ + /* Read cache trapped address */ + u32 addr = l2c_diag(0x42000000); + printk(KERN_EMERG "L2C: Cache Parity Error, addr[16:26] = 0x%08x\n", addr); + } + if (sr & L2C_SR_TPE){ + /* Read tag trapped address */ + u32 addr = l2c_diag(0x82000000) >> 16; + printk(KERN_EMERG "L2C: Tag Parity Error, addr[16:26] = 0x%08x\n", addr); + } + + /* Clear parity errors */ + if (sr & (L2C_SR_CPE | L2C_SR_TPE)){ + mtdcr(DCRN_L2C0_ADDR, 0); + mtdcr(DCRN_L2C0_CMD, L2C_CMD_CCP | L2C_CMD_CTE); + } else + printk(KERN_EMERG "L2C: LRU error\n"); + + return IRQ_HANDLED; +} + +/* Enable L2 cache */ +void __init ibm440gx_l2c_enable(void){ + u32 r; + unsigned long flags; + + /* Install error handler */ + if (request_irq(87, l2c_error_handler, SA_INTERRUPT, "L2C", 0) < 0){ + printk(KERN_ERR "Cannot install L2C error handler, cache is not enabled\n"); + return; + } + + local_irq_save(flags); + asm volatile ("sync" ::: "memory"); + + /* Disable SRAM */ + mtdcr(DCRN_SRAM0_DPC, mfdcr(DCRN_SRAM0_DPC) & ~SRAM_DPC_ENABLE); + mtdcr(DCRN_SRAM0_SB0CR, mfdcr(DCRN_SRAM0_SB0CR) & ~SRAM_SBCR_BU_MASK); + mtdcr(DCRN_SRAM0_SB1CR, mfdcr(DCRN_SRAM0_SB1CR) & ~SRAM_SBCR_BU_MASK); + mtdcr(DCRN_SRAM0_SB2CR, mfdcr(DCRN_SRAM0_SB2CR) & ~SRAM_SBCR_BU_MASK); + mtdcr(DCRN_SRAM0_SB3CR, mfdcr(DCRN_SRAM0_SB3CR) & ~SRAM_SBCR_BU_MASK); + + /* Enable L2_MODE without ICU/DCU */ + r = mfdcr(DCRN_L2C0_CFG) & ~(L2C_CFG_ICU | L2C_CFG_DCU | L2C_CFG_SS_MASK); + r |= L2C_CFG_L2M | L2C_CFG_SS_256; + mtdcr(DCRN_L2C0_CFG, r); + + mtdcr(DCRN_L2C0_ADDR, 0); + + /* Hardware Clear Command */ + mtdcr(DCRN_L2C0_CMD, L2C_CMD_HCC); + while (!(mfdcr(DCRN_L2C0_SR) & L2C_SR_CC)) ; + + /* Clear Cache Parity and Tag Errors */ + mtdcr(DCRN_L2C0_CMD, L2C_CMD_CCP | L2C_CMD_CTE); + + /* Enable 64G snoop region starting at 0 */ + r = mfdcr(DCRN_L2C0_SNP0) & ~(L2C_SNP_BA_MASK | L2C_SNP_SSR_MASK); + r |= L2C_SNP_SSR_32G | L2C_SNP_ESR; + mtdcr(DCRN_L2C0_SNP0, r); + + r = mfdcr(DCRN_L2C0_SNP1) & ~(L2C_SNP_BA_MASK | L2C_SNP_SSR_MASK); + r |= 0x80000000 | L2C_SNP_SSR_32G | L2C_SNP_ESR; + mtdcr(DCRN_L2C0_SNP1, r); + + asm volatile ("sync" ::: "memory"); + + /* Enable ICU/DCU ports */ + r = mfdcr(DCRN_L2C0_CFG); + r &= ~(L2C_CFG_DCW_MASK | L2C_CFG_PMUX_MASK | L2C_CFG_PMIM | L2C_CFG_TPEI + | L2C_CFG_CPEI | L2C_CFG_NAM | L2C_CFG_NBRM); + r |= L2C_CFG_ICU | L2C_CFG_DCU | L2C_CFG_TPC | L2C_CFG_CPC | L2C_CFG_FRAN + | L2C_CFG_CPIM | L2C_CFG_TPIM | L2C_CFG_LIM | L2C_CFG_SMCM; + mtdcr(DCRN_L2C0_CFG, r); + + asm volatile ("sync; isync" ::: "memory"); + local_irq_restore(flags); +} + +/* Disable L2 cache */ +void __init ibm440gx_l2c_disable(void){ + u32 r; + unsigned long flags; + + local_irq_save(flags); + asm volatile ("sync" ::: "memory"); + + /* Disable L2C mode */ + r = mfdcr(DCRN_L2C0_CFG) & ~(L2C_CFG_L2M | L2C_CFG_ICU | L2C_CFG_DCU); + mtdcr(DCRN_L2C0_CFG, r); + + /* Enable SRAM */ + mtdcr(DCRN_SRAM0_DPC, mfdcr(DCRN_SRAM0_DPC) | SRAM_DPC_ENABLE); + mtdcr(DCRN_SRAM0_SB0CR, + SRAM_SBCR_BAS0 | SRAM_SBCR_BS_64KB | SRAM_SBCR_BU_RW); + mtdcr(DCRN_SRAM0_SB1CR, + SRAM_SBCR_BAS1 | SRAM_SBCR_BS_64KB | SRAM_SBCR_BU_RW); + mtdcr(DCRN_SRAM0_SB2CR, + SRAM_SBCR_BAS2 | SRAM_SBCR_BS_64KB | SRAM_SBCR_BU_RW); + mtdcr(DCRN_SRAM0_SB3CR, + SRAM_SBCR_BAS3 | SRAM_SBCR_BS_64KB | SRAM_SBCR_BU_RW); + + asm volatile ("sync; isync" ::: "memory"); + local_irq_restore(flags); +} + +void __init ibm440gx_l2c_setup(struct ibm44x_clocks* p) +{ + /* Disable L2C on rev.A, rev.B and 800MHz version of rev.C, + enable it on all other revisions + */ + u32 pvr = mfspr(SPRN_PVR); + if (pvr == PVR_440GX_RA || pvr == PVR_440GX_RB || + (pvr == PVR_440GX_RC && p->cpu > 667000000)) + ibm440gx_l2c_disable(); + else + ibm440gx_l2c_enable(); +} + +int __init ibm440gx_get_eth_grp(void) +{ + return (SDR_READ(DCRN_SDR_PFC1) & DCRN_SDR_PFC1_EPS) >> DCRN_SDR_PFC1_EPS_SHIFT; +} + +void __init ibm440gx_set_eth_grp(int group) +{ + SDR_WRITE(DCRN_SDR_PFC1, (SDR_READ(DCRN_SDR_PFC1) & ~DCRN_SDR_PFC1_EPS) | (group << DCRN_SDR_PFC1_EPS_SHIFT)); +} + +void __init ibm440gx_tah_enable(void) +{ + /* Enable TAH0 and TAH1 */ + SDR_WRITE(DCRN_SDR_MFR,SDR_READ(DCRN_SDR_MFR) & + ~DCRN_SDR_MFR_TAH0); + SDR_WRITE(DCRN_SDR_MFR,SDR_READ(DCRN_SDR_MFR) & + ~DCRN_SDR_MFR_TAH1); +} + +int ibm440gx_show_cpuinfo(struct seq_file *m){ + + u32 l2c_cfg = mfdcr(DCRN_L2C0_CFG); + const char* s; + if (l2c_cfg & L2C_CFG_L2M){ + switch (l2c_cfg & (L2C_CFG_ICU | L2C_CFG_DCU)){ + case L2C_CFG_ICU: s = "I-Cache only"; break; + case L2C_CFG_DCU: s = "D-Cache only"; break; + default: s = "I-Cache/D-Cache"; break; + } + } + else + s = "disabled"; + + seq_printf(m, "L2-Cache\t: %s (0x%08x 0x%08x)\n", s, + l2c_cfg, mfdcr(DCRN_L2C0_SR)); + + return 0; +} + diff --git a/arch/ppc/syslib/ibm440gx_common.h b/arch/ppc/syslib/ibm440gx_common.h new file mode 100644 index 00000000000..e73aa0411d3 --- /dev/null +++ b/arch/ppc/syslib/ibm440gx_common.h @@ -0,0 +1,57 @@ +/* + * arch/ppc/kernel/ibm440gx_common.h + * + * PPC440GX system library + * + * Eugene Surovegin or + * Copyright (c) 2003, 2004 Zultys Technologies + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ +#ifdef __KERNEL__ +#ifndef __PPC_SYSLIB_IBM440GX_COMMON_H +#define __PPC_SYSLIB_IBM440GX_COMMON_H + +#ifndef __ASSEMBLY__ + +#include +#include +#include +#include + +/* + * Please, refer to the Figure 14.1 in 440GX user manual + * + * if internal UART clock is used, ser_clk is ignored + */ +void ibm440gx_get_clocks(struct ibm44x_clocks*, unsigned int sys_clk, + unsigned int ser_clk) __init; + +/* Enable L2 cache */ +void ibm440gx_l2c_enable(void) __init; + +/* Disable L2 cache */ +void ibm440gx_l2c_disable(void) __init; + +/* Enable/disable L2 cache for a particular chip revision */ +void ibm440gx_l2c_setup(struct ibm44x_clocks*) __init; + +/* Get Ethernet Group */ +int ibm440gx_get_eth_grp(void) __init; + +/* Set Ethernet Group */ +void ibm440gx_set_eth_grp(int group) __init; + +/* Enable TAH devices */ +void ibm440gx_tah_enable(void) __init; + +/* Add L2C info to /proc/cpuinfo */ +int ibm440gx_show_cpuinfo(struct seq_file*); + +#endif /* __ASSEMBLY__ */ +#endif /* __PPC_SYSLIB_IBM440GX_COMMON_H */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/syslib/ibm440sp_common.c b/arch/ppc/syslib/ibm440sp_common.c new file mode 100644 index 00000000000..417d4cff77a --- /dev/null +++ b/arch/ppc/syslib/ibm440sp_common.c @@ -0,0 +1,71 @@ +/* + * arch/ppc/syslib/ibm440sp_common.c + * + * PPC440SP system library + * + * Matt Porter + * Copyright 2002-2005 MontaVista Software Inc. + * + * Eugene Surovegin or + * Copyright (c) 2003, 2004 Zultys Technologies + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* + * Read the 440SP memory controller to get size of system memory. + */ +unsigned long __init ibm440sp_find_end_of_memory(void) +{ + u32 i; + u32 mem_size = 0; + + /* Read two bank sizes and sum */ + for (i=0; i<2; i++) + switch (mfdcr(DCRN_MQ0_BS0BAS + i) & MQ0_CONFIG_SIZE_MASK) { + case MQ0_CONFIG_SIZE_8M: + mem_size += PPC44x_MEM_SIZE_8M; + break; + case MQ0_CONFIG_SIZE_16M: + mem_size += PPC44x_MEM_SIZE_16M; + break; + case MQ0_CONFIG_SIZE_32M: + mem_size += PPC44x_MEM_SIZE_32M; + break; + case MQ0_CONFIG_SIZE_64M: + mem_size += PPC44x_MEM_SIZE_64M; + break; + case MQ0_CONFIG_SIZE_128M: + mem_size += PPC44x_MEM_SIZE_128M; + break; + case MQ0_CONFIG_SIZE_256M: + mem_size += PPC44x_MEM_SIZE_256M; + break; + case MQ0_CONFIG_SIZE_512M: + mem_size += PPC44x_MEM_SIZE_512M; + break; + case MQ0_CONFIG_SIZE_1G: + mem_size += PPC44x_MEM_SIZE_1G; + break; + case MQ0_CONFIG_SIZE_2G: + mem_size += PPC44x_MEM_SIZE_2G; + break; + default: + break; + } + return mem_size; +} diff --git a/arch/ppc/syslib/ibm440sp_common.h b/arch/ppc/syslib/ibm440sp_common.h new file mode 100644 index 00000000000..a21a9906dcc --- /dev/null +++ b/arch/ppc/syslib/ibm440sp_common.h @@ -0,0 +1,25 @@ +/* + * arch/ppc/syslib/ibm440sp_common.h + * + * PPC440SP system library + * + * Matt Porter + * Copyright 2004-2005 MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ +#ifdef __KERNEL__ +#ifndef __PPC_SYSLIB_IBM440SP_COMMON_H +#define __PPC_SYSLIB_IBM440SP_COMMON_H + +#ifndef __ASSEMBLY__ + +extern unsigned long __init ibm440sp_find_end_of_memory(void); + +#endif /* __ASSEMBLY__ */ +#endif /* __PPC_SYSLIB_IBM440SP_COMMON_H */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/syslib/ibm44x_common.c b/arch/ppc/syslib/ibm44x_common.c new file mode 100644 index 00000000000..7612e0623f9 --- /dev/null +++ b/arch/ppc/syslib/ibm44x_common.c @@ -0,0 +1,193 @@ +/* + * arch/ppc/syslib/ibm44x_common.c + * + * PPC44x system library + * + * Matt Porter + * Copyright 2002-2005 MontaVista Software Inc. + * + * Eugene Surovegin or + * Copyright (c) 2003, 2004 Zultys Technologies + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +phys_addr_t fixup_bigphys_addr(phys_addr_t addr, phys_addr_t size) +{ + phys_addr_t page_4gb = 0; + + /* + * Trap the least significant 32-bit portions of an + * address in the 440's 36-bit address space. Fix + * them up with the appropriate ERPN + */ + if ((addr >= PPC44x_IO_LO) && (addr <= PPC44x_IO_HI)) + page_4gb = PPC44x_IO_PAGE; + else if ((addr >= PPC44x_PCI0CFG_LO) && (addr <= PPC44x_PCI0CFG_HI)) + page_4gb = PPC44x_PCICFG_PAGE; +#ifdef CONFIG_440SP + else if ((addr >= PPC44x_PCI1CFG_LO) && (addr <= PPC44x_PCI1CFG_HI)) + page_4gb = PPC44x_PCICFG_PAGE; + else if ((addr >= PPC44x_PCI2CFG_LO) && (addr <= PPC44x_PCI2CFG_HI)) + page_4gb = PPC44x_PCICFG_PAGE; +#endif + else if ((addr >= PPC44x_PCIMEM_LO) && (addr <= PPC44x_PCIMEM_HI)) + page_4gb = PPC44x_PCIMEM_PAGE; + + return (page_4gb | addr); +}; +EXPORT_SYMBOL(fixup_bigphys_addr); + +void __init ibm44x_calibrate_decr(unsigned int freq) +{ + tb_ticks_per_jiffy = freq / HZ; + tb_to_us = mulhwu_scale_factor(freq, 1000000); + + /* Set the time base to zero */ + mtspr(SPRN_TBWL, 0); + mtspr(SPRN_TBWU, 0); + + /* Clear any pending timer interrupts */ + mtspr(SPRN_TSR, TSR_ENW | TSR_WIS | TSR_DIS | TSR_FIS); + + /* Enable decrementer interrupt */ + mtspr(SPRN_TCR, TCR_DIE); +} + +extern void abort(void); + +static void ibm44x_restart(char *cmd) +{ + local_irq_disable(); + abort(); +} + +static void ibm44x_power_off(void) +{ + local_irq_disable(); + for(;;); +} + +static void ibm44x_halt(void) +{ + local_irq_disable(); + for(;;); +} + +/* + * Read the 44x memory controller to get size of system memory. + */ +static unsigned long __init ibm44x_find_end_of_memory(void) +{ + u32 i, bank_config; + u32 mem_size = 0; + + for (i=0; i<4; i++) + { + switch (i) + { + case 0: + mtdcr(DCRN_SDRAM0_CFGADDR, SDRAM0_B0CR); + break; + case 1: + mtdcr(DCRN_SDRAM0_CFGADDR, SDRAM0_B1CR); + break; + case 2: + mtdcr(DCRN_SDRAM0_CFGADDR, SDRAM0_B2CR); + break; + case 3: + mtdcr(DCRN_SDRAM0_CFGADDR, SDRAM0_B3CR); + break; + } + + bank_config = mfdcr(DCRN_SDRAM0_CFGDATA); + + if (!(bank_config & SDRAM_CONFIG_BANK_ENABLE)) + continue; + switch (SDRAM_CONFIG_BANK_SIZE(bank_config)) + { + case SDRAM_CONFIG_SIZE_8M: + mem_size += PPC44x_MEM_SIZE_8M; + break; + case SDRAM_CONFIG_SIZE_16M: + mem_size += PPC44x_MEM_SIZE_16M; + break; + case SDRAM_CONFIG_SIZE_32M: + mem_size += PPC44x_MEM_SIZE_32M; + break; + case SDRAM_CONFIG_SIZE_64M: + mem_size += PPC44x_MEM_SIZE_64M; + break; + case SDRAM_CONFIG_SIZE_128M: + mem_size += PPC44x_MEM_SIZE_128M; + break; + case SDRAM_CONFIG_SIZE_256M: + mem_size += PPC44x_MEM_SIZE_256M; + break; + case SDRAM_CONFIG_SIZE_512M: + mem_size += PPC44x_MEM_SIZE_512M; + break; + } + } + return mem_size; +} + +void __init ibm44x_platform_init(void) +{ + ppc_md.init_IRQ = ppc4xx_pic_init; + ppc_md.find_end_of_memory = ibm44x_find_end_of_memory; + ppc_md.restart = ibm44x_restart; + ppc_md.power_off = ibm44x_power_off; + ppc_md.halt = ibm44x_halt; + +#ifdef CONFIG_SERIAL_TEXT_DEBUG + ppc_md.progress = gen550_progress; +#endif /* CONFIG_SERIAL_TEXT_DEBUG */ +#ifdef CONFIG_KGDB + ppc_md.kgdb_map_scc = gen550_kgdb_map_scc; +#endif + + /* + * The Abatron BDI JTAG debugger does not tolerate others + * mucking with the debug registers. + */ +#if !defined(CONFIG_BDI_SWITCH) + /* Enable internal debug mode */ + mtspr(SPRN_DBCR0, (DBCR0_IDM)); + + /* Clear any residual debug events */ + mtspr(SPRN_DBSR, 0xffffffff); +#endif +} + +/* Called from MachineCheckException */ +void platform_machine_check(struct pt_regs *regs) +{ + printk("PLB0: BEAR=0x%08x%08x ACR= 0x%08x BESR= 0x%08x\n", + mfdcr(DCRN_PLB0_BEARH), mfdcr(DCRN_PLB0_BEARL), + mfdcr(DCRN_PLB0_ACR), mfdcr(DCRN_PLB0_BESR)); + printk("POB0: BEAR=0x%08x%08x BESR0=0x%08x BESR1=0x%08x\n", + mfdcr(DCRN_POB0_BEARH), mfdcr(DCRN_POB0_BEARL), + mfdcr(DCRN_POB0_BESR0), mfdcr(DCRN_POB0_BESR1)); + printk("OPB0: BEAR=0x%08x%08x BSTAT=0x%08x\n", + mfdcr(DCRN_OPB0_BEARH), mfdcr(DCRN_OPB0_BEARL), + mfdcr(DCRN_OPB0_BSTAT)); +} diff --git a/arch/ppc/syslib/ibm44x_common.h b/arch/ppc/syslib/ibm44x_common.h new file mode 100644 index 00000000000..b14eb603ce0 --- /dev/null +++ b/arch/ppc/syslib/ibm44x_common.h @@ -0,0 +1,42 @@ +/* + * arch/ppc/kernel/ibm44x_common.h + * + * PPC44x system library + * + * Eugene Surovegin or + * Copyright (c) 2003, 2004 Zultys Technologies + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ +#ifdef __KERNEL__ +#ifndef __PPC_SYSLIB_IBM44x_COMMON_H +#define __PPC_SYSLIB_IBM44x_COMMON_H + +#ifndef __ASSEMBLY__ + +/* + * All clocks are in Hz + */ +struct ibm44x_clocks { + unsigned int vco; /* VCO, 0 if system PLL is bypassed */ + unsigned int cpu; /* CPUCoreClk */ + unsigned int plb; /* PLBClk */ + unsigned int opb; /* OPBClk */ + unsigned int ebc; /* PerClk */ + unsigned int uart0; + unsigned int uart1; +}; + +/* common 44x platform init */ +void ibm44x_platform_init(void) __init; + +/* initialize decrementer and tick-related variables */ +void ibm44x_calibrate_decr(unsigned int freq) __init; + +#endif /* __ASSEMBLY__ */ +#endif /* __PPC_SYSLIB_IBM44x_COMMON_H */ +#endif /* __KERNEL__ */ diff --git a/arch/ppc/syslib/ibm_ocp.c b/arch/ppc/syslib/ibm_ocp.c new file mode 100644 index 00000000000..3f6e55c7918 --- /dev/null +++ b/arch/ppc/syslib/ibm_ocp.c @@ -0,0 +1,9 @@ +#include +#include + +struct ocp_sys_info_data ocp_sys_info = { + .opb_bus_freq = 50000000, /* OPB Bus Frequency (Hz) */ + .ebc_bus_freq = 33333333, /* EBC Bus Frequency (Hz) */ +}; + +EXPORT_SYMBOL(ocp_sys_info); diff --git a/arch/ppc/syslib/indirect_pci.c b/arch/ppc/syslib/indirect_pci.c new file mode 100644 index 00000000000..a5a752609e2 --- /dev/null +++ b/arch/ppc/syslib/indirect_pci.c @@ -0,0 +1,135 @@ +/* + * Support for indirect PCI bridges. + * + * Copyright (C) 1998 Gabriel Paubert. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef CONFIG_PPC_INDIRECT_PCI_BE +#define PCI_CFG_OUT out_be32 +#else +#define PCI_CFG_OUT out_le32 +#endif + +static int +indirect_read_config(struct pci_bus *bus, unsigned int devfn, int offset, + int len, u32 *val) +{ + struct pci_controller *hose = bus->sysdata; + volatile void __iomem *cfg_data; + u8 cfg_type = 0; + + if (ppc_md.pci_exclude_device) + if (ppc_md.pci_exclude_device(bus->number, devfn)) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (hose->set_cfg_type) + if (bus->number != hose->first_busno) + cfg_type = 1; + + PCI_CFG_OUT(hose->cfg_addr, + (0x80000000 | ((bus->number - hose->bus_offset) << 16) + | (devfn << 8) | ((offset & 0xfc) | cfg_type))); + + /* + * Note: the caller has already checked that offset is + * suitably aligned and that len is 1, 2 or 4. + */ + cfg_data = hose->cfg_data + (offset & 3); + switch (len) { + case 1: + *val = in_8(cfg_data); + break; + case 2: + *val = in_le16(cfg_data); + break; + default: + *val = in_le32(cfg_data); + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static int +indirect_write_config(struct pci_bus *bus, unsigned int devfn, int offset, + int len, u32 val) +{ + struct pci_controller *hose = bus->sysdata; + volatile void __iomem *cfg_data; + u8 cfg_type = 0; + + if (ppc_md.pci_exclude_device) + if (ppc_md.pci_exclude_device(bus->number, devfn)) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (hose->set_cfg_type) + if (bus->number != hose->first_busno) + cfg_type = 1; + + PCI_CFG_OUT(hose->cfg_addr, + (0x80000000 | ((bus->number - hose->bus_offset) << 16) + | (devfn << 8) | ((offset & 0xfc) | cfg_type))); + + /* + * Note: the caller has already checked that offset is + * suitably aligned and that len is 1, 2 or 4. + */ + cfg_data = hose->cfg_data + (offset & 3); + switch (len) { + case 1: + out_8(cfg_data, val); + break; + case 2: + out_le16(cfg_data, val); + break; + default: + out_le32(cfg_data, val); + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops indirect_pci_ops = +{ + indirect_read_config, + indirect_write_config +}; + +void __init +setup_indirect_pci_nomap(struct pci_controller* hose, void __iomem * cfg_addr, + void __iomem * cfg_data) +{ + hose->cfg_addr = cfg_addr; + hose->cfg_data = cfg_data; + hose->ops = &indirect_pci_ops; +} + +void __init +setup_indirect_pci(struct pci_controller* hose, u32 cfg_addr, u32 cfg_data) +{ + unsigned long base = cfg_addr & PAGE_MASK; + void __iomem *mbase, *addr, *data; + + mbase = ioremap(base, PAGE_SIZE); + addr = mbase + (cfg_addr & ~PAGE_MASK); + if ((cfg_data & PAGE_MASK) != base) + mbase = ioremap(cfg_data & PAGE_MASK, PAGE_SIZE); + data = mbase + (cfg_data & ~PAGE_MASK); + setup_indirect_pci_nomap(hose, addr, data); +} diff --git a/arch/ppc/syslib/ipic.c b/arch/ppc/syslib/ipic.c new file mode 100644 index 00000000000..acb2cde3171 --- /dev/null +++ b/arch/ppc/syslib/ipic.c @@ -0,0 +1,646 @@ +/* + * include/asm-ppc/ipic.c + * + * IPIC routines implementations. + * + * Copyright 2005 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ipic.h" + +static struct ipic p_ipic; +static struct ipic * primary_ipic; + +static struct ipic_info ipic_info[] = { + [9] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_D, + .force = IPIC_SIFCR_H, + .bit = 24, + .prio_mask = 0, + }, + [10] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_D, + .force = IPIC_SIFCR_H, + .bit = 25, + .prio_mask = 1, + }, + [11] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_D, + .force = IPIC_SIFCR_H, + .bit = 26, + .prio_mask = 2, + }, + [14] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_D, + .force = IPIC_SIFCR_H, + .bit = 29, + .prio_mask = 5, + }, + [15] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_D, + .force = IPIC_SIFCR_H, + .bit = 30, + .prio_mask = 6, + }, + [16] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_D, + .force = IPIC_SIFCR_H, + .bit = 31, + .prio_mask = 7, + }, + [17] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SEMSR, + .prio = IPIC_SMPRR_A, + .force = IPIC_SEFCR, + .bit = 1, + .prio_mask = 5, + }, + [18] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SEMSR, + .prio = IPIC_SMPRR_A, + .force = IPIC_SEFCR, + .bit = 2, + .prio_mask = 6, + }, + [19] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SEMSR, + .prio = IPIC_SMPRR_A, + .force = IPIC_SEFCR, + .bit = 3, + .prio_mask = 7, + }, + [20] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SEMSR, + .prio = IPIC_SMPRR_B, + .force = IPIC_SEFCR, + .bit = 4, + .prio_mask = 4, + }, + [21] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SEMSR, + .prio = IPIC_SMPRR_B, + .force = IPIC_SEFCR, + .bit = 5, + .prio_mask = 5, + }, + [22] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SEMSR, + .prio = IPIC_SMPRR_B, + .force = IPIC_SEFCR, + .bit = 6, + .prio_mask = 6, + }, + [23] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SEMSR, + .prio = IPIC_SMPRR_B, + .force = IPIC_SEFCR, + .bit = 7, + .prio_mask = 7, + }, + [32] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_A, + .force = IPIC_SIFCR_H, + .bit = 0, + .prio_mask = 0, + }, + [33] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_A, + .force = IPIC_SIFCR_H, + .bit = 1, + .prio_mask = 1, + }, + [34] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_A, + .force = IPIC_SIFCR_H, + .bit = 2, + .prio_mask = 2, + }, + [35] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_A, + .force = IPIC_SIFCR_H, + .bit = 3, + .prio_mask = 3, + }, + [36] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_A, + .force = IPIC_SIFCR_H, + .bit = 4, + .prio_mask = 4, + }, + [37] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_A, + .force = IPIC_SIFCR_H, + .bit = 5, + .prio_mask = 5, + }, + [38] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_A, + .force = IPIC_SIFCR_H, + .bit = 6, + .prio_mask = 6, + }, + [39] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_A, + .force = IPIC_SIFCR_H, + .bit = 7, + .prio_mask = 7, + }, + [48] = { + .pend = IPIC_SEPNR, + .mask = IPIC_SEMSR, + .prio = IPIC_SMPRR_A, + .force = IPIC_SEFCR, + .bit = 0, + .prio_mask = 4, + }, + [64] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = IPIC_SMPRR_A, + .force = IPIC_SIFCR_L, + .bit = 0, + .prio_mask = 0, + }, + [65] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = IPIC_SMPRR_A, + .force = IPIC_SIFCR_L, + .bit = 1, + .prio_mask = 1, + }, + [66] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = IPIC_SMPRR_A, + .force = IPIC_SIFCR_L, + .bit = 2, + .prio_mask = 2, + }, + [67] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = IPIC_SMPRR_A, + .force = IPIC_SIFCR_L, + .bit = 3, + .prio_mask = 3, + }, + [68] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = IPIC_SMPRR_B, + .force = IPIC_SIFCR_L, + .bit = 4, + .prio_mask = 0, + }, + [69] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = IPIC_SMPRR_B, + .force = IPIC_SIFCR_L, + .bit = 5, + .prio_mask = 1, + }, + [70] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = IPIC_SMPRR_B, + .force = IPIC_SIFCR_L, + .bit = 6, + .prio_mask = 2, + }, + [71] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = IPIC_SMPRR_B, + .force = IPIC_SIFCR_L, + .bit = 7, + .prio_mask = 3, + }, + [72] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 8, + }, + [73] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 9, + }, + [74] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 10, + }, + [75] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 11, + }, + [76] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 12, + }, + [77] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 13, + }, + [78] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 14, + }, + [79] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 15, + }, + [80] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 16, + }, + [84] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 20, + }, + [85] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 21, + }, + [90] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 26, + }, + [91] = { + .pend = IPIC_SIPNR_H, + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 27, + }, +}; + +static inline u32 ipic_read(volatile u32 __iomem *base, unsigned int reg) +{ + return in_be32(base + (reg >> 2)); +} + +static inline void ipic_write(volatile u32 __iomem *base, unsigned int reg, u32 value) +{ + out_be32(base + (reg >> 2), value); +} + +static inline struct ipic * ipic_from_irq(unsigned int irq) +{ + return primary_ipic; +} + +static void ipic_enable_irq(unsigned int irq) +{ + struct ipic *ipic = ipic_from_irq(irq); + unsigned int src = irq - ipic->irq_offset; + u32 temp; + + temp = ipic_read(ipic->regs, ipic_info[src].mask); + temp |= (1 << (31 - ipic_info[src].bit)); + ipic_write(ipic->regs, ipic_info[src].mask, temp); +} + +static void ipic_disable_irq(unsigned int irq) +{ + struct ipic *ipic = ipic_from_irq(irq); + unsigned int src = irq - ipic->irq_offset; + u32 temp; + + temp = ipic_read(ipic->regs, ipic_info[src].mask); + temp &= ~(1 << (31 - ipic_info[src].bit)); + ipic_write(ipic->regs, ipic_info[src].mask, temp); +} + +static void ipic_disable_irq_and_ack(unsigned int irq) +{ + struct ipic *ipic = ipic_from_irq(irq); + unsigned int src = irq - ipic->irq_offset; + u32 temp; + + ipic_disable_irq(irq); + + temp = ipic_read(ipic->regs, ipic_info[src].pend); + temp |= (1 << (31 - ipic_info[src].bit)); + ipic_write(ipic->regs, ipic_info[src].pend, temp); +} + +static void ipic_end_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + ipic_enable_irq(irq); +} + +struct hw_interrupt_type ipic = { + .typename = " IPIC ", + .enable = ipic_enable_irq, + .disable = ipic_disable_irq, + .ack = ipic_disable_irq_and_ack, + .end = ipic_end_irq, +}; + +void __init ipic_init(phys_addr_t phys_addr, + unsigned int flags, + unsigned int irq_offset, + unsigned char *senses, + unsigned int senses_count) +{ + u32 i, temp = 0; + + primary_ipic = &p_ipic; + primary_ipic->regs = ioremap(phys_addr, MPC83xx_IPIC_SIZE); + + primary_ipic->irq_offset = irq_offset; + + ipic_write(primary_ipic->regs, IPIC_SICNR, 0x0); + + /* default priority scheme is grouped. If spread mode is required + * configure SICFR accordingly */ + if (flags & IPIC_SPREADMODE_GRP_A) + temp |= SICFR_IPSA; + if (flags & IPIC_SPREADMODE_GRP_D) + temp |= SICFR_IPSD; + if (flags & IPIC_SPREADMODE_MIX_A) + temp |= SICFR_MPSA; + if (flags & IPIC_SPREADMODE_MIX_B) + temp |= SICFR_MPSB; + + ipic_write(primary_ipic->regs, IPIC_SICNR, temp); + + /* handle MCP route */ + temp = 0; + if (flags & IPIC_DISABLE_MCP_OUT) + temp = SERCR_MCPR; + ipic_write(primary_ipic->regs, IPIC_SERCR, temp); + + /* handle routing of IRQ0 to MCP */ + temp = ipic_read(primary_ipic->regs, IPIC_SEMSR); + + if (flags & IPIC_IRQ0_MCP) + temp |= SEMSR_SIRQ0; + else + temp &= ~SEMSR_SIRQ0; + + ipic_write(primary_ipic->regs, IPIC_SEMSR, temp); + + for (i = 0 ; i < NR_IPIC_INTS ; i++) { + irq_desc[i+irq_offset].handler = &ipic; + irq_desc[i+irq_offset].status = IRQ_LEVEL; + } + + temp = 0; + for (i = 0 ; i < senses_count ; i++) { + if ((senses[i] & IRQ_SENSE_MASK) == IRQ_SENSE_EDGE) { + temp |= 1 << (16 - i); + if (i != 0) + irq_desc[i + irq_offset + MPC83xx_IRQ_EXT1 - 1].status = 0; + else + irq_desc[irq_offset + MPC83xx_IRQ_EXT0].status = 0; + } + } + ipic_write(primary_ipic->regs, IPIC_SECNR, temp); + + printk ("IPIC (%d IRQ sources, %d External IRQs) at %p\n", NR_IPIC_INTS, + senses_count, primary_ipic->regs); +} + +int ipic_set_priority(unsigned int irq, unsigned int priority) +{ + struct ipic *ipic = ipic_from_irq(irq); + unsigned int src = irq - ipic->irq_offset; + u32 temp; + + if (priority > 7) + return -EINVAL; + if (src > 127) + return -EINVAL; + if (ipic_info[src].prio == 0) + return -EINVAL; + + temp = ipic_read(ipic->regs, ipic_info[src].prio); + + if (priority < 4) { + temp &= ~(0x7 << (20 + (3 - priority) * 3)); + temp |= ipic_info[src].prio_mask << (20 + (3 - priority) * 3); + } else { + temp &= ~(0x7 << (4 + (7 - priority) * 3)); + temp |= ipic_info[src].prio_mask << (4 + (7 - priority) * 3); + } + + ipic_write(ipic->regs, ipic_info[src].prio, temp); + + return 0; +} + +void ipic_set_highest_priority(unsigned int irq) +{ + struct ipic *ipic = ipic_from_irq(irq); + unsigned int src = irq - ipic->irq_offset; + u32 temp; + + temp = ipic_read(ipic->regs, IPIC_SICFR); + + /* clear and set HPI */ + temp &= 0x7f000000; + temp |= (src & 0x7f) << 24; + + ipic_write(ipic->regs, IPIC_SICFR, temp); +} + +void ipic_set_default_priority(void) +{ + ipic_set_priority(MPC83xx_IRQ_TSEC1_TX, 0); + ipic_set_priority(MPC83xx_IRQ_TSEC1_RX, 1); + ipic_set_priority(MPC83xx_IRQ_TSEC1_ERROR, 2); + ipic_set_priority(MPC83xx_IRQ_TSEC2_TX, 3); + ipic_set_priority(MPC83xx_IRQ_TSEC2_RX, 4); + ipic_set_priority(MPC83xx_IRQ_TSEC2_ERROR, 5); + ipic_set_priority(MPC83xx_IRQ_USB2_DR, 6); + ipic_set_priority(MPC83xx_IRQ_USB2_MPH, 7); + + ipic_set_priority(MPC83xx_IRQ_UART1, 0); + ipic_set_priority(MPC83xx_IRQ_UART2, 1); + ipic_set_priority(MPC83xx_IRQ_SEC2, 2); + ipic_set_priority(MPC83xx_IRQ_IIC1, 5); + ipic_set_priority(MPC83xx_IRQ_IIC2, 6); + ipic_set_priority(MPC83xx_IRQ_SPI, 7); + ipic_set_priority(MPC83xx_IRQ_RTC_SEC, 0); + ipic_set_priority(MPC83xx_IRQ_PIT, 1); + ipic_set_priority(MPC83xx_IRQ_PCI1, 2); + ipic_set_priority(MPC83xx_IRQ_PCI2, 3); + ipic_set_priority(MPC83xx_IRQ_EXT0, 4); + ipic_set_priority(MPC83xx_IRQ_EXT1, 5); + ipic_set_priority(MPC83xx_IRQ_EXT2, 6); + ipic_set_priority(MPC83xx_IRQ_EXT3, 7); + ipic_set_priority(MPC83xx_IRQ_RTC_ALR, 0); + ipic_set_priority(MPC83xx_IRQ_MU, 1); + ipic_set_priority(MPC83xx_IRQ_SBA, 2); + ipic_set_priority(MPC83xx_IRQ_DMA, 3); + ipic_set_priority(MPC83xx_IRQ_EXT4, 4); + ipic_set_priority(MPC83xx_IRQ_EXT5, 5); + ipic_set_priority(MPC83xx_IRQ_EXT6, 6); + ipic_set_priority(MPC83xx_IRQ_EXT7, 7); +} + +void ipic_enable_mcp(enum ipic_mcp_irq mcp_irq) +{ + struct ipic *ipic = primary_ipic; + u32 temp; + + temp = ipic_read(ipic->regs, IPIC_SERMR); + temp |= (1 << (31 - mcp_irq)); + ipic_write(ipic->regs, IPIC_SERMR, temp); +} + +void ipic_disable_mcp(enum ipic_mcp_irq mcp_irq) +{ + struct ipic *ipic = primary_ipic; + u32 temp; + + temp = ipic_read(ipic->regs, IPIC_SERMR); + temp &= (1 << (31 - mcp_irq)); + ipic_write(ipic->regs, IPIC_SERMR, temp); +} + +u32 ipic_get_mcp_status(void) +{ + return ipic_read(primary_ipic->regs, IPIC_SERMR); +} + +void ipic_clear_mcp_status(u32 mask) +{ + ipic_write(primary_ipic->regs, IPIC_SERMR, mask); +} + +/* Return an interrupt vector or -1 if no interrupt is pending. */ +int ipic_get_irq(struct pt_regs *regs) +{ + int irq; + + irq = ipic_read(primary_ipic->regs, IPIC_SIVCR) & 0x7f; + + if (irq == 0) /* 0 --> no irq is pending */ + irq = -1; + + return irq; +} + +static struct sysdev_class ipic_sysclass = { + set_kset_name("ipic"), +}; + +static struct sys_device device_ipic = { + .id = 0, + .cls = &ipic_sysclass, +}; + +static int __init init_ipic_sysfs(void) +{ + int rc; + + if (!primary_ipic->regs) + return -ENODEV; + printk(KERN_DEBUG "Registering ipic with sysfs...\n"); + + rc = sysdev_class_register(&ipic_sysclass); + if (rc) { + printk(KERN_ERR "Failed registering ipic sys class\n"); + return -ENODEV; + } + rc = sysdev_register(&device_ipic); + if (rc) { + printk(KERN_ERR "Failed registering ipic sys device\n"); + return -ENODEV; + } + return 0; +} + +subsys_initcall(init_ipic_sysfs); diff --git a/arch/ppc/syslib/ipic.h b/arch/ppc/syslib/ipic.h new file mode 100644 index 00000000000..2b56a4fcf37 --- /dev/null +++ b/arch/ppc/syslib/ipic.h @@ -0,0 +1,49 @@ +/* + * arch/ppc/kernel/ipic.h + * + * IPIC private definitions and structure. + * + * Maintainer: Kumar Gala + * + * Copyright 2005 Freescale Semiconductor, Inc + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#ifndef __IPIC_H__ +#define __IPIC_H__ + +#include + +#define MPC83xx_IPIC_SIZE (0x00100) + +/* System Global Interrupt Configuration Register */ +#define SICFR_IPSA 0x00010000 +#define SICFR_IPSD 0x00080000 +#define SICFR_MPSA 0x00200000 +#define SICFR_MPSB 0x00400000 + +/* System External Interrupt Mask Register */ +#define SEMSR_SIRQ0 0x00008000 + +/* System Error Control Register */ +#define SERCR_MCPR 0x00000001 + +struct ipic { + volatile u32 __iomem *regs; + unsigned int irq_offset; +}; + +struct ipic_info { + u8 pend; /* pending register offset from base */ + u8 mask; /* mask register offset from base */ + u8 prio; /* priority register offset from base */ + u8 force; /* force register offset from base */ + u8 bit; /* register bit position (as per doc) + bit mask = 1 << (31 - bit) */ + u8 prio_mask; /* priority mask value */ +}; + +#endif /* __IPIC_H__ */ diff --git a/arch/ppc/syslib/m8260_pci.c b/arch/ppc/syslib/m8260_pci.c new file mode 100644 index 00000000000..bd564fb35ab --- /dev/null +++ b/arch/ppc/syslib/m8260_pci.c @@ -0,0 +1,194 @@ +/* + * (C) Copyright 2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2004 Red Hat, Inc. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "m8260_pci.h" + + +/* PCI bus configuration registers. + */ + +static void __init m8260_setup_pci(struct pci_controller *hose) +{ + volatile cpm2_map_t *immap = cpm2_immr; + unsigned long pocmr; + u16 tempShort; + +#ifndef CONFIG_ATC /* already done in U-Boot */ + /* + * Setting required to enable IRQ1-IRQ7 (SIUMCR [DPPC]), + * and local bus for PCI (SIUMCR [LBPC]). + */ + immap->im_siu_conf.siu_82xx.sc_siumcr = 0x00640000; +#endif + + /* Make PCI lowest priority */ + /* Each 4 bits is a device bus request and the MS 4bits + is highest priority */ + /* Bus 4bit value + --- ---------- + CPM high 0b0000 + CPM middle 0b0001 + CPM low 0b0010 + PCI reguest 0b0011 + Reserved 0b0100 + Reserved 0b0101 + Internal Core 0b0110 + External Master 1 0b0111 + External Master 2 0b1000 + External Master 3 0b1001 + The rest are reserved */ + immap->im_siu_conf.siu_82xx.sc_ppc_alrh = 0x61207893; + + /* Park bus on core while modifying PCI Bus accesses */ + immap->im_siu_conf.siu_82xx.sc_ppc_acr = 0x6; + + /* + * Set up master window that allows the CPU to access PCI space. This + * window is set up using the first SIU PCIBR registers. + */ + immap->im_memctl.memc_pcimsk0 = MPC826x_PCI_MASK; + immap->im_memctl.memc_pcibr0 = MPC826x_PCI_BASE | PCIBR_ENABLE; + + /* Disable machine check on no response or target abort */ + immap->im_pci.pci_emr = cpu_to_le32(0x1fe7); + /* Release PCI RST (by default the PCI RST signal is held low) */ + immap->im_pci.pci_gcr = cpu_to_le32(PCIGCR_PCI_BUS_EN); + + /* give it some time */ + mdelay(1); + + /* + * Set up master window that allows the CPU to access PCI Memory (prefetch) + * space. This window is set up using the first set of Outbound ATU registers. + */ + immap->im_pci.pci_potar0 = cpu_to_le32(MPC826x_PCI_LOWER_MEM >> 12); + immap->im_pci.pci_pobar0 = cpu_to_le32((MPC826x_PCI_LOWER_MEM - MPC826x_PCI_MEM_OFFSET) >> 12); + pocmr = ((MPC826x_PCI_UPPER_MEM - MPC826x_PCI_LOWER_MEM) >> 12) ^ 0xfffff; + immap->im_pci.pci_pocmr0 = cpu_to_le32(pocmr | POCMR_ENABLE | POCMR_PREFETCH_EN); + + /* + * Set up master window that allows the CPU to access PCI Memory (non-prefetch) + * space. This window is set up using the second set of Outbound ATU registers. + */ + immap->im_pci.pci_potar1 = cpu_to_le32(MPC826x_PCI_LOWER_MMIO >> 12); + immap->im_pci.pci_pobar1 = cpu_to_le32((MPC826x_PCI_LOWER_MMIO - MPC826x_PCI_MMIO_OFFSET) >> 12); + pocmr = ((MPC826x_PCI_UPPER_MMIO - MPC826x_PCI_LOWER_MMIO) >> 12) ^ 0xfffff; + immap->im_pci.pci_pocmr1 = cpu_to_le32(pocmr | POCMR_ENABLE); + + /* + * Set up master window that allows the CPU to access PCI IO space. This window + * is set up using the third set of Outbound ATU registers. + */ + immap->im_pci.pci_potar2 = cpu_to_le32(MPC826x_PCI_IO_BASE >> 12); + immap->im_pci.pci_pobar2 = cpu_to_le32(MPC826x_PCI_LOWER_IO >> 12); + pocmr = ((MPC826x_PCI_UPPER_IO - MPC826x_PCI_LOWER_IO) >> 12) ^ 0xfffff; + immap->im_pci.pci_pocmr2 = cpu_to_le32(pocmr | POCMR_ENABLE | POCMR_PCI_IO); + + /* + * Set up slave window that allows PCI masters to access MPC826x local memory. + * This window is set up using the first set of Inbound ATU registers + */ + + immap->im_pci.pci_pitar0 = cpu_to_le32(MPC826x_PCI_SLAVE_MEM_LOCAL >> 12); + immap->im_pci.pci_pibar0 = cpu_to_le32(MPC826x_PCI_SLAVE_MEM_BUS >> 12); + pocmr = ((MPC826x_PCI_SLAVE_MEM_SIZE-1) >> 12) ^ 0xfffff; + immap->im_pci.pci_picmr0 = cpu_to_le32(pocmr | PICMR_ENABLE | PICMR_PREFETCH_EN); + + /* See above for description - puts PCI request as highest priority */ + immap->im_siu_conf.siu_82xx.sc_ppc_alrh = 0x03124567; + + /* Park the bus on the PCI */ + immap->im_siu_conf.siu_82xx.sc_ppc_acr = PPC_ACR_BUS_PARK_PCI; + + /* Host mode - specify the bridge as a host-PCI bridge */ + early_write_config_word(hose, 0, 0, PCI_CLASS_DEVICE, PCI_CLASS_BRIDGE_HOST); + + /* Enable the host bridge to be a master on the PCI bus, and to act as a PCI memory target */ + early_read_config_word(hose, 0, 0, PCI_COMMAND, &tempShort); + early_write_config_word(hose, 0, 0, PCI_COMMAND, + tempShort | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY); +} + +void __init m8260_find_bridges(void) +{ + extern int pci_assign_all_busses; + struct pci_controller * hose; + + pci_assign_all_busses = 1; + + hose = pcibios_alloc_controller(); + + if (!hose) + return; + + ppc_md.pci_swizzle = common_swizzle; + + hose->first_busno = 0; + hose->bus_offset = 0; + hose->last_busno = 0xff; + + setup_m8260_indirect_pci(hose, + (unsigned long)&cpm2_immr->im_pci.pci_cfg_addr, + (unsigned long)&cpm2_immr->im_pci.pci_cfg_data); + + m8260_setup_pci(hose); + hose->pci_mem_offset = MPC826x_PCI_MEM_OFFSET; + + isa_io_base = + (unsigned long) ioremap(MPC826x_PCI_IO_BASE, + MPC826x_PCI_IO_SIZE); + hose->io_base_virt = (void *) isa_io_base; + + /* setup resources */ + pci_init_resource(&hose->mem_resources[0], + MPC826x_PCI_LOWER_MEM, + MPC826x_PCI_UPPER_MEM, + IORESOURCE_MEM|IORESOURCE_PREFETCH, "PCI prefetchable memory"); + + pci_init_resource(&hose->mem_resources[1], + MPC826x_PCI_LOWER_MMIO, + MPC826x_PCI_UPPER_MMIO, + IORESOURCE_MEM, "PCI memory"); + + pci_init_resource(&hose->io_resource, + MPC826x_PCI_LOWER_IO, + MPC826x_PCI_UPPER_IO, + IORESOURCE_IO, "PCI I/O"); +} diff --git a/arch/ppc/syslib/m8260_pci.h b/arch/ppc/syslib/m8260_pci.h new file mode 100644 index 00000000000..d1352120acd --- /dev/null +++ b/arch/ppc/syslib/m8260_pci.h @@ -0,0 +1,76 @@ + +#ifndef _PPC_KERNEL_M8260_PCI_H +#define _PPC_KERNEL_M8260_PCI_H + +#include + +/* + * Local->PCI map (from CPU) controlled by + * MPC826x master window + * + * 0x80000000 - 0xBFFFFFFF Total CPU2PCI space PCIBR0 + * + * 0x80000000 - 0x9FFFFFFF PCI Mem with prefetch (Outbound ATU #1) + * 0xA0000000 - 0xAFFFFFFF PCI Mem w/o prefetch (Outbound ATU #2) + * 0xB0000000 - 0xB0FFFFFF 32-bit PCI IO (Outbound ATU #3) + * + * PCI->Local map (from PCI) + * MPC826x slave window controlled by + * + * 0x00000000 - 0x07FFFFFF MPC826x local memory (Inbound ATU #1) + */ + +/* + * Slave window that allows PCI masters to access MPC826x local memory. + * This window is set up using the first set of Inbound ATU registers + */ + +#ifndef MPC826x_PCI_SLAVE_MEM_LOCAL +#define MPC826x_PCI_SLAVE_MEM_LOCAL (((struct bd_info *)__res)->bi_memstart) +#define MPC826x_PCI_SLAVE_MEM_BUS (((struct bd_info *)__res)->bi_memstart) +#define MPC826x_PCI_SLAVE_MEM_SIZE (((struct bd_info *)__res)->bi_memsize) +#endif + +/* + * This is the window that allows the CPU to access PCI address space. + * It will be setup with the SIU PCIBR0 register. All three PCI master + * windows, which allow the CPU to access PCI prefetch, non prefetch, + * and IO space (see below), must all fit within this window. + */ +#ifndef MPC826x_PCI_BASE +#define MPC826x_PCI_BASE 0x80000000 +#define MPC826x_PCI_MASK 0xc0000000 +#endif + +#ifndef MPC826x_PCI_LOWER_MEM +#define MPC826x_PCI_LOWER_MEM 0x80000000 +#define MPC826x_PCI_UPPER_MEM 0x9fffffff +#define MPC826x_PCI_MEM_OFFSET 0x00000000 +#endif + +#ifndef MPC826x_PCI_LOWER_MMIO +#define MPC826x_PCI_LOWER_MMIO 0xa0000000 +#define MPC826x_PCI_UPPER_MMIO 0xafffffff +#define MPC826x_PCI_MMIO_OFFSET 0x00000000 +#endif + +#ifndef MPC826x_PCI_LOWER_IO +#define MPC826x_PCI_LOWER_IO 0x00000000 +#define MPC826x_PCI_UPPER_IO 0x00ffffff +#define MPC826x_PCI_IO_BASE 0xb0000000 +#define MPC826x_PCI_IO_SIZE 0x01000000 +#endif + +#ifndef _IO_BASE +#define _IO_BASE isa_io_base +#endif + +#ifdef CONFIG_8260_PCI9 +struct pci_controller; +extern void setup_m8260_indirect_pci(struct pci_controller* hose, + u32 cfg_addr, u32 cfg_data); +#else +#define setup_m8260_indirect_pci setup_indirect_pci +#endif + +#endif /* _PPC_KERNEL_M8260_PCI_H */ diff --git a/arch/ppc/syslib/m8260_pci_erratum9.c b/arch/ppc/syslib/m8260_pci_erratum9.c new file mode 100644 index 00000000000..9c0582d639e --- /dev/null +++ b/arch/ppc/syslib/m8260_pci_erratum9.c @@ -0,0 +1,473 @@ +/* + * arch/ppc/platforms/mpc8260_pci9.c + * + * Workaround for device erratum PCI 9. + * See Motorola's "XPC826xA Family Device Errata Reference." + * The erratum applies to all 8260 family Hip4 processors. It is scheduled + * to be fixed in HiP4 Rev C. Erratum PCI 9 states that a simultaneous PCI + * inbound write transaction and PCI outbound read transaction can result in a + * bus deadlock. The suggested workaround is to use the IDMA controller to + * perform all reads from PCI configuration, memory, and I/O space. + * + * Author: andy_lowe@mvista.com + * + * 2003 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "m8260_pci.h" + +#ifdef CONFIG_8260_PCI9 +/*#include */ /* included in asm/io.h */ + +#define IDMA_XFER_BUF_SIZE 64 /* size of the IDMA transfer buffer */ + +/* define a structure for the IDMA dpram usage */ +typedef struct idma_dpram_s { + idma_t pram; /* IDMA parameter RAM */ + u_char xfer_buf[IDMA_XFER_BUF_SIZE]; /* IDMA transfer buffer */ + idma_bd_t bd; /* buffer descriptor */ +} idma_dpram_t; + +/* define offsets relative to start of IDMA dpram */ +#define IDMA_XFER_BUF_OFFSET (sizeof(idma_t)) +#define IDMA_BD_OFFSET (sizeof(idma_t) + IDMA_XFER_BUF_SIZE) + +/* define globals */ +static volatile idma_dpram_t *idma_dpram; + +/* Exactly one of CONFIG_8260_PCI9_IDMAn must be defined, + * where n is 1, 2, 3, or 4. This selects the IDMA channel used for + * the PCI9 workaround. + */ +#ifdef CONFIG_8260_PCI9_IDMA1 +#define IDMA_CHAN 0 +#define PROFF_IDMA PROFF_IDMA1_BASE +#define IDMA_PAGE CPM_CR_IDMA1_PAGE +#define IDMA_SBLOCK CPM_CR_IDMA1_SBLOCK +#endif +#ifdef CONFIG_8260_PCI9_IDMA2 +#define IDMA_CHAN 1 +#define PROFF_IDMA PROFF_IDMA2_BASE +#define IDMA_PAGE CPM_CR_IDMA2_PAGE +#define IDMA_SBLOCK CPM_CR_IDMA2_SBLOCK +#endif +#ifdef CONFIG_8260_PCI9_IDMA3 +#define IDMA_CHAN 2 +#define PROFF_IDMA PROFF_IDMA3_BASE +#define IDMA_PAGE CPM_CR_IDMA3_PAGE +#define IDMA_SBLOCK CPM_CR_IDMA3_SBLOCK +#endif +#ifdef CONFIG_8260_PCI9_IDMA4 +#define IDMA_CHAN 3 +#define PROFF_IDMA PROFF_IDMA4_BASE +#define IDMA_PAGE CPM_CR_IDMA4_PAGE +#define IDMA_SBLOCK CPM_CR_IDMA4_SBLOCK +#endif + +void idma_pci9_init(void) +{ + uint dpram_offset; + volatile idma_t *pram; + volatile im_idma_t *idma_reg; + volatile cpm2_map_t *immap = cpm2_immr; + + /* allocate IDMA dpram */ + dpram_offset = cpm_dpalloc(sizeof(idma_dpram_t), 64); + idma_dpram = cpm_dpram_addr(dpram_offset); + + /* initialize the IDMA parameter RAM */ + memset((void *)idma_dpram, 0, sizeof(idma_dpram_t)); + pram = &idma_dpram->pram; + pram->ibase = dpram_offset + IDMA_BD_OFFSET; + pram->dpr_buf = dpram_offset + IDMA_XFER_BUF_OFFSET; + pram->ss_max = 32; + pram->dts = 32; + + /* initialize the IDMA_BASE pointer to the IDMA parameter RAM */ + *((ushort *) &immap->im_dprambase[PROFF_IDMA]) = dpram_offset; + + /* initialize the IDMA registers */ + idma_reg = (volatile im_idma_t *) &immap->im_sdma.sdma_idsr1; + idma_reg[IDMA_CHAN].idmr = 0; /* mask all IDMA interrupts */ + idma_reg[IDMA_CHAN].idsr = 0xff; /* clear all event flags */ + + printk("<4>Using IDMA%d for MPC8260 device erratum PCI 9 workaround\n", + IDMA_CHAN + 1); + + return; +} + +/* Use the IDMA controller to transfer data from I/O memory to local RAM. + * The src address must be a physical address suitable for use by the DMA + * controller with no translation. The dst address must be a kernel virtual + * address. The dst address is translated to a physical address via + * virt_to_phys(). + * The sinc argument specifies whether or not the source address is incremented + * by the DMA controller. The source address is incremented if and only if sinc + * is non-zero. The destination address is always incremented since the + * destination is always host RAM. + */ +static void +idma_pci9_read(u8 *dst, u8 *src, int bytes, int unit_size, int sinc) +{ + unsigned long flags; + volatile idma_t *pram = &idma_dpram->pram; + volatile idma_bd_t *bd = &idma_dpram->bd; + volatile cpm2_map_t *immap = cpm2_immr; + + local_irq_save(flags); + + /* initialize IDMA parameter RAM for this transfer */ + if (sinc) + pram->dcm = IDMA_DCM_DMA_WRAP_64 | IDMA_DCM_SINC + | IDMA_DCM_DINC | IDMA_DCM_SD_MEM2MEM; + else + pram->dcm = IDMA_DCM_DMA_WRAP_64 | IDMA_DCM_DINC + | IDMA_DCM_SD_MEM2MEM; + pram->ibdptr = pram->ibase; + pram->sts = unit_size; + pram->istate = 0; + + /* initialize the buffer descriptor */ + bd->dst = virt_to_phys(dst); + bd->src = (uint) src; + bd->len = bytes; + bd->flags = IDMA_BD_V | IDMA_BD_W | IDMA_BD_I | IDMA_BD_L | IDMA_BD_DGBL + | IDMA_BD_DBO_BE | IDMA_BD_SBO_BE | IDMA_BD_SDTB; + + /* issue the START_IDMA command to the CP */ + while (immap->im_cpm.cp_cpcr & CPM_CR_FLG); + immap->im_cpm.cp_cpcr = mk_cr_cmd(IDMA_PAGE, IDMA_SBLOCK, 0, + CPM_CR_START_IDMA) | CPM_CR_FLG; + while (immap->im_cpm.cp_cpcr & CPM_CR_FLG); + + /* wait for transfer to complete */ + while(bd->flags & IDMA_BD_V); + + local_irq_restore(flags); + + return; +} + +/* Use the IDMA controller to transfer data from I/O memory to local RAM. + * The dst address must be a physical address suitable for use by the DMA + * controller with no translation. The src address must be a kernel virtual + * address. The src address is translated to a physical address via + * virt_to_phys(). + * The dinc argument specifies whether or not the dest address is incremented + * by the DMA controller. The source address is incremented if and only if sinc + * is non-zero. The source address is always incremented since the + * source is always host RAM. + */ +static void +idma_pci9_write(u8 *dst, u8 *src, int bytes, int unit_size, int dinc) +{ + unsigned long flags; + volatile idma_t *pram = &idma_dpram->pram; + volatile idma_bd_t *bd = &idma_dpram->bd; + volatile cpm2_map_t *immap = cpm2_immr; + + local_irq_save(flags); + + /* initialize IDMA parameter RAM for this transfer */ + if (dinc) + pram->dcm = IDMA_DCM_DMA_WRAP_64 | IDMA_DCM_SINC + | IDMA_DCM_DINC | IDMA_DCM_SD_MEM2MEM; + else + pram->dcm = IDMA_DCM_DMA_WRAP_64 | IDMA_DCM_SINC + | IDMA_DCM_SD_MEM2MEM; + pram->ibdptr = pram->ibase; + pram->sts = unit_size; + pram->istate = 0; + + /* initialize the buffer descriptor */ + bd->dst = (uint) dst; + bd->src = virt_to_phys(src); + bd->len = bytes; + bd->flags = IDMA_BD_V | IDMA_BD_W | IDMA_BD_I | IDMA_BD_L | IDMA_BD_DGBL + | IDMA_BD_DBO_BE | IDMA_BD_SBO_BE | IDMA_BD_SDTB; + + /* issue the START_IDMA command to the CP */ + while (immap->im_cpm.cp_cpcr & CPM_CR_FLG); + immap->im_cpm.cp_cpcr = mk_cr_cmd(IDMA_PAGE, IDMA_SBLOCK, 0, + CPM_CR_START_IDMA) | CPM_CR_FLG; + while (immap->im_cpm.cp_cpcr & CPM_CR_FLG); + + /* wait for transfer to complete */ + while(bd->flags & IDMA_BD_V); + + local_irq_restore(flags); + + return; +} + +/* Same as idma_pci9_read, but 16-bit little-endian byte swapping is performed + * if the unit_size is 2, and 32-bit little-endian byte swapping is performed if + * the unit_size is 4. + */ +static void +idma_pci9_read_le(u8 *dst, u8 *src, int bytes, int unit_size, int sinc) +{ + int i; + u8 *p; + + idma_pci9_read(dst, src, bytes, unit_size, sinc); + switch(unit_size) { + case 2: + for (i = 0, p = dst; i < bytes; i += 2, p += 2) + swab16s((u16 *) p); + break; + case 4: + for (i = 0, p = dst; i < bytes; i += 4, p += 4) + swab32s((u32 *) p); + break; + default: + break; + } +} +EXPORT_SYMBOL(idma_pci9_init); +EXPORT_SYMBOL(idma_pci9_read); +EXPORT_SYMBOL(idma_pci9_read_le); + +static inline int is_pci_mem(unsigned long addr) +{ + if (addr >= MPC826x_PCI_LOWER_MMIO && + addr <= MPC826x_PCI_UPPER_MMIO) + return 1; + if (addr >= MPC826x_PCI_LOWER_MEM && + addr <= MPC826x_PCI_UPPER_MEM) + return 1; + return 0; +} + +#define is_pci_mem(pa) ( (pa > 0x80000000) && (pa < 0xc0000000)) +int readb(volatile unsigned char *addr) +{ + u8 val; + unsigned long pa = iopa((unsigned long) addr); + + if (!is_pci_mem(pa)) + return in_8(addr); + + idma_pci9_read((u8 *)&val, (u8 *)pa, sizeof(val), sizeof(val), 0); + return val; +} + +int readw(volatile unsigned short *addr) +{ + u16 val; + unsigned long pa = iopa((unsigned long) addr); + + if (!is_pci_mem(pa)) + return in_le16(addr); + + idma_pci9_read((u8 *)&val, (u8 *)pa, sizeof(val), sizeof(val), 0); + return swab16(val); +} + +unsigned readl(volatile unsigned *addr) +{ + u32 val; + unsigned long pa = iopa((unsigned long) addr); + + if (!is_pci_mem(pa)) + return in_le32(addr); + + idma_pci9_read((u8 *)&val, (u8 *)pa, sizeof(val), sizeof(val), 0); + return swab32(val); +} + +int inb(unsigned port) +{ + u8 val; + u8 *addr = (u8 *)(port + _IO_BASE); + + idma_pci9_read((u8 *)&val, (u8 *)addr, sizeof(val), sizeof(val), 0); + return val; +} + +int inw(unsigned port) +{ + u16 val; + u8 *addr = (u8 *)(port + _IO_BASE); + + idma_pci9_read((u8 *)&val, (u8 *)addr, sizeof(val), sizeof(val), 0); + return swab16(val); +} + +unsigned inl(unsigned port) +{ + u32 val; + u8 *addr = (u8 *)(port + _IO_BASE); + + idma_pci9_read((u8 *)&val, (u8 *)addr, sizeof(val), sizeof(val), 0); + return swab32(val); +} + +void insb(unsigned port, void *buf, int ns) +{ + u8 *addr = (u8 *)(port + _IO_BASE); + + idma_pci9_read((u8 *)buf, (u8 *)addr, ns*sizeof(u8), sizeof(u8), 0); +} + +void insw(unsigned port, void *buf, int ns) +{ + u8 *addr = (u8 *)(port + _IO_BASE); + + idma_pci9_read((u8 *)buf, (u8 *)addr, ns*sizeof(u16), sizeof(u16), 0); +} + +void insl(unsigned port, void *buf, int nl) +{ + u8 *addr = (u8 *)(port + _IO_BASE); + + idma_pci9_read((u8 *)buf, (u8 *)addr, nl*sizeof(u32), sizeof(u32), 0); +} + +void insw_ns(unsigned port, void *buf, int ns) +{ + u8 *addr = (u8 *)(port + _IO_BASE); + + idma_pci9_read((u8 *)buf, (u8 *)addr, ns*sizeof(u16), sizeof(u16), 0); +} + +void insl_ns(unsigned port, void *buf, int nl) +{ + u8 *addr = (u8 *)(port + _IO_BASE); + + idma_pci9_read((u8 *)buf, (u8 *)addr, nl*sizeof(u32), sizeof(u32), 0); +} + +void *memcpy_fromio(void *dest, unsigned long src, size_t count) +{ + unsigned long pa = iopa((unsigned long) src); + + if (is_pci_mem(pa)) + idma_pci9_read((u8 *)dest, (u8 *)pa, count, 32, 1); + else + memcpy(dest, (void *)src, count); + return dest; +} + +EXPORT_SYMBOL(readb); +EXPORT_SYMBOL(readw); +EXPORT_SYMBOL(readl); +EXPORT_SYMBOL(inb); +EXPORT_SYMBOL(inw); +EXPORT_SYMBOL(inl); +EXPORT_SYMBOL(insb); +EXPORT_SYMBOL(insw); +EXPORT_SYMBOL(insl); +EXPORT_SYMBOL(insw_ns); +EXPORT_SYMBOL(insl_ns); +EXPORT_SYMBOL(memcpy_fromio); + +#endif /* ifdef CONFIG_8260_PCI9 */ + +/* Indirect PCI routines adapted from arch/ppc/kernel/indirect_pci.c. + * Copyright (C) 1998 Gabriel Paubert. + */ +#ifndef CONFIG_8260_PCI9 +#define cfg_read(val, addr, type, op) *val = op((type)(addr)) +#else +#define cfg_read(val, addr, type, op) \ + idma_pci9_read_le((u8*)(val),(u8*)(addr),sizeof(*(val)),sizeof(*(val)),0) +#endif + +#define cfg_write(val, addr, type, op) op((type *)(addr), (val)) + +static int indirect_write_config(struct pci_bus *pbus, unsigned int devfn, int where, + int size, u32 value) +{ + struct pci_controller *hose = pbus->sysdata; + u8 cfg_type = 0; + if (ppc_md.pci_exclude_device) + if (ppc_md.pci_exclude_device(pbus->number, devfn)) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (hose->set_cfg_type) + if (pbus->number != hose->first_busno) + cfg_type = 1; + + out_be32(hose->cfg_addr, + (((where & 0xfc) | cfg_type) << 24) | (devfn << 16) + | ((pbus->number - hose->bus_offset) << 8) | 0x80); + + switch (size) + { + case 1: + cfg_write(value, hose->cfg_data + (where & 3), u8, out_8); + break; + case 2: + cfg_write(value, hose->cfg_data + (where & 2), u16, out_le16); + break; + case 4: + cfg_write(value, hose->cfg_data + (where & 0), u32, out_le32); + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static int indirect_read_config(struct pci_bus *pbus, unsigned int devfn, int where, + int size, u32 *value) +{ + struct pci_controller *hose = pbus->sysdata; + u8 cfg_type = 0; + if (ppc_md.pci_exclude_device) + if (ppc_md.pci_exclude_device(pbus->number, devfn)) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (hose->set_cfg_type) + if (pbus->number != hose->first_busno) + cfg_type = 1; + + out_be32(hose->cfg_addr, + (((where & 0xfc) | cfg_type) << 24) | (devfn << 16) + | ((pbus->number - hose->bus_offset) << 8) | 0x80); + + switch (size) + { + case 1: + cfg_read(value, hose->cfg_data + (where & 3), u8 *, in_8); + break; + case 2: + cfg_read(value, hose->cfg_data + (where & 2), u16 *, in_le16); + break; + case 4: + cfg_read(value, hose->cfg_data + (where & 0), u32 *, in_le32); + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops indirect_pci_ops = +{ + .read = indirect_read_config, + .write = indirect_write_config, +}; + +void +setup_m8260_indirect_pci(struct pci_controller* hose, u32 cfg_addr, u32 cfg_data) +{ + hose->ops = &indirect_pci_ops; + hose->cfg_addr = (unsigned int *) ioremap(cfg_addr, 4); + hose->cfg_data = (unsigned char *) ioremap(cfg_data, 4); +} diff --git a/arch/ppc/syslib/m8260_setup.c b/arch/ppc/syslib/m8260_setup.c new file mode 100644 index 00000000000..23ea3f694de --- /dev/null +++ b/arch/ppc/syslib/m8260_setup.c @@ -0,0 +1,264 @@ +/* + * arch/ppc/syslib/m8260_setup.c + * + * Copyright (C) 1995 Linus Torvalds + * Adapted from 'alpha' version by Gary Thomas + * Modified by Cort Dougan (cort@cs.nmt.edu) + * Modified for MBX using prep/chrp/pmac functions by Dan (dmalek@jlc.net) + * Further modified for generic 8xx and 8260 by Dan. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cpm2_pic.h" + +unsigned char __res[sizeof(bd_t)]; + +extern void cpm2_reset(void); +extern void m8260_find_bridges(void); +extern void idma_pci9_init(void); + +/* Place-holder for board-specific init */ +void __attribute__ ((weak)) __init +m82xx_board_setup(void) +{ +} + +static void __init +m8260_setup_arch(void) +{ + /* Print out Vendor and Machine info. */ + printk(KERN_INFO "%s %s port\n", CPUINFO_VENDOR, CPUINFO_MACHINE); + + /* Reset the Communication Processor Module. */ + cpm2_reset(); +#ifdef CONFIG_8260_PCI9 + /* Initialise IDMA for PCI erratum workaround */ + idma_pci9_init(); +#endif +#ifdef CONFIG_PCI_8260 + m8260_find_bridges(); +#endif +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; +#endif + m82xx_board_setup(); +} + +/* The decrementer counts at the system (internal) clock frequency + * divided by four. + */ +static void __init +m8260_calibrate_decr(void) +{ + bd_t *binfo = (bd_t *)__res; + int freq, divisor; + + freq = binfo->bi_busfreq; + divisor = 4; + tb_ticks_per_jiffy = freq / HZ / divisor; + tb_to_us = mulhwu_scale_factor(freq / divisor, 1000000); +} + +/* The 8260 has an internal 1-second timer update register that + * we should use for this purpose. + */ +static uint rtc_time; + +static int +m8260_set_rtc_time(unsigned long time) +{ + rtc_time = time; + + return(0); +} + +static unsigned long +m8260_get_rtc_time(void) +{ + /* Get time from the RTC. + */ + return((unsigned long)rtc_time); +} + +#ifndef BOOTROM_RESTART_ADDR +#warning "Using default BOOTROM_RESTART_ADDR!" +#define BOOTROM_RESTART_ADDR 0xff000104 +#endif + +static void +m8260_restart(char *cmd) +{ + extern void m8260_gorom(bd_t *bi, uint addr); + uint startaddr; + + /* Most boot roms have a warmstart as the second instruction + * of the reset vector. If that doesn't work for you, change this + * or the reboot program to send a proper address. + */ + startaddr = BOOTROM_RESTART_ADDR; + if (cmd != NULL) { + if (!strncmp(cmd, "startaddr=", 10)) + startaddr = simple_strtoul(&cmd[10], NULL, 0); + } + + m8260_gorom((void*)__pa(__res), startaddr); +} + +static void +m8260_halt(void) +{ + local_irq_disable(); + while (1); +} + +static void +m8260_power_off(void) +{ + m8260_halt(); +} + +static int +m8260_show_cpuinfo(struct seq_file *m) +{ + bd_t *bp = (bd_t *)__res; + + seq_printf(m, "vendor\t\t: %s\n" + "machine\t\t: %s\n" + "\n" + "mem size\t\t: 0x%08x\n" + "console baud\t\t: %d\n" + "\n" + "core clock\t: %u MHz\n" + "CPM clock\t: %u MHz\n" + "bus clock\t: %u MHz\n", + CPUINFO_VENDOR, CPUINFO_MACHINE, bp->bi_memsize, + bp->bi_baudrate, bp->bi_intfreq / 1000000, + bp->bi_cpmfreq / 1000000, bp->bi_busfreq / 1000000); + return 0; +} + +/* Initialize the internal interrupt controller. The number of + * interrupts supported can vary with the processor type, and the + * 8260 family can have up to 64. + * External interrupts can be either edge or level triggered, and + * need to be initialized by the appropriate driver. + */ +static void __init +m8260_init_IRQ(void) +{ + cpm2_init_IRQ(); + + /* Initialize the default interrupt mapping priorities, + * in case the boot rom changed something on us. + */ + cpm2_immr->im_intctl.ic_siprr = 0x05309770; +} + +/* + * Same hack as 8xx + */ +static unsigned long __init +m8260_find_end_of_memory(void) +{ + bd_t *binfo = (bd_t *)__res; + + return binfo->bi_memsize; +} + +/* Map the IMMR, plus anything else we can cover + * in that upper space according to the memory controller + * chip select mapping. Grab another bunch of space + * below that for stuff we can't cover in the upper. + */ +static void __init +m8260_map_io(void) +{ + uint addr; + + /* Map IMMR region to a 256MB BAT */ + addr = (cpm2_immr != NULL) ? (uint)cpm2_immr : CPM_MAP_ADDR; + io_block_mapping(addr, addr, 0x10000000, _PAGE_IO); + + /* Map I/O region to a 256MB BAT */ + io_block_mapping(IO_VIRT_ADDR, IO_PHYS_ADDR, 0x10000000, _PAGE_IO); +} + +/* Place-holder for board-specific ppc_md hooking */ +void __attribute__ ((weak)) __init +m82xx_board_init(void) +{ +} + +/* Inputs: + * r3 - Optional pointer to a board information structure. + * r4 - Optional pointer to the physical starting address of the init RAM + * disk. + * r5 - Optional pointer to the physical ending address of the init RAM + * disk. + * r6 - Optional pointer to the physical starting address of any kernel + * command-line parameters. + * r7 - Optional pointer to the physical ending address of any kernel + * command-line parameters. + */ +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + + if ( r3 ) + memcpy( (void *)__res,(void *)(r3+KERNELBASE), sizeof(bd_t) ); + +#ifdef CONFIG_BLK_DEV_INITRD + /* take care of initrd if we have one */ + if ( r4 ) { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } +#endif /* CONFIG_BLK_DEV_INITRD */ + /* take care of cmd line */ + if ( r6 ) { + *(char *)(r7+KERNELBASE) = 0; + strcpy(cmd_line, (char *)(r6+KERNELBASE)); + } + + ppc_md.setup_arch = m8260_setup_arch; + ppc_md.show_cpuinfo = m8260_show_cpuinfo; + ppc_md.init_IRQ = m8260_init_IRQ; + ppc_md.get_irq = cpm2_get_irq; + + ppc_md.restart = m8260_restart; + ppc_md.power_off = m8260_power_off; + ppc_md.halt = m8260_halt; + + ppc_md.set_rtc_time = m8260_set_rtc_time; + ppc_md.get_rtc_time = m8260_get_rtc_time; + ppc_md.calibrate_decr = m8260_calibrate_decr; + + ppc_md.find_end_of_memory = m8260_find_end_of_memory; + ppc_md.setup_io_mappings = m8260_map_io; + + /* Call back for board-specific settings and overrides. */ + m82xx_board_init(); +} diff --git a/arch/ppc/syslib/m8xx_setup.c b/arch/ppc/syslib/m8xx_setup.c new file mode 100644 index 00000000000..c1db2ab1d15 --- /dev/null +++ b/arch/ppc/syslib/m8xx_setup.c @@ -0,0 +1,433 @@ +/* + * arch/ppc/kernel/setup.c + * + * Copyright (C) 1995 Linus Torvalds + * Adapted from 'alpha' version by Gary Thomas + * Modified by Cort Dougan (cort@cs.nmt.edu) + * Modified for MBX using prep/chrp/pmac functions by Dan (dmalek@jlc.net) + * Further modified for generic 8xx by Dan. + */ + +/* + * bootup setup stuff.. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ppc8xx_pic.h" + +static int m8xx_set_rtc_time(unsigned long time); +static unsigned long m8xx_get_rtc_time(void); +void m8xx_calibrate_decr(void); + +unsigned char __res[sizeof(bd_t)]; + +extern void m8xx_ide_init(void); + +extern unsigned long find_available_memory(void); +extern void m8xx_cpm_reset(uint cpm_page); +extern void m8xx_wdt_handler_install(bd_t *bp); +extern void rpxfb_alloc_pages(void); +extern void cpm_interrupt_init(void); + +void __attribute__ ((weak)) +board_init(void) +{ +} + +void __init +m8xx_setup_arch(void) +{ + int cpm_page; + + cpm_page = (int) alloc_bootmem_pages(PAGE_SIZE); + + /* Reset the Communication Processor Module. + */ + m8xx_cpm_reset(cpm_page); + +#ifdef CONFIG_FB_RPX + rpxfb_alloc_pages(); +#endif + +#ifdef notdef + ROOT_DEV = Root_HDA1; /* hda1 */ +#endif + +#ifdef CONFIG_BLK_DEV_INITRD +#if 0 + ROOT_DEV = Root_FD0; /* floppy */ + rd_prompt = 1; + rd_doload = 1; + rd_image_start = 0; +#endif +#if 0 /* XXX this may need to be updated for the new bootmem stuff, + or possibly just deleted (see set_phys_avail() in init.c). + - paulus. */ + /* initrd_start and size are setup by boot/head.S and kernel/head.S */ + if ( initrd_start ) + { + if (initrd_end > *memory_end_p) + { + printk("initrd extends beyond end of memory " + "(0x%08lx > 0x%08lx)\ndisabling initrd\n", + initrd_end,*memory_end_p); + initrd_start = 0; + } + } +#endif +#endif + board_init(); +} + +void +abort(void) +{ +#ifdef CONFIG_XMON + xmon(0); +#endif + machine_restart(NULL); + + /* not reached */ + for (;;); +} + +/* A place holder for time base interrupts, if they are ever enabled. */ +irqreturn_t timebase_interrupt(int irq, void * dev, struct pt_regs * regs) +{ + printk ("timebase_interrupt()\n"); + + return IRQ_HANDLED; +} + +static struct irqaction tbint_irqaction = { + .handler = timebase_interrupt, + .mask = CPU_MASK_NONE, + .name = "tbint", +}; + +/* The decrementer counts at the system (internal) clock frequency divided by + * sixteen, or external oscillator divided by four. We force the processor + * to use system clock divided by sixteen. + */ +void __init m8xx_calibrate_decr(void) +{ + bd_t *binfo = (bd_t *)__res; + int freq, fp, divisor; + + /* Unlock the SCCR. */ + ((volatile immap_t *)IMAP_ADDR)->im_clkrstk.cark_sccrk = ~KAPWR_KEY; + ((volatile immap_t *)IMAP_ADDR)->im_clkrstk.cark_sccrk = KAPWR_KEY; + + /* Force all 8xx processors to use divide by 16 processor clock. */ + ((volatile immap_t *)IMAP_ADDR)->im_clkrst.car_sccr |= 0x02000000; + + /* Processor frequency is MHz. + * The value 'fp' is the number of decrementer ticks per second. + */ + fp = binfo->bi_intfreq / 16; + freq = fp*60; /* try to make freq/1e6 an integer */ + divisor = 60; + printk("Decrementer Frequency = %d/%d\n", freq, divisor); + tb_ticks_per_jiffy = freq / HZ / divisor; + tb_to_us = mulhwu_scale_factor(freq / divisor, 1000000); + + /* Perform some more timer/timebase initialization. This used + * to be done elsewhere, but other changes caused it to get + * called more than once....that is a bad thing. + * + * First, unlock all of the registers we are going to modify. + * To protect them from corruption during power down, registers + * that are maintained by keep alive power are "locked". To + * modify these registers we have to write the key value to + * the key location associated with the register. + * Some boards power up with these unlocked, while others + * are locked. Writing anything (including the unlock code?) + * to the unlocked registers will lock them again. So, here + * we guarantee the registers are locked, then we unlock them + * for our use. + */ + ((volatile immap_t *)IMAP_ADDR)->im_sitk.sitk_tbscrk = ~KAPWR_KEY; + ((volatile immap_t *)IMAP_ADDR)->im_sitk.sitk_rtcsck = ~KAPWR_KEY; + ((volatile immap_t *)IMAP_ADDR)->im_sitk.sitk_tbk = ~KAPWR_KEY; + ((volatile immap_t *)IMAP_ADDR)->im_sitk.sitk_tbscrk = KAPWR_KEY; + ((volatile immap_t *)IMAP_ADDR)->im_sitk.sitk_rtcsck = KAPWR_KEY; + ((volatile immap_t *)IMAP_ADDR)->im_sitk.sitk_tbk = KAPWR_KEY; + + /* Disable the RTC one second and alarm interrupts. */ + ((volatile immap_t *)IMAP_ADDR)->im_sit.sit_rtcsc &= + ~(RTCSC_SIE | RTCSC_ALE); + /* Enable the RTC */ + ((volatile immap_t *)IMAP_ADDR)->im_sit.sit_rtcsc |= + (RTCSC_RTF | RTCSC_RTE); + + /* Enabling the decrementer also enables the timebase interrupts + * (or from the other point of view, to get decrementer interrupts + * we have to enable the timebase). The decrementer interrupt + * is wired into the vector table, nothing to do here for that. + */ + ((volatile immap_t *)IMAP_ADDR)->im_sit.sit_tbscr = + ((mk_int_int_mask(DEC_INTERRUPT) << 8) | + (TBSCR_TBF | TBSCR_TBE)); + + if (setup_irq(DEC_INTERRUPT, &tbint_irqaction)) + panic("Could not allocate timer IRQ!"); + +#ifdef CONFIG_8xx_WDT + /* Install watchdog timer handler early because it might be + * already enabled by the bootloader + */ + m8xx_wdt_handler_install(binfo); +#endif +} + +/* The RTC on the MPC8xx is an internal register. + * We want to protect this during power down, so we need to unlock, + * modify, and re-lock. + */ +static int +m8xx_set_rtc_time(unsigned long time) +{ + ((volatile immap_t *)IMAP_ADDR)->im_sitk.sitk_rtck = KAPWR_KEY; + ((volatile immap_t *)IMAP_ADDR)->im_sit.sit_rtc = time; + ((volatile immap_t *)IMAP_ADDR)->im_sitk.sitk_rtck = ~KAPWR_KEY; + return(0); +} + +static unsigned long +m8xx_get_rtc_time(void) +{ + /* Get time from the RTC. */ + return((unsigned long)(((immap_t *)IMAP_ADDR)->im_sit.sit_rtc)); +} + +static void +m8xx_restart(char *cmd) +{ + __volatile__ unsigned char dummy; + + local_irq_disable(); + ((immap_t *)IMAP_ADDR)->im_clkrst.car_plprcr |= 0x00000080; + + /* Clear the ME bit in MSR to cause checkstop on machine check + */ + mtmsr(mfmsr() & ~0x1000); + + dummy = ((immap_t *)IMAP_ADDR)->im_clkrst.res[0]; + printk("Restart failed\n"); + while(1); +} + +static void +m8xx_power_off(void) +{ + m8xx_restart(NULL); +} + +static void +m8xx_halt(void) +{ + m8xx_restart(NULL); +} + + +static int +m8xx_show_percpuinfo(struct seq_file *m, int i) +{ + bd_t *bp; + + bp = (bd_t *)__res; + + seq_printf(m, "clock\t\t: %ldMHz\n" + "bus clock\t: %ldMHz\n", + bp->bi_intfreq / 1000000, + bp->bi_busfreq / 1000000); + + return 0; +} + +#ifdef CONFIG_PCI +static struct irqaction mbx_i8259_irqaction = { + .handler = mbx_i8259_action, + .mask = CPU_MASK_NONE, + .name = "i8259 cascade", +}; +#endif + +/* Initialize the internal interrupt controller. The number of + * interrupts supported can vary with the processor type, and the + * 82xx family can have up to 64. + * External interrupts can be either edge or level triggered, and + * need to be initialized by the appropriate driver. + */ +static void __init +m8xx_init_IRQ(void) +{ + int i; + + for (i = SIU_IRQ_OFFSET ; i < SIU_IRQ_OFFSET + NR_SIU_INTS ; i++) + irq_desc[i].handler = &ppc8xx_pic; + + cpm_interrupt_init(); + +#if defined(CONFIG_PCI) + for (i = I8259_IRQ_OFFSET ; i < I8259_IRQ_OFFSET + NR_8259_INTS ; i++) + irq_desc[i].handler = &i8259_pic; + + i8259_pic_irq_offset = I8259_IRQ_OFFSET; + i8259_init(0); + + /* The i8259 cascade interrupt must be level sensitive. */ + ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_siel &= + ~(0x80000000 >> ISA_BRIDGE_INT); + + if (setup_irq(ISA_BRIDGE_INT, &mbx_i8259_irqaction)) + enable_irq(ISA_BRIDGE_INT); +#endif /* CONFIG_PCI */ +} + +/* -------------------------------------------------------------------- */ + +/* + * This is a big hack right now, but it may turn into something real + * someday. + * + * For the 8xx boards (at this time anyway), there is nothing to initialize + * associated the PROM. Rather than include all of the prom.c + * functions in the image just to get prom_init, all we really need right + * now is the initialization of the physical memory region. + */ +static unsigned long __init +m8xx_find_end_of_memory(void) +{ + bd_t *binfo; + extern unsigned char __res[]; + + binfo = (bd_t *)__res; + + return binfo->bi_memsize; +} + +/* + * Now map in some of the I/O space that is generically needed + * or shared with multiple devices. + * All of this fits into the same 4Mbyte region, so it only + * requires one page table page. (or at least it used to -- paulus) + */ +static void __init +m8xx_map_io(void) +{ + io_block_mapping(IMAP_ADDR, IMAP_ADDR, IMAP_SIZE, _PAGE_IO); +#ifdef CONFIG_MBX + io_block_mapping(NVRAM_ADDR, NVRAM_ADDR, NVRAM_SIZE, _PAGE_IO); + io_block_mapping(MBX_CSR_ADDR, MBX_CSR_ADDR, MBX_CSR_SIZE, _PAGE_IO); + io_block_mapping(PCI_CSR_ADDR, PCI_CSR_ADDR, PCI_CSR_SIZE, _PAGE_IO); + + /* Map some of the PCI/ISA I/O space to get the IDE interface. + */ + io_block_mapping(PCI_ISA_IO_ADDR, PCI_ISA_IO_ADDR, 0x4000, _PAGE_IO); + io_block_mapping(PCI_IDE_ADDR, PCI_IDE_ADDR, 0x4000, _PAGE_IO); +#endif +#if defined(CONFIG_RPXLITE) || defined(CONFIG_RPXCLASSIC) + io_block_mapping(RPX_CSR_ADDR, RPX_CSR_ADDR, RPX_CSR_SIZE, _PAGE_IO); +#if !defined(CONFIG_PCI) + io_block_mapping(_IO_BASE,_IO_BASE,_IO_BASE_SIZE, _PAGE_IO); +#endif +#endif +#if defined(CONFIG_HTDMSOUND) || defined(CONFIG_RPXTOUCH) || defined(CONFIG_FB_RPX) + io_block_mapping(HIOX_CSR_ADDR, HIOX_CSR_ADDR, HIOX_CSR_SIZE, _PAGE_IO); +#endif +#ifdef CONFIG_FADS + io_block_mapping(BCSR_ADDR, BCSR_ADDR, BCSR_SIZE, _PAGE_IO); +#endif +#ifdef CONFIG_PCI + io_block_mapping(PCI_CSR_ADDR, PCI_CSR_ADDR, PCI_CSR_SIZE, _PAGE_IO); +#endif +#if defined(CONFIG_NETTA) + io_block_mapping(_IO_BASE,_IO_BASE,_IO_BASE_SIZE, _PAGE_IO); +#endif +} + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + + if ( r3 ) + memcpy( (void *)__res,(void *)(r3+KERNELBASE), sizeof(bd_t) ); + +#ifdef CONFIG_PCI + m8xx_setup_pci_ptrs(); +#endif + +#ifdef CONFIG_BLK_DEV_INITRD + /* take care of initrd if we have one */ + if ( r4 ) + { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } +#endif /* CONFIG_BLK_DEV_INITRD */ + /* take care of cmd line */ + if ( r6 ) + { + *(char *)(r7+KERNELBASE) = 0; + strcpy(cmd_line, (char *)(r6+KERNELBASE)); + } + + ppc_md.setup_arch = m8xx_setup_arch; + ppc_md.show_percpuinfo = m8xx_show_percpuinfo; + ppc_md.irq_canonicalize = NULL; + ppc_md.init_IRQ = m8xx_init_IRQ; + ppc_md.get_irq = m8xx_get_irq; + ppc_md.init = NULL; + + ppc_md.restart = m8xx_restart; + ppc_md.power_off = m8xx_power_off; + ppc_md.halt = m8xx_halt; + + ppc_md.time_init = NULL; + ppc_md.set_rtc_time = m8xx_set_rtc_time; + ppc_md.get_rtc_time = m8xx_get_rtc_time; + ppc_md.calibrate_decr = m8xx_calibrate_decr; + + ppc_md.find_end_of_memory = m8xx_find_end_of_memory; + ppc_md.setup_io_mappings = m8xx_map_io; + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) + m8xx_ide_init(); +#endif +} diff --git a/arch/ppc/syslib/m8xx_wdt.c b/arch/ppc/syslib/m8xx_wdt.c new file mode 100644 index 00000000000..7838a44bfd1 --- /dev/null +++ b/arch/ppc/syslib/m8xx_wdt.c @@ -0,0 +1,99 @@ +/* + * m8xx_wdt.c - MPC8xx watchdog driver + * + * Author: Florian Schirmer + * + * 2002 (c) Florian Schirmer This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include + +static int wdt_timeout; + +void m8xx_wdt_reset(void) +{ + volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR; + + imap->im_siu_conf.sc_swsr = 0x556c; /* write magic1 */ + imap->im_siu_conf.sc_swsr = 0xaa39; /* write magic2 */ +} + +static irqreturn_t m8xx_wdt_interrupt(int irq, void *dev, struct pt_regs *regs) +{ + volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR; + + m8xx_wdt_reset(); + + imap->im_sit.sit_piscr |= PISCR_PS; /* clear irq */ + + return IRQ_HANDLED; +} + +void __init m8xx_wdt_handler_install(bd_t * binfo) +{ + volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR; + u32 pitc; + u32 sypcr; + u32 pitrtclk; + + sypcr = imap->im_siu_conf.sc_sypcr; + + if (!(sypcr & 0x04)) { + printk(KERN_NOTICE "m8xx_wdt: wdt disabled (SYPCR: 0x%08X)\n", + sypcr); + return; + } + + m8xx_wdt_reset(); + + printk(KERN_NOTICE + "m8xx_wdt: active wdt found (SWTC: 0x%04X, SWP: 0x%01X)\n", + (sypcr >> 16), sypcr & 0x01); + + wdt_timeout = (sypcr >> 16) & 0xFFFF; + + if (!wdt_timeout) + wdt_timeout = 0xFFFF; + + if (sypcr & 0x01) + wdt_timeout *= 2048; + + /* + * Fire trigger if half of the wdt ticked down + */ + + if (imap->im_sit.sit_rtcsc & RTCSC_38K) + pitrtclk = 9600; + else + pitrtclk = 8192; + + if ((wdt_timeout) > (UINT_MAX / pitrtclk)) + pitc = wdt_timeout / binfo->bi_intfreq * pitrtclk / 2; + else + pitc = pitrtclk * wdt_timeout / binfo->bi_intfreq / 2; + + imap->im_sit.sit_pitc = pitc << 16; + imap->im_sit.sit_piscr = + (mk_int_int_mask(PIT_INTERRUPT) << 8) | PISCR_PIE | PISCR_PTE; + + if (request_irq(PIT_INTERRUPT, m8xx_wdt_interrupt, 0, "watchdog", NULL)) + panic("m8xx_wdt: could not allocate watchdog irq!"); + + printk(KERN_NOTICE + "m8xx_wdt: keep-alive trigger installed (PITC: 0x%04X)\n", pitc); + + wdt_timeout /= binfo->bi_intfreq; +} + +int m8xx_wdt_get_timeout(void) +{ + return wdt_timeout; +} diff --git a/arch/ppc/syslib/m8xx_wdt.h b/arch/ppc/syslib/m8xx_wdt.h new file mode 100644 index 00000000000..0d81a9f8155 --- /dev/null +++ b/arch/ppc/syslib/m8xx_wdt.h @@ -0,0 +1,16 @@ +/* + * Author: Florian Schirmer + * + * 2002 (c) Florian Schirmer This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#ifndef _PPC_SYSLIB_M8XX_WDT_H +#define _PPC_SYSLIB_M8XX_WDT_H + +extern void m8xx_wdt_handler_install(bd_t * binfo); +extern int m8xx_wdt_get_timeout(void); +extern void m8xx_wdt_reset(void); + +#endif /* _PPC_SYSLIB_M8XX_WDT_H */ diff --git a/arch/ppc/syslib/mpc10x_common.c b/arch/ppc/syslib/mpc10x_common.c new file mode 100644 index 00000000000..fd93adfd464 --- /dev/null +++ b/arch/ppc/syslib/mpc10x_common.c @@ -0,0 +1,510 @@ +/* + * arch/ppc/syslib/mpc10x_common.c + * + * Common routines for the Motorola SPS MPC106, MPC107 and MPC8240 Host bridge, + * Mem ctlr, EPIC, etc. + * + * Author: Mark A. Greer + * mgreer@mvista.com + * + * 2001 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +/* + * *** WARNING - A BAT MUST be set to access the PCI config addr/data regs *** + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* The OCP structure is fixed by code below, before OCP initialises. + paddr depends on where the board places the EUMB. + - fixed in mpc10x_bridge_init(). + irq depends on two things: + > does the board use the EPIC at all? (PCORE does not). + > is the EPIC in serial or parallel mode? + - fixed in mpc10x_set_openpic(). +*/ + +#ifdef CONFIG_MPC10X_OPENPIC +#ifdef CONFIG_EPIC_SERIAL_MODE +#define EPIC_IRQ_BASE (epic_serial_mode ? 16 : 5) +#else +#define EPIC_IRQ_BASE 5 +#endif +#define MPC10X_I2C_IRQ (EPIC_IRQ_BASE + NUM_8259_INTERRUPTS) +#define MPC10X_DMA0_IRQ (EPIC_IRQ_BASE + 1 + NUM_8259_INTERRUPTS) +#define MPC10X_DMA1_IRQ (EPIC_IRQ_BASE + 2 + NUM_8259_INTERRUPTS) +#else +#define MPC10X_I2C_IRQ OCP_IRQ_NA +#define MPC10X_DMA0_IRQ OCP_IRQ_NA +#define MPC10X_DMA1_IRQ OCP_IRQ_NA +#endif + + +struct ocp_def core_ocp[] = { + { .vendor = OCP_VENDOR_INVALID + } +}; + +static struct ocp_fs_i2c_data mpc10x_i2c_data = { + .flags = 0 +}; +static struct ocp_def mpc10x_i2c_ocp = { + .vendor = OCP_VENDOR_MOTOROLA, + .function = OCP_FUNC_IIC, + .index = 0, + .additions = &mpc10x_i2c_data +}; + +static struct ocp_def mpc10x_dma_ocp[2] = { +{ .vendor = OCP_VENDOR_MOTOROLA, + .function = OCP_FUNC_DMA, + .index = 0 }, +{ .vendor = OCP_VENDOR_MOTOROLA, + .function = OCP_FUNC_DMA, + .index = 1 } +}; + +/* Set resources to match bridge memory map */ +void __init +mpc10x_bridge_set_resources(int map, struct pci_controller *hose) +{ + + switch (map) { + case MPC10X_MEM_MAP_A: + pci_init_resource(&hose->io_resource, + 0x00000000, + 0x3f7fffff, + IORESOURCE_IO, + "PCI host bridge"); + + pci_init_resource (&hose->mem_resources[0], + 0xc0000000, + 0xfeffffff, + IORESOURCE_MEM, + "PCI host bridge"); + break; + case MPC10X_MEM_MAP_B: + pci_init_resource(&hose->io_resource, + 0x00000000, + 0x00bfffff, + IORESOURCE_IO, + "PCI host bridge"); + + pci_init_resource (&hose->mem_resources[0], + 0x80000000, + 0xfcffffff, + IORESOURCE_MEM, + "PCI host bridge"); + break; + default: + printk("mpc10x_bridge_set_resources: " + "Invalid map specified\n"); + if (ppc_md.progress) + ppc_md.progress("mpc10x:exit1", 0x100); + } +} +/* + * Do some initialization and put the EUMB registers at the specified address + * (also map the EPIC registers into virtual space--OpenPIC_Addr will be set). + * + * The EPIC is not on the 106, only the 8240 and 107. + */ +int __init +mpc10x_bridge_init(struct pci_controller *hose, + uint current_map, + uint new_map, + uint phys_eumb_base) +{ + int host_bridge, picr1, picr1_bit; + ulong pci_config_addr, pci_config_data; + u_char pir, byte; + + if (ppc_md.progress) ppc_md.progress("mpc10x:enter", 0x100); + + /* Set up for current map so we can get at config regs */ + switch (current_map) { + case MPC10X_MEM_MAP_A: + setup_indirect_pci(hose, + MPC10X_MAPA_CNFG_ADDR, + MPC10X_MAPA_CNFG_DATA); + break; + case MPC10X_MEM_MAP_B: + setup_indirect_pci(hose, + MPC10X_MAPB_CNFG_ADDR, + MPC10X_MAPB_CNFG_DATA); + break; + default: + printk("mpc10x_bridge_init: %s\n", + "Invalid current map specified"); + if (ppc_md.progress) + ppc_md.progress("mpc10x:exit1", 0x100); + return -1; + } + + /* Make sure it's a supported bridge */ + early_read_config_dword(hose, + 0, + PCI_DEVFN(0,0), + PCI_VENDOR_ID, + &host_bridge); + + switch (host_bridge) { + case MPC10X_BRIDGE_106: + case MPC10X_BRIDGE_8240: + case MPC10X_BRIDGE_107: + case MPC10X_BRIDGE_8245: + break; + default: + if (ppc_md.progress) + ppc_md.progress("mpc10x:exit2", 0x100); + return -1; + } + + switch (new_map) { + case MPC10X_MEM_MAP_A: + MPC10X_SETUP_HOSE(hose, A); + pci_config_addr = MPC10X_MAPA_CNFG_ADDR; + pci_config_data = MPC10X_MAPA_CNFG_DATA; + picr1_bit = MPC10X_CFG_PICR1_ADDR_MAP_A; + break; + case MPC10X_MEM_MAP_B: + MPC10X_SETUP_HOSE(hose, B); + pci_config_addr = MPC10X_MAPB_CNFG_ADDR; + pci_config_data = MPC10X_MAPB_CNFG_DATA; + picr1_bit = MPC10X_CFG_PICR1_ADDR_MAP_B; + break; + default: + printk("mpc10x_bridge_init: %s\n", + "Invalid new map specified"); + if (ppc_md.progress) + ppc_md.progress("mpc10x:exit3", 0x100); + return -1; + } + + /* Make bridge use the 'new_map', if not already usng it */ + if (current_map != new_map) { + early_read_config_dword(hose, + 0, + PCI_DEVFN(0,0), + MPC10X_CFG_PICR1_REG, + &picr1); + + picr1 = (picr1 & ~MPC10X_CFG_PICR1_ADDR_MAP_MASK) | + picr1_bit; + + early_write_config_dword(hose, + 0, + PCI_DEVFN(0,0), + MPC10X_CFG_PICR1_REG, + picr1); + + asm volatile("sync"); + + /* Undo old mappings & map in new cfg data/addr regs */ + iounmap((void *)hose->cfg_addr); + iounmap((void *)hose->cfg_data); + + setup_indirect_pci(hose, + pci_config_addr, + pci_config_data); + } + + /* Setup resources to match map */ + mpc10x_bridge_set_resources(new_map, hose); + + /* + * Want processor accesses of 0xFDxxxxxx to be mapped + * to PCI memory space at 0x00000000. Do not want + * host bridge to respond to PCI memory accesses of + * 0xFDxxxxxx. Do not want host bridge to respond + * to PCI memory addresses 0xFD000000-0xFDFFFFFF; + * want processor accesses from 0x000A0000-0x000BFFFF + * to be forwarded to system memory. + * + * Only valid if not in agent mode and using MAP B. + */ + if (new_map == MPC10X_MEM_MAP_B) { + early_read_config_byte(hose, + 0, + PCI_DEVFN(0,0), + MPC10X_CFG_MAPB_OPTIONS_REG, + &byte); + + byte &= ~(MPC10X_CFG_MAPB_OPTIONS_PFAE | + MPC10X_CFG_MAPB_OPTIONS_PCICH | + MPC10X_CFG_MAPB_OPTIONS_PROCCH); + + if (host_bridge != MPC10X_BRIDGE_106) { + byte |= MPC10X_CFG_MAPB_OPTIONS_CFAE; + } + + early_write_config_byte(hose, + 0, + PCI_DEVFN(0,0), + MPC10X_CFG_MAPB_OPTIONS_REG, + byte); + } + + if (host_bridge != MPC10X_BRIDGE_106) { + early_read_config_byte(hose, + 0, + PCI_DEVFN(0,0), + MPC10X_CFG_PIR_REG, + &pir); + + if (pir != MPC10X_CFG_PIR_HOST_BRIDGE) { + printk("Host bridge in Agent mode\n"); + /* Read or Set LMBAR & PCSRBAR? */ + } + + /* Set base addr of the 8240/107 EUMB. */ + early_write_config_dword(hose, + 0, + PCI_DEVFN(0,0), + MPC10X_CFG_EUMBBAR, + phys_eumb_base); +#ifdef CONFIG_MPC10X_OPENPIC + /* Map EPIC register part of EUMB into vitual memory - PCORE + uses an i8259 instead of EPIC. */ + OpenPIC_Addr = + ioremap(phys_eumb_base + MPC10X_EUMB_EPIC_OFFSET, + MPC10X_EUMB_EPIC_SIZE); +#endif + mpc10x_i2c_ocp.paddr = phys_eumb_base + MPC10X_EUMB_I2C_OFFSET; + mpc10x_i2c_ocp.irq = MPC10X_I2C_IRQ; + ocp_add_one_device(&mpc10x_i2c_ocp); + mpc10x_dma_ocp[0].paddr = phys_eumb_base + + MPC10X_EUMB_DMA_OFFSET + 0x100; + mpc10x_dma_ocp[0].irq = MPC10X_DMA0_IRQ; + ocp_add_one_device(&mpc10x_dma_ocp[0]); + mpc10x_dma_ocp[1].paddr = phys_eumb_base + + MPC10X_EUMB_DMA_OFFSET + 0x200; + mpc10x_dma_ocp[1].irq = MPC10X_DMA1_IRQ; + ocp_add_one_device(&mpc10x_dma_ocp[1]); + } + +#ifdef CONFIG_MPC10X_STORE_GATHERING + mpc10x_enable_store_gathering(hose); +#else + mpc10x_disable_store_gathering(hose); +#endif + + /* + * 8240 erratum 26, 8241/8245 erratum 29, 107 erratum 23: speculative + * PCI reads may return stale data so turn off. + */ + if ((host_bridge == MPC10X_BRIDGE_8240) + || (host_bridge == MPC10X_BRIDGE_8245) + || (host_bridge == MPC10X_BRIDGE_107)) { + + early_read_config_dword(hose, 0, PCI_DEVFN(0,0), + MPC10X_CFG_PICR1_REG, &picr1); + + picr1 &= ~MPC10X_CFG_PICR1_SPEC_PCI_RD; + + early_write_config_dword(hose, 0, PCI_DEVFN(0,0), + MPC10X_CFG_PICR1_REG, picr1); + } + + /* + * 8241/8245 erratum 28: PCI reads from local memory may return + * stale data. Workaround by setting PICR2[0] to disable copyback + * optimization. Oddly, the latest available user manual for the + * 8245 (Rev 2., dated 10/2003) says PICR2[0] is reserverd. + */ + if (host_bridge == MPC10X_BRIDGE_8245) { + ulong picr2; + + early_read_config_dword(hose, 0, PCI_DEVFN(0,0), + MPC10X_CFG_PICR2_REG, &picr2); + + picr2 |= MPC10X_CFG_PICR2_COPYBACK_OPT; + + early_write_config_dword(hose, 0, PCI_DEVFN(0,0), + MPC10X_CFG_PICR2_REG, picr2); + } + + if (ppc_md.progress) ppc_md.progress("mpc10x:exit", 0x100); + return 0; +} + +/* + * Need to make our own PCI config space access macros because + * mpc10x_get_mem_size() is called before the data structures are set up for + * the 'early_xxx' and 'indirect_xxx' routines to work. + * Assumes bus 0. + */ +#define MPC10X_CFG_read(val, addr, type, op) *val = op((type)(addr)) +#define MPC10X_CFG_write(val, addr, type, op) op((type *)(addr), (val)) + +#define MPC10X_PCI_OP(rw, size, type, op, mask) \ +static void \ +mpc10x_##rw##_config_##size(uint *cfg_addr, uint *cfg_data, int devfn, int offset, type val) \ +{ \ + out_be32(cfg_addr, \ + ((offset & 0xfc) << 24) | (devfn << 16) \ + | (0 << 8) | 0x80); \ + MPC10X_CFG_##rw(val, cfg_data + (offset & mask), type, op); \ + return; \ +} + +MPC10X_PCI_OP(read, byte, u8 *, in_8, 3) +MPC10X_PCI_OP(read, dword, u32 *, in_le32, 0) +#if 0 /* Not used */ +MPC10X_PCI_OP(write, byte, u8, out_8, 3) +MPC10X_PCI_OP(read, word, u16 *, in_le16, 2) +MPC10X_PCI_OP(write, word, u16, out_le16, 2) +MPC10X_PCI_OP(write, dword, u32, out_le32, 0) +#endif + +/* + * Read the memory controller registers to determine the amount of memory in + * the system. This assumes that the firmware has correctly set up the memory + * controller registers. + */ +unsigned long __init +mpc10x_get_mem_size(uint mem_map) +{ + uint *config_addr, *config_data, val; + ulong start, end, total, offset; + int i; + u_char bank_enables; + + switch (mem_map) { + case MPC10X_MEM_MAP_A: + config_addr = (uint *)MPC10X_MAPA_CNFG_ADDR; + config_data = (uint *)MPC10X_MAPA_CNFG_DATA; + break; + case MPC10X_MEM_MAP_B: + config_addr = (uint *)MPC10X_MAPB_CNFG_ADDR; + config_data = (uint *)MPC10X_MAPB_CNFG_DATA; + break; + default: + return 0; + } + + mpc10x_read_config_byte(config_addr, + config_data, + PCI_DEVFN(0,0), + MPC10X_MCTLR_MEM_BANK_ENABLES, + &bank_enables); + + total = 0; + + for (i=0; i<8; i++) { + if (bank_enables & (1 << i)) { + offset = MPC10X_MCTLR_MEM_START_1 + ((i > 3) ? 4 : 0); + mpc10x_read_config_dword(config_addr, + config_data, + PCI_DEVFN(0,0), + offset, + &val); + start = (val >> ((i & 3) << 3)) & 0xff; + + offset = MPC10X_MCTLR_EXT_MEM_START_1 + ((i>3) ? 4 : 0); + mpc10x_read_config_dword(config_addr, + config_data, + PCI_DEVFN(0,0), + offset, + &val); + val = (val >> ((i & 3) << 3)) & 0x03; + start = (val << 28) | (start << 20); + + offset = MPC10X_MCTLR_MEM_END_1 + ((i > 3) ? 4 : 0); + mpc10x_read_config_dword(config_addr, + config_data, + PCI_DEVFN(0,0), + offset, + &val); + end = (val >> ((i & 3) << 3)) & 0xff; + + offset = MPC10X_MCTLR_EXT_MEM_END_1 + ((i > 3) ? 4 : 0); + mpc10x_read_config_dword(config_addr, + config_data, + PCI_DEVFN(0,0), + offset, + &val); + val = (val >> ((i & 3) << 3)) & 0x03; + end = (val << 28) | (end << 20) | 0xfffff; + + total += (end - start + 1); + } + } + + return total; +} + +int __init +mpc10x_enable_store_gathering(struct pci_controller *hose) +{ + uint picr1; + + early_read_config_dword(hose, + 0, + PCI_DEVFN(0,0), + MPC10X_CFG_PICR1_REG, + &picr1); + + picr1 |= MPC10X_CFG_PICR1_ST_GATH_EN; + + early_write_config_dword(hose, + 0, + PCI_DEVFN(0,0), + MPC10X_CFG_PICR1_REG, + picr1); + + return 0; +} + +int __init +mpc10x_disable_store_gathering(struct pci_controller *hose) +{ + uint picr1; + + early_read_config_dword(hose, + 0, + PCI_DEVFN(0,0), + MPC10X_CFG_PICR1_REG, + &picr1); + + picr1 &= ~MPC10X_CFG_PICR1_ST_GATH_EN; + + early_write_config_dword(hose, + 0, + PCI_DEVFN(0,0), + MPC10X_CFG_PICR1_REG, + picr1); + + return 0; +} + +#ifdef CONFIG_MPC10X_OPENPIC +void __init mpc10x_set_openpic(void) +{ + /* Map external IRQs */ + openpic_set_sources(0, EPIC_IRQ_BASE, OpenPIC_Addr + 0x10200); + /* Skip reserved space and map i2c and DMA Ch[01] */ + openpic_set_sources(EPIC_IRQ_BASE, 3, OpenPIC_Addr + 0x11020); + /* Skip reserved space and map Message Unit Interrupt (I2O) */ + openpic_set_sources(EPIC_IRQ_BASE + 3, 1, OpenPIC_Addr + 0x110C0); + + openpic_init(NUM_8259_INTERRUPTS); +} +#endif diff --git a/arch/ppc/syslib/mpc52xx_devices.c b/arch/ppc/syslib/mpc52xx_devices.c new file mode 100644 index 00000000000..ad5182efca1 --- /dev/null +++ b/arch/ppc/syslib/mpc52xx_devices.c @@ -0,0 +1,318 @@ +/* + * arch/ppc/syslib/mpc52xx_devices.c + * + * Freescale MPC52xx device descriptions + * + * + * Maintainer : Sylvain Munaut + * + * Copyright (C) 2005 Sylvain Munaut + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include +#include +#include +#include + + +static u64 mpc52xx_dma_mask = 0xffffffffULL; + +static struct fsl_i2c_platform_data mpc52xx_fsl_i2c_pdata = { + .device_flags = FSL_I2C_DEV_CLOCK_5200, +}; + + +/* We use relative offsets for IORESOURCE_MEM to be independent from the + * MBAR location at compile time + */ + +/* TODO Add the BestComm initiator channel to the device definitions, + possibly using IORESOURCE_DMA. But that's when BestComm is ready ... */ + +struct platform_device ppc_sys_platform_devices[] = { + [MPC52xx_MSCAN1] = { + .name = "mpc52xx-mscan", + .id = 0, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = 0x0900, + .end = 0x097f, + .flags = IORESOURCE_MEM, + }, + { + .start = MPC52xx_MSCAN1_IRQ, + .end = MPC52xx_MSCAN1_IRQ, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC52xx_MSCAN2] = { + .name = "mpc52xx-mscan", + .id = 1, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = 0x0980, + .end = 0x09ff, + .flags = IORESOURCE_MEM, + }, + { + .start = MPC52xx_MSCAN2_IRQ, + .end = MPC52xx_MSCAN2_IRQ, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC52xx_SPI] = { + .name = "mpc52xx-spi", + .id = -1, + .num_resources = 3, + .resource = (struct resource[]) { + { + .start = 0x0f00, + .end = 0x0f1f, + .flags = IORESOURCE_MEM, + }, + { + .name = "modf", + .start = MPC52xx_SPI_MODF_IRQ, + .end = MPC52xx_SPI_MODF_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .name = "spif", + .start = MPC52xx_SPI_SPIF_IRQ, + .end = MPC52xx_SPI_SPIF_IRQ, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC52xx_USB] = { + .name = "ppc-soc-ohci", + .id = -1, + .num_resources = 2, + .dev.dma_mask = &mpc52xx_dma_mask, + .dev.coherent_dma_mask = 0xffffffffULL, + .resource = (struct resource[]) { + { + .start = 0x1000, + .end = 0x10ff, + .flags = IORESOURCE_MEM, + }, + { + .start = MPC52xx_USB_IRQ, + .end = MPC52xx_USB_IRQ, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC52xx_BDLC] = { + .name = "mpc52xx-bdlc", + .id = -1, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = 0x1300, + .end = 0x130f, + .flags = IORESOURCE_MEM, + }, + { + .start = MPC52xx_BDLC_IRQ, + .end = MPC52xx_BDLC_IRQ, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC52xx_PSC1] = { + .name = "mpc52xx-psc", + .id = 0, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = 0x2000, + .end = 0x209f, + .flags = IORESOURCE_MEM, + }, + { + .start = MPC52xx_PSC1_IRQ, + .end = MPC52xx_PSC1_IRQ, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC52xx_PSC2] = { + .name = "mpc52xx-psc", + .id = 1, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = 0x2200, + .end = 0x229f, + .flags = IORESOURCE_MEM, + }, + { + .start = MPC52xx_PSC2_IRQ, + .end = MPC52xx_PSC2_IRQ, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC52xx_PSC3] = { + .name = "mpc52xx-psc", + .id = 2, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = 0x2400, + .end = 0x249f, + .flags = IORESOURCE_MEM, + }, + { + .start = MPC52xx_PSC3_IRQ, + .end = MPC52xx_PSC3_IRQ, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC52xx_PSC4] = { + .name = "mpc52xx-psc", + .id = 3, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = 0x2600, + .end = 0x269f, + .flags = IORESOURCE_MEM, + }, + { + .start = MPC52xx_PSC4_IRQ, + .end = MPC52xx_PSC4_IRQ, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC52xx_PSC5] = { + .name = "mpc52xx-psc", + .id = 4, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = 0x2800, + .end = 0x289f, + .flags = IORESOURCE_MEM, + }, + { + .start = MPC52xx_PSC5_IRQ, + .end = MPC52xx_PSC5_IRQ, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC52xx_PSC6] = { + .name = "mpc52xx-psc", + .id = 5, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = 0x2c00, + .end = 0x2c9f, + .flags = IORESOURCE_MEM, + }, + { + .start = MPC52xx_PSC6_IRQ, + .end = MPC52xx_PSC6_IRQ, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC52xx_FEC] = { + .name = "mpc52xx-fec", + .id = -1, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = 0x3000, + .end = 0x33ff, + .flags = IORESOURCE_MEM, + }, + { + .start = MPC52xx_FEC_IRQ, + .end = MPC52xx_FEC_IRQ, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC52xx_ATA] = { + .name = "mpc52xx-ata", + .id = -1, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = 0x3a00, + .end = 0x3aff, + .flags = IORESOURCE_MEM, + }, + { + .start = MPC52xx_ATA_IRQ, + .end = MPC52xx_ATA_IRQ, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC52xx_I2C1] = { + .name = "fsl-i2c", + .id = 0, + .dev.platform_data = &mpc52xx_fsl_i2c_pdata, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = 0x3d00, + .end = 0x3d1f, + .flags = IORESOURCE_MEM, + }, + { + .start = MPC52xx_I2C1_IRQ, + .end = MPC52xx_I2C1_IRQ, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC52xx_I2C2] = { + .name = "fsl-i2c", + .id = 1, + .dev.platform_data = &mpc52xx_fsl_i2c_pdata, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = 0x3d40, + .end = 0x3d5f, + .flags = IORESOURCE_MEM, + }, + { + .start = MPC52xx_I2C2_IRQ, + .end = MPC52xx_I2C2_IRQ, + .flags = IORESOURCE_IRQ, + }, + }, + }, +}; + + +static int __init mach_mpc52xx_fixup(struct platform_device *pdev) +{ + ppc_sys_fixup_mem_resource(pdev, MPC52xx_MBAR); + return 0; +} + +static int __init mach_mpc52xx_init(void) +{ + ppc_sys_device_fixup = mach_mpc52xx_fixup; + return 0; +} + +postcore_initcall(mach_mpc52xx_init); diff --git a/arch/ppc/syslib/mpc52xx_pci.c b/arch/ppc/syslib/mpc52xx_pci.c new file mode 100644 index 00000000000..c723efd954a --- /dev/null +++ b/arch/ppc/syslib/mpc52xx_pci.c @@ -0,0 +1,235 @@ +/* + * arch/ppc/syslib/mpc52xx_pci.c + * + * PCI code for the Freescale MPC52xx embedded CPU. + * + * + * Maintainer : Sylvain Munaut + * + * Copyright (C) 2004 Sylvain Munaut + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include + +#include + +#include +#include "mpc52xx_pci.h" + +#include + + +static int +mpc52xx_pci_read_config(struct pci_bus *bus, unsigned int devfn, + int offset, int len, u32 *val) +{ + struct pci_controller *hose = bus->sysdata; + u32 value; + + if (ppc_md.pci_exclude_device) + if (ppc_md.pci_exclude_device(bus->number, devfn)) + return PCIBIOS_DEVICE_NOT_FOUND; + + out_be32(hose->cfg_addr, + (1 << 31) | + ((bus->number - hose->bus_offset) << 16) | + (devfn << 8) | + (offset & 0xfc)); + + value = in_le32(hose->cfg_data); + + if (len != 4) { + value >>= ((offset & 0x3) << 3); + value &= 0xffffffff >> (32 - (len << 3)); + } + + *val = value; + + out_be32(hose->cfg_addr, 0); + + return PCIBIOS_SUCCESSFUL; +} + +static int +mpc52xx_pci_write_config(struct pci_bus *bus, unsigned int devfn, + int offset, int len, u32 val) +{ + struct pci_controller *hose = bus->sysdata; + u32 value, mask; + + if (ppc_md.pci_exclude_device) + if (ppc_md.pci_exclude_device(bus->number, devfn)) + return PCIBIOS_DEVICE_NOT_FOUND; + + out_be32(hose->cfg_addr, + (1 << 31) | + ((bus->number - hose->bus_offset) << 16) | + (devfn << 8) | + (offset & 0xfc)); + + if (len != 4) { + value = in_le32(hose->cfg_data); + + offset = (offset & 0x3) << 3; + mask = (0xffffffff >> (32 - (len << 3))); + mask <<= offset; + + value &= ~mask; + val = value | ((val << offset) & mask); + } + + out_le32(hose->cfg_data, val); + + out_be32(hose->cfg_addr, 0); + + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops mpc52xx_pci_ops = { + .read = mpc52xx_pci_read_config, + .write = mpc52xx_pci_write_config +}; + + +static void __init +mpc52xx_pci_setup(struct mpc52xx_pci __iomem *pci_regs) +{ + + /* Setup control regs */ + /* Nothing to do afaik */ + + /* Setup windows */ + out_be32(&pci_regs->iw0btar, MPC52xx_PCI_IWBTAR_TRANSLATION( + MPC52xx_PCI_MEM_START + MPC52xx_PCI_MEM_OFFSET, + MPC52xx_PCI_MEM_START, + MPC52xx_PCI_MEM_SIZE )); + + out_be32(&pci_regs->iw1btar, MPC52xx_PCI_IWBTAR_TRANSLATION( + MPC52xx_PCI_MMIO_START + MPC52xx_PCI_MEM_OFFSET, + MPC52xx_PCI_MMIO_START, + MPC52xx_PCI_MMIO_SIZE )); + + out_be32(&pci_regs->iw2btar, MPC52xx_PCI_IWBTAR_TRANSLATION( + MPC52xx_PCI_IO_BASE, + MPC52xx_PCI_IO_START, + MPC52xx_PCI_IO_SIZE )); + + out_be32(&pci_regs->iwcr, MPC52xx_PCI_IWCR_PACK( + ( MPC52xx_PCI_IWCR_ENABLE | /* iw0btar */ + MPC52xx_PCI_IWCR_READ_MULTI | + MPC52xx_PCI_IWCR_MEM ), + ( MPC52xx_PCI_IWCR_ENABLE | /* iw1btar */ + MPC52xx_PCI_IWCR_READ | + MPC52xx_PCI_IWCR_MEM ), + ( MPC52xx_PCI_IWCR_ENABLE | /* iw2btar */ + MPC52xx_PCI_IWCR_IO ) + )); + + + out_be32(&pci_regs->tbatr0, + MPC52xx_PCI_TBATR_ENABLE | MPC52xx_PCI_TARGET_IO ); + out_be32(&pci_regs->tbatr1, + MPC52xx_PCI_TBATR_ENABLE | MPC52xx_PCI_TARGET_MEM ); + + out_be32(&pci_regs->tcr, MPC52xx_PCI_TCR_LD); + + /* Reset the exteral bus ( internal PCI controller is NOT resetted ) */ + /* Not necessary and can be a bad thing if for example the bootloader + is displaying a splash screen or ... Just left here for + documentation purpose if anyone need it */ +#if 0 + u32 tmp; + tmp = in_be32(&pci_regs->gscr); + out_be32(&pci_regs->gscr, tmp | MPC52xx_PCI_GSCR_PR); + udelay(50); + out_be32(&pci_regs->gscr, tmp); +#endif +} + +static void __init +mpc52xx_pci_fixup_resources(struct pci_dev *dev) +{ + int i; + + /* We don't rely on boot loader for PCI and resets all + devices */ + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { + struct resource *res = &dev->resource[i]; + if (res->end > res->start) { /* Only valid resources */ + res->end -= res->start; + res->start = 0; + res->flags |= IORESOURCE_UNSET; + } + } + + /* The PCI Host bridge of MPC52xx has a prefetch memory resource + fixed to 1Gb. Doesn't fit in the resource system so we remove it */ + if ( (dev->vendor == PCI_VENDOR_ID_MOTOROLA) && + (dev->device == PCI_DEVICE_ID_MOTOROLA_MPC5200) ) { + struct resource *res = &dev->resource[1]; + res->start = res->end = res->flags = 0; + } +} + +void __init +mpc52xx_find_bridges(void) +{ + struct mpc52xx_pci __iomem *pci_regs; + struct pci_controller *hose; + + pci_assign_all_busses = 1; + + pci_regs = ioremap(MPC52xx_PA(MPC52xx_PCI_OFFSET), MPC52xx_PCI_SIZE); + if (!pci_regs) + return; + + hose = pcibios_alloc_controller(); + if (!hose) { + iounmap(pci_regs); + return; + } + + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pcibios_fixup_resources = mpc52xx_pci_fixup_resources; + + hose->first_busno = 0; + hose->last_busno = 0xff; + hose->bus_offset = 0; + hose->ops = &mpc52xx_pci_ops; + + mpc52xx_pci_setup(pci_regs); + + hose->pci_mem_offset = MPC52xx_PCI_MEM_OFFSET; + + isa_io_base = + (unsigned long) ioremap(MPC52xx_PCI_IO_BASE, + MPC52xx_PCI_IO_SIZE); + hose->io_base_virt = (void *) isa_io_base; + + hose->cfg_addr = &pci_regs->car; + hose->cfg_data = (void __iomem *) isa_io_base; + + /* Setup resources */ + pci_init_resource(&hose->mem_resources[0], + MPC52xx_PCI_MEM_START, + MPC52xx_PCI_MEM_STOP, + IORESOURCE_MEM|IORESOURCE_PREFETCH, + "PCI prefetchable memory"); + + pci_init_resource(&hose->mem_resources[1], + MPC52xx_PCI_MMIO_START, + MPC52xx_PCI_MMIO_STOP, + IORESOURCE_MEM, + "PCI memory"); + + pci_init_resource(&hose->io_resource, + MPC52xx_PCI_IO_START, + MPC52xx_PCI_IO_STOP, + IORESOURCE_IO, + "PCI I/O"); + +} diff --git a/arch/ppc/syslib/mpc52xx_pci.h b/arch/ppc/syslib/mpc52xx_pci.h new file mode 100644 index 00000000000..04b509a0253 --- /dev/null +++ b/arch/ppc/syslib/mpc52xx_pci.h @@ -0,0 +1,139 @@ +/* + * arch/ppc/syslib/mpc52xx_pci.h + * + * PCI Include file the Freescale MPC52xx embedded cpu chips + * + * + * Maintainer : Sylvain Munaut + * + * Inspired from code written by Dale Farnsworth + * for the 2.4 kernel. + * + * Copyright (C) 2004 Sylvain Munaut + * Copyright (C) 2003 MontaVista, Software, Inc. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#ifndef __SYSLIB_MPC52xx_PCI_H__ +#define __SYSLIB_MPC52xx_PCI_H__ + +/* ======================================================================== */ +/* PCI windows config */ +/* ======================================================================== */ + +/* + * Master windows : MPC52xx -> PCI + * + * 0x80000000 -> 0x9FFFFFFF PCI Mem prefetchable IW0BTAR + * 0xA0000000 -> 0xAFFFFFFF PCI Mem IW1BTAR + * 0xB0000000 -> 0xB0FFFFFF PCI IO IW2BTAR + * + * Slave windows : PCI -> MPC52xx + * + * 0xF0000000 -> 0xF003FFFF MPC52xx MBAR TBATR0 + * 0x00000000 -> 0x3FFFFFFF MPC52xx local memory TBATR1 + */ + +#define MPC52xx_PCI_MEM_OFFSET 0x00000000 /* Offset for MEM MMIO */ + +#define MPC52xx_PCI_MEM_START 0x80000000 +#define MPC52xx_PCI_MEM_SIZE 0x20000000 +#define MPC52xx_PCI_MEM_STOP (MPC52xx_PCI_MEM_START+MPC52xx_PCI_MEM_SIZE-1) + +#define MPC52xx_PCI_MMIO_START 0xa0000000 +#define MPC52xx_PCI_MMIO_SIZE 0x10000000 +#define MPC52xx_PCI_MMIO_STOP (MPC52xx_PCI_MMIO_START+MPC52xx_PCI_MMIO_SIZE-1) + +#define MPC52xx_PCI_IO_BASE 0xb0000000 + +#define MPC52xx_PCI_IO_START 0x00000000 +#define MPC52xx_PCI_IO_SIZE 0x01000000 +#define MPC52xx_PCI_IO_STOP (MPC52xx_PCI_IO_START+MPC52xx_PCI_IO_SIZE-1) + + +#define MPC52xx_PCI_TARGET_IO MPC52xx_MBAR +#define MPC52xx_PCI_TARGET_MEM 0x00000000 + + +/* ======================================================================== */ +/* Structures mapping & Defines for PCI Unit */ +/* ======================================================================== */ + +#define MPC52xx_PCI_GSCR_BM 0x40000000 +#define MPC52xx_PCI_GSCR_PE 0x20000000 +#define MPC52xx_PCI_GSCR_SE 0x10000000 +#define MPC52xx_PCI_GSCR_XLB2PCI_MASK 0x07000000 +#define MPC52xx_PCI_GSCR_XLB2PCI_SHIFT 24 +#define MPC52xx_PCI_GSCR_IPG2PCI_MASK 0x00070000 +#define MPC52xx_PCI_GSCR_IPG2PCI_SHIFT 16 +#define MPC52xx_PCI_GSCR_BME 0x00004000 +#define MPC52xx_PCI_GSCR_PEE 0x00002000 +#define MPC52xx_PCI_GSCR_SEE 0x00001000 +#define MPC52xx_PCI_GSCR_PR 0x00000001 + + +#define MPC52xx_PCI_IWBTAR_TRANSLATION(proc_ad,pci_ad,size) \ + ( ( (proc_ad) & 0xff000000 ) | \ + ( (((size) - 1) >> 8) & 0x00ff0000 ) | \ + ( ((pci_ad) >> 16) & 0x0000ff00 ) ) + +#define MPC52xx_PCI_IWCR_PACK(win0,win1,win2) (((win0) << 24) | \ + ((win1) << 16) | \ + ((win2) << 8)) + +#define MPC52xx_PCI_IWCR_DISABLE 0x0 +#define MPC52xx_PCI_IWCR_ENABLE 0x1 +#define MPC52xx_PCI_IWCR_READ 0x0 +#define MPC52xx_PCI_IWCR_READ_LINE 0x2 +#define MPC52xx_PCI_IWCR_READ_MULTI 0x4 +#define MPC52xx_PCI_IWCR_MEM 0x0 +#define MPC52xx_PCI_IWCR_IO 0x8 + +#define MPC52xx_PCI_TCR_P 0x01000000 +#define MPC52xx_PCI_TCR_LD 0x00010000 + +#define MPC52xx_PCI_TBATR_DISABLE 0x0 +#define MPC52xx_PCI_TBATR_ENABLE 0x1 + + +#ifndef __ASSEMBLY__ + +struct mpc52xx_pci { + u32 idr; /* PCI + 0x00 */ + u32 scr; /* PCI + 0x04 */ + u32 ccrir; /* PCI + 0x08 */ + u32 cr1; /* PCI + 0x0C */ + u32 bar0; /* PCI + 0x10 */ + u32 bar1; /* PCI + 0x14 */ + u8 reserved1[16]; /* PCI + 0x18 */ + u32 ccpr; /* PCI + 0x28 */ + u32 sid; /* PCI + 0x2C */ + u32 erbar; /* PCI + 0x30 */ + u32 cpr; /* PCI + 0x34 */ + u8 reserved2[4]; /* PCI + 0x38 */ + u32 cr2; /* PCI + 0x3C */ + u8 reserved3[32]; /* PCI + 0x40 */ + u32 gscr; /* PCI + 0x60 */ + u32 tbatr0; /* PCI + 0x64 */ + u32 tbatr1; /* PCI + 0x68 */ + u32 tcr; /* PCI + 0x6C */ + u32 iw0btar; /* PCI + 0x70 */ + u32 iw1btar; /* PCI + 0x74 */ + u32 iw2btar; /* PCI + 0x78 */ + u8 reserved4[4]; /* PCI + 0x7C */ + u32 iwcr; /* PCI + 0x80 */ + u32 icr; /* PCI + 0x84 */ + u32 isr; /* PCI + 0x88 */ + u32 arb; /* PCI + 0x8C */ + u8 reserved5[104]; /* PCI + 0x90 */ + u32 car; /* PCI + 0xF8 */ + u8 reserved6[4]; /* PCI + 0xFC */ +}; + +#endif /* __ASSEMBLY__ */ + + +#endif /* __SYSLIB_MPC52xx_PCI_H__ */ diff --git a/arch/ppc/syslib/mpc52xx_pic.c b/arch/ppc/syslib/mpc52xx_pic.c new file mode 100644 index 00000000000..4c4497e6251 --- /dev/null +++ b/arch/ppc/syslib/mpc52xx_pic.c @@ -0,0 +1,257 @@ +/* + * arch/ppc/syslib/mpc52xx_pic.c + * + * Programmable Interrupt Controller functions for the Freescale MPC52xx + * embedded CPU. + * + * + * Maintainer : Sylvain Munaut + * + * Based on (well, mostly copied from) the code from the 2.4 kernel by + * Dale Farnsworth and Kent Borg. + * + * Copyright (C) 2004 Sylvain Munaut + * Copyright (C) 2003 Montavista Software, Inc + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +static struct mpc52xx_intr __iomem *intr; +static struct mpc52xx_sdma __iomem *sdma; + +static void +mpc52xx_ic_disable(unsigned int irq) +{ + u32 val; + + if (irq == MPC52xx_IRQ0) { + val = in_be32(&intr->ctrl); + val &= ~(1 << 11); + out_be32(&intr->ctrl, val); + } + else if (irq < MPC52xx_IRQ1) { + BUG(); + } + else if (irq <= MPC52xx_IRQ3) { + val = in_be32(&intr->ctrl); + val &= ~(1 << (10 - (irq - MPC52xx_IRQ1))); + out_be32(&intr->ctrl, val); + } + else if (irq < MPC52xx_SDMA_IRQ_BASE) { + val = in_be32(&intr->main_mask); + val |= 1 << (16 - (irq - MPC52xx_MAIN_IRQ_BASE)); + out_be32(&intr->main_mask, val); + } + else if (irq < MPC52xx_PERP_IRQ_BASE) { + val = in_be32(&sdma->IntMask); + val |= 1 << (irq - MPC52xx_SDMA_IRQ_BASE); + out_be32(&sdma->IntMask, val); + } + else { + val = in_be32(&intr->per_mask); + val |= 1 << (31 - (irq - MPC52xx_PERP_IRQ_BASE)); + out_be32(&intr->per_mask, val); + } +} + +static void +mpc52xx_ic_enable(unsigned int irq) +{ + u32 val; + + if (irq == MPC52xx_IRQ0) { + val = in_be32(&intr->ctrl); + val |= 1 << 11; + out_be32(&intr->ctrl, val); + } + else if (irq < MPC52xx_IRQ1) { + BUG(); + } + else if (irq <= MPC52xx_IRQ3) { + val = in_be32(&intr->ctrl); + val |= 1 << (10 - (irq - MPC52xx_IRQ1)); + out_be32(&intr->ctrl, val); + } + else if (irq < MPC52xx_SDMA_IRQ_BASE) { + val = in_be32(&intr->main_mask); + val &= ~(1 << (16 - (irq - MPC52xx_MAIN_IRQ_BASE))); + out_be32(&intr->main_mask, val); + } + else if (irq < MPC52xx_PERP_IRQ_BASE) { + val = in_be32(&sdma->IntMask); + val &= ~(1 << (irq - MPC52xx_SDMA_IRQ_BASE)); + out_be32(&sdma->IntMask, val); + } + else { + val = in_be32(&intr->per_mask); + val &= ~(1 << (31 - (irq - MPC52xx_PERP_IRQ_BASE))); + out_be32(&intr->per_mask, val); + } +} + +static void +mpc52xx_ic_ack(unsigned int irq) +{ + u32 val; + + /* + * Only some irqs are reset here, others in interrupting hardware. + */ + + switch (irq) { + case MPC52xx_IRQ0: + val = in_be32(&intr->ctrl); + val |= 0x08000000; + out_be32(&intr->ctrl, val); + break; + case MPC52xx_CCS_IRQ: + val = in_be32(&intr->enc_status); + val |= 0x00000400; + out_be32(&intr->enc_status, val); + break; + case MPC52xx_IRQ1: + val = in_be32(&intr->ctrl); + val |= 0x04000000; + out_be32(&intr->ctrl, val); + break; + case MPC52xx_IRQ2: + val = in_be32(&intr->ctrl); + val |= 0x02000000; + out_be32(&intr->ctrl, val); + break; + case MPC52xx_IRQ3: + val = in_be32(&intr->ctrl); + val |= 0x01000000; + out_be32(&intr->ctrl, val); + break; + default: + if (irq >= MPC52xx_SDMA_IRQ_BASE + && irq < (MPC52xx_SDMA_IRQ_BASE + MPC52xx_SDMA_IRQ_NUM)) { + out_be32(&sdma->IntPend, + 1 << (irq - MPC52xx_SDMA_IRQ_BASE)); + } + break; + } +} + +static void +mpc52xx_ic_disable_and_ack(unsigned int irq) +{ + mpc52xx_ic_disable(irq); + mpc52xx_ic_ack(irq); +} + +static void +mpc52xx_ic_end(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) + mpc52xx_ic_enable(irq); +} + +static struct hw_interrupt_type mpc52xx_ic = { + .typename = " MPC52xx ", + .enable = mpc52xx_ic_enable, + .disable = mpc52xx_ic_disable, + .ack = mpc52xx_ic_disable_and_ack, + .end = mpc52xx_ic_end, +}; + +void __init +mpc52xx_init_irq(void) +{ + int i; + u32 intr_ctrl; + + /* Remap the necessary zones */ + intr = ioremap(MPC52xx_PA(MPC52xx_INTR_OFFSET), MPC52xx_INTR_SIZE); + sdma = ioremap(MPC52xx_PA(MPC52xx_SDMA_OFFSET), MPC52xx_SDMA_SIZE); + + if ((intr==NULL) || (sdma==NULL)) + panic("Can't ioremap PIC/SDMA register for init_irq !"); + + /* Disable all interrupt sources. */ + out_be32(&sdma->IntPend, 0xffffffff); /* 1 means clear pending */ + out_be32(&sdma->IntMask, 0xffffffff); /* 1 means disabled */ + out_be32(&intr->per_mask, 0x7ffffc00); /* 1 means disabled */ + out_be32(&intr->main_mask, 0x00010fff); /* 1 means disabled */ + intr_ctrl = in_be32(&intr->ctrl); + intr_ctrl &= 0x00ff0000; /* Keeps IRQ[0-3] config */ + intr_ctrl |= 0x0f000000 | /* clear IRQ 0-3 */ + 0x00001000 | /* MEE master external enable */ + 0x00000000 | /* 0 means disable IRQ 0-3 */ + 0x00000001; /* CEb route critical normally */ + out_be32(&intr->ctrl, intr_ctrl); + + /* Zero a bunch of the priority settings. */ + out_be32(&intr->per_pri1, 0); + out_be32(&intr->per_pri2, 0); + out_be32(&intr->per_pri3, 0); + out_be32(&intr->main_pri1, 0); + out_be32(&intr->main_pri2, 0); + + /* Initialize irq_desc[i].handler's with mpc52xx_ic. */ + for (i = 0; i < NR_IRQS; i++) { + irq_desc[i].handler = &mpc52xx_ic; + irq_desc[i].status = IRQ_LEVEL; + } + + #define IRQn_MODE(intr_ctrl,irq) (((intr_ctrl) >> (22-(i<<1))) & 0x03) + for (i=0 ; i<4 ; i++) { + int mode; + mode = IRQn_MODE(intr_ctrl,i); + if ((mode == 0x1) || (mode == 0x2)) + irq_desc[i?MPC52xx_IRQ1+i-1:MPC52xx_IRQ0].status = 0; + } +} + +int +mpc52xx_get_irq(struct pt_regs *regs) +{ + u32 status; + int irq = -1; + + status = in_be32(&intr->enc_status); + + if (status & 0x00000400) { /* critical */ + irq = (status >> 8) & 0x3; + if (irq == 2) /* high priority peripheral */ + goto peripheral; + irq += MPC52xx_CRIT_IRQ_BASE; + } + else if (status & 0x00200000) { /* main */ + irq = (status >> 16) & 0x1f; + if (irq == 4) /* low priority peripheral */ + goto peripheral; + irq += MPC52xx_MAIN_IRQ_BASE; + } + else if (status & 0x20000000) { /* peripheral */ +peripheral: + irq = (status >> 24) & 0x1f; + if (irq == 0) { /* bestcomm */ + status = in_be32(&sdma->IntPend); + irq = ffs(status) + MPC52xx_SDMA_IRQ_BASE-1; + } + else + irq += MPC52xx_PERP_IRQ_BASE; + } + + return irq; +} + diff --git a/arch/ppc/syslib/mpc52xx_setup.c b/arch/ppc/syslib/mpc52xx_setup.c new file mode 100644 index 00000000000..bb2374585a7 --- /dev/null +++ b/arch/ppc/syslib/mpc52xx_setup.c @@ -0,0 +1,230 @@ +/* + * arch/ppc/syslib/mpc52xx_setup.c + * + * Common code for the boards based on Freescale MPC52xx embedded CPU. + * + * + * Maintainer : Sylvain Munaut + * + * Support for other bootloaders than UBoot by Dale Farnsworth + * + * + * Copyright (C) 2004 Sylvain Munaut + * Copyright (C) 2003 Montavista Software, Inc + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include + +#include +#include +#include +#include +#include +#include + +extern bd_t __res; + +static int core_mult[] = { /* CPU Frequency multiplier, taken */ + 0, 0, 0, 10, 20, 20, 25, 45, /* from the datasheet used to compute */ + 30, 55, 40, 50, 0, 60, 35, 0, /* CPU frequency from XLB freq and */ + 30, 25, 65, 10, 70, 20, 75, 45, /* external jumper config */ + 0, 55, 40, 50, 80, 60, 35, 0 +}; + +void +mpc52xx_restart(char *cmd) +{ + struct mpc52xx_gpt __iomem *gpt0 = MPC52xx_VA(MPC52xx_GPTx_OFFSET(0)); + + local_irq_disable(); + + /* Turn on the watchdog and wait for it to expire. It effectively + does a reset */ + out_be32(&gpt0->count, 0x000000ff); + out_be32(&gpt0->mode, 0x00009004); + + while (1); +} + +void +mpc52xx_halt(void) +{ + local_irq_disable(); + + while (1); +} + +void +mpc52xx_power_off(void) +{ + /* By default we don't have any way of shut down. + If a specific board wants to, it can set the power down + code to any hardware implementation dependent code */ + mpc52xx_halt(); +} + + +void __init +mpc52xx_set_bat(void) +{ + /* Set BAT 2 to map the 0xf0000000 area */ + /* This mapping is used during mpc52xx_progress, + * mpc52xx_find_end_of_memory, and UARTs/GPIO access for debug + */ + mb(); + mtspr(SPRN_DBAT2U, 0xf0001ffe); + mtspr(SPRN_DBAT2L, 0xf000002a); + mb(); +} + +void __init +mpc52xx_map_io(void) +{ + /* Here we only map the MBAR */ + io_block_mapping( + MPC52xx_MBAR_VIRT, MPC52xx_MBAR, MPC52xx_MBAR_SIZE, _PAGE_IO); +} + + +#ifdef CONFIG_SERIAL_TEXT_DEBUG +#ifndef MPC52xx_PF_CONSOLE_PORT +#error "mpc52xx PSC for console not selected" +#endif + +static void +mpc52xx_psc_putc(struct mpc52xx_psc __iomem *psc, unsigned char c) +{ + while (!(in_be16(&psc->mpc52xx_psc_status) & + MPC52xx_PSC_SR_TXRDY)); + out_8(&psc->mpc52xx_psc_buffer_8, c); +} + +void +mpc52xx_progress(char *s, unsigned short hex) +{ + char c; + struct mpc52xx_psc __iomem *psc; + + psc = MPC52xx_VA(MPC52xx_PSCx_OFFSET(MPC52xx_PF_CONSOLE_PORT)); + + while ((c = *s++) != 0) { + if (c == '\n') + mpc52xx_psc_putc(psc, '\r'); + mpc52xx_psc_putc(psc, c); + } + + mpc52xx_psc_putc(psc, '\r'); + mpc52xx_psc_putc(psc, '\n'); +} + +#endif /* CONFIG_SERIAL_TEXT_DEBUG */ + + +unsigned long __init +mpc52xx_find_end_of_memory(void) +{ + u32 ramsize = __res.bi_memsize; + + /* + * if bootloader passed a memsize, just use it + * else get size from sdram config registers + */ + if (ramsize == 0) { + struct mpc52xx_mmap_ctl __iomem *mmap_ctl; + u32 sdram_config_0, sdram_config_1; + + /* Temp BAT2 mapping active when this is called ! */ + mmap_ctl = MPC52xx_VA(MPC52xx_MMAP_CTL_OFFSET); + + sdram_config_0 = in_be32(&mmap_ctl->sdram0); + sdram_config_1 = in_be32(&mmap_ctl->sdram1); + + if ((sdram_config_0 & 0x1f) >= 0x13) + ramsize = 1 << ((sdram_config_0 & 0xf) + 17); + + if (((sdram_config_1 & 0x1f) >= 0x13) && + ((sdram_config_1 & 0xfff00000) == ramsize)) + ramsize += 1 << ((sdram_config_1 & 0xf) + 17); + } + + return ramsize; +} + +void __init +mpc52xx_calibrate_decr(void) +{ + int current_time, previous_time; + int tbl_start, tbl_end; + unsigned int xlbfreq, cpufreq, ipbfreq, pcifreq, divisor; + + xlbfreq = __res.bi_busfreq; + /* if bootloader didn't pass bus frequencies, calculate them */ + if (xlbfreq == 0) { + /* Get RTC & Clock manager modules */ + struct mpc52xx_rtc __iomem *rtc; + struct mpc52xx_cdm __iomem *cdm; + + rtc = ioremap(MPC52xx_PA(MPC52xx_RTC_OFFSET), MPC52xx_RTC_SIZE); + cdm = ioremap(MPC52xx_PA(MPC52xx_CDM_OFFSET), MPC52xx_CDM_SIZE); + + if ((rtc==NULL) || (cdm==NULL)) + panic("Can't ioremap RTC/CDM while computing bus freq"); + + /* Count bus clock during 1/64 sec */ + out_be32(&rtc->dividers, 0x8f1f0000); /* Set RTC 64x faster */ + previous_time = in_be32(&rtc->time); + while ((current_time = in_be32(&rtc->time)) == previous_time) ; + tbl_start = get_tbl(); + previous_time = current_time; + while ((current_time = in_be32(&rtc->time)) == previous_time) ; + tbl_end = get_tbl(); + out_be32(&rtc->dividers, 0xffff0000); /* Restore RTC */ + + /* Compute all frequency from that & CDM settings */ + xlbfreq = (tbl_end - tbl_start) << 8; + cpufreq = (xlbfreq * core_mult[in_be32(&cdm->rstcfg)&0x1f])/10; + ipbfreq = (in_8(&cdm->ipb_clk_sel) & 1) ? + xlbfreq / 2 : xlbfreq; + switch (in_8(&cdm->pci_clk_sel) & 3) { + case 0: + pcifreq = ipbfreq; + break; + case 1: + pcifreq = ipbfreq / 2; + break; + default: + pcifreq = xlbfreq / 4; + break; + } + __res.bi_busfreq = xlbfreq; + __res.bi_intfreq = cpufreq; + __res.bi_ipbfreq = ipbfreq; + __res.bi_pcifreq = pcifreq; + + /* Release mapping */ + iounmap(rtc); + iounmap(cdm); + } + + divisor = 4; + + tb_ticks_per_jiffy = xlbfreq / HZ / divisor; + tb_to_us = mulhwu_scale_factor(xlbfreq / divisor, 1000000); +} + +int mpc52xx_match_psc_function(int psc_idx, const char *func) +{ + struct mpc52xx_psc_func *cf = mpc52xx_psc_functions; + + while ((cf->id != -1) && (cf->func != NULL)) { + if ((cf->id == psc_idx) && !strcmp(cf->func,func)) + return 1; + cf++; + } + + return 0; +} diff --git a/arch/ppc/syslib/mpc52xx_sys.c b/arch/ppc/syslib/mpc52xx_sys.c new file mode 100644 index 00000000000..9a0f90aa8aa --- /dev/null +++ b/arch/ppc/syslib/mpc52xx_sys.c @@ -0,0 +1,38 @@ +/* + * arch/ppc/syslib/mpc52xx_sys.c + * + * Freescale MPC52xx system descriptions + * + * + * Maintainer : Sylvain Munaut + * + * Copyright (C) 2005 Sylvain Munaut + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include + +struct ppc_sys_spec *cur_ppc_sys_spec; +struct ppc_sys_spec ppc_sys_specs[] = { + { + .ppc_sys_name = "5200", + .mask = 0xffff0000, + .value = 0x80110000, + .num_devices = 15, + .device_list = (enum ppc_sys_devices[]) + { + MPC52xx_MSCAN1, MPC52xx_MSCAN2, MPC52xx_SPI, + MPC52xx_USB, MPC52xx_BDLC, MPC52xx_PSC1, MPC52xx_PSC2, + MPC52xx_PSC3, MPC52xx_PSC4, MPC52xx_PSC5, MPC52xx_PSC6, + MPC52xx_FEC, MPC52xx_ATA, MPC52xx_I2C1, MPC52xx_I2C2, + }, + }, + { /* default match */ + .ppc_sys_name = "", + .mask = 0x00000000, + .value = 0x00000000, + }, +}; diff --git a/arch/ppc/syslib/mpc83xx_devices.c b/arch/ppc/syslib/mpc83xx_devices.c new file mode 100644 index 00000000000..5c1a919eaab --- /dev/null +++ b/arch/ppc/syslib/mpc83xx_devices.c @@ -0,0 +1,237 @@ +/* + * arch/ppc/platforms/83xx/mpc83xx_devices.c + * + * MPC83xx Device descriptions + * + * Maintainer: Kumar Gala + * + * Copyright 2005 Freescale Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* We use offsets for IORESOURCE_MEM since we do not know at compile time + * what IMMRBAR is, will get fixed up by mach_mpc83xx_fixup + */ + +static struct gianfar_platform_data mpc83xx_tsec1_pdata = { + .device_flags = FSL_GIANFAR_DEV_HAS_GIGABIT | + FSL_GIANFAR_DEV_HAS_COALESCE | FSL_GIANFAR_DEV_HAS_RMON | + FSL_GIANFAR_DEV_HAS_MULTI_INTR, + .phy_reg_addr = 0x24000, +}; + +static struct gianfar_platform_data mpc83xx_tsec2_pdata = { + .device_flags = FSL_GIANFAR_DEV_HAS_GIGABIT | + FSL_GIANFAR_DEV_HAS_COALESCE | FSL_GIANFAR_DEV_HAS_RMON | + FSL_GIANFAR_DEV_HAS_MULTI_INTR, + .phy_reg_addr = 0x24000, +}; + +static struct fsl_i2c_platform_data mpc83xx_fsl_i2c1_pdata = { + .device_flags = FSL_I2C_DEV_SEPARATE_DFSRR, +}; + +static struct fsl_i2c_platform_data mpc83xx_fsl_i2c2_pdata = { + .device_flags = FSL_I2C_DEV_SEPARATE_DFSRR, +}; + +static struct plat_serial8250_port serial_platform_data[] = { + [0] = { + .mapbase = 0x4500, + .irq = MPC83xx_IRQ_UART1, + .iotype = UPIO_MEM, + .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, + }, + [1] = { + .mapbase = 0x4600, + .irq = MPC83xx_IRQ_UART2, + .iotype = UPIO_MEM, + .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, + }, +}; + +struct platform_device ppc_sys_platform_devices[] = { + [MPC83xx_TSEC1] = { + .name = "fsl-gianfar", + .id = 1, + .dev.platform_data = &mpc83xx_tsec1_pdata, + .num_resources = 4, + .resource = (struct resource[]) { + { + .start = 0x24000, + .end = 0x24fff, + .flags = IORESOURCE_MEM, + }, + { + .name = "tx", + .start = MPC83xx_IRQ_TSEC1_TX, + .end = MPC83xx_IRQ_TSEC1_TX, + .flags = IORESOURCE_IRQ, + }, + { + .name = "rx", + .start = MPC83xx_IRQ_TSEC1_RX, + .end = MPC83xx_IRQ_TSEC1_RX, + .flags = IORESOURCE_IRQ, + }, + { + .name = "error", + .start = MPC83xx_IRQ_TSEC1_ERROR, + .end = MPC83xx_IRQ_TSEC1_ERROR, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC83xx_TSEC2] = { + .name = "fsl-gianfar", + .id = 2, + .dev.platform_data = &mpc83xx_tsec2_pdata, + .num_resources = 4, + .resource = (struct resource[]) { + { + .start = 0x25000, + .end = 0x25fff, + .flags = IORESOURCE_MEM, + }, + { + .name = "tx", + .start = MPC83xx_IRQ_TSEC2_TX, + .end = MPC83xx_IRQ_TSEC2_TX, + .flags = IORESOURCE_IRQ, + }, + { + .name = "rx", + .start = MPC83xx_IRQ_TSEC2_RX, + .end = MPC83xx_IRQ_TSEC2_RX, + .flags = IORESOURCE_IRQ, + }, + { + .name = "error", + .start = MPC83xx_IRQ_TSEC2_ERROR, + .end = MPC83xx_IRQ_TSEC2_ERROR, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC83xx_IIC1] = { + .name = "fsl-i2c", + .id = 1, + .dev.platform_data = &mpc83xx_fsl_i2c1_pdata, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = 0x3000, + .end = 0x30ff, + .flags = IORESOURCE_MEM, + }, + { + .start = MPC83xx_IRQ_IIC1, + .end = MPC83xx_IRQ_IIC1, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC83xx_IIC2] = { + .name = "fsl-i2c", + .id = 2, + .dev.platform_data = &mpc83xx_fsl_i2c2_pdata, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = 0x3100, + .end = 0x31ff, + .flags = IORESOURCE_MEM, + }, + { + .start = MPC83xx_IRQ_IIC2, + .end = MPC83xx_IRQ_IIC2, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC83xx_DUART] = { + .name = "serial8250", + .id = 0, + .dev.platform_data = serial_platform_data, + }, + [MPC83xx_SEC2] = { + .name = "fsl-sec2", + .id = 1, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = 0x30000, + .end = 0x3ffff, + .flags = IORESOURCE_MEM, + }, + { + .start = MPC83xx_IRQ_SEC2, + .end = MPC83xx_IRQ_SEC2, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC83xx_USB2_DR] = { + .name = "fsl-usb2-dr", + .id = 1, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = 0x22000, + .end = 0x22fff, + .flags = IORESOURCE_MEM, + }, + { + .start = MPC83xx_IRQ_USB2_DR, + .end = MPC83xx_IRQ_USB2_DR, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC83xx_USB2_MPH] = { + .name = "fsl-usb2-mph", + .id = 1, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = 0x23000, + .end = 0x23fff, + .flags = IORESOURCE_MEM, + }, + { + .start = MPC83xx_IRQ_USB2_MPH, + .end = MPC83xx_IRQ_USB2_MPH, + .flags = IORESOURCE_IRQ, + }, + }, + }, +}; + +static int __init mach_mpc83xx_fixup(struct platform_device *pdev) +{ + ppc_sys_fixup_mem_resource(pdev, immrbar); + return 0; +} + +static int __init mach_mpc83xx_init(void) +{ + if (ppc_md.progress) + ppc_md.progress("mach_mpc83xx_init:enter", 0); + ppc_sys_device_fixup = mach_mpc83xx_fixup; + return 0; +} + +postcore_initcall(mach_mpc83xx_init); diff --git a/arch/ppc/syslib/mpc83xx_sys.c b/arch/ppc/syslib/mpc83xx_sys.c new file mode 100644 index 00000000000..29aa6335002 --- /dev/null +++ b/arch/ppc/syslib/mpc83xx_sys.c @@ -0,0 +1,100 @@ +/* + * arch/ppc/platforms/83xx/mpc83xx_sys.c + * + * MPC83xx System descriptions + * + * Maintainer: Kumar Gala + * + * Copyright 2005 Freescale Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include + +struct ppc_sys_spec *cur_ppc_sys_spec; +struct ppc_sys_spec ppc_sys_specs[] = { + { + .ppc_sys_name = "8349E", + .mask = 0xFFFF0000, + .value = 0x80500000, + .num_devices = 8, + .device_list = (enum ppc_sys_devices[]) + { + MPC83xx_TSEC1, MPC83xx_TSEC2, MPC83xx_IIC1, + MPC83xx_IIC2, MPC83xx_DUART, MPC83xx_SEC2, + MPC83xx_USB2_DR, MPC83xx_USB2_MPH + }, + }, + { + .ppc_sys_name = "8349", + .mask = 0xFFFF0000, + .value = 0x80510000, + .num_devices = 7, + .device_list = (enum ppc_sys_devices[]) + { + MPC83xx_TSEC1, MPC83xx_TSEC2, MPC83xx_IIC1, + MPC83xx_IIC2, MPC83xx_DUART, + MPC83xx_USB2_DR, MPC83xx_USB2_MPH + }, + }, + { + .ppc_sys_name = "8347E", + .mask = 0xFFFF0000, + .value = 0x80520000, + .num_devices = 8, + .device_list = (enum ppc_sys_devices[]) + { + MPC83xx_TSEC1, MPC83xx_TSEC2, MPC83xx_IIC1, + MPC83xx_IIC2, MPC83xx_DUART, MPC83xx_SEC2, + MPC83xx_USB2_DR, MPC83xx_USB2_MPH + }, + }, + { + .ppc_sys_name = "8347", + .mask = 0xFFFF0000, + .value = 0x80530000, + .num_devices = 7, + .device_list = (enum ppc_sys_devices[]) + { + MPC83xx_TSEC1, MPC83xx_TSEC2, MPC83xx_IIC1, + MPC83xx_IIC2, MPC83xx_DUART, + MPC83xx_USB2_DR, MPC83xx_USB2_MPH + }, + }, + { + .ppc_sys_name = "8343E", + .mask = 0xFFFF0000, + .value = 0x80540000, + .num_devices = 7, + .device_list = (enum ppc_sys_devices[]) + { + MPC83xx_TSEC1, MPC83xx_TSEC2, MPC83xx_IIC1, + MPC83xx_IIC2, MPC83xx_DUART, MPC83xx_SEC2, + MPC83xx_USB2_DR, + }, + }, + { + .ppc_sys_name = "8343", + .mask = 0xFFFF0000, + .value = 0x80550000, + .num_devices = 6, + .device_list = (enum ppc_sys_devices[]) + { + MPC83xx_TSEC1, MPC83xx_TSEC2, MPC83xx_IIC1, + MPC83xx_IIC2, MPC83xx_DUART, + MPC83xx_USB2_DR, + }, + }, + { /* default match */ + .ppc_sys_name = "", + .mask = 0x00000000, + .value = 0x00000000, + }, +}; diff --git a/arch/ppc/syslib/mpc85xx_devices.c b/arch/ppc/syslib/mpc85xx_devices.c new file mode 100644 index 00000000000..a231795ee26 --- /dev/null +++ b/arch/ppc/syslib/mpc85xx_devices.c @@ -0,0 +1,552 @@ +/* + * arch/ppc/platforms/85xx/mpc85xx_devices.c + * + * MPC85xx Device descriptions + * + * Maintainer: Kumar Gala + * + * Copyright 2005 Freescale Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* We use offsets for IORESOURCE_MEM since we do not know at compile time + * what CCSRBAR is, will get fixed up by mach_mpc85xx_fixup + */ + +static struct gianfar_platform_data mpc85xx_tsec1_pdata = { + .device_flags = FSL_GIANFAR_DEV_HAS_GIGABIT | + FSL_GIANFAR_DEV_HAS_COALESCE | FSL_GIANFAR_DEV_HAS_RMON | + FSL_GIANFAR_DEV_HAS_MULTI_INTR, + .phy_reg_addr = MPC85xx_ENET1_OFFSET, +}; + +static struct gianfar_platform_data mpc85xx_tsec2_pdata = { + .device_flags = FSL_GIANFAR_DEV_HAS_GIGABIT | + FSL_GIANFAR_DEV_HAS_COALESCE | FSL_GIANFAR_DEV_HAS_RMON | + FSL_GIANFAR_DEV_HAS_MULTI_INTR, + .phy_reg_addr = MPC85xx_ENET1_OFFSET, +}; + +static struct gianfar_platform_data mpc85xx_fec_pdata = { + .phy_reg_addr = MPC85xx_ENET1_OFFSET, +}; + +static struct fsl_i2c_platform_data mpc85xx_fsl_i2c_pdata = { + .device_flags = FSL_I2C_DEV_SEPARATE_DFSRR, +}; + +static struct plat_serial8250_port serial_platform_data[] = { + [0] = { + .mapbase = 0x4500, + .irq = MPC85xx_IRQ_DUART, + .iotype = UPIO_MEM, + .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ, + }, + [1] = { + .mapbase = 0x4600, + .irq = MPC85xx_IRQ_DUART, + .iotype = UPIO_MEM, + .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ, + }, +}; + +struct platform_device ppc_sys_platform_devices[] = { + [MPC85xx_TSEC1] = { + .name = "fsl-gianfar", + .id = 1, + .dev.platform_data = &mpc85xx_tsec1_pdata, + .num_resources = 4, + .resource = (struct resource[]) { + { + .start = MPC85xx_ENET1_OFFSET, + .end = MPC85xx_ENET1_OFFSET + + MPC85xx_ENET1_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "tx", + .start = MPC85xx_IRQ_TSEC1_TX, + .end = MPC85xx_IRQ_TSEC1_TX, + .flags = IORESOURCE_IRQ, + }, + { + .name = "rx", + .start = MPC85xx_IRQ_TSEC1_RX, + .end = MPC85xx_IRQ_TSEC1_RX, + .flags = IORESOURCE_IRQ, + }, + { + .name = "error", + .start = MPC85xx_IRQ_TSEC1_ERROR, + .end = MPC85xx_IRQ_TSEC1_ERROR, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC85xx_TSEC2] = { + .name = "fsl-gianfar", + .id = 2, + .dev.platform_data = &mpc85xx_tsec2_pdata, + .num_resources = 4, + .resource = (struct resource[]) { + { + .start = MPC85xx_ENET2_OFFSET, + .end = MPC85xx_ENET2_OFFSET + + MPC85xx_ENET2_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "tx", + .start = MPC85xx_IRQ_TSEC2_TX, + .end = MPC85xx_IRQ_TSEC2_TX, + .flags = IORESOURCE_IRQ, + }, + { + .name = "rx", + .start = MPC85xx_IRQ_TSEC2_RX, + .end = MPC85xx_IRQ_TSEC2_RX, + .flags = IORESOURCE_IRQ, + }, + { + .name = "error", + .start = MPC85xx_IRQ_TSEC2_ERROR, + .end = MPC85xx_IRQ_TSEC2_ERROR, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC85xx_FEC] = { + .name = "fsl-gianfar", + .id = 3, + .dev.platform_data = &mpc85xx_fec_pdata, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = MPC85xx_ENET3_OFFSET, + .end = MPC85xx_ENET3_OFFSET + + MPC85xx_ENET3_SIZE - 1, + .flags = IORESOURCE_MEM, + + }, + { + .start = MPC85xx_IRQ_FEC, + .end = MPC85xx_IRQ_FEC, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC85xx_IIC1] = { + .name = "fsl-i2c", + .id = 1, + .dev.platform_data = &mpc85xx_fsl_i2c_pdata, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = MPC85xx_IIC1_OFFSET, + .end = MPC85xx_IIC1_OFFSET + + MPC85xx_IIC1_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = MPC85xx_IRQ_IIC1, + .end = MPC85xx_IRQ_IIC1, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC85xx_DMA0] = { + .name = "fsl-dma", + .id = 0, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = MPC85xx_DMA0_OFFSET, + .end = MPC85xx_DMA0_OFFSET + + MPC85xx_DMA0_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = MPC85xx_IRQ_DMA0, + .end = MPC85xx_IRQ_DMA0, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC85xx_DMA1] = { + .name = "fsl-dma", + .id = 1, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = MPC85xx_DMA1_OFFSET, + .end = MPC85xx_DMA1_OFFSET + + MPC85xx_DMA1_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = MPC85xx_IRQ_DMA1, + .end = MPC85xx_IRQ_DMA1, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC85xx_DMA2] = { + .name = "fsl-dma", + .id = 2, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = MPC85xx_DMA2_OFFSET, + .end = MPC85xx_DMA2_OFFSET + + MPC85xx_DMA2_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = MPC85xx_IRQ_DMA2, + .end = MPC85xx_IRQ_DMA2, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC85xx_DMA3] = { + .name = "fsl-dma", + .id = 3, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = MPC85xx_DMA3_OFFSET, + .end = MPC85xx_DMA3_OFFSET + + MPC85xx_DMA3_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = MPC85xx_IRQ_DMA3, + .end = MPC85xx_IRQ_DMA3, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC85xx_DUART] = { + .name = "serial8250", + .id = 0, + .dev.platform_data = serial_platform_data, + }, + [MPC85xx_PERFMON] = { + .name = "fsl-perfmon", + .id = 1, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = MPC85xx_PERFMON_OFFSET, + .end = MPC85xx_PERFMON_OFFSET + + MPC85xx_PERFMON_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = MPC85xx_IRQ_PERFMON, + .end = MPC85xx_IRQ_PERFMON, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC85xx_SEC2] = { + .name = "fsl-sec2", + .id = 1, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = MPC85xx_SEC2_OFFSET, + .end = MPC85xx_SEC2_OFFSET + + MPC85xx_SEC2_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = MPC85xx_IRQ_SEC2, + .end = MPC85xx_IRQ_SEC2, + .flags = IORESOURCE_IRQ, + }, + }, + }, +#ifdef CONFIG_CPM2 + [MPC85xx_CPM_FCC1] = { + .name = "fsl-cpm-fcc", + .id = 1, + .num_resources = 3, + .resource = (struct resource[]) { + { + .start = 0x91300, + .end = 0x9131F, + .flags = IORESOURCE_MEM, + }, + { + .start = 0x91380, + .end = 0x9139F, + .flags = IORESOURCE_MEM, + }, + { + .start = SIU_INT_FCC1, + .end = SIU_INT_FCC1, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC85xx_CPM_FCC2] = { + .name = "fsl-cpm-fcc", + .id = 2, + .num_resources = 3, + .resource = (struct resource[]) { + { + .start = 0x91320, + .end = 0x9133F, + .flags = IORESOURCE_MEM, + }, + { + .start = 0x913A0, + .end = 0x913CF, + .flags = IORESOURCE_MEM, + }, + { + .start = SIU_INT_FCC2, + .end = SIU_INT_FCC2, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC85xx_CPM_FCC3] = { + .name = "fsl-cpm-fcc", + .id = 3, + .num_resources = 3, + .resource = (struct resource[]) { + { + .start = 0x91340, + .end = 0x9135F, + .flags = IORESOURCE_MEM, + }, + { + .start = 0x913D0, + .end = 0x913FF, + .flags = IORESOURCE_MEM, + }, + { + .start = SIU_INT_FCC3, + .end = SIU_INT_FCC3, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC85xx_CPM_I2C] = { + .name = "fsl-cpm-i2c", + .id = 1, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = 0x91860, + .end = 0x918BF, + .flags = IORESOURCE_MEM, + }, + { + .start = SIU_INT_I2C, + .end = SIU_INT_I2C, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC85xx_CPM_SCC1] = { + .name = "fsl-cpm-scc", + .id = 1, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = 0x91A00, + .end = 0x91A1F, + .flags = IORESOURCE_MEM, + }, + { + .start = SIU_INT_SCC1, + .end = SIU_INT_SCC1, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC85xx_CPM_SCC2] = { + .name = "fsl-cpm-scc", + .id = 2, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = 0x91A20, + .end = 0x91A3F, + .flags = IORESOURCE_MEM, + }, + { + .start = SIU_INT_SCC2, + .end = SIU_INT_SCC2, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC85xx_CPM_SCC3] = { + .name = "fsl-cpm-scc", + .id = 3, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = 0x91A40, + .end = 0x91A5F, + .flags = IORESOURCE_MEM, + }, + { + .start = SIU_INT_SCC3, + .end = SIU_INT_SCC3, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC85xx_CPM_SCC4] = { + .name = "fsl-cpm-scc", + .id = 4, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = 0x91A60, + .end = 0x91A7F, + .flags = IORESOURCE_MEM, + }, + { + .start = SIU_INT_SCC4, + .end = SIU_INT_SCC4, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC85xx_CPM_SPI] = { + .name = "fsl-cpm-spi", + .id = 1, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = 0x91AA0, + .end = 0x91AFF, + .flags = IORESOURCE_MEM, + }, + { + .start = SIU_INT_SPI, + .end = SIU_INT_SPI, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC85xx_CPM_MCC1] = { + .name = "fsl-cpm-mcc", + .id = 1, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = 0x91B30, + .end = 0x91B3F, + .flags = IORESOURCE_MEM, + }, + { + .start = SIU_INT_MCC1, + .end = SIU_INT_MCC1, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC85xx_CPM_MCC2] = { + .name = "fsl-cpm-mcc", + .id = 2, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = 0x91B50, + .end = 0x91B5F, + .flags = IORESOURCE_MEM, + }, + { + .start = SIU_INT_MCC2, + .end = SIU_INT_MCC2, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC85xx_CPM_SMC1] = { + .name = "fsl-cpm-smc", + .id = 1, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = 0x91A80, + .end = 0x91A8F, + .flags = IORESOURCE_MEM, + }, + { + .start = SIU_INT_SMC1, + .end = SIU_INT_SMC1, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC85xx_CPM_SMC2] = { + .name = "fsl-cpm-smc", + .id = 2, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = 0x91A90, + .end = 0x91A9F, + .flags = IORESOURCE_MEM, + }, + { + .start = SIU_INT_SMC2, + .end = SIU_INT_SMC2, + .flags = IORESOURCE_IRQ, + }, + }, + }, + [MPC85xx_CPM_USB] = { + .name = "fsl-cpm-usb", + .id = 2, + .num_resources = 2, + .resource = (struct resource[]) { + { + .start = 0x91B60, + .end = 0x91B7F, + .flags = IORESOURCE_MEM, + }, + { + .start = SIU_INT_USB, + .end = SIU_INT_USB, + .flags = IORESOURCE_IRQ, + }, + }, + }, +#endif /* CONFIG_CPM2 */ +}; + +static int __init mach_mpc85xx_fixup(struct platform_device *pdev) +{ + ppc_sys_fixup_mem_resource(pdev, CCSRBAR); + return 0; +} + +static int __init mach_mpc85xx_init(void) +{ + ppc_sys_device_fixup = mach_mpc85xx_fixup; + return 0; +} + +postcore_initcall(mach_mpc85xx_init); diff --git a/arch/ppc/syslib/mpc85xx_sys.c b/arch/ppc/syslib/mpc85xx_sys.c new file mode 100644 index 00000000000..d806a92a940 --- /dev/null +++ b/arch/ppc/syslib/mpc85xx_sys.c @@ -0,0 +1,118 @@ +/* + * arch/ppc/platforms/85xx/mpc85xx_sys.c + * + * MPC85xx System descriptions + * + * Maintainer: Kumar Gala + * + * Copyright 2005 Freescale Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include + +struct ppc_sys_spec *cur_ppc_sys_spec; +struct ppc_sys_spec ppc_sys_specs[] = { + { + .ppc_sys_name = "8540", + .mask = 0xFFFF0000, + .value = 0x80300000, + .num_devices = 10, + .device_list = (enum ppc_sys_devices[]) + { + MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_FEC, MPC85xx_IIC1, + MPC85xx_DMA0, MPC85xx_DMA1, MPC85xx_DMA2, MPC85xx_DMA3, + MPC85xx_PERFMON, MPC85xx_DUART, + }, + }, + { + .ppc_sys_name = "8560", + .mask = 0xFFFF0000, + .value = 0x80700000, + .num_devices = 19, + .device_list = (enum ppc_sys_devices[]) + { + MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_IIC1, + MPC85xx_DMA0, MPC85xx_DMA1, MPC85xx_DMA2, MPC85xx_DMA3, + MPC85xx_PERFMON, + MPC85xx_CPM_SPI, MPC85xx_CPM_I2C, MPC85xx_CPM_SCC1, + MPC85xx_CPM_SCC2, MPC85xx_CPM_SCC3, MPC85xx_CPM_SCC4, + MPC85xx_CPM_FCC1, MPC85xx_CPM_FCC2, MPC85xx_CPM_FCC3, + MPC85xx_CPM_MCC1, MPC85xx_CPM_MCC2, + }, + }, + { + .ppc_sys_name = "8541", + .mask = 0xFFFF0000, + .value = 0x80720000, + .num_devices = 13, + .device_list = (enum ppc_sys_devices[]) + { + MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_IIC1, + MPC85xx_DMA0, MPC85xx_DMA1, MPC85xx_DMA2, MPC85xx_DMA3, + MPC85xx_PERFMON, MPC85xx_DUART, + MPC85xx_CPM_SPI, MPC85xx_CPM_I2C, + MPC85xx_CPM_FCC1, MPC85xx_CPM_FCC2, + }, + }, + { + .ppc_sys_name = "8541E", + .mask = 0xFFFF0000, + .value = 0x807A0000, + .num_devices = 14, + .device_list = (enum ppc_sys_devices[]) + { + MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_IIC1, + MPC85xx_DMA0, MPC85xx_DMA1, MPC85xx_DMA2, MPC85xx_DMA3, + MPC85xx_PERFMON, MPC85xx_DUART, MPC85xx_SEC2, + MPC85xx_CPM_SPI, MPC85xx_CPM_I2C, + MPC85xx_CPM_FCC1, MPC85xx_CPM_FCC2, + }, + }, + { + .ppc_sys_name = "8555", + .mask = 0xFFFF0000, + .value = 0x80710000, + .num_devices = 19, + .device_list = (enum ppc_sys_devices[]) + { + MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_IIC1, + MPC85xx_DMA0, MPC85xx_DMA1, MPC85xx_DMA2, MPC85xx_DMA3, + MPC85xx_PERFMON, MPC85xx_DUART, + MPC85xx_CPM_SPI, MPC85xx_CPM_I2C, MPC85xx_CPM_SCC1, + MPC85xx_CPM_SCC2, MPC85xx_CPM_SCC3, + MPC85xx_CPM_FCC1, MPC85xx_CPM_FCC2, + MPC85xx_CPM_SMC1, MPC85xx_CPM_SMC2, + MPC85xx_CPM_USB, + }, + }, + { + .ppc_sys_name = "8555E", + .mask = 0xFFFF0000, + .value = 0x80790000, + .num_devices = 20, + .device_list = (enum ppc_sys_devices[]) + { + MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_IIC1, + MPC85xx_DMA0, MPC85xx_DMA1, MPC85xx_DMA2, MPC85xx_DMA3, + MPC85xx_PERFMON, MPC85xx_DUART, MPC85xx_SEC2, + MPC85xx_CPM_SPI, MPC85xx_CPM_I2C, MPC85xx_CPM_SCC1, + MPC85xx_CPM_SCC2, MPC85xx_CPM_SCC3, + MPC85xx_CPM_FCC1, MPC85xx_CPM_FCC2, + MPC85xx_CPM_SMC1, MPC85xx_CPM_SMC2, + MPC85xx_CPM_USB, + }, + }, + { /* default match */ + .ppc_sys_name = "", + .mask = 0x00000000, + .value = 0x00000000, + }, +}; diff --git a/arch/ppc/syslib/mv64360_pic.c b/arch/ppc/syslib/mv64360_pic.c new file mode 100644 index 00000000000..74d8996418e --- /dev/null +++ b/arch/ppc/syslib/mv64360_pic.c @@ -0,0 +1,426 @@ +/* + * arch/ppc/kernel/mv64360_pic.c + * + * Interrupt controller support for Marvell's MV64360. + * + * Author: Rabeeh Khoury + * Based on MV64360 PIC written by + * Chris Zankel + * Mark A. Greer + * + * Copyright 2004 MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/* + * This file contains the specific functions to support the MV64360 + * interrupt controller. + * + * The MV64360 has two main interrupt registers (high and low) that + * summarizes the interrupts generated by the units of the MV64360. + * Each bit is assigned to an interrupt number, where the low register + * are assigned from IRQ0 to IRQ31 and the high cause register + * from IRQ32 to IRQ63 + * The GPP (General Purpose Pins) interrupts are assigned from IRQ64 (GPP0) + * to IRQ95 (GPP31). + * get_irq() returns the lowest interrupt number that is currently asserted. + * + * Note: + * - This driver does not initialize the GPP when used as an interrupt + * input. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef CONFIG_IRQ_ALL_CPUS +#error "The mv64360 does not support distribution of IRQs on all CPUs" +#endif +/* ========================== forward declaration ========================== */ + +static void mv64360_unmask_irq(unsigned int); +static void mv64360_mask_irq(unsigned int); +static irqreturn_t mv64360_cpu_error_int_handler(int, void *, struct pt_regs *); +static irqreturn_t mv64360_sram_error_int_handler(int, void *, + struct pt_regs *); +static irqreturn_t mv64360_pci_error_int_handler(int, void *, struct pt_regs *); + +/* ========================== local declarations =========================== */ + +struct hw_interrupt_type mv64360_pic = { + .typename = " mv64360 ", + .enable = mv64360_unmask_irq, + .disable = mv64360_mask_irq, + .ack = mv64360_mask_irq, + .end = mv64360_unmask_irq, +}; + +#define CPU_INTR_STR "mv64360 cpu interface error" +#define SRAM_INTR_STR "mv64360 internal sram error" +#define PCI0_INTR_STR "mv64360 pci 0 error" +#define PCI1_INTR_STR "mv64360 pci 1 error" + +static struct mv64x60_handle bh; + +u32 mv64360_irq_base = 0; /* MV64360 handles the next 96 IRQs from here */ + +/* mv64360_init_irq() + * + * This function initializes the interrupt controller. It assigns + * all interrupts from IRQ0 to IRQ95 to the mv64360 interrupt controller. + * + * Input Variable(s): + * None. + * + * Outpu. Variable(s): + * None. + * + * Returns: + * void + * + * Note: + * We register all GPP inputs as interrupt source, but disable them. + */ +void __init +mv64360_init_irq(void) +{ + int i; + + if (ppc_md.progress) + ppc_md.progress("mv64360_init_irq: enter", 0x0); + + bh.v_base = mv64x60_get_bridge_vbase(); + + ppc_cached_irq_mask[0] = 0; + ppc_cached_irq_mask[1] = 0x0f000000; /* Enable GPP intrs */ + ppc_cached_irq_mask[2] = 0; + + /* disable all interrupts and clear current interrupts */ + mv64x60_write(&bh, MV64x60_GPP_INTR_CAUSE, 0); + mv64x60_write(&bh, MV64x60_GPP_INTR_MASK, ppc_cached_irq_mask[2]); + mv64x60_write(&bh, MV64360_IC_CPU0_INTR_MASK_LO,ppc_cached_irq_mask[0]); + mv64x60_write(&bh, MV64360_IC_CPU0_INTR_MASK_HI,ppc_cached_irq_mask[1]); + + /* All interrupts are level interrupts */ + for (i = mv64360_irq_base; i < (mv64360_irq_base + 96); i++) { + irq_desc[i].status |= IRQ_LEVEL; + irq_desc[i].handler = &mv64360_pic; + } + + if (ppc_md.progress) + ppc_md.progress("mv64360_init_irq: exit", 0x0); +} + +/* mv64360_get_irq() + * + * This function returns the lowest interrupt number of all interrupts that + * are currently asserted. + * + * Input Variable(s): + * struct pt_regs* not used + * + * Output Variable(s): + * None. + * + * Returns: + * int or -2 (bogus interrupt) + * + */ +int +mv64360_get_irq(struct pt_regs *regs) +{ + int irq; + int irq_gpp; + +#ifdef CONFIG_SMP + /* + * Second CPU gets only doorbell (message) interrupts. + * The doorbell interrupt is BIT28 in the main interrupt low cause reg. + */ + int cpu_nr = smp_processor_id(); + if (cpu_nr == 1) { + if (!(mv64x60_read(&bh, MV64360_IC_MAIN_CAUSE_LO) & + (1 << MV64x60_IRQ_DOORBELL))) + return -1; + return mv64360_irq_base + MV64x60_IRQ_DOORBELL; + } +#endif + + irq = mv64x60_read(&bh, MV64360_IC_MAIN_CAUSE_LO); + irq = __ilog2((irq & 0x3dfffffe) & ppc_cached_irq_mask[0]); + + if (irq == -1) { + irq = mv64x60_read(&bh, MV64360_IC_MAIN_CAUSE_HI); + irq = __ilog2((irq & 0x1f0003f7) & ppc_cached_irq_mask[1]); + + if (irq == -1) + irq = -2; /* bogus interrupt, should never happen */ + else { + if ((irq >= 24) && (irq < MV64x60_IRQ_DOORBELL)) { + irq_gpp = mv64x60_read(&bh, + MV64x60_GPP_INTR_CAUSE); + irq_gpp = __ilog2(irq_gpp & + ppc_cached_irq_mask[2]); + + if (irq_gpp == -1) + irq = -2; + else { + irq = irq_gpp + 64; + mv64x60_write(&bh, + MV64x60_GPP_INTR_CAUSE, + ~(1 << (irq - 64))); + } + } + else + irq += 32; + } + } + + (void)mv64x60_read(&bh, MV64x60_GPP_INTR_CAUSE); + + if (irq < 0) + return (irq); + else + return (mv64360_irq_base + irq); +} + +/* mv64360_unmask_irq() + * + * This function enables an interrupt. + * + * Input Variable(s): + * unsigned int interrupt number (IRQ0...IRQ95). + * + * Output Variable(s): + * None. + * + * Returns: + * void + */ +static void +mv64360_unmask_irq(unsigned int irq) +{ +#ifdef CONFIG_SMP + /* second CPU gets only doorbell interrupts */ + if ((irq - mv64360_irq_base) == MV64x60_IRQ_DOORBELL) { + mv64x60_set_bits(&bh, MV64360_IC_CPU1_INTR_MASK_LO, + (1 << MV64x60_IRQ_DOORBELL)); + return; + } +#endif + irq -= mv64360_irq_base; + + if (irq > 31) { + if (irq > 63) /* unmask GPP irq */ + mv64x60_write(&bh, MV64x60_GPP_INTR_MASK, + ppc_cached_irq_mask[2] |= (1 << (irq - 64))); + else /* mask high interrupt register */ + mv64x60_write(&bh, MV64360_IC_CPU0_INTR_MASK_HI, + ppc_cached_irq_mask[1] |= (1 << (irq - 32))); + } + else /* mask low interrupt register */ + mv64x60_write(&bh, MV64360_IC_CPU0_INTR_MASK_LO, + ppc_cached_irq_mask[0] |= (1 << irq)); + + (void)mv64x60_read(&bh, MV64x60_GPP_INTR_MASK); + return; +} + +/* mv64360_mask_irq() + * + * This function disables the requested interrupt. + * + * Input Variable(s): + * unsigned int interrupt number (IRQ0...IRQ95). + * + * Output Variable(s): + * None. + * + * Returns: + * void + */ +static void +mv64360_mask_irq(unsigned int irq) +{ +#ifdef CONFIG_SMP + if ((irq - mv64360_irq_base) == MV64x60_IRQ_DOORBELL) { + mv64x60_clr_bits(&bh, MV64360_IC_CPU1_INTR_MASK_LO, + (1 << MV64x60_IRQ_DOORBELL)); + return; + } +#endif + irq -= mv64360_irq_base; + + if (irq > 31) { + if (irq > 63) /* mask GPP irq */ + mv64x60_write(&bh, MV64x60_GPP_INTR_MASK, + ppc_cached_irq_mask[2] &= ~(1 << (irq - 64))); + else /* mask high interrupt register */ + mv64x60_write(&bh, MV64360_IC_CPU0_INTR_MASK_HI, + ppc_cached_irq_mask[1] &= ~(1 << (irq - 32))); + } + else /* mask low interrupt register */ + mv64x60_write(&bh, MV64360_IC_CPU0_INTR_MASK_LO, + ppc_cached_irq_mask[0] &= ~(1 << irq)); + + (void)mv64x60_read(&bh, MV64x60_GPP_INTR_MASK); + return; +} + +static irqreturn_t +mv64360_cpu_error_int_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + printk(KERN_ERR "mv64360_cpu_error_int_handler: %s 0x%08x\n", + "Error on CPU interface - Cause regiser", + mv64x60_read(&bh, MV64x60_CPU_ERR_CAUSE)); + printk(KERN_ERR "\tCPU error register dump:\n"); + printk(KERN_ERR "\tAddress low 0x%08x\n", + mv64x60_read(&bh, MV64x60_CPU_ERR_ADDR_LO)); + printk(KERN_ERR "\tAddress high 0x%08x\n", + mv64x60_read(&bh, MV64x60_CPU_ERR_ADDR_HI)); + printk(KERN_ERR "\tData low 0x%08x\n", + mv64x60_read(&bh, MV64x60_CPU_ERR_DATA_LO)); + printk(KERN_ERR "\tData high 0x%08x\n", + mv64x60_read(&bh, MV64x60_CPU_ERR_DATA_HI)); + printk(KERN_ERR "\tParity 0x%08x\n", + mv64x60_read(&bh, MV64x60_CPU_ERR_PARITY)); + mv64x60_write(&bh, MV64x60_CPU_ERR_CAUSE, 0); + return IRQ_HANDLED; +} + +static irqreturn_t +mv64360_sram_error_int_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + printk(KERN_ERR "mv64360_sram_error_int_handler: %s 0x%08x\n", + "Error in internal SRAM - Cause register", + mv64x60_read(&bh, MV64360_SRAM_ERR_CAUSE)); + printk(KERN_ERR "\tSRAM error register dump:\n"); + printk(KERN_ERR "\tAddress Low 0x%08x\n", + mv64x60_read(&bh, MV64360_SRAM_ERR_ADDR_LO)); + printk(KERN_ERR "\tAddress High 0x%08x\n", + mv64x60_read(&bh, MV64360_SRAM_ERR_ADDR_HI)); + printk(KERN_ERR "\tData Low 0x%08x\n", + mv64x60_read(&bh, MV64360_SRAM_ERR_DATA_LO)); + printk(KERN_ERR "\tData High 0x%08x\n", + mv64x60_read(&bh, MV64360_SRAM_ERR_DATA_HI)); + printk(KERN_ERR "\tParity 0x%08x\n", + mv64x60_read(&bh, MV64360_SRAM_ERR_PARITY)); + mv64x60_write(&bh, MV64360_SRAM_ERR_CAUSE, 0); + return IRQ_HANDLED; +} + +static irqreturn_t +mv64360_pci_error_int_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + u32 val; + unsigned int pci_bus = (unsigned int)dev_id; + + if (pci_bus == 0) { /* Error on PCI 0 */ + val = mv64x60_read(&bh, MV64x60_PCI0_ERR_CAUSE); + printk(KERN_ERR "%s: Error in PCI %d Interface\n", + "mv64360_pci_error_int_handler", pci_bus); + printk(KERN_ERR "\tPCI %d error register dump:\n", pci_bus); + printk(KERN_ERR "\tCause register 0x%08x\n", val); + printk(KERN_ERR "\tAddress Low 0x%08x\n", + mv64x60_read(&bh, MV64x60_PCI0_ERR_ADDR_LO)); + printk(KERN_ERR "\tAddress High 0x%08x\n", + mv64x60_read(&bh, MV64x60_PCI0_ERR_ADDR_HI)); + printk(KERN_ERR "\tAttribute 0x%08x\n", + mv64x60_read(&bh, MV64x60_PCI0_ERR_DATA_LO)); + printk(KERN_ERR "\tCommand 0x%08x\n", + mv64x60_read(&bh, MV64x60_PCI0_ERR_CMD)); + mv64x60_write(&bh, MV64x60_PCI0_ERR_CAUSE, ~val); + } + if (pci_bus == 1) { /* Error on PCI 1 */ + val = mv64x60_read(&bh, MV64x60_PCI1_ERR_CAUSE); + printk(KERN_ERR "%s: Error in PCI %d Interface\n", + "mv64360_pci_error_int_handler", pci_bus); + printk(KERN_ERR "\tPCI %d error register dump:\n", pci_bus); + printk(KERN_ERR "\tCause register 0x%08x\n", val); + printk(KERN_ERR "\tAddress Low 0x%08x\n", + mv64x60_read(&bh, MV64x60_PCI1_ERR_ADDR_LO)); + printk(KERN_ERR "\tAddress High 0x%08x\n", + mv64x60_read(&bh, MV64x60_PCI1_ERR_ADDR_HI)); + printk(KERN_ERR "\tAttribute 0x%08x\n", + mv64x60_read(&bh, MV64x60_PCI1_ERR_DATA_LO)); + printk(KERN_ERR "\tCommand 0x%08x\n", + mv64x60_read(&bh, MV64x60_PCI1_ERR_CMD)); + mv64x60_write(&bh, MV64x60_PCI1_ERR_CAUSE, ~val); + } + return IRQ_HANDLED; +} + +static int __init +mv64360_register_hdlrs(void) +{ + u32 mask; + int rc; + + /* Clear old errors and register CPU interface error intr handler */ + mv64x60_write(&bh, MV64x60_CPU_ERR_CAUSE, 0); + if ((rc = request_irq(MV64x60_IRQ_CPU_ERR + mv64360_irq_base, + mv64360_cpu_error_int_handler, SA_INTERRUPT, CPU_INTR_STR, 0))) + printk(KERN_WARNING "Can't register cpu error handler: %d", rc); + + mv64x60_write(&bh, MV64x60_CPU_ERR_MASK, 0); + mv64x60_write(&bh, MV64x60_CPU_ERR_MASK, 0x000000ff); + + /* Clear old errors and register internal SRAM error intr handler */ + mv64x60_write(&bh, MV64360_SRAM_ERR_CAUSE, 0); + if ((rc = request_irq(MV64360_IRQ_SRAM_PAR_ERR + mv64360_irq_base, + mv64360_sram_error_int_handler,SA_INTERRUPT,SRAM_INTR_STR, 0))) + printk(KERN_WARNING "Can't register SRAM error handler: %d",rc); + + /* + * Bit 0 reserved on 64360 and erratum FEr PCI-#11 (PCI internal + * data parity error set incorrectly) on rev 0 & 1 of 64460 requires + * bit 0 to be cleared. + */ + mask = 0x00a50c24; + + if ((mv64x60_get_bridge_type() == MV64x60_TYPE_MV64460) && + (mv64x60_get_bridge_rev() > 1)) + mask |= 0x1; /* enable DPErr on 64460 */ + + /* Clear old errors and register PCI 0 error intr handler */ + mv64x60_write(&bh, MV64x60_PCI0_ERR_CAUSE, 0); + if ((rc = request_irq(MV64360_IRQ_PCI0 + mv64360_irq_base, + mv64360_pci_error_int_handler, + SA_INTERRUPT, PCI0_INTR_STR, (void *)0))) + printk(KERN_WARNING "Can't register pci 0 error handler: %d", + rc); + + mv64x60_write(&bh, MV64x60_PCI0_ERR_MASK, 0); + mv64x60_write(&bh, MV64x60_PCI0_ERR_MASK, mask); + + /* Clear old errors and register PCI 1 error intr handler */ + mv64x60_write(&bh, MV64x60_PCI1_ERR_CAUSE, 0); + if ((rc = request_irq(MV64360_IRQ_PCI1 + mv64360_irq_base, + mv64360_pci_error_int_handler, + SA_INTERRUPT, PCI1_INTR_STR, (void *)1))) + printk(KERN_WARNING "Can't register pci 1 error handler: %d", + rc); + + mv64x60_write(&bh, MV64x60_PCI1_ERR_MASK, 0); + mv64x60_write(&bh, MV64x60_PCI1_ERR_MASK, mask); + + return 0; +} + +arch_initcall(mv64360_register_hdlrs); diff --git a/arch/ppc/syslib/mv64x60.c b/arch/ppc/syslib/mv64x60.c new file mode 100644 index 00000000000..7b241e7876b --- /dev/null +++ b/arch/ppc/syslib/mv64x60.c @@ -0,0 +1,2392 @@ +/* + * arch/ppc/syslib/mv64x60.c + * + * Common routines for the Marvell/Galileo Discovery line of host bridges + * (gt64260, mv64360, mv64460, ...). + * + * Author: Mark A. Greer + * + * 2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +u8 mv64x60_pci_exclude_bridge = 1; +spinlock_t mv64x60_lock = SPIN_LOCK_UNLOCKED; + +static phys_addr_t mv64x60_bridge_pbase = 0; +static void *mv64x60_bridge_vbase = 0; +static u32 mv64x60_bridge_type = MV64x60_TYPE_INVALID; +static u32 mv64x60_bridge_rev = 0; + +static u32 gt64260_translate_size(u32 base, u32 size, u32 num_bits); +static u32 gt64260_untranslate_size(u32 base, u32 size, u32 num_bits); +static void gt64260_set_pci2mem_window(struct pci_controller *hose, u32 bus, + u32 window, u32 base); +static void gt64260_set_pci2regs_window(struct mv64x60_handle *bh, + struct pci_controller *hose, u32 bus, u32 base); +static u32 gt64260_is_enabled_32bit(struct mv64x60_handle *bh, u32 window); +static void gt64260_enable_window_32bit(struct mv64x60_handle *bh, u32 window); +static void gt64260_disable_window_32bit(struct mv64x60_handle *bh, u32 window); +static void gt64260_enable_window_64bit(struct mv64x60_handle *bh, u32 window); +static void gt64260_disable_window_64bit(struct mv64x60_handle *bh, u32 window); +static void gt64260_disable_all_windows(struct mv64x60_handle *bh, + struct mv64x60_setup_info *si); +static void gt64260a_chip_specific_init(struct mv64x60_handle *bh, + struct mv64x60_setup_info *si); +static void gt64260b_chip_specific_init(struct mv64x60_handle *bh, + struct mv64x60_setup_info *si); + +static u32 mv64360_translate_size(u32 base, u32 size, u32 num_bits); +static u32 mv64360_untranslate_size(u32 base, u32 size, u32 num_bits); +static void mv64360_set_pci2mem_window(struct pci_controller *hose, u32 bus, + u32 window, u32 base); +static void mv64360_set_pci2regs_window(struct mv64x60_handle *bh, + struct pci_controller *hose, u32 bus, u32 base); +static u32 mv64360_is_enabled_32bit(struct mv64x60_handle *bh, u32 window); +static void mv64360_enable_window_32bit(struct mv64x60_handle *bh, u32 window); +static void mv64360_disable_window_32bit(struct mv64x60_handle *bh, u32 window); +static void mv64360_enable_window_64bit(struct mv64x60_handle *bh, u32 window); +static void mv64360_disable_window_64bit(struct mv64x60_handle *bh, u32 window); +static void mv64360_disable_all_windows(struct mv64x60_handle *bh, + struct mv64x60_setup_info *si); +static void mv64360_config_io2mem_windows(struct mv64x60_handle *bh, + struct mv64x60_setup_info *si, + u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2]); +static void mv64360_set_mpsc2regs_window(struct mv64x60_handle *bh, u32 base); +static void mv64360_chip_specific_init(struct mv64x60_handle *bh, + struct mv64x60_setup_info *si); +static void mv64460_chip_specific_init(struct mv64x60_handle *bh, + struct mv64x60_setup_info *si); + + +/* + * Define tables that have the chip-specific info for each type of + * Marvell bridge chip. + */ +static struct mv64x60_chip_info gt64260a_ci __initdata = { /* GT64260A */ + .translate_size = gt64260_translate_size, + .untranslate_size = gt64260_untranslate_size, + .set_pci2mem_window = gt64260_set_pci2mem_window, + .set_pci2regs_window = gt64260_set_pci2regs_window, + .is_enabled_32bit = gt64260_is_enabled_32bit, + .enable_window_32bit = gt64260_enable_window_32bit, + .disable_window_32bit = gt64260_disable_window_32bit, + .enable_window_64bit = gt64260_enable_window_64bit, + .disable_window_64bit = gt64260_disable_window_64bit, + .disable_all_windows = gt64260_disable_all_windows, + .chip_specific_init = gt64260a_chip_specific_init, + .window_tab_32bit = gt64260_32bit_windows, + .window_tab_64bit = gt64260_64bit_windows, +}; + +static struct mv64x60_chip_info gt64260b_ci __initdata = { /* GT64260B */ + .translate_size = gt64260_translate_size, + .untranslate_size = gt64260_untranslate_size, + .set_pci2mem_window = gt64260_set_pci2mem_window, + .set_pci2regs_window = gt64260_set_pci2regs_window, + .is_enabled_32bit = gt64260_is_enabled_32bit, + .enable_window_32bit = gt64260_enable_window_32bit, + .disable_window_32bit = gt64260_disable_window_32bit, + .enable_window_64bit = gt64260_enable_window_64bit, + .disable_window_64bit = gt64260_disable_window_64bit, + .disable_all_windows = gt64260_disable_all_windows, + .chip_specific_init = gt64260b_chip_specific_init, + .window_tab_32bit = gt64260_32bit_windows, + .window_tab_64bit = gt64260_64bit_windows, +}; + +static struct mv64x60_chip_info mv64360_ci __initdata = { /* MV64360 */ + .translate_size = mv64360_translate_size, + .untranslate_size = mv64360_untranslate_size, + .set_pci2mem_window = mv64360_set_pci2mem_window, + .set_pci2regs_window = mv64360_set_pci2regs_window, + .is_enabled_32bit = mv64360_is_enabled_32bit, + .enable_window_32bit = mv64360_enable_window_32bit, + .disable_window_32bit = mv64360_disable_window_32bit, + .enable_window_64bit = mv64360_enable_window_64bit, + .disable_window_64bit = mv64360_disable_window_64bit, + .disable_all_windows = mv64360_disable_all_windows, + .config_io2mem_windows = mv64360_config_io2mem_windows, + .set_mpsc2regs_window = mv64360_set_mpsc2regs_window, + .chip_specific_init = mv64360_chip_specific_init, + .window_tab_32bit = mv64360_32bit_windows, + .window_tab_64bit = mv64360_64bit_windows, +}; + +static struct mv64x60_chip_info mv64460_ci __initdata = { /* MV64460 */ + .translate_size = mv64360_translate_size, + .untranslate_size = mv64360_untranslate_size, + .set_pci2mem_window = mv64360_set_pci2mem_window, + .set_pci2regs_window = mv64360_set_pci2regs_window, + .is_enabled_32bit = mv64360_is_enabled_32bit, + .enable_window_32bit = mv64360_enable_window_32bit, + .disable_window_32bit = mv64360_disable_window_32bit, + .enable_window_64bit = mv64360_enable_window_64bit, + .disable_window_64bit = mv64360_disable_window_64bit, + .disable_all_windows = mv64360_disable_all_windows, + .config_io2mem_windows = mv64360_config_io2mem_windows, + .set_mpsc2regs_window = mv64360_set_mpsc2regs_window, + .chip_specific_init = mv64460_chip_specific_init, + .window_tab_32bit = mv64360_32bit_windows, + .window_tab_64bit = mv64360_64bit_windows, +}; + +/* + ***************************************************************************** + * + * Platform Device Definitions + * + ***************************************************************************** + */ +#ifdef CONFIG_SERIAL_MPSC +static struct mpsc_shared_pdata mv64x60_mpsc_shared_pdata = { + .mrr_val = 0x3ffffe38, + .rcrr_val = 0, + .tcrr_val = 0, + .intr_cause_val = 0, + .intr_mask_val = 0, +}; + +static struct resource mv64x60_mpsc_shared_resources[] = { + /* Do not change the order of the IORESOURCE_MEM resources */ + [0] = { + .name = "mpsc routing base", + .start = MV64x60_MPSC_ROUTING_OFFSET, + .end = MV64x60_MPSC_ROUTING_OFFSET + + MPSC_ROUTING_REG_BLOCK_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "sdma intr base", + .start = MV64x60_SDMA_INTR_OFFSET, + .end = MV64x60_SDMA_INTR_OFFSET + + MPSC_SDMA_INTR_REG_BLOCK_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device mpsc_shared_device = { /* Shared device */ + .name = MPSC_SHARED_NAME, + .id = 0, + .num_resources = ARRAY_SIZE(mv64x60_mpsc_shared_resources), + .resource = mv64x60_mpsc_shared_resources, + .dev = { + .platform_data = &mv64x60_mpsc_shared_pdata, + }, +}; + +static struct mpsc_pdata mv64x60_mpsc0_pdata = { + .mirror_regs = 0, + .cache_mgmt = 0, + .max_idle = 0, + .default_baud = 9600, + .default_bits = 8, + .default_parity = 'n', + .default_flow = 'n', + .chr_1_val = 0x00000000, + .chr_2_val = 0x00000000, + .chr_10_val = 0x00000003, + .mpcr_val = 0, + .bcr_val = 0, + .brg_can_tune = 0, + .brg_clk_src = 8, /* Default to TCLK */ + .brg_clk_freq = 100000000, /* Default to 100 MHz */ +}; + +static struct resource mv64x60_mpsc0_resources[] = { + /* Do not change the order of the IORESOURCE_MEM resources */ + [0] = { + .name = "mpsc 0 base", + .start = MV64x60_MPSC_0_OFFSET, + .end = MV64x60_MPSC_0_OFFSET + MPSC_REG_BLOCK_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "sdma 0 base", + .start = MV64x60_SDMA_0_OFFSET, + .end = MV64x60_SDMA_0_OFFSET + MPSC_SDMA_REG_BLOCK_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [2] = { + .name = "brg 0 base", + .start = MV64x60_BRG_0_OFFSET, + .end = MV64x60_BRG_0_OFFSET + MPSC_BRG_REG_BLOCK_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [3] = { + .name = "sdma 0 irq", + .start = MV64x60_IRQ_SDMA_0, + .end = MV64x60_IRQ_SDMA_0, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device mpsc0_device = { + .name = MPSC_CTLR_NAME, + .id = 0, + .num_resources = ARRAY_SIZE(mv64x60_mpsc0_resources), + .resource = mv64x60_mpsc0_resources, + .dev = { + .platform_data = &mv64x60_mpsc0_pdata, + }, +}; + +static struct mpsc_pdata mv64x60_mpsc1_pdata = { + .mirror_regs = 0, + .cache_mgmt = 0, + .max_idle = 0, + .default_baud = 9600, + .default_bits = 8, + .default_parity = 'n', + .default_flow = 'n', + .chr_1_val = 0x00000000, + .chr_1_val = 0x00000000, + .chr_2_val = 0x00000000, + .chr_10_val = 0x00000003, + .mpcr_val = 0, + .bcr_val = 0, + .brg_can_tune = 0, + .brg_clk_src = 8, /* Default to TCLK */ + .brg_clk_freq = 100000000, /* Default to 100 MHz */ +}; + +static struct resource mv64x60_mpsc1_resources[] = { + /* Do not change the order of the IORESOURCE_MEM resources */ + [0] = { + .name = "mpsc 1 base", + .start = MV64x60_MPSC_1_OFFSET, + .end = MV64x60_MPSC_1_OFFSET + MPSC_REG_BLOCK_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "sdma 1 base", + .start = MV64x60_SDMA_1_OFFSET, + .end = MV64x60_SDMA_1_OFFSET + MPSC_SDMA_REG_BLOCK_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [2] = { + .name = "brg 1 base", + .start = MV64x60_BRG_1_OFFSET, + .end = MV64x60_BRG_1_OFFSET + MPSC_BRG_REG_BLOCK_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [3] = { + .name = "sdma 1 irq", + .start = MV64360_IRQ_SDMA_1, + .end = MV64360_IRQ_SDMA_1, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device mpsc1_device = { + .name = MPSC_CTLR_NAME, + .id = 1, + .num_resources = ARRAY_SIZE(mv64x60_mpsc1_resources), + .resource = mv64x60_mpsc1_resources, + .dev = { + .platform_data = &mv64x60_mpsc1_pdata, + }, +}; +#endif + +#ifdef CONFIG_MV643XX_ETH +static struct resource mv64x60_eth_shared_resources[] = { + [0] = { + .name = "ethernet shared base", + .start = MV643XX_ETH_SHARED_REGS, + .end = MV643XX_ETH_SHARED_REGS + + MV643XX_ETH_SHARED_REGS_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device mv64x60_eth_shared_device = { + .name = MV643XX_ETH_SHARED_NAME, + .id = 0, + .num_resources = ARRAY_SIZE(mv64x60_eth_shared_resources), + .resource = mv64x60_eth_shared_resources, +}; + +#ifdef CONFIG_MV643XX_ETH_0 +static struct resource mv64x60_eth0_resources[] = { + [0] = { + .name = "eth0 irq", + .start = MV64x60_IRQ_ETH_0, + .end = MV64x60_IRQ_ETH_0, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mv643xx_eth_platform_data eth0_pd; + +static struct platform_device eth0_device = { + .name = MV643XX_ETH_NAME, + .id = 0, + .num_resources = ARRAY_SIZE(mv64x60_eth0_resources), + .resource = mv64x60_eth0_resources, + .dev = { + .platform_data = ð0_pd, + }, +}; +#endif + +#ifdef CONFIG_MV643XX_ETH_1 +static struct resource mv64x60_eth1_resources[] = { + [0] = { + .name = "eth1 irq", + .start = MV64x60_IRQ_ETH_1, + .end = MV64x60_IRQ_ETH_1, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mv643xx_eth_platform_data eth1_pd; + +static struct platform_device eth1_device = { + .name = MV643XX_ETH_NAME, + .id = 1, + .num_resources = ARRAY_SIZE(mv64x60_eth1_resources), + .resource = mv64x60_eth1_resources, + .dev = { + .platform_data = ð1_pd, + }, +}; +#endif + +#ifdef CONFIG_MV643XX_ETH_2 +static struct resource mv64x60_eth2_resources[] = { + [0] = { + .name = "eth2 irq", + .start = MV64x60_IRQ_ETH_2, + .end = MV64x60_IRQ_ETH_2, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mv643xx_eth_platform_data eth2_pd; + +static struct platform_device eth2_device = { + .name = MV643XX_ETH_NAME, + .id = 2, + .num_resources = ARRAY_SIZE(mv64x60_eth2_resources), + .resource = mv64x60_eth2_resources, + .dev = { + .platform_data = ð2_pd, + }, +}; +#endif +#endif + +#ifdef CONFIG_I2C_MV64XXX +static struct mv64xxx_i2c_pdata mv64xxx_i2c_pdata = { + .freq_m = 8, + .freq_n = 3, + .timeout = 1000, /* Default timeout of 1 second */ + .retries = 1, +}; + +static struct resource mv64xxx_i2c_resources[] = { + /* Do not change the order of the IORESOURCE_MEM resources */ + [0] = { + .name = "mv64xxx i2c base", + .start = MV64XXX_I2C_OFFSET, + .end = MV64XXX_I2C_OFFSET + MV64XXX_I2C_REG_BLOCK_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "mv64xxx i2c irq", + .start = MV64x60_IRQ_I2C, + .end = MV64x60_IRQ_I2C, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device i2c_device = { + .name = MV64XXX_I2C_CTLR_NAME, + .id = 0, + .num_resources = ARRAY_SIZE(mv64xxx_i2c_resources), + .resource = mv64xxx_i2c_resources, + .dev = { + .platform_data = &mv64xxx_i2c_pdata, + }, +}; +#endif + +static struct platform_device *mv64x60_pd_devs[] __initdata = { +#ifdef CONFIG_SERIAL_MPSC + &mpsc_shared_device, + &mpsc0_device, + &mpsc1_device, +#endif +#ifdef CONFIG_MV643XX_ETH + &mv64x60_eth_shared_device, +#endif +#ifdef CONFIG_MV643XX_ETH_0 + ð0_device, +#endif +#ifdef CONFIG_MV643XX_ETH_1 + ð1_device, +#endif +#ifdef CONFIG_MV643XX_ETH_2 + ð2_device, +#endif +#ifdef CONFIG_I2C_MV64XXX + &i2c_device, +#endif +}; + +/* + ***************************************************************************** + * + * Bridge Initialization Routines + * + ***************************************************************************** + */ +/* + * mv64x60_init() + * + * Initialze the bridge based on setting passed in via 'si'. The bridge + * handle, 'bh', will be set so that it can be used to make subsequent + * calls to routines in this file. + */ +int __init +mv64x60_init(struct mv64x60_handle *bh, struct mv64x60_setup_info *si) +{ + u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2]; + + if (ppc_md.progress) + ppc_md.progress("mv64x60 initialization", 0x0); + + spin_lock_init(&mv64x60_lock); + mv64x60_early_init(bh, si); + + if (mv64x60_get_type(bh) || mv64x60_setup_for_chip(bh)) { + iounmap(bh->v_base); + bh->v_base = 0; + if (ppc_md.progress) + ppc_md.progress("mv64x60_init: Can't determine chip",0); + return -1; + } + + bh->ci->disable_all_windows(bh, si); + mv64x60_get_mem_windows(bh, mem_windows); + mv64x60_config_cpu2mem_windows(bh, si, mem_windows); + + if (bh->ci->config_io2mem_windows) + bh->ci->config_io2mem_windows(bh, si, mem_windows); + if (bh->ci->set_mpsc2regs_window) + bh->ci->set_mpsc2regs_window(bh, si->phys_reg_base); + + if (si->pci_1.enable_bus) { + bh->io_base_b = (u32)ioremap(si->pci_1.pci_io.cpu_base, + si->pci_1.pci_io.size); + isa_io_base = bh->io_base_b; + } + + if (si->pci_0.enable_bus) { + bh->io_base_a = (u32)ioremap(si->pci_0.pci_io.cpu_base, + si->pci_0.pci_io.size); + isa_io_base = bh->io_base_a; + + mv64x60_alloc_hose(bh, MV64x60_PCI0_CONFIG_ADDR, + MV64x60_PCI0_CONFIG_DATA, &bh->hose_a); + mv64x60_config_resources(bh->hose_a, &si->pci_0, bh->io_base_a); + mv64x60_config_pci_params(bh->hose_a, &si->pci_0); + + mv64x60_config_cpu2pci_windows(bh, &si->pci_0, 0); + mv64x60_config_pci2mem_windows(bh, bh->hose_a, &si->pci_0, 0, + mem_windows); + bh->ci->set_pci2regs_window(bh, bh->hose_a, 0, + si->phys_reg_base); + } + + if (si->pci_1.enable_bus) { + mv64x60_alloc_hose(bh, MV64x60_PCI1_CONFIG_ADDR, + MV64x60_PCI1_CONFIG_DATA, &bh->hose_b); + mv64x60_config_resources(bh->hose_b, &si->pci_1, bh->io_base_b); + mv64x60_config_pci_params(bh->hose_b, &si->pci_1); + + mv64x60_config_cpu2pci_windows(bh, &si->pci_1, 1); + mv64x60_config_pci2mem_windows(bh, bh->hose_b, &si->pci_1, 1, + mem_windows); + bh->ci->set_pci2regs_window(bh, bh->hose_b, 1, + si->phys_reg_base); + } + + bh->ci->chip_specific_init(bh, si); + mv64x60_pd_fixup(bh, mv64x60_pd_devs, ARRAY_SIZE(mv64x60_pd_devs)); + + return 0; +} + +/* + * mv64x60_early_init() + * + * Do some bridge work that must take place before we start messing with + * the bridge for real. + */ +void __init +mv64x60_early_init(struct mv64x60_handle *bh, struct mv64x60_setup_info *si) +{ + struct pci_controller hose_a, hose_b; + + memset(bh, 0, sizeof(*bh)); + + bh->p_base = si->phys_reg_base; + bh->v_base = ioremap(bh->p_base, MV64x60_INTERNAL_SPACE_SIZE); + + mv64x60_bridge_pbase = bh->p_base; + mv64x60_bridge_vbase = bh->v_base; + + /* Assuming pci mode [reserved] bits 4:5 on 64260 are 0 */ + bh->pci_mode_a = mv64x60_read(bh, MV64x60_PCI0_MODE) & + MV64x60_PCIMODE_MASK; + bh->pci_mode_b = mv64x60_read(bh, MV64x60_PCI1_MODE) & + MV64x60_PCIMODE_MASK; + + /* Need temporary hose structs to call mv64x60_set_bus() */ + memset(&hose_a, 0, sizeof(hose_a)); + memset(&hose_b, 0, sizeof(hose_b)); + setup_indirect_pci_nomap(&hose_a, bh->v_base + MV64x60_PCI0_CONFIG_ADDR, + bh->v_base + MV64x60_PCI0_CONFIG_DATA); + setup_indirect_pci_nomap(&hose_b, bh->v_base + MV64x60_PCI1_CONFIG_ADDR, + bh->v_base + MV64x60_PCI1_CONFIG_DATA); + bh->hose_a = &hose_a; + bh->hose_b = &hose_b; + + mv64x60_set_bus(bh, 0, 0); + mv64x60_set_bus(bh, 1, 0); + + bh->hose_a = NULL; + bh->hose_b = NULL; + + /* Clear bit 0 of PCI addr decode control so PCI->CPU remap 1:1 */ + mv64x60_clr_bits(bh, MV64x60_PCI0_PCI_DECODE_CNTL, 0x00000001); + mv64x60_clr_bits(bh, MV64x60_PCI1_PCI_DECODE_CNTL, 0x00000001); + + /* Bit 12 MUST be 0; set bit 27--don't auto-update cpu remap regs */ + mv64x60_clr_bits(bh, MV64x60_CPU_CONFIG, (1<<12)); + mv64x60_set_bits(bh, MV64x60_CPU_CONFIG, (1<<27)); + + mv64x60_set_bits(bh, MV64x60_PCI0_TO_RETRY, 0xffff); + mv64x60_set_bits(bh, MV64x60_PCI1_TO_RETRY, 0xffff); + + return; +} + +/* + ***************************************************************************** + * + * Window Config Routines + * + ***************************************************************************** + */ +/* + * mv64x60_get_32bit_window() + * + * Determine the base address and size of a 32-bit window on the bridge. + */ +void __init +mv64x60_get_32bit_window(struct mv64x60_handle *bh, u32 window, + u32 *base, u32 *size) +{ + u32 val, base_reg, size_reg, base_bits, size_bits; + u32 (*get_from_field)(u32 val, u32 num_bits); + + base_reg = bh->ci->window_tab_32bit[window].base_reg; + + if (base_reg != 0) { + size_reg = bh->ci->window_tab_32bit[window].size_reg; + base_bits = bh->ci->window_tab_32bit[window].base_bits; + size_bits = bh->ci->window_tab_32bit[window].size_bits; + get_from_field= bh->ci->window_tab_32bit[window].get_from_field; + + val = mv64x60_read(bh, base_reg); + *base = get_from_field(val, base_bits); + + if (size_reg != 0) { + val = mv64x60_read(bh, size_reg); + val = get_from_field(val, size_bits); + *size = bh->ci->untranslate_size(*base, val, size_bits); + } + else + *size = 0; + } + else { + *base = 0; + *size = 0; + } + + pr_debug("get 32bit window: %d, base: 0x%x, size: 0x%x\n", + window, *base, *size); + + return; +} + +/* + * mv64x60_set_32bit_window() + * + * Set the base address and size of a 32-bit window on the bridge. + */ +void __init +mv64x60_set_32bit_window(struct mv64x60_handle *bh, u32 window, + u32 base, u32 size, u32 other_bits) +{ + u32 val, base_reg, size_reg, base_bits, size_bits; + u32 (*map_to_field)(u32 val, u32 num_bits); + + pr_debug("set 32bit window: %d, base: 0x%x, size: 0x%x, other: 0x%x\n", + window, base, size, other_bits); + + base_reg = bh->ci->window_tab_32bit[window].base_reg; + + if (base_reg != 0) { + size_reg = bh->ci->window_tab_32bit[window].size_reg; + base_bits = bh->ci->window_tab_32bit[window].base_bits; + size_bits = bh->ci->window_tab_32bit[window].size_bits; + map_to_field = bh->ci->window_tab_32bit[window].map_to_field; + + val = map_to_field(base, base_bits) | other_bits; + mv64x60_write(bh, base_reg, val); + + if (size_reg != 0) { + val = bh->ci->translate_size(base, size, size_bits); + val = map_to_field(val, size_bits); + mv64x60_write(bh, size_reg, val); + } + + (void)mv64x60_read(bh, base_reg); /* Flush FIFO */ + } + + return; +} + +/* + * mv64x60_get_64bit_window() + * + * Determine the base address and size of a 64-bit window on the bridge. + */ +void __init +mv64x60_get_64bit_window(struct mv64x60_handle *bh, u32 window, + u32 *base_hi, u32 *base_lo, u32 *size) +{ + u32 val, base_lo_reg, size_reg, base_lo_bits, size_bits; + u32 (*get_from_field)(u32 val, u32 num_bits); + + base_lo_reg = bh->ci->window_tab_64bit[window].base_lo_reg; + + if (base_lo_reg != 0) { + size_reg = bh->ci->window_tab_64bit[window].size_reg; + base_lo_bits = bh->ci->window_tab_64bit[window].base_lo_bits; + size_bits = bh->ci->window_tab_64bit[window].size_bits; + get_from_field= bh->ci->window_tab_64bit[window].get_from_field; + + *base_hi = mv64x60_read(bh, + bh->ci->window_tab_64bit[window].base_hi_reg); + + val = mv64x60_read(bh, base_lo_reg); + *base_lo = get_from_field(val, base_lo_bits); + + if (size_reg != 0) { + val = mv64x60_read(bh, size_reg); + val = get_from_field(val, size_bits); + *size = bh->ci->untranslate_size(*base_lo, val, + size_bits); + } + else + *size = 0; + } + else { + *base_hi = 0; + *base_lo = 0; + *size = 0; + } + + pr_debug("get 64bit window: %d, base hi: 0x%x, base lo: 0x%x, " + "size: 0x%x\n", window, *base_hi, *base_lo, *size); + + return; +} + +/* + * mv64x60_set_64bit_window() + * + * Set the base address and size of a 64-bit window on the bridge. + */ +void __init +mv64x60_set_64bit_window(struct mv64x60_handle *bh, u32 window, + u32 base_hi, u32 base_lo, u32 size, u32 other_bits) +{ + u32 val, base_lo_reg, size_reg, base_lo_bits, size_bits; + u32 (*map_to_field)(u32 val, u32 num_bits); + + pr_debug("set 64bit window: %d, base hi: 0x%x, base lo: 0x%x, " + "size: 0x%x, other: 0x%x\n", + window, base_hi, base_lo, size, other_bits); + + base_lo_reg = bh->ci->window_tab_64bit[window].base_lo_reg; + + if (base_lo_reg != 0) { + size_reg = bh->ci->window_tab_64bit[window].size_reg; + base_lo_bits = bh->ci->window_tab_64bit[window].base_lo_bits; + size_bits = bh->ci->window_tab_64bit[window].size_bits; + map_to_field = bh->ci->window_tab_64bit[window].map_to_field; + + mv64x60_write(bh, bh->ci->window_tab_64bit[window].base_hi_reg, + base_hi); + + val = map_to_field(base_lo, base_lo_bits) | other_bits; + mv64x60_write(bh, base_lo_reg, val); + + if (size_reg != 0) { + val = bh->ci->translate_size(base_lo, size, size_bits); + val = map_to_field(val, size_bits); + mv64x60_write(bh, size_reg, val); + } + + (void)mv64x60_read(bh, base_lo_reg); /* Flush FIFO */ + } + + return; +} + +/* + * mv64x60_mask() + * + * Take the high-order 'num_bits' of 'val' & mask off low bits. + */ +u32 __init +mv64x60_mask(u32 val, u32 num_bits) +{ + return val & (0xffffffff << (32 - num_bits)); +} + +/* + * mv64x60_shift_left() + * + * Take the low-order 'num_bits' of 'val', shift left to align at bit 31 (MSB). + */ +u32 __init +mv64x60_shift_left(u32 val, u32 num_bits) +{ + return val << (32 - num_bits); +} + +/* + * mv64x60_shift_right() + * + * Take the high-order 'num_bits' of 'val', shift right to align at bit 0 (LSB). + */ +u32 __init +mv64x60_shift_right(u32 val, u32 num_bits) +{ + return val >> (32 - num_bits); +} + +/* + ***************************************************************************** + * + * Chip Identification Routines + * + ***************************************************************************** + */ +/* + * mv64x60_get_type() + * + * Determine the type of bridge chip we have. + */ +int __init +mv64x60_get_type(struct mv64x60_handle *bh) +{ + struct pci_controller hose; + u16 val; + u8 save_exclude; + + memset(&hose, 0, sizeof(hose)); + setup_indirect_pci_nomap(&hose, bh->v_base + MV64x60_PCI0_CONFIG_ADDR, + bh->v_base + MV64x60_PCI0_CONFIG_DATA); + + save_exclude = mv64x60_pci_exclude_bridge; + mv64x60_pci_exclude_bridge = 0; + /* Sanity check of bridge's Vendor ID */ + early_read_config_word(&hose, 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID, &val); + + if (val != PCI_VENDOR_ID_MARVELL) { + mv64x60_pci_exclude_bridge = save_exclude; + return -1; + } + + /* Get the revision of the chip */ + early_read_config_word(&hose, 0, PCI_DEVFN(0, 0), PCI_CLASS_REVISION, + &val); + bh->rev = (u32)(val & 0xff); + + /* Figure out the type of Marvell bridge it is */ + early_read_config_word(&hose, 0, PCI_DEVFN(0, 0), PCI_DEVICE_ID, &val); + mv64x60_pci_exclude_bridge = save_exclude; + + switch (val) { + case PCI_DEVICE_ID_MARVELL_GT64260: + switch (bh->rev) { + case GT64260_REV_A: + bh->type = MV64x60_TYPE_GT64260A; + break; + + default: + printk(KERN_WARNING "Unsupported GT64260 rev %04x\n", + bh->rev); + /* Assume its similar to a 'B' rev and fallthru */ + case GT64260_REV_B: + bh->type = MV64x60_TYPE_GT64260B; + break; + } + break; + + case PCI_DEVICE_ID_MARVELL_MV64360: + /* Marvell won't tell me how to distinguish a 64361 & 64362 */ + bh->type = MV64x60_TYPE_MV64360; + break; + + case PCI_DEVICE_ID_MARVELL_MV64460: + bh->type = MV64x60_TYPE_MV64460; + break; + + default: + printk(KERN_ERR "Unknown Marvell bridge type %04x\n", val); + return -1; + } + + /* Hang onto bridge type & rev for PIC code */ + mv64x60_bridge_type = bh->type; + mv64x60_bridge_rev = bh->rev; + + return 0; +} + +/* + * mv64x60_setup_for_chip() + * + * Set 'bh' to use the proper set of routine for the bridge chip that we have. + */ +int __init +mv64x60_setup_for_chip(struct mv64x60_handle *bh) +{ + int rc = 0; + + /* Set up chip-specific info based on the chip/bridge type */ + switch(bh->type) { + case MV64x60_TYPE_GT64260A: + bh->ci = >64260a_ci; + break; + + case MV64x60_TYPE_GT64260B: + bh->ci = >64260b_ci; + break; + + case MV64x60_TYPE_MV64360: + bh->ci = &mv64360_ci; + break; + + case MV64x60_TYPE_MV64460: + bh->ci = &mv64460_ci; + break; + + case MV64x60_TYPE_INVALID: + default: + if (ppc_md.progress) + ppc_md.progress("mv64x60: Unsupported bridge", 0x0); + printk(KERN_ERR "mv64x60: Unsupported bridge\n"); + rc = -1; + } + + return rc; +} + +/* + * mv64x60_get_bridge_vbase() + * + * Return the virtual address of the bridge's registers. + */ +void * +mv64x60_get_bridge_vbase(void) +{ + return mv64x60_bridge_vbase; +} + +/* + * mv64x60_get_bridge_type() + * + * Return the type of bridge on the platform. + */ +u32 +mv64x60_get_bridge_type(void) +{ + return mv64x60_bridge_type; +} + +/* + * mv64x60_get_bridge_rev() + * + * Return the revision of the bridge on the platform. + */ +u32 +mv64x60_get_bridge_rev(void) +{ + return mv64x60_bridge_rev; +} + +/* + ***************************************************************************** + * + * System Memory Window Related Routines + * + ***************************************************************************** + */ +/* + * mv64x60_get_mem_size() + * + * Calculate the amount of memory that the memory controller is set up for. + * This should only be used by board-specific code if there is no other + * way to determine the amount of memory in the system. + */ +u32 __init +mv64x60_get_mem_size(u32 bridge_base, u32 chip_type) +{ + struct mv64x60_handle bh; + u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2]; + u32 rc = 0; + + memset(&bh, 0, sizeof(bh)); + + bh.type = chip_type; + bh.v_base = (void *)bridge_base; + + if (!mv64x60_setup_for_chip(&bh)) { + mv64x60_get_mem_windows(&bh, mem_windows); + rc = mv64x60_calc_mem_size(&bh, mem_windows); + } + + return rc; +} + +/* + * mv64x60_get_mem_windows() + * + * Get the values in the memory controller & return in the 'mem_windows' array. + */ +void __init +mv64x60_get_mem_windows(struct mv64x60_handle *bh, + u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2]) +{ + u32 i, win; + + for (win=MV64x60_CPU2MEM_0_WIN,i=0;win<=MV64x60_CPU2MEM_3_WIN;win++,i++) + if (bh->ci->is_enabled_32bit(bh, win)) + mv64x60_get_32bit_window(bh, win, + &mem_windows[i][0], &mem_windows[i][1]); + else { + mem_windows[i][0] = 0; + mem_windows[i][1] = 0; + } + + return; +} + +/* + * mv64x60_calc_mem_size() + * + * Using the memory controller register values in 'mem_windows', determine + * how much memory it is set up for. + */ +u32 __init +mv64x60_calc_mem_size(struct mv64x60_handle *bh, + u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2]) +{ + u32 i, total = 0; + + for (i=0; iSystem MEM, PCI Config Routines + * + ***************************************************************************** + */ +/* + * mv64x60_config_cpu2mem_windows() + * + * Configure CPU->Memory windows on the bridge. + */ +static u32 prot_tab[] __initdata = { + MV64x60_CPU_PROT_0_WIN, MV64x60_CPU_PROT_1_WIN, + MV64x60_CPU_PROT_2_WIN, MV64x60_CPU_PROT_3_WIN +}; + +static u32 cpu_snoop_tab[] __initdata = { + MV64x60_CPU_SNOOP_0_WIN, MV64x60_CPU_SNOOP_1_WIN, + MV64x60_CPU_SNOOP_2_WIN, MV64x60_CPU_SNOOP_3_WIN +}; + +void __init +mv64x60_config_cpu2mem_windows(struct mv64x60_handle *bh, + struct mv64x60_setup_info *si, + u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2]) +{ + u32 i, win; + + /* Set CPU protection & snoop windows */ + for (win=MV64x60_CPU2MEM_0_WIN,i=0;win<=MV64x60_CPU2MEM_3_WIN;win++,i++) + if (bh->ci->is_enabled_32bit(bh, win)) { + mv64x60_set_32bit_window(bh, prot_tab[i], + mem_windows[i][0], mem_windows[i][1], + si->cpu_prot_options[i]); + bh->ci->enable_window_32bit(bh, prot_tab[i]); + + if (bh->ci->window_tab_32bit[cpu_snoop_tab[i]]. + base_reg != 0) { + mv64x60_set_32bit_window(bh, cpu_snoop_tab[i], + mem_windows[i][0], mem_windows[i][1], + si->cpu_snoop_options[i]); + bh->ci->enable_window_32bit(bh, + cpu_snoop_tab[i]); + } + + } + + return; +} + +/* + * mv64x60_config_cpu2pci_windows() + * + * Configure the CPU->PCI windows for one of the PCI buses. + */ +static u32 win_tab[2][4] __initdata = { + { MV64x60_CPU2PCI0_IO_WIN, MV64x60_CPU2PCI0_MEM_0_WIN, + MV64x60_CPU2PCI0_MEM_1_WIN, MV64x60_CPU2PCI0_MEM_2_WIN }, + { MV64x60_CPU2PCI1_IO_WIN, MV64x60_CPU2PCI1_MEM_0_WIN, + MV64x60_CPU2PCI1_MEM_1_WIN, MV64x60_CPU2PCI1_MEM_2_WIN }, +}; + +static u32 remap_tab[2][4] __initdata = { + { MV64x60_CPU2PCI0_IO_REMAP_WIN, MV64x60_CPU2PCI0_MEM_0_REMAP_WIN, + MV64x60_CPU2PCI0_MEM_1_REMAP_WIN, MV64x60_CPU2PCI0_MEM_2_REMAP_WIN }, + { MV64x60_CPU2PCI1_IO_REMAP_WIN, MV64x60_CPU2PCI1_MEM_0_REMAP_WIN, + MV64x60_CPU2PCI1_MEM_1_REMAP_WIN, MV64x60_CPU2PCI1_MEM_2_REMAP_WIN } +}; + +void __init +mv64x60_config_cpu2pci_windows(struct mv64x60_handle *bh, + struct mv64x60_pci_info *pi, u32 bus) +{ + int i; + + if (pi->pci_io.size > 0) { + mv64x60_set_32bit_window(bh, win_tab[bus][0], + pi->pci_io.cpu_base, pi->pci_io.size, pi->pci_io.swap); + mv64x60_set_32bit_window(bh, remap_tab[bus][0], + pi->pci_io.pci_base_lo, 0, 0); + bh->ci->enable_window_32bit(bh, win_tab[bus][0]); + } + else /* Actually, the window should already be disabled */ + bh->ci->disable_window_32bit(bh, win_tab[bus][0]); + + for (i=0; i<3; i++) + if (pi->pci_mem[i].size > 0) { + mv64x60_set_32bit_window(bh, win_tab[bus][i+1], + pi->pci_mem[i].cpu_base, pi->pci_mem[i].size, + pi->pci_mem[i].swap); + mv64x60_set_64bit_window(bh, remap_tab[bus][i+1], + pi->pci_mem[i].pci_base_hi, + pi->pci_mem[i].pci_base_lo, 0, 0); + bh->ci->enable_window_32bit(bh, win_tab[bus][i+1]); + } + else /* Actually, the window should already be disabled */ + bh->ci->disable_window_32bit(bh, win_tab[bus][i+1]); + + return; +} + +/* + ***************************************************************************** + * + * PCI->System MEM Config Routines + * + ***************************************************************************** + */ +/* + * mv64x60_config_pci2mem_windows() + * + * Configure the PCI->Memory windows on the bridge. + */ +static u32 pci_acc_tab[2][4] __initdata = { + { MV64x60_PCI02MEM_ACC_CNTL_0_WIN, MV64x60_PCI02MEM_ACC_CNTL_1_WIN, + MV64x60_PCI02MEM_ACC_CNTL_2_WIN, MV64x60_PCI02MEM_ACC_CNTL_3_WIN }, + { MV64x60_PCI12MEM_ACC_CNTL_0_WIN, MV64x60_PCI12MEM_ACC_CNTL_1_WIN, + MV64x60_PCI12MEM_ACC_CNTL_2_WIN, MV64x60_PCI12MEM_ACC_CNTL_3_WIN } +}; + +static u32 pci_snoop_tab[2][4] __initdata = { + { MV64x60_PCI02MEM_SNOOP_0_WIN, MV64x60_PCI02MEM_SNOOP_1_WIN, + MV64x60_PCI02MEM_SNOOP_2_WIN, MV64x60_PCI02MEM_SNOOP_3_WIN }, + { MV64x60_PCI12MEM_SNOOP_0_WIN, MV64x60_PCI12MEM_SNOOP_1_WIN, + MV64x60_PCI12MEM_SNOOP_2_WIN, MV64x60_PCI12MEM_SNOOP_3_WIN } +}; + +static u32 pci_size_tab[2][4] __initdata = { + { MV64x60_PCI0_MEM_0_SIZE, MV64x60_PCI0_MEM_1_SIZE, + MV64x60_PCI0_MEM_2_SIZE, MV64x60_PCI0_MEM_3_SIZE }, + { MV64x60_PCI1_MEM_0_SIZE, MV64x60_PCI1_MEM_1_SIZE, + MV64x60_PCI1_MEM_2_SIZE, MV64x60_PCI1_MEM_3_SIZE } +}; + +void __init +mv64x60_config_pci2mem_windows(struct mv64x60_handle *bh, + struct pci_controller *hose, struct mv64x60_pci_info *pi, + u32 bus, u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2]) +{ + u32 i, win; + + /* + * Set the access control, snoop, BAR size, and window base addresses. + * PCI->MEM windows base addresses will match exactly what the + * CPU->MEM windows are. + */ + for (win=MV64x60_CPU2MEM_0_WIN,i=0;win<=MV64x60_CPU2MEM_3_WIN;win++,i++) + if (bh->ci->is_enabled_32bit(bh, win)) { + mv64x60_set_64bit_window(bh, + pci_acc_tab[bus][i], 0, + mem_windows[i][0], mem_windows[i][1], + pi->acc_cntl_options[i]); + bh->ci->enable_window_64bit(bh, pci_acc_tab[bus][i]); + + if (bh->ci->window_tab_64bit[ + pci_snoop_tab[bus][i]].base_lo_reg != 0) { + + mv64x60_set_64bit_window(bh, + pci_snoop_tab[bus][i], 0, + mem_windows[i][0], mem_windows[i][1], + pi->snoop_options[i]); + bh->ci->enable_window_64bit(bh, + pci_snoop_tab[bus][i]); + } + + bh->ci->set_pci2mem_window(hose, bus, i, + mem_windows[i][0]); + mv64x60_write(bh, pci_size_tab[bus][i], + mv64x60_mask(mem_windows[i][1] - 1, 20)); + + /* Enable the window */ + mv64x60_clr_bits(bh, ((bus == 0) ? + MV64x60_PCI0_BAR_ENABLE : + MV64x60_PCI1_BAR_ENABLE), (1 << i)); + } + + return; +} + +/* + ***************************************************************************** + * + * Hose & Resource Alloc/Init Routines + * + ***************************************************************************** + */ +/* + * mv64x60_alloc_hoses() + * + * Allocate the PCI hose structures for the bridge's PCI buses. + */ +void __init +mv64x60_alloc_hose(struct mv64x60_handle *bh, u32 cfg_addr, u32 cfg_data, + struct pci_controller **hose) +{ + *hose = pcibios_alloc_controller(); + setup_indirect_pci_nomap(*hose, bh->v_base + cfg_addr, + bh->v_base + cfg_data); + return; +} + +/* + * mv64x60_config_resources() + * + * Calculate the offsets, etc. for the hose structures to reflect all of + * the address remapping that happens as you go from CPU->PCI and PCI->MEM. + */ +void __init +mv64x60_config_resources(struct pci_controller *hose, + struct mv64x60_pci_info *pi, u32 io_base) +{ + int i; + /* 2 hoses; 4 resources/hose; string <= 64 bytes */ + static char s[2][4][64]; + + if (pi->pci_io.size != 0) { + sprintf(s[hose->index][0], "PCI hose %d I/O Space", + hose->index); + pci_init_resource(&hose->io_resource, io_base - isa_io_base, + io_base - isa_io_base + pi->pci_io.size - 1, + IORESOURCE_IO, s[hose->index][0]); + hose->io_space.start = pi->pci_io.pci_base_lo; + hose->io_space.end = pi->pci_io.pci_base_lo + pi->pci_io.size-1; + hose->io_base_phys = pi->pci_io.cpu_base; + hose->io_base_virt = (void *)isa_io_base; + } + + for (i=0; i<3; i++) + if (pi->pci_mem[i].size != 0) { + sprintf(s[hose->index][i+1], "PCI hose %d MEM Space %d", + hose->index, i); + pci_init_resource(&hose->mem_resources[i], + pi->pci_mem[i].cpu_base, + pi->pci_mem[i].cpu_base + pi->pci_mem[i].size-1, + IORESOURCE_MEM, s[hose->index][i+1]); + } + + hose->mem_space.end = pi->pci_mem[0].pci_base_lo + + pi->pci_mem[0].size - 1; + hose->pci_mem_offset = pi->pci_mem[0].cpu_base - + pi->pci_mem[0].pci_base_lo; + return; +} + +/* + * mv64x60_config_pci_params() + * + * Configure a hose's PCI config space parameters. + */ +void __init +mv64x60_config_pci_params(struct pci_controller *hose, + struct mv64x60_pci_info *pi) +{ + u32 devfn; + u16 u16_val; + u8 save_exclude; + + devfn = PCI_DEVFN(0,0); + + save_exclude = mv64x60_pci_exclude_bridge; + mv64x60_pci_exclude_bridge = 0; + + /* Set class code to indicate host bridge */ + u16_val = PCI_CLASS_BRIDGE_HOST; /* 0x0600 (host bridge) */ + early_write_config_word(hose, 0, devfn, PCI_CLASS_DEVICE, u16_val); + + /* Enable bridge to be PCI master & respond to PCI MEM cycles */ + early_read_config_word(hose, 0, devfn, PCI_COMMAND, &u16_val); + u16_val &= ~(PCI_COMMAND_IO | PCI_COMMAND_INVALIDATE | + PCI_COMMAND_PARITY | PCI_COMMAND_SERR | PCI_COMMAND_FAST_BACK); + u16_val |= pi->pci_cmd_bits | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; + early_write_config_word(hose, 0, devfn, PCI_COMMAND, u16_val); + + /* Set latency timer, cache line size, clear BIST */ + u16_val = (pi->latency_timer << 8) | (L1_CACHE_LINE_SIZE >> 2); + early_write_config_word(hose, 0, devfn, PCI_CACHE_LINE_SIZE, u16_val); + + mv64x60_pci_exclude_bridge = save_exclude; + return; +} + +/* + ***************************************************************************** + * + * PCI Related Routine + * + ***************************************************************************** + */ +/* + * mv64x60_set_bus() + * + * Set the bus number for the hose directly under the bridge. + */ +void __init +mv64x60_set_bus(struct mv64x60_handle *bh, u32 bus, u32 child_bus) +{ + struct pci_controller *hose; + u32 pci_mode, p2p_cfg, pci_cfg_offset, val; + u8 save_exclude; + + if (bus == 0) { + pci_mode = bh->pci_mode_a; + p2p_cfg = MV64x60_PCI0_P2P_CONFIG; + pci_cfg_offset = 0x64; + hose = bh->hose_a; + } + else { + pci_mode = bh->pci_mode_b; + p2p_cfg = MV64x60_PCI1_P2P_CONFIG; + pci_cfg_offset = 0xe4; + hose = bh->hose_b; + } + + child_bus &= 0xff; + val = mv64x60_read(bh, p2p_cfg); + + if (pci_mode == MV64x60_PCIMODE_CONVENTIONAL) { + val &= 0xe0000000; /* Force dev num to 0, turn off P2P bridge */ + val |= (child_bus << 16) | 0xff; + mv64x60_write(bh, p2p_cfg, val); + (void)mv64x60_read(bh, p2p_cfg); /* Flush FIFO */ + } + else { /* PCI-X */ + /* + * Need to use the current bus/dev number (that's in the + * P2P CONFIG reg) to access the bridge's pci config space. + */ + save_exclude = mv64x60_pci_exclude_bridge; + mv64x60_pci_exclude_bridge = 0; + early_write_config_dword(hose, (val & 0x00ff0000) >> 16, + PCI_DEVFN(((val & 0x1f000000) >> 24), 0), + pci_cfg_offset, child_bus << 8); + mv64x60_pci_exclude_bridge = save_exclude; + } + + return; +} + +/* + * mv64x60_pci_exclude_device() + * + * This routine is used to make the bridge not appear when the + * PCI subsystem is accessing PCI devices (in PCI config space). + */ +int +mv64x60_pci_exclude_device(u8 bus, u8 devfn) +{ + struct pci_controller *hose; + + hose = pci_bus_to_hose(bus); + + /* Skip slot 0 on both hoses */ + if ((mv64x60_pci_exclude_bridge == 1) && (PCI_SLOT(devfn) == 0) && + (hose->first_busno == bus)) + + return PCIBIOS_DEVICE_NOT_FOUND; + else + return PCIBIOS_SUCCESSFUL; +} /* mv64x60_pci_exclude_device() */ + +/* + ***************************************************************************** + * + * Platform Device Routines + * + ***************************************************************************** + */ + +/* + * mv64x60_pd_fixup() + * + * Need to add the base addr of where the bridge's regs are mapped in the + * physical addr space so drivers can ioremap() them. + */ +void __init +mv64x60_pd_fixup(struct mv64x60_handle *bh, struct platform_device *pd_devs[], + u32 entries) +{ + struct resource *r; + u32 i, j; + + for (i=0; istart += bh->p_base; + r->end += bh->p_base; + j++; + } + } + + return; +} + +/* + * mv64x60_add_pds() + * + * Add the mv64x60 platform devices to the list of platform devices. + */ +static int __init +mv64x60_add_pds(void) +{ + return platform_add_devices(mv64x60_pd_devs, + ARRAY_SIZE(mv64x60_pd_devs)); +} +arch_initcall(mv64x60_add_pds); + +/* + ***************************************************************************** + * + * GT64260-Specific Routines + * + ***************************************************************************** + */ +/* + * gt64260_translate_size() + * + * On the GT64260, the size register is really the "top" address of the window. + */ +static u32 __init +gt64260_translate_size(u32 base, u32 size, u32 num_bits) +{ + return base + mv64x60_mask(size - 1, num_bits); +} + +/* + * gt64260_untranslate_size() + * + * Translate the top address of a window into a window size. + */ +static u32 __init +gt64260_untranslate_size(u32 base, u32 size, u32 num_bits) +{ + if (size >= base) + size = size - base + (1 << (32 - num_bits)); + else + size = 0; + + return size; +} + +/* + * gt64260_set_pci2mem_window() + * + * The PCI->MEM window registers are actually in PCI config space so need + * to set them by setting the correct config space BARs. + */ +static u32 gt64260_reg_addrs[2][4] __initdata = { + { 0x10, 0x14, 0x18, 0x1c }, { 0x90, 0x94, 0x98, 0x9c } +}; + +static void __init +gt64260_set_pci2mem_window(struct pci_controller *hose, u32 bus, u32 window, + u32 base) +{ + u8 save_exclude; + + pr_debug("set pci->mem window: %d, hose: %d, base: 0x%x\n", window, + hose->index, base); + + save_exclude = mv64x60_pci_exclude_bridge; + mv64x60_pci_exclude_bridge = 0; + early_write_config_dword(hose, 0, PCI_DEVFN(0, 0), + gt64260_reg_addrs[bus][window], mv64x60_mask(base, 20) | 0x8); + mv64x60_pci_exclude_bridge = save_exclude; + + return; +} + +/* + * gt64260_set_pci2regs_window() + * + * Set where the bridge's registers appear in PCI MEM space. + */ +static u32 gt64260_offset[2] __initdata = {0x20, 0xa0}; + +static void __init +gt64260_set_pci2regs_window(struct mv64x60_handle *bh, + struct pci_controller *hose, u32 bus, u32 base) +{ + u8 save_exclude; + + pr_debug("set pci->internal regs hose: %d, base: 0x%x\n", hose->index, + base); + + save_exclude = mv64x60_pci_exclude_bridge; + mv64x60_pci_exclude_bridge = 0; + early_write_config_dword(hose, 0, PCI_DEVFN(0,0), gt64260_offset[bus], + (base << 16)); + mv64x60_pci_exclude_bridge = save_exclude; + + return; +} + +/* + * gt64260_is_enabled_32bit() + * + * On a GT64260, a window is enabled iff its top address is >= to its base + * address. + */ +static u32 __init +gt64260_is_enabled_32bit(struct mv64x60_handle *bh, u32 window) +{ + u32 rc = 0; + + if ((gt64260_32bit_windows[window].base_reg != 0) && + (gt64260_32bit_windows[window].size_reg != 0) && + ((mv64x60_read(bh, gt64260_32bit_windows[window].size_reg) & + ((1 << gt64260_32bit_windows[window].size_bits) - 1)) >= + (mv64x60_read(bh, gt64260_32bit_windows[window].base_reg) & + ((1 << gt64260_32bit_windows[window].base_bits) - 1)))) + + rc = 1; + + return rc; +} + +/* + * gt64260_enable_window_32bit() + * + * On the GT64260, a window is enabled iff the top address is >= to the base + * address of the window. Since the window has already been configured by + * the time this routine is called, we have nothing to do here. + */ +static void __init +gt64260_enable_window_32bit(struct mv64x60_handle *bh, u32 window) +{ + pr_debug("enable 32bit window: %d\n", window); + return; +} + +/* + * gt64260_disable_window_32bit() + * + * On a GT64260, you disable a window by setting its top address to be less + * than its base address. + */ +static void __init +gt64260_disable_window_32bit(struct mv64x60_handle *bh, u32 window) +{ + pr_debug("disable 32bit window: %d, base_reg: 0x%x, size_reg: 0x%x\n", + window, gt64260_32bit_windows[window].base_reg, + gt64260_32bit_windows[window].size_reg); + + if ((gt64260_32bit_windows[window].base_reg != 0) && + (gt64260_32bit_windows[window].size_reg != 0)) { + + /* To disable, make bottom reg higher than top reg */ + mv64x60_write(bh, gt64260_32bit_windows[window].base_reg,0xfff); + mv64x60_write(bh, gt64260_32bit_windows[window].size_reg, 0); + } + + return; +} + +/* + * gt64260_enable_window_64bit() + * + * On the GT64260, a window is enabled iff the top address is >= to the base + * address of the window. Since the window has already been configured by + * the time this routine is called, we have nothing to do here. + */ +static void __init +gt64260_enable_window_64bit(struct mv64x60_handle *bh, u32 window) +{ + pr_debug("enable 64bit window: %d\n", window); + return; /* Enabled when window configured (i.e., when top >= base) */ +} + +/* + * gt64260_disable_window_64bit() + * + * On a GT64260, you disable a window by setting its top address to be less + * than its base address. + */ +static void __init +gt64260_disable_window_64bit(struct mv64x60_handle *bh, u32 window) +{ + pr_debug("disable 64bit window: %d, base_reg: 0x%x, size_reg: 0x%x\n", + window, gt64260_64bit_windows[window].base_lo_reg, + gt64260_64bit_windows[window].size_reg); + + if ((gt64260_64bit_windows[window].base_lo_reg != 0) && + (gt64260_64bit_windows[window].size_reg != 0)) { + + /* To disable, make bottom reg higher than top reg */ + mv64x60_write(bh, gt64260_64bit_windows[window].base_lo_reg, + 0xfff); + mv64x60_write(bh, gt64260_64bit_windows[window].base_hi_reg, 0); + mv64x60_write(bh, gt64260_64bit_windows[window].size_reg, 0); + } + + return; +} + +/* + * gt64260_disable_all_windows() + * + * The GT64260 has several windows that aren't represented in the table of + * windows at the top of this file. This routine turns all of them off + * except for the memory controller windows, of course. + */ +static void __init +gt64260_disable_all_windows(struct mv64x60_handle *bh, + struct mv64x60_setup_info *si) +{ + u32 i, preserve; + + /* Disable 32bit windows (don't disable cpu->mem windows) */ + for (i=MV64x60_CPU2DEV_0_WIN; iwindow_preserve_mask_32_lo & (1 << i); + else + preserve = si->window_preserve_mask_32_hi & (1<<(i-32)); + + if (!preserve) + gt64260_disable_window_32bit(bh, i); + } + + /* Disable 64bit windows */ + for (i=0; iwindow_preserve_mask_64 & (1<MEM access cntl wins not in gt64260_64bit_windows[] */ + mv64x60_write(bh, MV64x60_PCI0_ACC_CNTL_4_BASE_LO, 0xfff); + mv64x60_write(bh, MV64x60_PCI0_ACC_CNTL_4_BASE_HI, 0); + mv64x60_write(bh, MV64x60_PCI0_ACC_CNTL_4_SIZE, 0); + mv64x60_write(bh, MV64x60_PCI0_ACC_CNTL_5_BASE_LO, 0xfff); + mv64x60_write(bh, MV64x60_PCI0_ACC_CNTL_5_BASE_HI, 0); + mv64x60_write(bh, MV64x60_PCI0_ACC_CNTL_5_SIZE, 0); + mv64x60_write(bh, GT64260_PCI0_ACC_CNTL_6_BASE_LO, 0xfff); + mv64x60_write(bh, GT64260_PCI0_ACC_CNTL_6_BASE_HI, 0); + mv64x60_write(bh, GT64260_PCI0_ACC_CNTL_6_SIZE, 0); + mv64x60_write(bh, GT64260_PCI0_ACC_CNTL_7_BASE_LO, 0xfff); + mv64x60_write(bh, GT64260_PCI0_ACC_CNTL_7_BASE_HI, 0); + mv64x60_write(bh, GT64260_PCI0_ACC_CNTL_7_SIZE, 0); + + mv64x60_write(bh, MV64x60_PCI1_ACC_CNTL_4_BASE_LO, 0xfff); + mv64x60_write(bh, MV64x60_PCI1_ACC_CNTL_4_BASE_HI, 0); + mv64x60_write(bh, MV64x60_PCI1_ACC_CNTL_4_SIZE, 0); + mv64x60_write(bh, MV64x60_PCI1_ACC_CNTL_5_BASE_LO, 0xfff); + mv64x60_write(bh, MV64x60_PCI1_ACC_CNTL_5_BASE_HI, 0); + mv64x60_write(bh, MV64x60_PCI1_ACC_CNTL_5_SIZE, 0); + mv64x60_write(bh, GT64260_PCI1_ACC_CNTL_6_BASE_LO, 0xfff); + mv64x60_write(bh, GT64260_PCI1_ACC_CNTL_6_BASE_HI, 0); + mv64x60_write(bh, GT64260_PCI1_ACC_CNTL_6_SIZE, 0); + mv64x60_write(bh, GT64260_PCI1_ACC_CNTL_7_BASE_LO, 0xfff); + mv64x60_write(bh, GT64260_PCI1_ACC_CNTL_7_BASE_HI, 0); + mv64x60_write(bh, GT64260_PCI1_ACC_CNTL_7_SIZE, 0); + + /* Disable all PCI-> windows */ + mv64x60_set_bits(bh, MV64x60_PCI0_BAR_ENABLE, 0x07fffdff); + mv64x60_set_bits(bh, MV64x60_PCI1_BAR_ENABLE, 0x07fffdff); + + /* + * Some firmwares enable a bunch of intr sources + * for the PCI INT output pins. + */ + mv64x60_write(bh, GT64260_IC_CPU_INTR_MASK_LO, 0); + mv64x60_write(bh, GT64260_IC_CPU_INTR_MASK_HI, 0); + mv64x60_write(bh, GT64260_IC_PCI0_INTR_MASK_LO, 0); + mv64x60_write(bh, GT64260_IC_PCI0_INTR_MASK_HI, 0); + mv64x60_write(bh, GT64260_IC_PCI1_INTR_MASK_LO, 0); + mv64x60_write(bh, GT64260_IC_PCI1_INTR_MASK_HI, 0); + mv64x60_write(bh, GT64260_IC_CPU_INT_0_MASK, 0); + mv64x60_write(bh, GT64260_IC_CPU_INT_1_MASK, 0); + mv64x60_write(bh, GT64260_IC_CPU_INT_2_MASK, 0); + mv64x60_write(bh, GT64260_IC_CPU_INT_3_MASK, 0); + + return; +} + +/* + * gt64260a_chip_specific_init() + * + * Implement errata work arounds for the GT64260A. + */ +static void __init +gt64260a_chip_specific_init(struct mv64x60_handle *bh, + struct mv64x60_setup_info *si) +{ +#ifdef CONFIG_SERIAL_MPSC + struct resource *r; +#endif +#if !defined(CONFIG_NOT_COHERENT_CACHE) + u32 val; + u8 save_exclude; +#endif + + if (si->pci_0.enable_bus) + mv64x60_set_bits(bh, MV64x60_PCI0_CMD, + ((1<<4) | (1<<5) | (1<<9) | (1<<13))); + + if (si->pci_1.enable_bus) + mv64x60_set_bits(bh, MV64x60_PCI1_CMD, + ((1<<4) | (1<<5) | (1<<9) | (1<<13))); + + /* + * Dave Wilhardt found that bit 4 in the PCI Command registers must + * be set if you are using cache coherency. + */ +#if !defined(CONFIG_NOT_COHERENT_CACHE) + /* Res #MEM-4 -- cpu read buffer to buffer 1 */ + if ((mv64x60_read(bh, MV64x60_CPU_MODE) & 0xf0) == 0x40) + mv64x60_set_bits(bh, GT64260_SDRAM_CONFIG, (1<<26)); + + save_exclude = mv64x60_pci_exclude_bridge; + mv64x60_pci_exclude_bridge = 0; + if (si->pci_0.enable_bus) { + early_read_config_dword(bh->hose_a, 0, PCI_DEVFN(0,0), + PCI_COMMAND, &val); + val |= PCI_COMMAND_INVALIDATE; + early_write_config_dword(bh->hose_a, 0, PCI_DEVFN(0,0), + PCI_COMMAND, val); + } + + if (si->pci_1.enable_bus) { + early_read_config_dword(bh->hose_b, 0, PCI_DEVFN(0,0), + PCI_COMMAND, &val); + val |= PCI_COMMAND_INVALIDATE; + early_write_config_dword(bh->hose_b, 0, PCI_DEVFN(0,0), + PCI_COMMAND, val); + } + mv64x60_pci_exclude_bridge = save_exclude; +#endif + + /* Disable buffer/descriptor snooping */ + mv64x60_clr_bits(bh, 0xf280, (1<< 6) | (1<<14) | (1<<22) | (1<<30)); + mv64x60_clr_bits(bh, 0xf2c0, (1<< 6) | (1<<14) | (1<<22) | (1<<30)); + +#ifdef CONFIG_SERIAL_MPSC + mv64x60_mpsc0_pdata.mirror_regs = 1; + mv64x60_mpsc0_pdata.cache_mgmt = 1; + mv64x60_mpsc1_pdata.mirror_regs = 1; + mv64x60_mpsc1_pdata.cache_mgmt = 1; + + if ((r = platform_get_resource(&mpsc1_device, IORESOURCE_IRQ, 0)) + != NULL) { + + r->start = MV64x60_IRQ_SDMA_0; + r->end = MV64x60_IRQ_SDMA_0; + } +#endif + + return; +} + +/* + * gt64260b_chip_specific_init() + * + * Implement errata work arounds for the GT64260B. + */ +static void __init +gt64260b_chip_specific_init(struct mv64x60_handle *bh, + struct mv64x60_setup_info *si) +{ +#ifdef CONFIG_SERIAL_MPSC + struct resource *r; +#endif +#if !defined(CONFIG_NOT_COHERENT_CACHE) + u32 val; + u8 save_exclude; +#endif + + if (si->pci_0.enable_bus) + mv64x60_set_bits(bh, MV64x60_PCI0_CMD, + ((1<<4) | (1<<5) | (1<<9) | (1<<13))); + + if (si->pci_1.enable_bus) + mv64x60_set_bits(bh, MV64x60_PCI1_CMD, + ((1<<4) | (1<<5) | (1<<9) | (1<<13))); + + /* + * Dave Wilhardt found that bit 4 in the PCI Command registers must + * be set if you are using cache coherency. + */ +#if !defined(CONFIG_NOT_COHERENT_CACHE) + mv64x60_set_bits(bh, GT64260_CPU_WB_PRIORITY_BUFFER_DEPTH, 0xf); + + /* Res #MEM-4 -- cpu read buffer to buffer 1 */ + if ((mv64x60_read(bh, MV64x60_CPU_MODE) & 0xf0) == 0x40) + mv64x60_set_bits(bh, GT64260_SDRAM_CONFIG, (1<<26)); + + save_exclude = mv64x60_pci_exclude_bridge; + mv64x60_pci_exclude_bridge = 0; + if (si->pci_0.enable_bus) { + early_read_config_dword(bh->hose_a, 0, PCI_DEVFN(0,0), + PCI_COMMAND, &val); + val |= PCI_COMMAND_INVALIDATE; + early_write_config_dword(bh->hose_a, 0, PCI_DEVFN(0,0), + PCI_COMMAND, val); + } + + if (si->pci_1.enable_bus) { + early_read_config_dword(bh->hose_b, 0, PCI_DEVFN(0,0), + PCI_COMMAND, &val); + val |= PCI_COMMAND_INVALIDATE; + early_write_config_dword(bh->hose_b, 0, PCI_DEVFN(0,0), + PCI_COMMAND, val); + } + mv64x60_pci_exclude_bridge = save_exclude; +#endif + + /* Disable buffer/descriptor snooping */ + mv64x60_clr_bits(bh, 0xf280, (1<< 6) | (1<<14) | (1<<22) | (1<<30)); + mv64x60_clr_bits(bh, 0xf2c0, (1<< 6) | (1<<14) | (1<<22) | (1<<30)); + +#ifdef CONFIG_SERIAL_MPSC + /* + * The 64260B is not supposed to have the bug where the MPSC & ENET + * can't access cache coherent regions. However, testing has shown + * that the MPSC, at least, still has this bug. + */ + mv64x60_mpsc0_pdata.cache_mgmt = 1; + mv64x60_mpsc1_pdata.cache_mgmt = 1; + + if ((r = platform_get_resource(&mpsc1_device, IORESOURCE_IRQ, 0)) + != NULL) { + + r->start = MV64x60_IRQ_SDMA_0; + r->end = MV64x60_IRQ_SDMA_0; + } +#endif + + return; +} + +/* + ***************************************************************************** + * + * MV64360-Specific Routines + * + ***************************************************************************** + */ +/* + * mv64360_translate_size() + * + * On the MV64360, the size register is set similar to the size you get + * from a pci config space BAR register. That is, programmed from LSB to MSB + * as a sequence of 1's followed by a sequence of 0's. IOW, "size -1" with the + * assumption that the size is a power of 2. + */ +static u32 __init +mv64360_translate_size(u32 base_addr, u32 size, u32 num_bits) +{ + return mv64x60_mask(size - 1, num_bits); +} + +/* + * mv64360_untranslate_size() + * + * Translate the size register value of a window into a window size. + */ +static u32 __init +mv64360_untranslate_size(u32 base_addr, u32 size, u32 num_bits) +{ + if (size > 0) { + size >>= (32 - num_bits); + size++; + size <<= (32 - num_bits); + } + + return size; +} + +/* + * mv64360_set_pci2mem_window() + * + * The PCI->MEM window registers are actually in PCI config space so need + * to set them by setting the correct config space BARs. + */ +struct { + u32 fcn; + u32 base_hi_bar; + u32 base_lo_bar; +} static mv64360_reg_addrs[2][4] __initdata = { + {{ 0, 0x14, 0x10 }, { 0, 0x1c, 0x18 }, + { 1, 0x14, 0x10 }, { 1, 0x1c, 0x18 }}, + {{ 0, 0x94, 0x90 }, { 0, 0x9c, 0x98 }, + { 1, 0x94, 0x90 }, { 1, 0x9c, 0x98 }} +}; + +static void __init +mv64360_set_pci2mem_window(struct pci_controller *hose, u32 bus, u32 window, + u32 base) +{ + u8 save_exclude; + + pr_debug("set pci->mem window: %d, hose: %d, base: 0x%x\n", window, + hose->index, base); + + save_exclude = mv64x60_pci_exclude_bridge; + mv64x60_pci_exclude_bridge = 0; + early_write_config_dword(hose, 0, + PCI_DEVFN(0, mv64360_reg_addrs[bus][window].fcn), + mv64360_reg_addrs[bus][window].base_hi_bar, 0); + early_write_config_dword(hose, 0, + PCI_DEVFN(0, mv64360_reg_addrs[bus][window].fcn), + mv64360_reg_addrs[bus][window].base_lo_bar, + mv64x60_mask(base,20) | 0xc); + mv64x60_pci_exclude_bridge = save_exclude; + + return; +} + +/* + * mv64360_set_pci2regs_window() + * + * Set where the bridge's registers appear in PCI MEM space. + */ +static u32 mv64360_offset[2][2] __initdata = {{0x20, 0x24}, {0xa0, 0xa4}}; + +static void __init +mv64360_set_pci2regs_window(struct mv64x60_handle *bh, + struct pci_controller *hose, u32 bus, u32 base) +{ + u8 save_exclude; + + pr_debug("set pci->internal regs hose: %d, base: 0x%x\n", hose->index, + base); + + save_exclude = mv64x60_pci_exclude_bridge; + mv64x60_pci_exclude_bridge = 0; + early_write_config_dword(hose, 0, PCI_DEVFN(0,0), + mv64360_offset[bus][0], (base << 16)); + early_write_config_dword(hose, 0, PCI_DEVFN(0,0), + mv64360_offset[bus][1], 0); + mv64x60_pci_exclude_bridge = save_exclude; + + return; +} + +/* + * mv64360_is_enabled_32bit() + * + * On a MV64360, a window is enabled by either clearing a bit in the + * CPU BAR Enable reg or setting a bit in the window's base reg. + * Note that this doesn't work for windows on the PCI slave side but we don't + * check those so its okay. + */ +static u32 __init +mv64360_is_enabled_32bit(struct mv64x60_handle *bh, u32 window) +{ + u32 extra, rc = 0; + + if (((mv64360_32bit_windows[window].base_reg != 0) && + (mv64360_32bit_windows[window].size_reg != 0)) || + (window == MV64x60_CPU2SRAM_WIN)) { + + extra = mv64360_32bit_windows[window].extra; + + switch (extra & MV64x60_EXTRA_MASK) { + case MV64x60_EXTRA_CPUWIN_ENAB: + rc = (mv64x60_read(bh, MV64360_CPU_BAR_ENABLE) & + (1 << (extra & 0x1f))) == 0; + break; + + case MV64x60_EXTRA_CPUPROT_ENAB: + rc = (mv64x60_read(bh, + mv64360_32bit_windows[window].base_reg) & + (1 << (extra & 0x1f))) != 0; + break; + + case MV64x60_EXTRA_ENET_ENAB: + rc = (mv64x60_read(bh, MV64360_ENET2MEM_BAR_ENABLE) & + (1 << (extra & 0x7))) == 0; + break; + + case MV64x60_EXTRA_MPSC_ENAB: + rc = (mv64x60_read(bh, MV64360_MPSC2MEM_BAR_ENABLE) & + (1 << (extra & 0x3))) == 0; + break; + + case MV64x60_EXTRA_IDMA_ENAB: + rc = (mv64x60_read(bh, MV64360_IDMA2MEM_BAR_ENABLE) & + (1 << (extra & 0x7))) == 0; + break; + + default: + printk(KERN_ERR "mv64360_is_enabled: %s\n", + "32bit table corrupted"); + } + } + + return rc; +} + +/* + * mv64360_enable_window_32bit() + * + * On a MV64360, a window is enabled by either clearing a bit in the + * CPU BAR Enable reg or setting a bit in the window's base reg. + */ +static void __init +mv64360_enable_window_32bit(struct mv64x60_handle *bh, u32 window) +{ + u32 extra; + + pr_debug("enable 32bit window: %d\n", window); + + if (((mv64360_32bit_windows[window].base_reg != 0) && + (mv64360_32bit_windows[window].size_reg != 0)) || + (window == MV64x60_CPU2SRAM_WIN)) { + + extra = mv64360_32bit_windows[window].extra; + + switch (extra & MV64x60_EXTRA_MASK) { + case MV64x60_EXTRA_CPUWIN_ENAB: + mv64x60_clr_bits(bh, MV64360_CPU_BAR_ENABLE, + (1 << (extra & 0x1f))); + break; + + case MV64x60_EXTRA_CPUPROT_ENAB: + mv64x60_set_bits(bh, + mv64360_32bit_windows[window].base_reg, + (1 << (extra & 0x1f))); + break; + + case MV64x60_EXTRA_ENET_ENAB: + mv64x60_clr_bits(bh, MV64360_ENET2MEM_BAR_ENABLE, + (1 << (extra & 0x7))); + break; + + case MV64x60_EXTRA_MPSC_ENAB: + mv64x60_clr_bits(bh, MV64360_MPSC2MEM_BAR_ENABLE, + (1 << (extra & 0x3))); + break; + + case MV64x60_EXTRA_IDMA_ENAB: + mv64x60_clr_bits(bh, MV64360_IDMA2MEM_BAR_ENABLE, + (1 << (extra & 0x7))); + break; + + default: + printk(KERN_ERR "mv64360_enable: %s\n", + "32bit table corrupted"); + } + } + + return; +} + +/* + * mv64360_disable_window_32bit() + * + * On a MV64360, a window is disabled by either setting a bit in the + * CPU BAR Enable reg or clearing a bit in the window's base reg. + */ +static void __init +mv64360_disable_window_32bit(struct mv64x60_handle *bh, u32 window) +{ + u32 extra; + + pr_debug("disable 32bit window: %d, base_reg: 0x%x, size_reg: 0x%x\n", + window, mv64360_32bit_windows[window].base_reg, + mv64360_32bit_windows[window].size_reg); + + if (((mv64360_32bit_windows[window].base_reg != 0) && + (mv64360_32bit_windows[window].size_reg != 0)) || + (window == MV64x60_CPU2SRAM_WIN)) { + + extra = mv64360_32bit_windows[window].extra; + + switch (extra & MV64x60_EXTRA_MASK) { + case MV64x60_EXTRA_CPUWIN_ENAB: + mv64x60_set_bits(bh, MV64360_CPU_BAR_ENABLE, + (1 << (extra & 0x1f))); + break; + + case MV64x60_EXTRA_CPUPROT_ENAB: + mv64x60_clr_bits(bh, + mv64360_32bit_windows[window].base_reg, + (1 << (extra & 0x1f))); + break; + + case MV64x60_EXTRA_ENET_ENAB: + mv64x60_set_bits(bh, MV64360_ENET2MEM_BAR_ENABLE, + (1 << (extra & 0x7))); + break; + + case MV64x60_EXTRA_MPSC_ENAB: + mv64x60_set_bits(bh, MV64360_MPSC2MEM_BAR_ENABLE, + (1 << (extra & 0x3))); + break; + + case MV64x60_EXTRA_IDMA_ENAB: + mv64x60_set_bits(bh, MV64360_IDMA2MEM_BAR_ENABLE, + (1 << (extra & 0x7))); + break; + + default: + printk(KERN_ERR "mv64360_disable: %s\n", + "32bit table corrupted"); + } + } + + return; +} + +/* + * mv64360_enable_window_64bit() + * + * On the MV64360, a 64-bit window is enabled by setting a bit in the window's + * base reg. + */ +static void __init +mv64360_enable_window_64bit(struct mv64x60_handle *bh, u32 window) +{ + pr_debug("enable 64bit window: %d\n", window); + + if ((mv64360_64bit_windows[window].base_lo_reg!= 0) && + (mv64360_64bit_windows[window].size_reg != 0)) { + + if ((mv64360_64bit_windows[window].extra & MV64x60_EXTRA_MASK) + == MV64x60_EXTRA_PCIACC_ENAB) + + mv64x60_set_bits(bh, + mv64360_64bit_windows[window].base_lo_reg, + (1 << (mv64360_64bit_windows[window].extra & + 0x1f))); + else + printk(KERN_ERR "mv64360_enable: %s\n", + "64bit table corrupted"); + } + + return; +} + +/* + * mv64360_disable_window_64bit() + * + * On a MV64360, a 64-bit window is disabled by clearing a bit in the window's + * base reg. + */ +static void __init +mv64360_disable_window_64bit(struct mv64x60_handle *bh, u32 window) +{ + pr_debug("disable 64bit window: %d, base_reg: 0x%x, size_reg: 0x%x\n", + window, mv64360_64bit_windows[window].base_lo_reg, + mv64360_64bit_windows[window].size_reg); + + if ((mv64360_64bit_windows[window].base_lo_reg != 0) && + (mv64360_64bit_windows[window].size_reg != 0)) { + + if ((mv64360_64bit_windows[window].extra & MV64x60_EXTRA_MASK) + == MV64x60_EXTRA_PCIACC_ENAB) + + mv64x60_clr_bits(bh, + mv64360_64bit_windows[window].base_lo_reg, + (1 << (mv64360_64bit_windows[window].extra & + 0x1f))); + else + printk(KERN_ERR "mv64360_disable: %s\n", + "64bit table corrupted"); + } + + return; +} + +/* + * mv64360_disable_all_windows() + * + * The MV64360 has a few windows that aren't represented in the table of + * windows at the top of this file. This routine turns all of them off + * except for the memory controller windows, of course. + */ +static void __init +mv64360_disable_all_windows(struct mv64x60_handle *bh, + struct mv64x60_setup_info *si) +{ + u32 preserve, i; + + /* Disable 32bit windows (don't disable cpu->mem windows) */ + for (i=MV64x60_CPU2DEV_0_WIN; iwindow_preserve_mask_32_lo & (1 << i); + else + preserve = si->window_preserve_mask_32_hi & (1<<(i-32)); + + if (!preserve) + mv64360_disable_window_32bit(bh, i); + } + + /* Disable 64bit windows */ + for (i=0; iwindow_preserve_mask_64 & (1<MEM access cntl wins not in mv64360_64bit_windows[] */ + mv64x60_clr_bits(bh, MV64x60_PCI0_ACC_CNTL_4_BASE_LO, 0); + mv64x60_clr_bits(bh, MV64x60_PCI0_ACC_CNTL_5_BASE_LO, 0); + mv64x60_clr_bits(bh, MV64x60_PCI1_ACC_CNTL_4_BASE_LO, 0); + mv64x60_clr_bits(bh, MV64x60_PCI1_ACC_CNTL_5_BASE_LO, 0); + + /* Disable all PCI-> windows */ + mv64x60_set_bits(bh, MV64x60_PCI0_BAR_ENABLE, 0x0000f9ff); + mv64x60_set_bits(bh, MV64x60_PCI1_BAR_ENABLE, 0x0000f9ff); + + return; +} + +/* + * mv64360_config_io2mem_windows() + * + * ENET, MPSC, and IDMA ctlrs on the MV64[34]60 have separate windows that + * must be set up so that the respective ctlr can access system memory. + */ +static u32 enet_tab[MV64x60_CPU2MEM_WINDOWS] __initdata = { + MV64x60_ENET2MEM_0_WIN, MV64x60_ENET2MEM_1_WIN, + MV64x60_ENET2MEM_2_WIN, MV64x60_ENET2MEM_3_WIN, +}; + +static u32 mpsc_tab[MV64x60_CPU2MEM_WINDOWS] __initdata = { + MV64x60_MPSC2MEM_0_WIN, MV64x60_MPSC2MEM_1_WIN, + MV64x60_MPSC2MEM_2_WIN, MV64x60_MPSC2MEM_3_WIN, +}; + +static u32 idma_tab[MV64x60_CPU2MEM_WINDOWS] __initdata = { + MV64x60_IDMA2MEM_0_WIN, MV64x60_IDMA2MEM_1_WIN, + MV64x60_IDMA2MEM_2_WIN, MV64x60_IDMA2MEM_3_WIN, +}; + +static u32 dram_selects[MV64x60_CPU2MEM_WINDOWS] __initdata = + { 0xe, 0xd, 0xb, 0x7 }; + +static void __init +mv64360_config_io2mem_windows(struct mv64x60_handle *bh, + struct mv64x60_setup_info *si, + u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2]) +{ + u32 i, win; + + pr_debug("config_io2regs_windows: enet, mpsc, idma -> bridge regs\n"); + + mv64x60_write(bh, MV64360_ENET2MEM_ACC_PROT_0, 0); + mv64x60_write(bh, MV64360_ENET2MEM_ACC_PROT_1, 0); + mv64x60_write(bh, MV64360_ENET2MEM_ACC_PROT_2, 0); + + mv64x60_write(bh, MV64360_MPSC2MEM_ACC_PROT_0, 0); + mv64x60_write(bh, MV64360_MPSC2MEM_ACC_PROT_1, 0); + + mv64x60_write(bh, MV64360_IDMA2MEM_ACC_PROT_0, 0); + mv64x60_write(bh, MV64360_IDMA2MEM_ACC_PROT_1, 0); + mv64x60_write(bh, MV64360_IDMA2MEM_ACC_PROT_2, 0); + mv64x60_write(bh, MV64360_IDMA2MEM_ACC_PROT_3, 0); + + /* Assume that mem ctlr has no more windows than embedded I/O ctlr */ + for (win=MV64x60_CPU2MEM_0_WIN,i=0;win<=MV64x60_CPU2MEM_3_WIN;win++,i++) + if (bh->ci->is_enabled_32bit(bh, win)) { + mv64x60_set_32bit_window(bh, enet_tab[i], + mem_windows[i][0], mem_windows[i][1], + (dram_selects[i] << 8) | + (si->enet_options[i] & 0x3000)); + bh->ci->enable_window_32bit(bh, enet_tab[i]); + + /* Give enet r/w access to memory region */ + mv64x60_set_bits(bh, MV64360_ENET2MEM_ACC_PROT_0, + (0x3 << (i << 1))); + mv64x60_set_bits(bh, MV64360_ENET2MEM_ACC_PROT_1, + (0x3 << (i << 1))); + mv64x60_set_bits(bh, MV64360_ENET2MEM_ACC_PROT_2, + (0x3 << (i << 1))); + + mv64x60_set_32bit_window(bh, mpsc_tab[i], + mem_windows[i][0], mem_windows[i][1], + (dram_selects[i] << 8) | + (si->mpsc_options[i] & 0x3000)); + bh->ci->enable_window_32bit(bh, mpsc_tab[i]); + + /* Give mpsc r/w access to memory region */ + mv64x60_set_bits(bh, MV64360_MPSC2MEM_ACC_PROT_0, + (0x3 << (i << 1))); + mv64x60_set_bits(bh, MV64360_MPSC2MEM_ACC_PROT_1, + (0x3 << (i << 1))); + + mv64x60_set_32bit_window(bh, idma_tab[i], + mem_windows[i][0], mem_windows[i][1], + (dram_selects[i] << 8) | + (si->idma_options[i] & 0x3000)); + bh->ci->enable_window_32bit(bh, idma_tab[i]); + + /* Give idma r/w access to memory region */ + mv64x60_set_bits(bh, MV64360_IDMA2MEM_ACC_PROT_0, + (0x3 << (i << 1))); + mv64x60_set_bits(bh, MV64360_IDMA2MEM_ACC_PROT_1, + (0x3 << (i << 1))); + mv64x60_set_bits(bh, MV64360_IDMA2MEM_ACC_PROT_2, + (0x3 << (i << 1))); + mv64x60_set_bits(bh, MV64360_IDMA2MEM_ACC_PROT_3, + (0x3 << (i << 1))); + } + + return; +} + +/* + * mv64360_set_mpsc2regs_window() + * + * MPSC has a window to the bridge's internal registers. Call this routine + * to change that window so it doesn't conflict with the windows mapping the + * mpsc to system memory. + */ +static void __init +mv64360_set_mpsc2regs_window(struct mv64x60_handle *bh, u32 base) +{ + pr_debug("set mpsc->internal regs, base: 0x%x\n", base); + + mv64x60_write(bh, MV64360_MPSC2REGS_BASE, base & 0xffff0000); + return; +} + +/* + * mv64360_chip_specific_init() + * + * No errata work arounds for the MV64360 implemented at this point. + */ +static void __init +mv64360_chip_specific_init(struct mv64x60_handle *bh, + struct mv64x60_setup_info *si) +{ +#ifdef CONFIG_SERIAL_MPSC + mv64x60_mpsc0_pdata.brg_can_tune = 1; + mv64x60_mpsc0_pdata.cache_mgmt = 1; + mv64x60_mpsc1_pdata.brg_can_tune = 1; + mv64x60_mpsc1_pdata.cache_mgmt = 1; +#endif + + return; +} + +/* + * mv64460_chip_specific_init() + * + * No errata work arounds for the MV64460 implemented at this point. + */ +static void __init +mv64460_chip_specific_init(struct mv64x60_handle *bh, + struct mv64x60_setup_info *si) +{ +#ifdef CONFIG_SERIAL_MPSC + mv64x60_mpsc0_pdata.brg_can_tune = 1; + mv64x60_mpsc1_pdata.brg_can_tune = 1; +#endif + return; +} diff --git a/arch/ppc/syslib/mv64x60_dbg.c b/arch/ppc/syslib/mv64x60_dbg.c new file mode 100644 index 00000000000..2927c7adf5e --- /dev/null +++ b/arch/ppc/syslib/mv64x60_dbg.c @@ -0,0 +1,123 @@ +/* + * arch/ppc/syslib/mv64x60_dbg.c + * + * KGDB and progress routines for the Marvell/Galileo MV64x60 (Discovery). + * + * Author: Mark A. Greer + * + * 2003 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +/* + ***************************************************************************** + * + * Low-level MPSC/UART I/O routines + * + ***************************************************************************** + */ + + +#include +#include +#include +#include + + +#if defined(CONFIG_SERIAL_TEXT_DEBUG) + +#define MPSC_CHR_1 0x000c +#define MPSC_CHR_2 0x0010 + +static struct mv64x60_handle mv64x60_dbg_bh; + +void +mv64x60_progress_init(u32 base) +{ + mv64x60_dbg_bh.v_base = base; + return; +} + +static void +mv64x60_polled_putc(int chan, char c) +{ + u32 offset; + + if (chan == 0) + offset = 0x8000; + else + offset = 0x9000; + + mv64x60_write(&mv64x60_dbg_bh, offset + MPSC_CHR_1, (u32)c); + mv64x60_write(&mv64x60_dbg_bh, offset + MPSC_CHR_2, 0x200); + udelay(2000); +} + +void +mv64x60_mpsc_progress(char *s, unsigned short hex) +{ + volatile char c; + + mv64x60_polled_putc(0, '\r'); + + while ((c = *s++) != 0) + mv64x60_polled_putc(0, c); + + mv64x60_polled_putc(0, '\n'); + mv64x60_polled_putc(0, '\r'); + + return; +} +#endif /* CONFIG_SERIAL_TEXT_DEBUG */ + + +#if defined(CONFIG_KGDB) + +#if defined(CONFIG_KGDB_TTYS0) +#define KGDB_PORT 0 +#elif defined(CONFIG_KGDB_TTYS1) +#define KGDB_PORT 1 +#else +#error "Invalid kgdb_tty port" +#endif + +void +putDebugChar(unsigned char c) +{ + mv64x60_polled_putc(KGDB_PORT, (char)c); +} + +int +getDebugChar(void) +{ + unsigned char c; + + while (!mv64x60_polled_getc(KGDB_PORT, &c)); + return (int)c; +} + +void +putDebugString(char* str) +{ + while (*str != '\0') { + putDebugChar(*str); + str++; + } + putDebugChar('\r'); + return; +} + +void +kgdb_interruptible(int enable) +{ +} + +void +kgdb_map_scc(void) +{ + if (ppc_md.early_serial_map) + ppc_md.early_serial_map(); +} +#endif /* CONFIG_KGDB */ diff --git a/arch/ppc/syslib/mv64x60_win.c b/arch/ppc/syslib/mv64x60_win.c new file mode 100644 index 00000000000..b6f0f5dcf6e --- /dev/null +++ b/arch/ppc/syslib/mv64x60_win.c @@ -0,0 +1,1168 @@ +/* + * arch/ppc/syslib/mv64x60_win.c + * + * Tables with info on how to manipulate the 32 & 64 bit windows on the + * various types of Marvell bridge chips. + * + * Author: Mark A. Greer + * + * 2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + ***************************************************************************** + * + * Tables describing how to set up windows on each type of bridge + * + ***************************************************************************** + */ +struct mv64x60_32bit_window + gt64260_32bit_windows[MV64x60_32BIT_WIN_COUNT] __initdata = { + /* CPU->MEM Windows */ + [MV64x60_CPU2MEM_0_WIN] = { + .base_reg = MV64x60_CPU2MEM_0_BASE, + .size_reg = MV64x60_CPU2MEM_0_SIZE, + .base_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_CPU2MEM_1_WIN] = { + .base_reg = MV64x60_CPU2MEM_1_BASE, + .size_reg = MV64x60_CPU2MEM_1_SIZE, + .base_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_CPU2MEM_2_WIN] = { + .base_reg = MV64x60_CPU2MEM_2_BASE, + .size_reg = MV64x60_CPU2MEM_2_SIZE, + .base_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_CPU2MEM_3_WIN] = { + .base_reg = MV64x60_CPU2MEM_3_BASE, + .size_reg = MV64x60_CPU2MEM_3_SIZE, + .base_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + /* CPU->Device Windows */ + [MV64x60_CPU2DEV_0_WIN] = { + .base_reg = MV64x60_CPU2DEV_0_BASE, + .size_reg = MV64x60_CPU2DEV_0_SIZE, + .base_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_CPU2DEV_1_WIN] = { + .base_reg = MV64x60_CPU2DEV_1_BASE, + .size_reg = MV64x60_CPU2DEV_1_SIZE, + .base_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_CPU2DEV_2_WIN] = { + .base_reg = MV64x60_CPU2DEV_2_BASE, + .size_reg = MV64x60_CPU2DEV_2_SIZE, + .base_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_CPU2DEV_3_WIN] = { + .base_reg = MV64x60_CPU2DEV_3_BASE, + .size_reg = MV64x60_CPU2DEV_3_SIZE, + .base_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + /* CPU->Boot Window */ + [MV64x60_CPU2BOOT_WIN] = { + .base_reg = MV64x60_CPU2BOOT_0_BASE, + .size_reg = MV64x60_CPU2BOOT_0_SIZE, + .base_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + /* CPU->PCI 0 Windows */ + [MV64x60_CPU2PCI0_IO_WIN] = { + .base_reg = MV64x60_CPU2PCI0_IO_BASE, + .size_reg = MV64x60_CPU2PCI0_IO_SIZE, + .base_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_CPU2PCI0_MEM_0_WIN] = { + .base_reg = MV64x60_CPU2PCI0_MEM_0_BASE, + .size_reg = MV64x60_CPU2PCI0_MEM_0_SIZE, + .base_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_CPU2PCI0_MEM_1_WIN] = { + .base_reg = MV64x60_CPU2PCI0_MEM_1_BASE, + .size_reg = MV64x60_CPU2PCI0_MEM_1_SIZE, + .base_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_CPU2PCI0_MEM_2_WIN] = { + .base_reg = MV64x60_CPU2PCI0_MEM_2_BASE, + .size_reg = MV64x60_CPU2PCI0_MEM_2_SIZE, + .base_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_CPU2PCI0_MEM_3_WIN] = { + .base_reg = MV64x60_CPU2PCI0_MEM_3_BASE, + .size_reg = MV64x60_CPU2PCI0_MEM_3_SIZE, + .base_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + /* CPU->PCI 1 Windows */ + [MV64x60_CPU2PCI1_IO_WIN] = { + .base_reg = MV64x60_CPU2PCI1_IO_BASE, + .size_reg = MV64x60_CPU2PCI1_IO_SIZE, + .base_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_CPU2PCI1_MEM_0_WIN] = { + .base_reg = MV64x60_CPU2PCI1_MEM_0_BASE, + .size_reg = MV64x60_CPU2PCI1_MEM_0_SIZE, + .base_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_CPU2PCI1_MEM_1_WIN] = { + .base_reg = MV64x60_CPU2PCI1_MEM_1_BASE, + .size_reg = MV64x60_CPU2PCI1_MEM_1_SIZE, + .base_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_CPU2PCI1_MEM_2_WIN] = { + .base_reg = MV64x60_CPU2PCI1_MEM_2_BASE, + .size_reg = MV64x60_CPU2PCI1_MEM_2_SIZE, + .base_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_CPU2PCI1_MEM_3_WIN] = { + .base_reg = MV64x60_CPU2PCI1_MEM_3_BASE, + .size_reg = MV64x60_CPU2PCI1_MEM_3_SIZE, + .base_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + /* CPU->SRAM Window (64260 has no integrated SRAM) */ + /* CPU->PCI 0 Remap I/O Window */ + [MV64x60_CPU2PCI0_IO_REMAP_WIN] = { + .base_reg = MV64x60_CPU2PCI0_IO_REMAP, + .size_reg = 0, + .base_bits = 12, + .size_bits = 0, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + /* CPU->PCI 1 Remap I/O Window */ + [MV64x60_CPU2PCI1_IO_REMAP_WIN] = { + .base_reg = MV64x60_CPU2PCI1_IO_REMAP, + .size_reg = 0, + .base_bits = 12, + .size_bits = 0, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + /* CPU Memory Protection Windows */ + [MV64x60_CPU_PROT_0_WIN] = { + .base_reg = MV64x60_CPU_PROT_BASE_0, + .size_reg = MV64x60_CPU_PROT_SIZE_0, + .base_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_CPU_PROT_1_WIN] = { + .base_reg = MV64x60_CPU_PROT_BASE_1, + .size_reg = MV64x60_CPU_PROT_SIZE_1, + .base_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_CPU_PROT_2_WIN] = { + .base_reg = MV64x60_CPU_PROT_BASE_2, + .size_reg = MV64x60_CPU_PROT_SIZE_2, + .base_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_CPU_PROT_3_WIN] = { + .base_reg = MV64x60_CPU_PROT_BASE_3, + .size_reg = MV64x60_CPU_PROT_SIZE_3, + .base_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + /* CPU Snoop Windows */ + [MV64x60_CPU_SNOOP_0_WIN] = { + .base_reg = GT64260_CPU_SNOOP_BASE_0, + .size_reg = GT64260_CPU_SNOOP_SIZE_0, + .base_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_CPU_SNOOP_1_WIN] = { + .base_reg = GT64260_CPU_SNOOP_BASE_1, + .size_reg = GT64260_CPU_SNOOP_SIZE_1, + .base_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_CPU_SNOOP_2_WIN] = { + .base_reg = GT64260_CPU_SNOOP_BASE_2, + .size_reg = GT64260_CPU_SNOOP_SIZE_2, + .base_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_CPU_SNOOP_3_WIN] = { + .base_reg = GT64260_CPU_SNOOP_BASE_3, + .size_reg = GT64260_CPU_SNOOP_SIZE_3, + .base_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + /* PCI 0->System Memory Remap Windows */ + [MV64x60_PCI02MEM_REMAP_0_WIN] = { + .base_reg = MV64x60_PCI0_SLAVE_MEM_0_REMAP, + .size_reg = 0, + .base_bits = 20, + .size_bits = 0, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = 0 }, + [MV64x60_PCI02MEM_REMAP_1_WIN] = { + .base_reg = MV64x60_PCI0_SLAVE_MEM_1_REMAP, + .size_reg = 0, + .base_bits = 20, + .size_bits = 0, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = 0 }, + [MV64x60_PCI02MEM_REMAP_2_WIN] = { + .base_reg = MV64x60_PCI0_SLAVE_MEM_1_REMAP, + .size_reg = 0, + .base_bits = 20, + .size_bits = 0, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = 0 }, + [MV64x60_PCI02MEM_REMAP_3_WIN] = { + .base_reg = MV64x60_PCI0_SLAVE_MEM_1_REMAP, + .size_reg = 0, + .base_bits = 20, + .size_bits = 0, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = 0 }, + /* PCI 1->System Memory Remap Windows */ + [MV64x60_PCI12MEM_REMAP_0_WIN] = { + .base_reg = MV64x60_PCI1_SLAVE_MEM_0_REMAP, + .size_reg = 0, + .base_bits = 20, + .size_bits = 0, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = 0 }, + [MV64x60_PCI12MEM_REMAP_1_WIN] = { + .base_reg = MV64x60_PCI1_SLAVE_MEM_1_REMAP, + .size_reg = 0, + .base_bits = 20, + .size_bits = 0, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = 0 }, + [MV64x60_PCI12MEM_REMAP_2_WIN] = { + .base_reg = MV64x60_PCI1_SLAVE_MEM_1_REMAP, + .size_reg = 0, + .base_bits = 20, + .size_bits = 0, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = 0 }, + [MV64x60_PCI12MEM_REMAP_3_WIN] = { + .base_reg = MV64x60_PCI1_SLAVE_MEM_1_REMAP, + .size_reg = 0, + .base_bits = 20, + .size_bits = 0, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = 0 }, + /* ENET->SRAM Window (64260 doesn't have separate windows) */ + /* MPSC->SRAM Window (64260 doesn't have separate windows) */ + /* IDMA->SRAM Window (64260 doesn't have separate windows) */ +}; + +struct mv64x60_64bit_window + gt64260_64bit_windows[MV64x60_64BIT_WIN_COUNT] __initdata = { + /* CPU->PCI 0 MEM Remap Windows */ + [MV64x60_CPU2PCI0_MEM_0_REMAP_WIN] = { + .base_hi_reg = MV64x60_CPU2PCI0_MEM_0_REMAP_HI, + .base_lo_reg = MV64x60_CPU2PCI0_MEM_0_REMAP_LO, + .size_reg = 0, + .base_lo_bits = 12, + .size_bits = 0, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_CPU2PCI0_MEM_1_REMAP_WIN] = { + .base_hi_reg = MV64x60_CPU2PCI0_MEM_1_REMAP_HI, + .base_lo_reg = MV64x60_CPU2PCI0_MEM_1_REMAP_LO, + .size_reg = 0, + .base_lo_bits = 12, + .size_bits = 0, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_CPU2PCI0_MEM_2_REMAP_WIN] = { + .base_hi_reg = MV64x60_CPU2PCI0_MEM_2_REMAP_HI, + .base_lo_reg = MV64x60_CPU2PCI0_MEM_2_REMAP_LO, + .size_reg = 0, + .base_lo_bits = 12, + .size_bits = 0, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_CPU2PCI0_MEM_3_REMAP_WIN] = { + .base_hi_reg = MV64x60_CPU2PCI0_MEM_3_REMAP_HI, + .base_lo_reg = MV64x60_CPU2PCI0_MEM_3_REMAP_LO, + .size_reg = 0, + .base_lo_bits = 12, + .size_bits = 0, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + /* CPU->PCI 1 MEM Remap Windows */ + [MV64x60_CPU2PCI1_MEM_0_REMAP_WIN] = { + .base_hi_reg = MV64x60_CPU2PCI1_MEM_0_REMAP_HI, + .base_lo_reg = MV64x60_CPU2PCI1_MEM_0_REMAP_LO, + .size_reg = 0, + .base_lo_bits = 12, + .size_bits = 0, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_CPU2PCI1_MEM_1_REMAP_WIN] = { + .base_hi_reg = MV64x60_CPU2PCI1_MEM_1_REMAP_HI, + .base_lo_reg = MV64x60_CPU2PCI1_MEM_1_REMAP_LO, + .size_reg = 0, + .base_lo_bits = 12, + .size_bits = 0, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_CPU2PCI1_MEM_2_REMAP_WIN] = { + .base_hi_reg = MV64x60_CPU2PCI1_MEM_2_REMAP_HI, + .base_lo_reg = MV64x60_CPU2PCI1_MEM_2_REMAP_LO, + .size_reg = 0, + .base_lo_bits = 12, + .size_bits = 0, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_CPU2PCI1_MEM_3_REMAP_WIN] = { + .base_hi_reg = MV64x60_CPU2PCI1_MEM_3_REMAP_HI, + .base_lo_reg = MV64x60_CPU2PCI1_MEM_3_REMAP_LO, + .size_reg = 0, + .base_lo_bits = 12, + .size_bits = 0, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + /* PCI 0->MEM Access Control Windows */ + [MV64x60_PCI02MEM_ACC_CNTL_0_WIN] = { + .base_hi_reg = MV64x60_PCI0_ACC_CNTL_0_BASE_HI, + .base_lo_reg = MV64x60_PCI0_ACC_CNTL_0_BASE_LO, + .size_reg = MV64x60_PCI0_ACC_CNTL_0_SIZE, + .base_lo_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_PCI02MEM_ACC_CNTL_1_WIN] = { + .base_hi_reg = MV64x60_PCI0_ACC_CNTL_1_BASE_HI, + .base_lo_reg = MV64x60_PCI0_ACC_CNTL_1_BASE_LO, + .size_reg = MV64x60_PCI0_ACC_CNTL_1_SIZE, + .base_lo_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_PCI02MEM_ACC_CNTL_2_WIN] = { + .base_hi_reg = MV64x60_PCI0_ACC_CNTL_2_BASE_HI, + .base_lo_reg = MV64x60_PCI0_ACC_CNTL_2_BASE_LO, + .size_reg = MV64x60_PCI0_ACC_CNTL_2_SIZE, + .base_lo_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_PCI02MEM_ACC_CNTL_3_WIN] = { + .base_hi_reg = MV64x60_PCI0_ACC_CNTL_3_BASE_HI, + .base_lo_reg = MV64x60_PCI0_ACC_CNTL_3_BASE_LO, + .size_reg = MV64x60_PCI0_ACC_CNTL_3_SIZE, + .base_lo_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + /* PCI 1->MEM Access Control Windows */ + [MV64x60_PCI12MEM_ACC_CNTL_0_WIN] = { + .base_hi_reg = MV64x60_PCI1_ACC_CNTL_0_BASE_HI, + .base_lo_reg = MV64x60_PCI1_ACC_CNTL_0_BASE_LO, + .size_reg = MV64x60_PCI1_ACC_CNTL_0_SIZE, + .base_lo_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_PCI12MEM_ACC_CNTL_1_WIN] = { + .base_hi_reg = MV64x60_PCI1_ACC_CNTL_1_BASE_HI, + .base_lo_reg = MV64x60_PCI1_ACC_CNTL_1_BASE_LO, + .size_reg = MV64x60_PCI1_ACC_CNTL_1_SIZE, + .base_lo_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_PCI12MEM_ACC_CNTL_2_WIN] = { + .base_hi_reg = MV64x60_PCI1_ACC_CNTL_2_BASE_HI, + .base_lo_reg = MV64x60_PCI1_ACC_CNTL_2_BASE_LO, + .size_reg = MV64x60_PCI1_ACC_CNTL_2_SIZE, + .base_lo_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_PCI12MEM_ACC_CNTL_3_WIN] = { + .base_hi_reg = MV64x60_PCI1_ACC_CNTL_3_BASE_HI, + .base_lo_reg = MV64x60_PCI1_ACC_CNTL_3_BASE_LO, + .size_reg = MV64x60_PCI1_ACC_CNTL_3_SIZE, + .base_lo_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + /* PCI 0->MEM Snoop Windows */ + [MV64x60_PCI02MEM_SNOOP_0_WIN] = { + .base_hi_reg = GT64260_PCI0_SNOOP_0_BASE_HI, + .base_lo_reg = GT64260_PCI0_SNOOP_0_BASE_LO, + .size_reg = GT64260_PCI0_SNOOP_0_SIZE, + .base_lo_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_PCI02MEM_SNOOP_1_WIN] = { + .base_hi_reg = GT64260_PCI0_SNOOP_1_BASE_HI, + .base_lo_reg = GT64260_PCI0_SNOOP_1_BASE_LO, + .size_reg = GT64260_PCI0_SNOOP_1_SIZE, + .base_lo_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_PCI02MEM_SNOOP_2_WIN] = { + .base_hi_reg = GT64260_PCI0_SNOOP_2_BASE_HI, + .base_lo_reg = GT64260_PCI0_SNOOP_2_BASE_LO, + .size_reg = GT64260_PCI0_SNOOP_2_SIZE, + .base_lo_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_PCI02MEM_SNOOP_3_WIN] = { + .base_hi_reg = GT64260_PCI0_SNOOP_3_BASE_HI, + .base_lo_reg = GT64260_PCI0_SNOOP_3_BASE_LO, + .size_reg = GT64260_PCI0_SNOOP_3_SIZE, + .base_lo_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + /* PCI 1->MEM Snoop Windows */ + [MV64x60_PCI12MEM_SNOOP_0_WIN] = { + .base_hi_reg = GT64260_PCI1_SNOOP_0_BASE_HI, + .base_lo_reg = GT64260_PCI1_SNOOP_0_BASE_LO, + .size_reg = GT64260_PCI1_SNOOP_0_SIZE, + .base_lo_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_PCI12MEM_SNOOP_1_WIN] = { + .base_hi_reg = GT64260_PCI1_SNOOP_1_BASE_HI, + .base_lo_reg = GT64260_PCI1_SNOOP_1_BASE_LO, + .size_reg = GT64260_PCI1_SNOOP_1_SIZE, + .base_lo_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_PCI12MEM_SNOOP_2_WIN] = { + .base_hi_reg = GT64260_PCI1_SNOOP_2_BASE_HI, + .base_lo_reg = GT64260_PCI1_SNOOP_2_BASE_LO, + .size_reg = GT64260_PCI1_SNOOP_2_SIZE, + .base_lo_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_PCI12MEM_SNOOP_3_WIN] = { + .base_hi_reg = GT64260_PCI1_SNOOP_3_BASE_HI, + .base_lo_reg = GT64260_PCI1_SNOOP_3_BASE_LO, + .size_reg = GT64260_PCI1_SNOOP_3_SIZE, + .base_lo_bits = 12, + .size_bits = 12, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, +}; + +struct mv64x60_32bit_window + mv64360_32bit_windows[MV64x60_32BIT_WIN_COUNT] __initdata = { + /* CPU->MEM Windows */ + [MV64x60_CPU2MEM_0_WIN] = { + .base_reg = MV64x60_CPU2MEM_0_BASE, + .size_reg = MV64x60_CPU2MEM_0_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = MV64x60_EXTRA_CPUWIN_ENAB | 0 }, + [MV64x60_CPU2MEM_1_WIN] = { + .base_reg = MV64x60_CPU2MEM_1_BASE, + .size_reg = MV64x60_CPU2MEM_1_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = MV64x60_EXTRA_CPUWIN_ENAB | 1 }, + [MV64x60_CPU2MEM_2_WIN] = { + .base_reg = MV64x60_CPU2MEM_2_BASE, + .size_reg = MV64x60_CPU2MEM_2_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = MV64x60_EXTRA_CPUWIN_ENAB | 2 }, + [MV64x60_CPU2MEM_3_WIN] = { + .base_reg = MV64x60_CPU2MEM_3_BASE, + .size_reg = MV64x60_CPU2MEM_3_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = MV64x60_EXTRA_CPUWIN_ENAB | 3 }, + /* CPU->Device Windows */ + [MV64x60_CPU2DEV_0_WIN] = { + .base_reg = MV64x60_CPU2DEV_0_BASE, + .size_reg = MV64x60_CPU2DEV_0_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = MV64x60_EXTRA_CPUWIN_ENAB | 4 }, + [MV64x60_CPU2DEV_1_WIN] = { + .base_reg = MV64x60_CPU2DEV_1_BASE, + .size_reg = MV64x60_CPU2DEV_1_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = MV64x60_EXTRA_CPUWIN_ENAB | 5 }, + [MV64x60_CPU2DEV_2_WIN] = { + .base_reg = MV64x60_CPU2DEV_2_BASE, + .size_reg = MV64x60_CPU2DEV_2_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = MV64x60_EXTRA_CPUWIN_ENAB | 6 }, + [MV64x60_CPU2DEV_3_WIN] = { + .base_reg = MV64x60_CPU2DEV_3_BASE, + .size_reg = MV64x60_CPU2DEV_3_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = MV64x60_EXTRA_CPUWIN_ENAB | 7 }, + /* CPU->Boot Window */ + [MV64x60_CPU2BOOT_WIN] = { + .base_reg = MV64x60_CPU2BOOT_0_BASE, + .size_reg = MV64x60_CPU2BOOT_0_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = MV64x60_EXTRA_CPUWIN_ENAB | 8 }, + /* CPU->PCI 0 Windows */ + [MV64x60_CPU2PCI0_IO_WIN] = { + .base_reg = MV64x60_CPU2PCI0_IO_BASE, + .size_reg = MV64x60_CPU2PCI0_IO_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = MV64x60_EXTRA_CPUWIN_ENAB | 9 }, + [MV64x60_CPU2PCI0_MEM_0_WIN] = { + .base_reg = MV64x60_CPU2PCI0_MEM_0_BASE, + .size_reg = MV64x60_CPU2PCI0_MEM_0_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = MV64x60_EXTRA_CPUWIN_ENAB | 10 }, + [MV64x60_CPU2PCI0_MEM_1_WIN] = { + .base_reg = MV64x60_CPU2PCI0_MEM_1_BASE, + .size_reg = MV64x60_CPU2PCI0_MEM_1_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = MV64x60_EXTRA_CPUWIN_ENAB | 11 }, + [MV64x60_CPU2PCI0_MEM_2_WIN] = { + .base_reg = MV64x60_CPU2PCI0_MEM_2_BASE, + .size_reg = MV64x60_CPU2PCI0_MEM_2_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = MV64x60_EXTRA_CPUWIN_ENAB | 12 }, + [MV64x60_CPU2PCI0_MEM_3_WIN] = { + .base_reg = MV64x60_CPU2PCI0_MEM_3_BASE, + .size_reg = MV64x60_CPU2PCI0_MEM_3_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = MV64x60_EXTRA_CPUWIN_ENAB | 13 }, + /* CPU->PCI 1 Windows */ + [MV64x60_CPU2PCI1_IO_WIN] = { + .base_reg = MV64x60_CPU2PCI1_IO_BASE, + .size_reg = MV64x60_CPU2PCI1_IO_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = MV64x60_EXTRA_CPUWIN_ENAB | 14 }, + [MV64x60_CPU2PCI1_MEM_0_WIN] = { + .base_reg = MV64x60_CPU2PCI1_MEM_0_BASE, + .size_reg = MV64x60_CPU2PCI1_MEM_0_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = MV64x60_EXTRA_CPUWIN_ENAB | 15 }, + [MV64x60_CPU2PCI1_MEM_1_WIN] = { + .base_reg = MV64x60_CPU2PCI1_MEM_1_BASE, + .size_reg = MV64x60_CPU2PCI1_MEM_1_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = MV64x60_EXTRA_CPUWIN_ENAB | 16 }, + [MV64x60_CPU2PCI1_MEM_2_WIN] = { + .base_reg = MV64x60_CPU2PCI1_MEM_2_BASE, + .size_reg = MV64x60_CPU2PCI1_MEM_2_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = MV64x60_EXTRA_CPUWIN_ENAB | 17 }, + [MV64x60_CPU2PCI1_MEM_3_WIN] = { + .base_reg = MV64x60_CPU2PCI1_MEM_3_BASE, + .size_reg = MV64x60_CPU2PCI1_MEM_3_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = MV64x60_EXTRA_CPUWIN_ENAB | 18 }, + /* CPU->SRAM Window */ + [MV64x60_CPU2SRAM_WIN] = { + .base_reg = MV64360_CPU2SRAM_BASE, + .size_reg = 0, + .base_bits = 16, + .size_bits = 0, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = MV64x60_EXTRA_CPUWIN_ENAB | 19 }, + /* CPU->PCI 0 Remap I/O Window */ + [MV64x60_CPU2PCI0_IO_REMAP_WIN] = { + .base_reg = MV64x60_CPU2PCI0_IO_REMAP, + .size_reg = 0, + .base_bits = 16, + .size_bits = 0, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + /* CPU->PCI 1 Remap I/O Window */ + [MV64x60_CPU2PCI1_IO_REMAP_WIN] = { + .base_reg = MV64x60_CPU2PCI1_IO_REMAP, + .size_reg = 0, + .base_bits = 16, + .size_bits = 0, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + /* CPU Memory Protection Windows */ + [MV64x60_CPU_PROT_0_WIN] = { + .base_reg = MV64x60_CPU_PROT_BASE_0, + .size_reg = MV64x60_CPU_PROT_SIZE_0, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = MV64x60_EXTRA_CPUPROT_ENAB | 31 }, + [MV64x60_CPU_PROT_1_WIN] = { + .base_reg = MV64x60_CPU_PROT_BASE_1, + .size_reg = MV64x60_CPU_PROT_SIZE_1, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = MV64x60_EXTRA_CPUPROT_ENAB | 31 }, + [MV64x60_CPU_PROT_2_WIN] = { + .base_reg = MV64x60_CPU_PROT_BASE_2, + .size_reg = MV64x60_CPU_PROT_SIZE_2, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = MV64x60_EXTRA_CPUPROT_ENAB | 31 }, + [MV64x60_CPU_PROT_3_WIN] = { + .base_reg = MV64x60_CPU_PROT_BASE_3, + .size_reg = MV64x60_CPU_PROT_SIZE_3, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = MV64x60_EXTRA_CPUPROT_ENAB | 31 }, + /* CPU Snoop Windows -- don't exist on 64360 */ + /* PCI 0->System Memory Remap Windows */ + [MV64x60_PCI02MEM_REMAP_0_WIN] = { + .base_reg = MV64x60_PCI0_SLAVE_MEM_0_REMAP, + .size_reg = 0, + .base_bits = 20, + .size_bits = 0, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = 0 }, + [MV64x60_PCI02MEM_REMAP_1_WIN] = { + .base_reg = MV64x60_PCI0_SLAVE_MEM_1_REMAP, + .size_reg = 0, + .base_bits = 20, + .size_bits = 0, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = 0 }, + [MV64x60_PCI02MEM_REMAP_2_WIN] = { + .base_reg = MV64x60_PCI0_SLAVE_MEM_1_REMAP, + .size_reg = 0, + .base_bits = 20, + .size_bits = 0, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = 0 }, + [MV64x60_PCI02MEM_REMAP_3_WIN] = { + .base_reg = MV64x60_PCI0_SLAVE_MEM_1_REMAP, + .size_reg = 0, + .base_bits = 20, + .size_bits = 0, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = 0 }, + /* PCI 1->System Memory Remap Windows */ + [MV64x60_PCI12MEM_REMAP_0_WIN] = { + .base_reg = MV64x60_PCI1_SLAVE_MEM_0_REMAP, + .size_reg = 0, + .base_bits = 20, + .size_bits = 0, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = 0 }, + [MV64x60_PCI12MEM_REMAP_1_WIN] = { + .base_reg = MV64x60_PCI1_SLAVE_MEM_1_REMAP, + .size_reg = 0, + .base_bits = 20, + .size_bits = 0, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = 0 }, + [MV64x60_PCI12MEM_REMAP_2_WIN] = { + .base_reg = MV64x60_PCI1_SLAVE_MEM_1_REMAP, + .size_reg = 0, + .base_bits = 20, + .size_bits = 0, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = 0 }, + [MV64x60_PCI12MEM_REMAP_3_WIN] = { + .base_reg = MV64x60_PCI1_SLAVE_MEM_1_REMAP, + .size_reg = 0, + .base_bits = 20, + .size_bits = 0, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = 0 }, + /* ENET->System Memory Windows */ + [MV64x60_ENET2MEM_0_WIN] = { + .base_reg = MV64360_ENET2MEM_0_BASE, + .size_reg = MV64360_ENET2MEM_0_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = MV64x60_EXTRA_ENET_ENAB | 0 }, + [MV64x60_ENET2MEM_1_WIN] = { + .base_reg = MV64360_ENET2MEM_1_BASE, + .size_reg = MV64360_ENET2MEM_1_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = MV64x60_EXTRA_ENET_ENAB | 1 }, + [MV64x60_ENET2MEM_2_WIN] = { + .base_reg = MV64360_ENET2MEM_2_BASE, + .size_reg = MV64360_ENET2MEM_2_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = MV64x60_EXTRA_ENET_ENAB | 2 }, + [MV64x60_ENET2MEM_3_WIN] = { + .base_reg = MV64360_ENET2MEM_3_BASE, + .size_reg = MV64360_ENET2MEM_3_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = MV64x60_EXTRA_ENET_ENAB | 3 }, + [MV64x60_ENET2MEM_4_WIN] = { + .base_reg = MV64360_ENET2MEM_4_BASE, + .size_reg = MV64360_ENET2MEM_4_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = MV64x60_EXTRA_ENET_ENAB | 4 }, + [MV64x60_ENET2MEM_5_WIN] = { + .base_reg = MV64360_ENET2MEM_5_BASE, + .size_reg = MV64360_ENET2MEM_5_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = MV64x60_EXTRA_ENET_ENAB | 5 }, + /* MPSC->System Memory Windows */ + [MV64x60_MPSC2MEM_0_WIN] = { + .base_reg = MV64360_MPSC2MEM_0_BASE, + .size_reg = MV64360_MPSC2MEM_0_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = MV64x60_EXTRA_MPSC_ENAB | 0 }, + [MV64x60_MPSC2MEM_1_WIN] = { + .base_reg = MV64360_MPSC2MEM_1_BASE, + .size_reg = MV64360_MPSC2MEM_1_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = MV64x60_EXTRA_MPSC_ENAB | 1 }, + [MV64x60_MPSC2MEM_2_WIN] = { + .base_reg = MV64360_MPSC2MEM_2_BASE, + .size_reg = MV64360_MPSC2MEM_2_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = MV64x60_EXTRA_MPSC_ENAB | 2 }, + [MV64x60_MPSC2MEM_3_WIN] = { + .base_reg = MV64360_MPSC2MEM_3_BASE, + .size_reg = MV64360_MPSC2MEM_3_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = MV64x60_EXTRA_MPSC_ENAB | 3 }, + /* IDMA->System Memory Windows */ + [MV64x60_IDMA2MEM_0_WIN] = { + .base_reg = MV64360_IDMA2MEM_0_BASE, + .size_reg = MV64360_IDMA2MEM_0_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = MV64x60_EXTRA_IDMA_ENAB | 0 }, + [MV64x60_IDMA2MEM_1_WIN] = { + .base_reg = MV64360_IDMA2MEM_1_BASE, + .size_reg = MV64360_IDMA2MEM_1_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = MV64x60_EXTRA_IDMA_ENAB | 1 }, + [MV64x60_IDMA2MEM_2_WIN] = { + .base_reg = MV64360_IDMA2MEM_2_BASE, + .size_reg = MV64360_IDMA2MEM_2_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = MV64x60_EXTRA_IDMA_ENAB | 2 }, + [MV64x60_IDMA2MEM_3_WIN] = { + .base_reg = MV64360_IDMA2MEM_3_BASE, + .size_reg = MV64360_IDMA2MEM_3_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = MV64x60_EXTRA_IDMA_ENAB | 3 }, + [MV64x60_IDMA2MEM_4_WIN] = { + .base_reg = MV64360_IDMA2MEM_4_BASE, + .size_reg = MV64360_IDMA2MEM_4_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = MV64x60_EXTRA_IDMA_ENAB | 4 }, + [MV64x60_IDMA2MEM_5_WIN] = { + .base_reg = MV64360_IDMA2MEM_5_BASE, + .size_reg = MV64360_IDMA2MEM_5_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = MV64x60_EXTRA_IDMA_ENAB | 5 }, + [MV64x60_IDMA2MEM_6_WIN] = { + .base_reg = MV64360_IDMA2MEM_6_BASE, + .size_reg = MV64360_IDMA2MEM_6_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = MV64x60_EXTRA_IDMA_ENAB | 6 }, + [MV64x60_IDMA2MEM_7_WIN] = { + .base_reg = MV64360_IDMA2MEM_7_BASE, + .size_reg = MV64360_IDMA2MEM_7_SIZE, + .base_bits = 16, + .size_bits = 16, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = MV64x60_EXTRA_IDMA_ENAB | 7 }, +}; + +struct mv64x60_64bit_window + mv64360_64bit_windows[MV64x60_64BIT_WIN_COUNT] __initdata = { + /* CPU->PCI 0 MEM Remap Windows */ + [MV64x60_CPU2PCI0_MEM_0_REMAP_WIN] = { + .base_hi_reg = MV64x60_CPU2PCI0_MEM_0_REMAP_HI, + .base_lo_reg = MV64x60_CPU2PCI0_MEM_0_REMAP_LO, + .size_reg = 0, + .base_lo_bits = 16, + .size_bits = 0, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_CPU2PCI0_MEM_1_REMAP_WIN] = { + .base_hi_reg = MV64x60_CPU2PCI0_MEM_1_REMAP_HI, + .base_lo_reg = MV64x60_CPU2PCI0_MEM_1_REMAP_LO, + .size_reg = 0, + .base_lo_bits = 16, + .size_bits = 0, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_CPU2PCI0_MEM_2_REMAP_WIN] = { + .base_hi_reg = MV64x60_CPU2PCI0_MEM_2_REMAP_HI, + .base_lo_reg = MV64x60_CPU2PCI0_MEM_2_REMAP_LO, + .size_reg = 0, + .base_lo_bits = 16, + .size_bits = 0, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_CPU2PCI0_MEM_3_REMAP_WIN] = { + .base_hi_reg = MV64x60_CPU2PCI0_MEM_3_REMAP_HI, + .base_lo_reg = MV64x60_CPU2PCI0_MEM_3_REMAP_LO, + .size_reg = 0, + .base_lo_bits = 16, + .size_bits = 0, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + /* CPU->PCI 1 MEM Remap Windows */ + [MV64x60_CPU2PCI1_MEM_0_REMAP_WIN] = { + .base_hi_reg = MV64x60_CPU2PCI1_MEM_0_REMAP_HI, + .base_lo_reg = MV64x60_CPU2PCI1_MEM_0_REMAP_LO, + .size_reg = 0, + .base_lo_bits = 16, + .size_bits = 0, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_CPU2PCI1_MEM_1_REMAP_WIN] = { + .base_hi_reg = MV64x60_CPU2PCI1_MEM_1_REMAP_HI, + .base_lo_reg = MV64x60_CPU2PCI1_MEM_1_REMAP_LO, + .size_reg = 0, + .base_lo_bits = 16, + .size_bits = 0, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_CPU2PCI1_MEM_2_REMAP_WIN] = { + .base_hi_reg = MV64x60_CPU2PCI1_MEM_2_REMAP_HI, + .base_lo_reg = MV64x60_CPU2PCI1_MEM_2_REMAP_LO, + .size_reg = 0, + .base_lo_bits = 16, + .size_bits = 0, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + [MV64x60_CPU2PCI1_MEM_3_REMAP_WIN] = { + .base_hi_reg = MV64x60_CPU2PCI1_MEM_3_REMAP_HI, + .base_lo_reg = MV64x60_CPU2PCI1_MEM_3_REMAP_LO, + .size_reg = 0, + .base_lo_bits = 16, + .size_bits = 0, + .get_from_field = mv64x60_shift_left, + .map_to_field = mv64x60_shift_right, + .extra = 0 }, + /* PCI 0->MEM Access Control Windows */ + [MV64x60_PCI02MEM_ACC_CNTL_0_WIN] = { + .base_hi_reg = MV64x60_PCI0_ACC_CNTL_0_BASE_HI, + .base_lo_reg = MV64x60_PCI0_ACC_CNTL_0_BASE_LO, + .size_reg = MV64x60_PCI0_ACC_CNTL_0_SIZE, + .base_lo_bits = 20, + .size_bits = 20, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = MV64x60_EXTRA_PCIACC_ENAB | 0 }, + [MV64x60_PCI02MEM_ACC_CNTL_1_WIN] = { + .base_hi_reg = MV64x60_PCI0_ACC_CNTL_1_BASE_HI, + .base_lo_reg = MV64x60_PCI0_ACC_CNTL_1_BASE_LO, + .size_reg = MV64x60_PCI0_ACC_CNTL_1_SIZE, + .base_lo_bits = 20, + .size_bits = 20, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = MV64x60_EXTRA_PCIACC_ENAB | 0 }, + [MV64x60_PCI02MEM_ACC_CNTL_2_WIN] = { + .base_hi_reg = MV64x60_PCI0_ACC_CNTL_2_BASE_HI, + .base_lo_reg = MV64x60_PCI0_ACC_CNTL_2_BASE_LO, + .size_reg = MV64x60_PCI0_ACC_CNTL_2_SIZE, + .base_lo_bits = 20, + .size_bits = 20, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = MV64x60_EXTRA_PCIACC_ENAB | 0 }, + [MV64x60_PCI02MEM_ACC_CNTL_3_WIN] = { + .base_hi_reg = MV64x60_PCI0_ACC_CNTL_3_BASE_HI, + .base_lo_reg = MV64x60_PCI0_ACC_CNTL_3_BASE_LO, + .size_reg = MV64x60_PCI0_ACC_CNTL_3_SIZE, + .base_lo_bits = 20, + .size_bits = 20, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = MV64x60_EXTRA_PCIACC_ENAB | 0 }, + /* PCI 1->MEM Access Control Windows */ + [MV64x60_PCI12MEM_ACC_CNTL_0_WIN] = { + .base_hi_reg = MV64x60_PCI1_ACC_CNTL_0_BASE_HI, + .base_lo_reg = MV64x60_PCI1_ACC_CNTL_0_BASE_LO, + .size_reg = MV64x60_PCI1_ACC_CNTL_0_SIZE, + .base_lo_bits = 20, + .size_bits = 20, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = MV64x60_EXTRA_PCIACC_ENAB | 0 }, + [MV64x60_PCI12MEM_ACC_CNTL_1_WIN] = { + .base_hi_reg = MV64x60_PCI1_ACC_CNTL_1_BASE_HI, + .base_lo_reg = MV64x60_PCI1_ACC_CNTL_1_BASE_LO, + .size_reg = MV64x60_PCI1_ACC_CNTL_1_SIZE, + .base_lo_bits = 20, + .size_bits = 20, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = MV64x60_EXTRA_PCIACC_ENAB | 0 }, + [MV64x60_PCI12MEM_ACC_CNTL_2_WIN] = { + .base_hi_reg = MV64x60_PCI1_ACC_CNTL_2_BASE_HI, + .base_lo_reg = MV64x60_PCI1_ACC_CNTL_2_BASE_LO, + .size_reg = MV64x60_PCI1_ACC_CNTL_2_SIZE, + .base_lo_bits = 20, + .size_bits = 20, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = MV64x60_EXTRA_PCIACC_ENAB | 0 }, + [MV64x60_PCI12MEM_ACC_CNTL_3_WIN] = { + .base_hi_reg = MV64x60_PCI1_ACC_CNTL_3_BASE_HI, + .base_lo_reg = MV64x60_PCI1_ACC_CNTL_3_BASE_LO, + .size_reg = MV64x60_PCI1_ACC_CNTL_3_SIZE, + .base_lo_bits = 20, + .size_bits = 20, + .get_from_field = mv64x60_mask, + .map_to_field = mv64x60_mask, + .extra = MV64x60_EXTRA_PCIACC_ENAB | 0 }, + /* PCI 0->MEM Snoop Windows -- don't exist on 64360 */ + /* PCI 1->MEM Snoop Windows -- don't exist on 64360 */ +}; diff --git a/arch/ppc/syslib/ocp.c b/arch/ppc/syslib/ocp.c new file mode 100644 index 00000000000..a5156c5179a --- /dev/null +++ b/arch/ppc/syslib/ocp.c @@ -0,0 +1,485 @@ +/* + * ocp.c + * + * (c) Benjamin Herrenschmidt (benh@kernel.crashing.org) + * Mipsys - France + * + * Derived from work (c) Armin Kuster akuster@pacbell.net + * + * Additional support and port to 2.6 LDM/sysfs by + * Matt Porter + * Copyright 2004 MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * OCP (On Chip Peripheral) is a software emulated "bus" with a + * pseudo discovery method for dumb peripherals. Usually these type + * of peripherals are found on embedded SoC (System On a Chip) + * processors or highly integrated system controllers that have + * a host bridge and many peripherals. Common examples where + * this is already used include the PPC4xx, PPC85xx, MPC52xx, + * and MV64xxx parts. + * + * This subsystem creates a standard OCP bus type within the + * device model. The devices on the OCP bus are seeded by an + * an initial OCP device array created by the arch-specific + * Device entries can be added/removed/modified through OCP + * helper functions to accomodate system and board-specific + * parameters commonly found in embedded systems. OCP also + * provides a standard method for devices to describe extended + * attributes about themselves to the system. A standard access + * method allows OCP drivers to obtain the information, both + * SoC-specific and system/board-specific, needed for operation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +//#define DBG(x) printk x +#define DBG(x) + +extern int mem_init_done; + +extern struct ocp_def core_ocp[]; /* Static list of devices, provided by + CPU core */ + +LIST_HEAD(ocp_devices); /* List of all OCP devices */ +DECLARE_RWSEM(ocp_devices_sem); /* Global semaphores for those lists */ + +static int ocp_inited; + +/* Sysfs support */ +#define OCP_DEF_ATTR(field, format_string) \ +static ssize_t \ +show_##field(struct device *dev, char *buf) \ +{ \ + struct ocp_device *odev = to_ocp_dev(dev); \ + \ + return sprintf(buf, format_string, odev->def->field); \ +} \ +static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL); + +OCP_DEF_ATTR(vendor, "0x%04x\n"); +OCP_DEF_ATTR(function, "0x%04x\n"); +OCP_DEF_ATTR(index, "0x%04x\n"); +#ifdef CONFIG_PTE_64BIT +OCP_DEF_ATTR(paddr, "0x%016Lx\n"); +#else +OCP_DEF_ATTR(paddr, "0x%08lx\n"); +#endif +OCP_DEF_ATTR(irq, "%d\n"); +OCP_DEF_ATTR(pm, "%lu\n"); + +void ocp_create_sysfs_dev_files(struct ocp_device *odev) +{ + struct device *dev = &odev->dev; + + /* Current OCP device def attributes */ + device_create_file(dev, &dev_attr_vendor); + device_create_file(dev, &dev_attr_function); + device_create_file(dev, &dev_attr_index); + device_create_file(dev, &dev_attr_paddr); + device_create_file(dev, &dev_attr_irq); + device_create_file(dev, &dev_attr_pm); + /* Current OCP device additions attributes */ + if (odev->def->additions && odev->def->show) + odev->def->show(dev); +} + +/** + * ocp_device_match - Match one driver to one device + * @drv: driver to match + * @dev: device to match + * + * This function returns 0 if the driver and device don't match + */ +static int +ocp_device_match(struct device *dev, struct device_driver *drv) +{ + struct ocp_device *ocp_dev = to_ocp_dev(dev); + struct ocp_driver *ocp_drv = to_ocp_drv(drv); + const struct ocp_device_id *ids = ocp_drv->id_table; + + if (!ids) + return 0; + + while (ids->vendor || ids->function) { + if ((ids->vendor == OCP_ANY_ID + || ids->vendor == ocp_dev->def->vendor) + && (ids->function == OCP_ANY_ID + || ids->function == ocp_dev->def->function)) + return 1; + ids++; + } + return 0; +} + +static int +ocp_device_probe(struct device *dev) +{ + int error = 0; + struct ocp_driver *drv; + struct ocp_device *ocp_dev; + + drv = to_ocp_drv(dev->driver); + ocp_dev = to_ocp_dev(dev); + + if (drv->probe) { + error = drv->probe(ocp_dev); + if (error >= 0) { + ocp_dev->driver = drv; + error = 0; + } + } + return error; +} + +static int +ocp_device_remove(struct device *dev) +{ + struct ocp_device *ocp_dev = to_ocp_dev(dev); + + if (ocp_dev->driver) { + if (ocp_dev->driver->remove) + ocp_dev->driver->remove(ocp_dev); + ocp_dev->driver = NULL; + } + return 0; +} + +static int +ocp_device_suspend(struct device *dev, u32 state) +{ + struct ocp_device *ocp_dev = to_ocp_dev(dev); + struct ocp_driver *ocp_drv = to_ocp_drv(dev->driver); + + if (dev->driver && ocp_drv->suspend) + return ocp_drv->suspend(ocp_dev, state); + return 0; +} + +static int +ocp_device_resume(struct device *dev) +{ + struct ocp_device *ocp_dev = to_ocp_dev(dev); + struct ocp_driver *ocp_drv = to_ocp_drv(dev->driver); + + if (dev->driver && ocp_drv->resume) + return ocp_drv->resume(ocp_dev); + return 0; +} + +struct bus_type ocp_bus_type = { + .name = "ocp", + .match = ocp_device_match, + .suspend = ocp_device_suspend, + .resume = ocp_device_resume, +}; + +/** + * ocp_register_driver - Register an OCP driver + * @drv: pointer to statically defined ocp_driver structure + * + * The driver's probe() callback is called either recursively + * by this function or upon later call of ocp_driver_init + * + * NOTE: Detection of devices is a 2 pass step on this implementation, + * hotswap isn't supported. First, all OCP devices are put in the device + * list, _then_ all drivers are probed on each match. + */ +int +ocp_register_driver(struct ocp_driver *drv) +{ + /* initialize common driver fields */ + drv->driver.name = drv->name; + drv->driver.bus = &ocp_bus_type; + drv->driver.probe = ocp_device_probe; + drv->driver.remove = ocp_device_remove; + + /* register with core */ + return driver_register(&drv->driver); +} + +/** + * ocp_unregister_driver - Unregister an OCP driver + * @drv: pointer to statically defined ocp_driver structure + * + * The driver's remove() callback is called recursively + * by this function for any device already registered + */ +void +ocp_unregister_driver(struct ocp_driver *drv) +{ + DBG(("ocp: ocp_unregister_driver(%s)...\n", drv->name)); + + driver_unregister(&drv->driver); + + DBG(("ocp: ocp_unregister_driver(%s)... done.\n", drv->name)); +} + +/* Core of ocp_find_device(). Caller must hold ocp_devices_sem */ +static struct ocp_device * +__ocp_find_device(unsigned int vendor, unsigned int function, int index) +{ + struct list_head *entry; + struct ocp_device *dev, *found = NULL; + + DBG(("ocp: __ocp_find_device(vendor: %x, function: %x, index: %d)...\n", vendor, function, index)); + + list_for_each(entry, &ocp_devices) { + dev = list_entry(entry, struct ocp_device, link); + if (vendor != OCP_ANY_ID && vendor != dev->def->vendor) + continue; + if (function != OCP_ANY_ID && function != dev->def->function) + continue; + if (index != OCP_ANY_INDEX && index != dev->def->index) + continue; + found = dev; + break; + } + + DBG(("ocp: __ocp_find_device(vendor: %x, function: %x, index: %d)... done\n", vendor, function, index)); + + return found; +} + +/** + * ocp_find_device - Find a device by function & index + * @vendor: vendor ID of the device (or OCP_ANY_ID) + * @function: function code of the device (or OCP_ANY_ID) + * @idx: index of the device (or OCP_ANY_INDEX) + * + * This function allows a lookup of a given function by it's + * index, it's typically used to find the MAL or ZMII associated + * with an EMAC or similar horrors. + * You can pass vendor, though you usually want OCP_ANY_ID there... + */ +struct ocp_device * +ocp_find_device(unsigned int vendor, unsigned int function, int index) +{ + struct ocp_device *dev; + + down_read(&ocp_devices_sem); + dev = __ocp_find_device(vendor, function, index); + up_read(&ocp_devices_sem); + + return dev; +} + +/** + * ocp_get_one_device - Find a def by function & index + * @vendor: vendor ID of the device (or OCP_ANY_ID) + * @function: function code of the device (or OCP_ANY_ID) + * @idx: index of the device (or OCP_ANY_INDEX) + * + * This function allows a lookup of a given ocp_def by it's + * vendor, function, and index. The main purpose for is to + * allow modification of the def before binding to the driver + */ +struct ocp_def * +ocp_get_one_device(unsigned int vendor, unsigned int function, int index) +{ + struct ocp_device *dev; + struct ocp_def *found = NULL; + + DBG(("ocp: ocp_get_one_device(vendor: %x, function: %x, index: %d)...\n", + vendor, function, index)); + + dev = ocp_find_device(vendor, function, index); + + if (dev) + found = dev->def; + + DBG(("ocp: ocp_get_one_device(vendor: %x, function: %x, index: %d)... done.\n", + vendor, function, index)); + + return found; +} + +/** + * ocp_add_one_device - Add a device + * @def: static device definition structure + * + * This function adds a device definition to the + * device list. It may only be called before + * ocp_driver_init() and will return an error + * otherwise. + */ +int +ocp_add_one_device(struct ocp_def *def) +{ + struct ocp_device *dev; + + DBG(("ocp: ocp_add_one_device()...\n")); + + /* Can't be called after ocp driver init */ + if (ocp_inited) + return 1; + + if (mem_init_done) + dev = kmalloc(sizeof(*dev), GFP_KERNEL); + else + dev = alloc_bootmem(sizeof(*dev)); + + if (dev == NULL) + return 1; + memset(dev, 0, sizeof(*dev)); + dev->def = def; + dev->current_state = 4; + sprintf(dev->name, "OCP device %04x:%04x:%04x", + dev->def->vendor, dev->def->function, dev->def->index); + down_write(&ocp_devices_sem); + list_add_tail(&dev->link, &ocp_devices); + up_write(&ocp_devices_sem); + + DBG(("ocp: ocp_add_one_device()...done\n")); + + return 0; +} + +/** + * ocp_remove_one_device - Remove a device by function & index + * @vendor: vendor ID of the device (or OCP_ANY_ID) + * @function: function code of the device (or OCP_ANY_ID) + * @idx: index of the device (or OCP_ANY_INDEX) + * + * This function allows removal of a given function by its + * index. It may only be called before ocp_driver_init() + * and will return an error otherwise. + */ +int +ocp_remove_one_device(unsigned int vendor, unsigned int function, int index) +{ + struct ocp_device *dev; + + DBG(("ocp: ocp_remove_one_device(vendor: %x, function: %x, index: %d)...\n", vendor, function, index)); + + /* Can't be called after ocp driver init */ + if (ocp_inited) + return 1; + + down_write(&ocp_devices_sem); + dev = __ocp_find_device(vendor, function, index); + list_del((struct list_head *)dev); + up_write(&ocp_devices_sem); + + DBG(("ocp: ocp_remove_one_device(vendor: %x, function: %x, index: %d)... done.\n", vendor, function, index)); + + return 0; +} + +/** + * ocp_for_each_device - Iterate over OCP devices + * @callback: routine to execute for each ocp device. + * @arg: user data to be passed to callback routine. + * + * This routine holds the ocp_device semaphore, so the + * callback routine cannot modify the ocp_device list. + */ +void +ocp_for_each_device(void(*callback)(struct ocp_device *, void *arg), void *arg) +{ + struct list_head *entry; + + if (callback) { + down_read(&ocp_devices_sem); + list_for_each(entry, &ocp_devices) + callback(list_entry(entry, struct ocp_device, link), + arg); + up_read(&ocp_devices_sem); + } +} + +/** + * ocp_early_init - Init OCP device management + * + * This function builds the list of devices before setup_arch. + * This allows platform code to modify the device lists before + * they are bound to drivers (changes to paddr, removing devices + * etc) + */ +int __init +ocp_early_init(void) +{ + struct ocp_def *def; + + DBG(("ocp: ocp_early_init()...\n")); + + /* Fill the devices list */ + for (def = core_ocp; def->vendor != OCP_VENDOR_INVALID; def++) + ocp_add_one_device(def); + + DBG(("ocp: ocp_early_init()... done.\n")); + + return 0; +} + +/** + * ocp_driver_init - Init OCP device management + * + * This function is meant to be called via OCP bus registration. + */ +static int __init +ocp_driver_init(void) +{ + int ret = 0, index = 0; + struct device *ocp_bus; + struct list_head *entry; + struct ocp_device *dev; + + if (ocp_inited) + return ret; + ocp_inited = 1; + + DBG(("ocp: ocp_driver_init()...\n")); + + /* Allocate/register primary OCP bus */ + ocp_bus = kmalloc(sizeof(struct device), GFP_KERNEL); + if (ocp_bus == NULL) + return 1; + memset(ocp_bus, 0, sizeof(struct device)); + strcpy(ocp_bus->bus_id, "ocp"); + + bus_register(&ocp_bus_type); + + device_register(ocp_bus); + + /* Put each OCP device into global device list */ + list_for_each(entry, &ocp_devices) { + dev = list_entry(entry, struct ocp_device, link); + sprintf(dev->dev.bus_id, "%2.2x", index); + dev->dev.parent = ocp_bus; + dev->dev.bus = &ocp_bus_type; + device_register(&dev->dev); + ocp_create_sysfs_dev_files(dev); + index++; + } + + DBG(("ocp: ocp_driver_init()... done.\n")); + + return 0; +} + +postcore_initcall(ocp_driver_init); + +EXPORT_SYMBOL(ocp_bus_type); +EXPORT_SYMBOL(ocp_find_device); +EXPORT_SYMBOL(ocp_register_driver); +EXPORT_SYMBOL(ocp_unregister_driver); diff --git a/arch/ppc/syslib/of_device.c b/arch/ppc/syslib/of_device.c new file mode 100644 index 00000000000..46269ed21ae --- /dev/null +++ b/arch/ppc/syslib/of_device.c @@ -0,0 +1,273 @@ +#include +#include +#include +#include +#include +#include +#include + +/** + * of_match_device - Tell if an of_device structure has a matching + * of_match structure + * @ids: array of of device match structures to search in + * @dev: the of device structure to match against + * + * Used by a driver to check whether an of_device present in the + * system is in its list of supported devices. + */ +const struct of_match * of_match_device(const struct of_match *matches, + const struct of_device *dev) +{ + if (!dev->node) + return NULL; + while (matches->name || matches->type || matches->compatible) { + int match = 1; + if (matches->name && matches->name != OF_ANY_MATCH) + match &= dev->node->name + && !strcmp(matches->name, dev->node->name); + if (matches->type && matches->type != OF_ANY_MATCH) + match &= dev->node->type + && !strcmp(matches->type, dev->node->type); + if (matches->compatible && matches->compatible != OF_ANY_MATCH) + match &= device_is_compatible(dev->node, + matches->compatible); + if (match) + return matches; + matches++; + } + return NULL; +} + +static int of_platform_bus_match(struct device *dev, struct device_driver *drv) +{ + struct of_device * of_dev = to_of_device(dev); + struct of_platform_driver * of_drv = to_of_platform_driver(drv); + const struct of_match * matches = of_drv->match_table; + + if (!matches) + return 0; + + return of_match_device(matches, of_dev) != NULL; +} + +struct of_device *of_dev_get(struct of_device *dev) +{ + struct device *tmp; + + if (!dev) + return NULL; + tmp = get_device(&dev->dev); + if (tmp) + return to_of_device(tmp); + else + return NULL; +} + +void of_dev_put(struct of_device *dev) +{ + if (dev) + put_device(&dev->dev); +} + + +static int of_device_probe(struct device *dev) +{ + int error = -ENODEV; + struct of_platform_driver *drv; + struct of_device *of_dev; + const struct of_match *match; + + drv = to_of_platform_driver(dev->driver); + of_dev = to_of_device(dev); + + if (!drv->probe) + return error; + + of_dev_get(of_dev); + + match = of_match_device(drv->match_table, of_dev); + if (match) + error = drv->probe(of_dev, match); + if (error) + of_dev_put(of_dev); + + return error; +} + +static int of_device_remove(struct device *dev) +{ + struct of_device * of_dev = to_of_device(dev); + struct of_platform_driver * drv = to_of_platform_driver(dev->driver); + + if (dev->driver && drv->remove) + drv->remove(of_dev); + return 0; +} + +static int of_device_suspend(struct device *dev, u32 state) +{ + struct of_device * of_dev = to_of_device(dev); + struct of_platform_driver * drv = to_of_platform_driver(dev->driver); + int error = 0; + + if (dev->driver && drv->suspend) + error = drv->suspend(of_dev, state); + return error; +} + +static int of_device_resume(struct device * dev) +{ + struct of_device * of_dev = to_of_device(dev); + struct of_platform_driver * drv = to_of_platform_driver(dev->driver); + int error = 0; + + if (dev->driver && drv->resume) + error = drv->resume(of_dev); + return error; +} + +struct bus_type of_platform_bus_type = { + .name = "of_platform", + .match = of_platform_bus_match, + .suspend = of_device_suspend, + .resume = of_device_resume, +}; + +static int __init of_bus_driver_init(void) +{ + return bus_register(&of_platform_bus_type); +} + +postcore_initcall(of_bus_driver_init); + +int of_register_driver(struct of_platform_driver *drv) +{ + int count = 0; + + /* initialize common driver fields */ + drv->driver.name = drv->name; + drv->driver.bus = &of_platform_bus_type; + drv->driver.probe = of_device_probe; + drv->driver.remove = of_device_remove; + + /* register with core */ + count = driver_register(&drv->driver); + return count ? count : 1; +} + +void of_unregister_driver(struct of_platform_driver *drv) +{ + driver_unregister(&drv->driver); +} + + +static ssize_t dev_show_devspec(struct device *dev, char *buf) +{ + struct of_device *ofdev; + + ofdev = to_of_device(dev); + return sprintf(buf, "%s", ofdev->node->full_name); +} + +static DEVICE_ATTR(devspec, S_IRUGO, dev_show_devspec, NULL); + +/** + * of_release_dev - free an of device structure when all users of it are finished. + * @dev: device that's been disconnected + * + * Will be called only by the device core when all users of this of device are + * done. + */ +void of_release_dev(struct device *dev) +{ + struct of_device *ofdev; + + ofdev = to_of_device(dev); + of_node_put(ofdev->node); + kfree(ofdev); +} + +int of_device_register(struct of_device *ofdev) +{ + int rc; + struct of_device **odprop; + + BUG_ON(ofdev->node == NULL); + + odprop = (struct of_device **)get_property(ofdev->node, "linux,device", NULL); + if (!odprop) { + struct property *new_prop; + + new_prop = kmalloc(sizeof(struct property) + sizeof(struct of_device *), + GFP_KERNEL); + if (new_prop == NULL) + return -ENOMEM; + new_prop->name = "linux,device"; + new_prop->length = sizeof(sizeof(struct of_device *)); + new_prop->value = (unsigned char *)&new_prop[1]; + odprop = (struct of_device **)new_prop->value; + *odprop = NULL; + prom_add_property(ofdev->node, new_prop); + } + *odprop = ofdev; + + rc = device_register(&ofdev->dev); + if (rc) + return rc; + + device_create_file(&ofdev->dev, &dev_attr_devspec); + + return 0; +} + +void of_device_unregister(struct of_device *ofdev) +{ + struct of_device **odprop; + + device_remove_file(&ofdev->dev, &dev_attr_devspec); + + odprop = (struct of_device **)get_property(ofdev->node, "linux,device", NULL); + if (odprop) + *odprop = NULL; + + device_unregister(&ofdev->dev); +} + +struct of_device* of_platform_device_create(struct device_node *np, const char *bus_id) +{ + struct of_device *dev; + u32 *reg; + + dev = kmalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return NULL; + memset(dev, 0, sizeof(*dev)); + + dev->node = of_node_get(np); + dev->dma_mask = 0xffffffffUL; + dev->dev.dma_mask = &dev->dma_mask; + dev->dev.parent = NULL; + dev->dev.bus = &of_platform_bus_type; + dev->dev.release = of_release_dev; + + reg = (u32 *)get_property(np, "reg", NULL); + strlcpy(dev->dev.bus_id, bus_id, BUS_ID_SIZE); + + if (of_device_register(dev) != 0) { + kfree(dev); + return NULL; + } + + return dev; +} + +EXPORT_SYMBOL(of_match_device); +EXPORT_SYMBOL(of_platform_bus_type); +EXPORT_SYMBOL(of_register_driver); +EXPORT_SYMBOL(of_unregister_driver); +EXPORT_SYMBOL(of_device_register); +EXPORT_SYMBOL(of_device_unregister); +EXPORT_SYMBOL(of_dev_get); +EXPORT_SYMBOL(of_dev_put); +EXPORT_SYMBOL(of_platform_device_create); +EXPORT_SYMBOL(of_release_dev); diff --git a/arch/ppc/syslib/open_pic.c b/arch/ppc/syslib/open_pic.c new file mode 100644 index 00000000000..406f36a8a68 --- /dev/null +++ b/arch/ppc/syslib/open_pic.c @@ -0,0 +1,1083 @@ +/* + * arch/ppc/kernel/open_pic.c -- OpenPIC Interrupt Handling + * + * Copyright (C) 1997 Geert Uytterhoeven + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "open_pic_defs.h" + +#if defined(CONFIG_PRPMC800) || defined(CONFIG_85xx) +#define OPENPIC_BIG_ENDIAN +#endif + +void __iomem *OpenPIC_Addr; +static volatile struct OpenPIC __iomem *OpenPIC = NULL; + +/* + * We define OpenPIC_InitSenses table thusly: + * bit 0x1: sense, 0 for edge and 1 for level. + * bit 0x2: polarity, 0 for negative, 1 for positive. + */ +u_int OpenPIC_NumInitSenses __initdata = 0; +u_char *OpenPIC_InitSenses __initdata = NULL; +extern int use_of_interrupt_tree; + +static u_int NumProcessors; +static u_int NumSources; +static int open_pic_irq_offset; +static volatile OpenPIC_Source __iomem *ISR[NR_IRQS]; +static int openpic_cascade_irq = -1; +static int (*openpic_cascade_fn)(struct pt_regs *); + +/* Global Operations */ +static void openpic_disable_8259_pass_through(void); +static void openpic_set_spurious(u_int vector); + +#ifdef CONFIG_SMP +/* Interprocessor Interrupts */ +static void openpic_initipi(u_int ipi, u_int pri, u_int vector); +static irqreturn_t openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *); +#endif + +/* Timer Interrupts */ +static void openpic_inittimer(u_int timer, u_int pri, u_int vector); +static void openpic_maptimer(u_int timer, cpumask_t cpumask); + +/* Interrupt Sources */ +static void openpic_enable_irq(u_int irq); +static void openpic_disable_irq(u_int irq); +static void openpic_initirq(u_int irq, u_int pri, u_int vector, int polarity, + int is_level); +static void openpic_mapirq(u_int irq, cpumask_t cpumask, cpumask_t keepmask); + +/* + * These functions are not used but the code is kept here + * for completeness and future reference. + */ +#ifdef notused +static void openpic_enable_8259_pass_through(void); +static u_int openpic_get_priority(void); +static u_int openpic_get_spurious(void); +static void openpic_set_sense(u_int irq, int sense); +#endif /* notused */ + +/* + * Description of the openpic for the higher-level irq code + */ +static void openpic_end_irq(unsigned int irq_nr); +static void openpic_ack_irq(unsigned int irq_nr); +static void openpic_set_affinity(unsigned int irq_nr, cpumask_t cpumask); + +struct hw_interrupt_type open_pic = { + .typename = " OpenPIC ", + .enable = openpic_enable_irq, + .disable = openpic_disable_irq, + .ack = openpic_ack_irq, + .end = openpic_end_irq, + .set_affinity = openpic_set_affinity, +}; + +#ifdef CONFIG_SMP +static void openpic_end_ipi(unsigned int irq_nr); +static void openpic_ack_ipi(unsigned int irq_nr); +static void openpic_enable_ipi(unsigned int irq_nr); +static void openpic_disable_ipi(unsigned int irq_nr); + +struct hw_interrupt_type open_pic_ipi = { + .typename = " OpenPIC ", + .enable = openpic_enable_ipi, + .disable = openpic_disable_ipi, + .ack = openpic_ack_ipi, + .end = openpic_end_ipi, +}; +#endif /* CONFIG_SMP */ + +/* + * Accesses to the current processor's openpic registers + */ +#ifdef CONFIG_SMP +#define THIS_CPU Processor[cpu] +#define DECL_THIS_CPU int cpu = smp_hw_index[smp_processor_id()] +#define CHECK_THIS_CPU check_arg_cpu(cpu) +#else +#define THIS_CPU Processor[0] +#define DECL_THIS_CPU +#define CHECK_THIS_CPU +#endif /* CONFIG_SMP */ + +#if 1 +#define check_arg_ipi(ipi) \ + if (ipi < 0 || ipi >= OPENPIC_NUM_IPI) \ + printk("open_pic.c:%d: invalid ipi %d\n", __LINE__, ipi); +#define check_arg_timer(timer) \ + if (timer < 0 || timer >= OPENPIC_NUM_TIMERS) \ + printk("open_pic.c:%d: invalid timer %d\n", __LINE__, timer); +#define check_arg_vec(vec) \ + if (vec < 0 || vec >= OPENPIC_NUM_VECTORS) \ + printk("open_pic.c:%d: invalid vector %d\n", __LINE__, vec); +#define check_arg_pri(pri) \ + if (pri < 0 || pri >= OPENPIC_NUM_PRI) \ + printk("open_pic.c:%d: invalid priority %d\n", __LINE__, pri); +/* + * Print out a backtrace if it's out of range, since if it's larger than NR_IRQ's + * data has probably been corrupted and we're going to panic or deadlock later + * anyway --Troy + */ +#define check_arg_irq(irq) \ + if (irq < open_pic_irq_offset || irq >= NumSources+open_pic_irq_offset \ + || ISR[irq - open_pic_irq_offset] == 0) { \ + printk("open_pic.c:%d: invalid irq %d\n", __LINE__, irq); \ + dump_stack(); } +#define check_arg_cpu(cpu) \ + if (cpu < 0 || cpu >= NumProcessors){ \ + printk("open_pic.c:%d: invalid cpu %d\n", __LINE__, cpu); \ + dump_stack(); } +#else +#define check_arg_ipi(ipi) do {} while (0) +#define check_arg_timer(timer) do {} while (0) +#define check_arg_vec(vec) do {} while (0) +#define check_arg_pri(pri) do {} while (0) +#define check_arg_irq(irq) do {} while (0) +#define check_arg_cpu(cpu) do {} while (0) +#endif + +u_int openpic_read(volatile u_int __iomem *addr) +{ + u_int val; + +#ifdef OPENPIC_BIG_ENDIAN + val = in_be32(addr); +#else + val = in_le32(addr); +#endif + return val; +} + +static inline void openpic_write(volatile u_int __iomem *addr, u_int val) +{ +#ifdef OPENPIC_BIG_ENDIAN + out_be32(addr, val); +#else + out_le32(addr, val); +#endif +} + +static inline u_int openpic_readfield(volatile u_int __iomem *addr, u_int mask) +{ + u_int val = openpic_read(addr); + return val & mask; +} + +inline void openpic_writefield(volatile u_int __iomem *addr, u_int mask, + u_int field) +{ + u_int val = openpic_read(addr); + openpic_write(addr, (val & ~mask) | (field & mask)); +} + +static inline void openpic_clearfield(volatile u_int __iomem *addr, u_int mask) +{ + openpic_writefield(addr, mask, 0); +} + +static inline void openpic_setfield(volatile u_int __iomem *addr, u_int mask) +{ + openpic_writefield(addr, mask, mask); +} + +static void openpic_safe_writefield(volatile u_int __iomem *addr, u_int mask, + u_int field) +{ + openpic_setfield(addr, OPENPIC_MASK); + while (openpic_read(addr) & OPENPIC_ACTIVITY); + openpic_writefield(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK); +} + +#ifdef CONFIG_SMP +/* yes this is right ... bug, feature, you decide! -- tgall */ +u_int openpic_read_IPI(volatile u_int __iomem * addr) +{ + u_int val = 0; +#if defined(OPENPIC_BIG_ENDIAN) || defined(CONFIG_POWER3) + val = in_be32(addr); +#else + val = in_le32(addr); +#endif + return val; +} + +/* because of the power3 be / le above, this is needed */ +inline void openpic_writefield_IPI(volatile u_int __iomem * addr, u_int mask, u_int field) +{ + u_int val = openpic_read_IPI(addr); + openpic_write(addr, (val & ~mask) | (field & mask)); +} + +static inline void openpic_clearfield_IPI(volatile u_int __iomem *addr, u_int mask) +{ + openpic_writefield_IPI(addr, mask, 0); +} + +static inline void openpic_setfield_IPI(volatile u_int __iomem *addr, u_int mask) +{ + openpic_writefield_IPI(addr, mask, mask); +} + +static void openpic_safe_writefield_IPI(volatile u_int __iomem *addr, u_int mask, u_int field) +{ + openpic_setfield_IPI(addr, OPENPIC_MASK); + + /* wait until it's not in use */ + /* BenH: Is this code really enough ? I would rather check the result + * and eventually retry ... + */ + while(openpic_read_IPI(addr) & OPENPIC_ACTIVITY); + + openpic_writefield_IPI(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK); +} +#endif /* CONFIG_SMP */ + +#ifdef CONFIG_EPIC_SERIAL_MODE +/* On platforms that may use EPIC serial mode, the default is enabled. */ +int epic_serial_mode = 1; + +static void __init openpic_eicr_set_clk(u_int clkval) +{ + openpic_writefield(&OpenPIC->Global.Global_Configuration1, + OPENPIC_EICR_S_CLK_MASK, (clkval << 28)); +} + +static void __init openpic_enable_sie(void) +{ + openpic_setfield(&OpenPIC->Global.Global_Configuration1, + OPENPIC_EICR_SIE); +} +#endif + +#if defined(CONFIG_EPIC_SERIAL_MODE) || defined(CONFIG_PM) +static void openpic_reset(void) +{ + openpic_setfield(&OpenPIC->Global.Global_Configuration0, + OPENPIC_CONFIG_RESET); + while (openpic_readfield(&OpenPIC->Global.Global_Configuration0, + OPENPIC_CONFIG_RESET)) + mb(); +} +#endif + +void __init openpic_set_sources(int first_irq, int num_irqs, void __iomem *first_ISR) +{ + volatile OpenPIC_Source __iomem *src = first_ISR; + int i, last_irq; + + last_irq = first_irq + num_irqs; + if (last_irq > NumSources) + NumSources = last_irq; + if (src == 0) + src = &((struct OpenPIC __iomem *)OpenPIC_Addr)->Source[first_irq]; + for (i = first_irq; i < last_irq; ++i, ++src) + ISR[i] = src; +} + +/* + * The `offset' parameter defines where the interrupts handled by the + * OpenPIC start in the space of interrupt numbers that the kernel knows + * about. In other words, the OpenPIC's IRQ0 is numbered `offset' in the + * kernel's interrupt numbering scheme. + * We assume there is only one OpenPIC. + */ +void __init openpic_init(int offset) +{ + u_int t, i; + u_int timerfreq; + const char *version; + + if (!OpenPIC_Addr) { + printk("No OpenPIC found !\n"); + return; + } + OpenPIC = (volatile struct OpenPIC __iomem *)OpenPIC_Addr; + +#ifdef CONFIG_EPIC_SERIAL_MODE + /* Have to start from ground zero. + */ + openpic_reset(); +#endif + + if (ppc_md.progress) ppc_md.progress("openpic: enter", 0x122); + + t = openpic_read(&OpenPIC->Global.Feature_Reporting0); + switch (t & OPENPIC_FEATURE_VERSION_MASK) { + case 1: + version = "1.0"; + break; + case 2: + version = "1.2"; + break; + case 3: + version = "1.3"; + break; + default: + version = "?"; + break; + } + NumProcessors = ((t & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >> + OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT) + 1; + if (NumSources == 0) + openpic_set_sources(0, + ((t & OPENPIC_FEATURE_LAST_SOURCE_MASK) >> + OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1, + NULL); + printk("OpenPIC Version %s (%d CPUs and %d IRQ sources) at %p\n", + version, NumProcessors, NumSources, OpenPIC); + timerfreq = openpic_read(&OpenPIC->Global.Timer_Frequency); + if (timerfreq) + printk("OpenPIC timer frequency is %d.%06d MHz\n", + timerfreq / 1000000, timerfreq % 1000000); + + open_pic_irq_offset = offset; + + /* Initialize timer interrupts */ + if ( ppc_md.progress ) ppc_md.progress("openpic: timer",0x3ba); + for (i = 0; i < OPENPIC_NUM_TIMERS; i++) { + /* Disabled, Priority 0 */ + openpic_inittimer(i, 0, OPENPIC_VEC_TIMER+i+offset); + /* No processor */ + openpic_maptimer(i, CPU_MASK_NONE); + } + +#ifdef CONFIG_SMP + /* Initialize IPI interrupts */ + if ( ppc_md.progress ) ppc_md.progress("openpic: ipi",0x3bb); + for (i = 0; i < OPENPIC_NUM_IPI; i++) { + /* Disabled, Priority 10..13 */ + openpic_initipi(i, 10+i, OPENPIC_VEC_IPI+i+offset); + /* IPIs are per-CPU */ + irq_desc[OPENPIC_VEC_IPI+i+offset].status |= IRQ_PER_CPU; + irq_desc[OPENPIC_VEC_IPI+i+offset].handler = &open_pic_ipi; + } +#endif + + /* Initialize external interrupts */ + if (ppc_md.progress) ppc_md.progress("openpic: external",0x3bc); + + openpic_set_priority(0xf); + + /* Init all external sources, including possibly the cascade. */ + for (i = 0; i < NumSources; i++) { + int sense; + + if (ISR[i] == 0) + continue; + + /* the bootloader may have left it enabled (bad !) */ + openpic_disable_irq(i+offset); + + sense = (i < OpenPIC_NumInitSenses)? OpenPIC_InitSenses[i]: \ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE); + + if (sense & IRQ_SENSE_MASK) + irq_desc[i+offset].status = IRQ_LEVEL; + + /* Enabled, Priority 8 */ + openpic_initirq(i, 8, i+offset, (sense & IRQ_POLARITY_MASK), + (sense & IRQ_SENSE_MASK)); + /* Processor 0 */ + openpic_mapirq(i, CPU_MASK_CPU0, CPU_MASK_NONE); + } + + /* Init descriptors */ + for (i = offset; i < NumSources + offset; i++) + irq_desc[i].handler = &open_pic; + + /* Initialize the spurious interrupt */ + if (ppc_md.progress) ppc_md.progress("openpic: spurious",0x3bd); + openpic_set_spurious(OPENPIC_VEC_SPURIOUS); + openpic_disable_8259_pass_through(); +#ifdef CONFIG_EPIC_SERIAL_MODE + if (epic_serial_mode) { + openpic_eicr_set_clk(7); /* Slowest value until we know better */ + openpic_enable_sie(); + } +#endif + openpic_set_priority(0); + + if (ppc_md.progress) ppc_md.progress("openpic: exit",0x222); +} + +#ifdef notused +static void openpic_enable_8259_pass_through(void) +{ + openpic_clearfield(&OpenPIC->Global.Global_Configuration0, + OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); +} +#endif /* notused */ + +static void openpic_disable_8259_pass_through(void) +{ + openpic_setfield(&OpenPIC->Global.Global_Configuration0, + OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); +} + +/* + * Find out the current interrupt + */ +u_int openpic_irq(void) +{ + u_int vec; + DECL_THIS_CPU; + + CHECK_THIS_CPU; + vec = openpic_readfield(&OpenPIC->THIS_CPU.Interrupt_Acknowledge, + OPENPIC_VECTOR_MASK); + return vec; +} + +void openpic_eoi(void) +{ + DECL_THIS_CPU; + + CHECK_THIS_CPU; + openpic_write(&OpenPIC->THIS_CPU.EOI, 0); + /* Handle PCI write posting */ + (void)openpic_read(&OpenPIC->THIS_CPU.EOI); +} + +#ifdef notused +static u_int openpic_get_priority(void) +{ + DECL_THIS_CPU; + + CHECK_THIS_CPU; + return openpic_readfield(&OpenPIC->THIS_CPU.Current_Task_Priority, + OPENPIC_CURRENT_TASK_PRIORITY_MASK); +} +#endif /* notused */ + +void openpic_set_priority(u_int pri) +{ + DECL_THIS_CPU; + + CHECK_THIS_CPU; + check_arg_pri(pri); + openpic_writefield(&OpenPIC->THIS_CPU.Current_Task_Priority, + OPENPIC_CURRENT_TASK_PRIORITY_MASK, pri); +} + +/* + * Get/set the spurious vector + */ +#ifdef notused +static u_int openpic_get_spurious(void) +{ + return openpic_readfield(&OpenPIC->Global.Spurious_Vector, + OPENPIC_VECTOR_MASK); +} +#endif /* notused */ + +static void openpic_set_spurious(u_int vec) +{ + check_arg_vec(vec); + openpic_writefield(&OpenPIC->Global.Spurious_Vector, OPENPIC_VECTOR_MASK, + vec); +} + +#ifdef CONFIG_SMP +/* + * Convert a cpu mask from logical to physical cpu numbers. + */ +static inline cpumask_t physmask(cpumask_t cpumask) +{ + int i; + cpumask_t mask = CPU_MASK_NONE; + + cpus_and(cpumask, cpu_online_map, cpumask); + + for (i = 0; i < NR_CPUS; i++) + if (cpu_isset(i, cpumask)) + cpu_set(smp_hw_index[i], mask); + + return mask; +} +#else +#define physmask(cpumask) (cpumask) +#endif + +void openpic_reset_processor_phys(u_int mask) +{ + openpic_write(&OpenPIC->Global.Processor_Initialization, mask); +} + +#if defined(CONFIG_SMP) || defined(CONFIG_PM) +static DEFINE_SPINLOCK(openpic_setup_lock); +#endif + +#ifdef CONFIG_SMP +/* + * Initialize an interprocessor interrupt (and disable it) + * + * ipi: OpenPIC interprocessor interrupt number + * pri: interrupt source priority + * vec: the vector it will produce + */ +static void __init openpic_initipi(u_int ipi, u_int pri, u_int vec) +{ + check_arg_ipi(ipi); + check_arg_pri(pri); + check_arg_vec(vec); + openpic_safe_writefield_IPI(&OpenPIC->Global.IPI_Vector_Priority(ipi), + OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK, + (pri << OPENPIC_PRIORITY_SHIFT) | vec); +} + +/* + * Send an IPI to one or more CPUs + * + * Externally called, however, it takes an IPI number (0...OPENPIC_NUM_IPI) + * and not a system-wide interrupt number + */ +void openpic_cause_IPI(u_int ipi, cpumask_t cpumask) +{ + cpumask_t phys; + DECL_THIS_CPU; + + CHECK_THIS_CPU; + check_arg_ipi(ipi); + phys = physmask(cpumask); + openpic_write(&OpenPIC->THIS_CPU.IPI_Dispatch(ipi), + cpus_addr(physmask(cpumask))[0]); +} + +void openpic_request_IPIs(void) +{ + int i; + + /* + * Make sure this matches what is defined in smp.c for + * smp_message_{pass|recv}() or what shows up in + * /proc/interrupts will be wrong!!! --Troy */ + + if (OpenPIC == NULL) + return; + + /* IPIs are marked SA_INTERRUPT as they must run with irqs disabled */ + request_irq(OPENPIC_VEC_IPI+open_pic_irq_offset, + openpic_ipi_action, SA_INTERRUPT, + "IPI0 (call function)", NULL); + request_irq(OPENPIC_VEC_IPI+open_pic_irq_offset+1, + openpic_ipi_action, SA_INTERRUPT, + "IPI1 (reschedule)", NULL); + request_irq(OPENPIC_VEC_IPI+open_pic_irq_offset+2, + openpic_ipi_action, SA_INTERRUPT, + "IPI2 (invalidate tlb)", NULL); + request_irq(OPENPIC_VEC_IPI+open_pic_irq_offset+3, + openpic_ipi_action, SA_INTERRUPT, + "IPI3 (xmon break)", NULL); + + for ( i = 0; i < OPENPIC_NUM_IPI ; i++ ) + openpic_enable_ipi(OPENPIC_VEC_IPI+open_pic_irq_offset+i); +} + +/* + * Do per-cpu setup for SMP systems. + * + * Get IPI's working and start taking interrupts. + * -- Cort + */ + +void __devinit do_openpic_setup_cpu(void) +{ +#ifdef CONFIG_IRQ_ALL_CPUS + int i; + cpumask_t msk = CPU_MASK_NONE; +#endif + spin_lock(&openpic_setup_lock); + +#ifdef CONFIG_IRQ_ALL_CPUS + cpu_set(smp_hw_index[smp_processor_id()], msk); + + /* let the openpic know we want intrs. default affinity + * is 0xffffffff until changed via /proc + * That's how it's done on x86. If we want it differently, then + * we should make sure we also change the default values of irq_affinity + * in irq.c. + */ + for (i = 0; i < NumSources; i++) + openpic_mapirq(i, msk, CPU_MASK_ALL); +#endif /* CONFIG_IRQ_ALL_CPUS */ + openpic_set_priority(0); + + spin_unlock(&openpic_setup_lock); +} +#endif /* CONFIG_SMP */ + +/* + * Initialize a timer interrupt (and disable it) + * + * timer: OpenPIC timer number + * pri: interrupt source priority + * vec: the vector it will produce + */ +static void __init openpic_inittimer(u_int timer, u_int pri, u_int vec) +{ + check_arg_timer(timer); + check_arg_pri(pri); + check_arg_vec(vec); + openpic_safe_writefield(&OpenPIC->Global.Timer[timer].Vector_Priority, + OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK, + (pri << OPENPIC_PRIORITY_SHIFT) | vec); +} + +/* + * Map a timer interrupt to one or more CPUs + */ +static void __init openpic_maptimer(u_int timer, cpumask_t cpumask) +{ + cpumask_t phys = physmask(cpumask); + check_arg_timer(timer); + openpic_write(&OpenPIC->Global.Timer[timer].Destination, + cpus_addr(phys)[0]); +} + +/* + * Initalize the interrupt source which will generate an NMI. + * This raises the interrupt's priority from 8 to 9. + * + * irq: The logical IRQ which generates an NMI. + */ +void __init +openpic_init_nmi_irq(u_int irq) +{ + check_arg_irq(irq); + openpic_safe_writefield(&ISR[irq - open_pic_irq_offset]->Vector_Priority, + OPENPIC_PRIORITY_MASK, + 9 << OPENPIC_PRIORITY_SHIFT); +} + +/* + * + * All functions below take an offset'ed irq argument + * + */ + +/* + * Hookup a cascade to the OpenPIC. + */ + +static struct irqaction openpic_cascade_irqaction = { + .handler = no_action, + .flags = SA_INTERRUPT, + .mask = CPU_MASK_NONE, +}; + +void __init +openpic_hookup_cascade(u_int irq, char *name, + int (*cascade_fn)(struct pt_regs *)) +{ + openpic_cascade_irq = irq; + openpic_cascade_fn = cascade_fn; + + if (setup_irq(irq, &openpic_cascade_irqaction)) + printk("Unable to get OpenPIC IRQ %d for cascade\n", + irq - open_pic_irq_offset); +} + +/* + * Enable/disable an external interrupt source + * + * Externally called, irq is an offseted system-wide interrupt number + */ +static void openpic_enable_irq(u_int irq) +{ + volatile u_int __iomem *vpp; + + check_arg_irq(irq); + vpp = &ISR[irq - open_pic_irq_offset]->Vector_Priority; + openpic_clearfield(vpp, OPENPIC_MASK); + /* make sure mask gets to controller before we return to user */ + do { + mb(); /* sync is probably useless here */ + } while (openpic_readfield(vpp, OPENPIC_MASK)); +} + +static void openpic_disable_irq(u_int irq) +{ + volatile u_int __iomem *vpp; + u32 vp; + + check_arg_irq(irq); + vpp = &ISR[irq - open_pic_irq_offset]->Vector_Priority; + openpic_setfield(vpp, OPENPIC_MASK); + /* make sure mask gets to controller before we return to user */ + do { + mb(); /* sync is probably useless here */ + vp = openpic_readfield(vpp, OPENPIC_MASK | OPENPIC_ACTIVITY); + } while((vp & OPENPIC_ACTIVITY) && !(vp & OPENPIC_MASK)); +} + +#ifdef CONFIG_SMP +/* + * Enable/disable an IPI interrupt source + * + * Externally called, irq is an offseted system-wide interrupt number + */ +void openpic_enable_ipi(u_int irq) +{ + irq -= (OPENPIC_VEC_IPI+open_pic_irq_offset); + check_arg_ipi(irq); + openpic_clearfield_IPI(&OpenPIC->Global.IPI_Vector_Priority(irq), OPENPIC_MASK); + +} + +void openpic_disable_ipi(u_int irq) +{ + irq -= (OPENPIC_VEC_IPI+open_pic_irq_offset); + check_arg_ipi(irq); + openpic_setfield_IPI(&OpenPIC->Global.IPI_Vector_Priority(irq), OPENPIC_MASK); +} +#endif + +/* + * Initialize an interrupt source (and disable it!) + * + * irq: OpenPIC interrupt number + * pri: interrupt source priority + * vec: the vector it will produce + * pol: polarity (1 for positive, 0 for negative) + * sense: 1 for level, 0 for edge + */ +static void __init +openpic_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense) +{ + openpic_safe_writefield(&ISR[irq]->Vector_Priority, + OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK | + OPENPIC_SENSE_MASK | OPENPIC_POLARITY_MASK, + (pri << OPENPIC_PRIORITY_SHIFT) | vec | + (pol ? OPENPIC_POLARITY_POSITIVE : + OPENPIC_POLARITY_NEGATIVE) | + (sense ? OPENPIC_SENSE_LEVEL : OPENPIC_SENSE_EDGE)); +} + +/* + * Map an interrupt source to one or more CPUs + */ +static void openpic_mapirq(u_int irq, cpumask_t physmask, cpumask_t keepmask) +{ + if (ISR[irq] == 0) + return; + if (!cpus_empty(keepmask)) { + cpumask_t irqdest = { .bits[0] = openpic_read(&ISR[irq]->Destination) }; + cpus_and(irqdest, irqdest, keepmask); + cpus_or(physmask, physmask, irqdest); + } + openpic_write(&ISR[irq]->Destination, cpus_addr(physmask)[0]); +} + +#ifdef notused +/* + * Set the sense for an interrupt source (and disable it!) + * + * sense: 1 for level, 0 for edge + */ +static void openpic_set_sense(u_int irq, int sense) +{ + if (ISR[irq] != 0) + openpic_safe_writefield(&ISR[irq]->Vector_Priority, + OPENPIC_SENSE_LEVEL, + (sense ? OPENPIC_SENSE_LEVEL : 0)); +} +#endif /* notused */ + +/* No spinlocks, should not be necessary with the OpenPIC + * (1 register = 1 interrupt and we have the desc lock). + */ +static void openpic_ack_irq(unsigned int irq_nr) +{ +#ifdef __SLOW_VERSION__ + openpic_disable_irq(irq_nr); + openpic_eoi(); +#else + if ((irq_desc[irq_nr].status & IRQ_LEVEL) == 0) + openpic_eoi(); +#endif +} + +static void openpic_end_irq(unsigned int irq_nr) +{ +#ifdef __SLOW_VERSION__ + if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS)) + && irq_desc[irq_nr].action) + openpic_enable_irq(irq_nr); +#else + if ((irq_desc[irq_nr].status & IRQ_LEVEL) != 0) + openpic_eoi(); +#endif +} + +static void openpic_set_affinity(unsigned int irq_nr, cpumask_t cpumask) +{ + openpic_mapirq(irq_nr - open_pic_irq_offset, physmask(cpumask), CPU_MASK_NONE); +} + +#ifdef CONFIG_SMP +static void openpic_ack_ipi(unsigned int irq_nr) +{ + openpic_eoi(); +} + +static void openpic_end_ipi(unsigned int irq_nr) +{ +} + +static irqreturn_t openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *regs) +{ + smp_message_recv(cpl-OPENPIC_VEC_IPI-open_pic_irq_offset, regs); + return IRQ_HANDLED; +} + +#endif /* CONFIG_SMP */ + +int +openpic_get_irq(struct pt_regs *regs) +{ + int irq = openpic_irq(); + + /* + * Check for the cascade interrupt and call the cascaded + * interrupt controller function (usually i8259_irq) if so. + * This should move to irq.c eventually. -- paulus + */ + if (irq == openpic_cascade_irq && openpic_cascade_fn != NULL) { + int cirq = openpic_cascade_fn(regs); + + /* Allow for the cascade being shared with other devices */ + if (cirq != -1) { + irq = cirq; + openpic_eoi(); + } + } else if (irq == OPENPIC_VEC_SPURIOUS) + irq = -1; + return irq; +} + +#ifdef CONFIG_SMP +void +smp_openpic_message_pass(int target, int msg, unsigned long data, int wait) +{ + cpumask_t mask = CPU_MASK_ALL; + /* make sure we're sending something that translates to an IPI */ + if (msg > 0x3) { + printk("SMP %d: smp_message_pass: unknown msg %d\n", + smp_processor_id(), msg); + return; + } + switch (target) { + case MSG_ALL: + openpic_cause_IPI(msg, mask); + break; + case MSG_ALL_BUT_SELF: + cpu_clear(smp_processor_id(), mask); + openpic_cause_IPI(msg, mask); + break; + default: + openpic_cause_IPI(msg, cpumask_of_cpu(target)); + break; + } +} +#endif /* CONFIG_SMP */ + +#ifdef CONFIG_PM + +/* + * We implement the IRQ controller as a sysdev and put it + * to sleep at powerdown stage (the callback is named suspend, + * but it's old semantics, for the Device Model, it's really + * powerdown). The possible problem is that another sysdev that + * happens to be suspend after this one will have interrupts off, + * that may be an issue... For now, this isn't an issue on pmac + * though... + */ + +static u32 save_ipi_vp[OPENPIC_NUM_IPI]; +static u32 save_irq_src_vp[OPENPIC_MAX_SOURCES]; +static u32 save_irq_src_dest[OPENPIC_MAX_SOURCES]; +static u32 save_cpu_task_pri[OPENPIC_MAX_PROCESSORS]; +static int openpic_suspend_count; + +static void openpic_cached_enable_irq(u_int irq) +{ + check_arg_irq(irq); + save_irq_src_vp[irq - open_pic_irq_offset] &= ~OPENPIC_MASK; +} + +static void openpic_cached_disable_irq(u_int irq) +{ + check_arg_irq(irq); + save_irq_src_vp[irq - open_pic_irq_offset] |= OPENPIC_MASK; +} + +/* WARNING: Can be called directly by the cpufreq code with NULL parameter, + * we need something better to deal with that... Maybe switch to S1 for + * cpufreq changes + */ +int openpic_suspend(struct sys_device *sysdev, u32 state) +{ + int i; + unsigned long flags; + + spin_lock_irqsave(&openpic_setup_lock, flags); + + if (openpic_suspend_count++ > 0) { + spin_unlock_irqrestore(&openpic_setup_lock, flags); + return 0; + } + + openpic_set_priority(0xf); + + open_pic.enable = openpic_cached_enable_irq; + open_pic.disable = openpic_cached_disable_irq; + + for (i=0; iProcessor[i].Current_Task_Priority); + openpic_writefield(&OpenPIC->Processor[i].Current_Task_Priority, + OPENPIC_CURRENT_TASK_PRIORITY_MASK, 0xf); + } + + for (i=0; iGlobal.IPI_Vector_Priority(i)); + for (i=0; iVector_Priority) & ~OPENPIC_ACTIVITY; + save_irq_src_dest[i] = openpic_read(&ISR[i]->Destination); + } + + spin_unlock_irqrestore(&openpic_setup_lock, flags); + + return 0; +} + +/* WARNING: Can be called directly by the cpufreq code with NULL parameter, + * we need something better to deal with that... Maybe switch to S1 for + * cpufreq changes + */ +int openpic_resume(struct sys_device *sysdev) +{ + int i; + unsigned long flags; + u32 vppmask = OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK | + OPENPIC_SENSE_MASK | OPENPIC_POLARITY_MASK | + OPENPIC_MASK; + + spin_lock_irqsave(&openpic_setup_lock, flags); + + if ((--openpic_suspend_count) > 0) { + spin_unlock_irqrestore(&openpic_setup_lock, flags); + return 0; + } + + openpic_reset(); + + /* OpenPIC sometimes seem to need some time to be fully back up... */ + do { + openpic_set_spurious(OPENPIC_VEC_SPURIOUS); + } while(openpic_readfield(&OpenPIC->Global.Spurious_Vector, OPENPIC_VECTOR_MASK) + != OPENPIC_VEC_SPURIOUS); + + openpic_disable_8259_pass_through(); + + for (i=0; iGlobal.IPI_Vector_Priority(i), + save_ipi_vp[i]); + for (i=0; iDestination, save_irq_src_dest[i]); + openpic_write(&ISR[i]->Vector_Priority, save_irq_src_vp[i]); + /* make sure mask gets to controller before we return to user */ + do { + openpic_write(&ISR[i]->Vector_Priority, save_irq_src_vp[i]); + } while (openpic_readfield(&ISR[i]->Vector_Priority, vppmask) + != (save_irq_src_vp[i] & vppmask)); + } + for (i=0; iProcessor[i].Current_Task_Priority, + save_cpu_task_pri[i]); + + open_pic.enable = openpic_enable_irq; + open_pic.disable = openpic_disable_irq; + + openpic_set_priority(0); + + spin_unlock_irqrestore(&openpic_setup_lock, flags); + + return 0; +} + +#endif /* CONFIG_PM */ + +static struct sysdev_class openpic_sysclass = { + set_kset_name("openpic"), +}; + +static struct sys_device device_openpic = { + .id = 0, + .cls = &openpic_sysclass, +}; + +static struct sysdev_driver driver_openpic = { +#ifdef CONFIG_PM + .suspend = &openpic_suspend, + .resume = &openpic_resume, +#endif /* CONFIG_PM */ +}; + +static int __init init_openpic_sysfs(void) +{ + int rc; + + if (!OpenPIC_Addr) + return -ENODEV; + printk(KERN_DEBUG "Registering openpic with sysfs...\n"); + rc = sysdev_class_register(&openpic_sysclass); + if (rc) { + printk(KERN_ERR "Failed registering openpic sys class\n"); + return -ENODEV; + } + rc = sysdev_register(&device_openpic); + if (rc) { + printk(KERN_ERR "Failed registering openpic sys device\n"); + return -ENODEV; + } + rc = sysdev_driver_register(&openpic_sysclass, &driver_openpic); + if (rc) { + printk(KERN_ERR "Failed registering openpic sys driver\n"); + return -ENODEV; + } + return 0; +} + +subsys_initcall(init_openpic_sysfs); + diff --git a/arch/ppc/syslib/open_pic2.c b/arch/ppc/syslib/open_pic2.c new file mode 100644 index 00000000000..ea26da0d8b6 --- /dev/null +++ b/arch/ppc/syslib/open_pic2.c @@ -0,0 +1,716 @@ +/* + * arch/ppc/kernel/open_pic.c -- OpenPIC Interrupt Handling + * + * Copyright (C) 1997 Geert Uytterhoeven + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + * + * This is a duplicate of open_pic.c that deals with U3s MPIC on + * G5 PowerMacs. It's the same file except it's using big endian + * register accesses + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "open_pic_defs.h" + +void *OpenPIC2_Addr; +static volatile struct OpenPIC *OpenPIC2 = NULL; +/* + * We define OpenPIC_InitSenses table thusly: + * bit 0x1: sense, 0 for edge and 1 for level. + * bit 0x2: polarity, 0 for negative, 1 for positive. + */ +extern u_int OpenPIC_NumInitSenses; +extern u_char *OpenPIC_InitSenses; +extern int use_of_interrupt_tree; + +static u_int NumProcessors; +static u_int NumSources; +static int open_pic2_irq_offset; +static volatile OpenPIC_Source *ISR[NR_IRQS]; + +/* Global Operations */ +static void openpic2_disable_8259_pass_through(void); +static void openpic2_set_priority(u_int pri); +static void openpic2_set_spurious(u_int vector); + +/* Timer Interrupts */ +static void openpic2_inittimer(u_int timer, u_int pri, u_int vector); +static void openpic2_maptimer(u_int timer, u_int cpumask); + +/* Interrupt Sources */ +static void openpic2_enable_irq(u_int irq); +static void openpic2_disable_irq(u_int irq); +static void openpic2_initirq(u_int irq, u_int pri, u_int vector, int polarity, + int is_level); +static void openpic2_mapirq(u_int irq, u_int cpumask, u_int keepmask); + +/* + * These functions are not used but the code is kept here + * for completeness and future reference. + */ +static void openpic2_reset(void); +#ifdef notused +static void openpic2_enable_8259_pass_through(void); +static u_int openpic2_get_priority(void); +static u_int openpic2_get_spurious(void); +static void openpic2_set_sense(u_int irq, int sense); +#endif /* notused */ + +/* + * Description of the openpic for the higher-level irq code + */ +static void openpic2_end_irq(unsigned int irq_nr); +static void openpic2_ack_irq(unsigned int irq_nr); + +struct hw_interrupt_type open_pic2 = { + " OpenPIC2 ", + NULL, + NULL, + openpic2_enable_irq, + openpic2_disable_irq, + openpic2_ack_irq, + openpic2_end_irq, +}; + +/* + * Accesses to the current processor's openpic registers + * On cascaded controller, this is only CPU 0 + */ +#define THIS_CPU Processor[0] +#define DECL_THIS_CPU +#define CHECK_THIS_CPU + +#if 1 +#define check_arg_ipi(ipi) \ + if (ipi < 0 || ipi >= OPENPIC_NUM_IPI) \ + printk("open_pic.c:%d: illegal ipi %d\n", __LINE__, ipi); +#define check_arg_timer(timer) \ + if (timer < 0 || timer >= OPENPIC_NUM_TIMERS) \ + printk("open_pic.c:%d: illegal timer %d\n", __LINE__, timer); +#define check_arg_vec(vec) \ + if (vec < 0 || vec >= OPENPIC_NUM_VECTORS) \ + printk("open_pic.c:%d: illegal vector %d\n", __LINE__, vec); +#define check_arg_pri(pri) \ + if (pri < 0 || pri >= OPENPIC_NUM_PRI) \ + printk("open_pic.c:%d: illegal priority %d\n", __LINE__, pri); +/* + * Print out a backtrace if it's out of range, since if it's larger than NR_IRQ's + * data has probably been corrupted and we're going to panic or deadlock later + * anyway --Troy + */ +extern unsigned long* _get_SP(void); +#define check_arg_irq(irq) \ + if (irq < open_pic2_irq_offset || irq >= NumSources+open_pic2_irq_offset \ + || ISR[irq - open_pic2_irq_offset] == 0) { \ + printk("open_pic.c:%d: illegal irq %d\n", __LINE__, irq); \ + /*print_backtrace(_get_SP());*/ } +#define check_arg_cpu(cpu) \ + if (cpu < 0 || cpu >= NumProcessors){ \ + printk("open_pic2.c:%d: illegal cpu %d\n", __LINE__, cpu); \ + /*print_backtrace(_get_SP());*/ } +#else +#define check_arg_ipi(ipi) do {} while (0) +#define check_arg_timer(timer) do {} while (0) +#define check_arg_vec(vec) do {} while (0) +#define check_arg_pri(pri) do {} while (0) +#define check_arg_irq(irq) do {} while (0) +#define check_arg_cpu(cpu) do {} while (0) +#endif + +static u_int openpic2_read(volatile u_int *addr) +{ + u_int val; + + val = in_be32(addr); + return val; +} + +static inline void openpic2_write(volatile u_int *addr, u_int val) +{ + out_be32(addr, val); +} + +static inline u_int openpic2_readfield(volatile u_int *addr, u_int mask) +{ + u_int val = openpic2_read(addr); + return val & mask; +} + +inline void openpic2_writefield(volatile u_int *addr, u_int mask, + u_int field) +{ + u_int val = openpic2_read(addr); + openpic2_write(addr, (val & ~mask) | (field & mask)); +} + +static inline void openpic2_clearfield(volatile u_int *addr, u_int mask) +{ + openpic2_writefield(addr, mask, 0); +} + +static inline void openpic2_setfield(volatile u_int *addr, u_int mask) +{ + openpic2_writefield(addr, mask, mask); +} + +static void openpic2_safe_writefield(volatile u_int *addr, u_int mask, + u_int field) +{ + openpic2_setfield(addr, OPENPIC_MASK); + while (openpic2_read(addr) & OPENPIC_ACTIVITY); + openpic2_writefield(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK); +} + +static void openpic2_reset(void) +{ + openpic2_setfield(&OpenPIC2->Global.Global_Configuration0, + OPENPIC_CONFIG_RESET); + while (openpic2_readfield(&OpenPIC2->Global.Global_Configuration0, + OPENPIC_CONFIG_RESET)) + mb(); +} + +void __init openpic2_set_sources(int first_irq, int num_irqs, void *first_ISR) +{ + volatile OpenPIC_Source *src = first_ISR; + int i, last_irq; + + last_irq = first_irq + num_irqs; + if (last_irq > NumSources) + NumSources = last_irq; + if (src == 0) + src = &((struct OpenPIC *)OpenPIC2_Addr)->Source[first_irq]; + for (i = first_irq; i < last_irq; ++i, ++src) + ISR[i] = src; +} + +/* + * The `offset' parameter defines where the interrupts handled by the + * OpenPIC start in the space of interrupt numbers that the kernel knows + * about. In other words, the OpenPIC's IRQ0 is numbered `offset' in the + * kernel's interrupt numbering scheme. + * We assume there is only one OpenPIC. + */ +void __init openpic2_init(int offset) +{ + u_int t, i; + u_int timerfreq; + const char *version; + + if (!OpenPIC2_Addr) { + printk("No OpenPIC2 found !\n"); + return; + } + OpenPIC2 = (volatile struct OpenPIC *)OpenPIC2_Addr; + + if (ppc_md.progress) ppc_md.progress("openpic: enter", 0x122); + + t = openpic2_read(&OpenPIC2->Global.Feature_Reporting0); + switch (t & OPENPIC_FEATURE_VERSION_MASK) { + case 1: + version = "1.0"; + break; + case 2: + version = "1.2"; + break; + case 3: + version = "1.3"; + break; + default: + version = "?"; + break; + } + NumProcessors = ((t & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >> + OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT) + 1; + if (NumSources == 0) + openpic2_set_sources(0, + ((t & OPENPIC_FEATURE_LAST_SOURCE_MASK) >> + OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1, + NULL); + printk("OpenPIC (2) Version %s (%d CPUs and %d IRQ sources) at %p\n", + version, NumProcessors, NumSources, OpenPIC2); + timerfreq = openpic2_read(&OpenPIC2->Global.Timer_Frequency); + if (timerfreq) + printk("OpenPIC timer frequency is %d.%06d MHz\n", + timerfreq / 1000000, timerfreq % 1000000); + + open_pic2_irq_offset = offset; + + /* Initialize timer interrupts */ + if ( ppc_md.progress ) ppc_md.progress("openpic2: timer",0x3ba); + for (i = 0; i < OPENPIC_NUM_TIMERS; i++) { + /* Disabled, Priority 0 */ + openpic2_inittimer(i, 0, OPENPIC2_VEC_TIMER+i+offset); + /* No processor */ + openpic2_maptimer(i, 0); + } + + /* Initialize external interrupts */ + if (ppc_md.progress) ppc_md.progress("openpic2: external",0x3bc); + + openpic2_set_priority(0xf); + + /* Init all external sources, including possibly the cascade. */ + for (i = 0; i < NumSources; i++) { + int sense; + + if (ISR[i] == 0) + continue; + + /* the bootloader may have left it enabled (bad !) */ + openpic2_disable_irq(i+offset); + + sense = (i < OpenPIC_NumInitSenses)? OpenPIC_InitSenses[i]: \ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE); + + if (sense & IRQ_SENSE_MASK) + irq_desc[i+offset].status = IRQ_LEVEL; + + /* Enabled, Priority 8 */ + openpic2_initirq(i, 8, i+offset, (sense & IRQ_POLARITY_MASK), + (sense & IRQ_SENSE_MASK)); + /* Processor 0 */ + openpic2_mapirq(i, 1<<0, 0); + } + + /* Init descriptors */ + for (i = offset; i < NumSources + offset; i++) + irq_desc[i].handler = &open_pic2; + + /* Initialize the spurious interrupt */ + if (ppc_md.progress) ppc_md.progress("openpic2: spurious",0x3bd); + openpic2_set_spurious(OPENPIC2_VEC_SPURIOUS+offset); + + openpic2_disable_8259_pass_through(); + openpic2_set_priority(0); + + if (ppc_md.progress) ppc_md.progress("openpic2: exit",0x222); +} + +#ifdef notused +static void openpic2_enable_8259_pass_through(void) +{ + openpic2_clearfield(&OpenPIC2->Global.Global_Configuration0, + OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); +} +#endif /* notused */ + +/* This can't be __init, it is used in openpic_sleep_restore_intrs */ +static void openpic2_disable_8259_pass_through(void) +{ + openpic2_setfield(&OpenPIC2->Global.Global_Configuration0, + OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); +} + +/* + * Find out the current interrupt + */ +u_int openpic2_irq(void) +{ + u_int vec; + DECL_THIS_CPU; + + CHECK_THIS_CPU; + vec = openpic2_readfield(&OpenPIC2->THIS_CPU.Interrupt_Acknowledge, + OPENPIC_VECTOR_MASK); + return vec; +} + +void openpic2_eoi(void) +{ + DECL_THIS_CPU; + + CHECK_THIS_CPU; + openpic2_write(&OpenPIC2->THIS_CPU.EOI, 0); + /* Handle PCI write posting */ + (void)openpic2_read(&OpenPIC2->THIS_CPU.EOI); +} + +#ifdef notused +static u_int openpic2_get_priority(void) +{ + DECL_THIS_CPU; + + CHECK_THIS_CPU; + return openpic2_readfield(&OpenPIC2->THIS_CPU.Current_Task_Priority, + OPENPIC_CURRENT_TASK_PRIORITY_MASK); +} +#endif /* notused */ + +static void __init openpic2_set_priority(u_int pri) +{ + DECL_THIS_CPU; + + CHECK_THIS_CPU; + check_arg_pri(pri); + openpic2_writefield(&OpenPIC2->THIS_CPU.Current_Task_Priority, + OPENPIC_CURRENT_TASK_PRIORITY_MASK, pri); +} + +/* + * Get/set the spurious vector + */ +#ifdef notused +static u_int openpic2_get_spurious(void) +{ + return openpic2_readfield(&OpenPIC2->Global.Spurious_Vector, + OPENPIC_VECTOR_MASK); +} +#endif /* notused */ + +/* This can't be __init, it is used in openpic_sleep_restore_intrs */ +static void openpic2_set_spurious(u_int vec) +{ + check_arg_vec(vec); + openpic2_writefield(&OpenPIC2->Global.Spurious_Vector, OPENPIC_VECTOR_MASK, + vec); +} + +static DEFINE_SPINLOCK(openpic2_setup_lock); + +/* + * Initialize a timer interrupt (and disable it) + * + * timer: OpenPIC timer number + * pri: interrupt source priority + * vec: the vector it will produce + */ +static void __init openpic2_inittimer(u_int timer, u_int pri, u_int vec) +{ + check_arg_timer(timer); + check_arg_pri(pri); + check_arg_vec(vec); + openpic2_safe_writefield(&OpenPIC2->Global.Timer[timer].Vector_Priority, + OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK, + (pri << OPENPIC_PRIORITY_SHIFT) | vec); +} + +/* + * Map a timer interrupt to one or more CPUs + */ +static void __init openpic2_maptimer(u_int timer, u_int cpumask) +{ + check_arg_timer(timer); + openpic2_write(&OpenPIC2->Global.Timer[timer].Destination, + cpumask); +} + +/* + * Initalize the interrupt source which will generate an NMI. + * This raises the interrupt's priority from 8 to 9. + * + * irq: The logical IRQ which generates an NMI. + */ +void __init +openpic2_init_nmi_irq(u_int irq) +{ + check_arg_irq(irq); + openpic2_safe_writefield(&ISR[irq - open_pic2_irq_offset]->Vector_Priority, + OPENPIC_PRIORITY_MASK, + 9 << OPENPIC_PRIORITY_SHIFT); +} + +/* + * + * All functions below take an offset'ed irq argument + * + */ + + +/* + * Enable/disable an external interrupt source + * + * Externally called, irq is an offseted system-wide interrupt number + */ +static void openpic2_enable_irq(u_int irq) +{ + volatile u_int *vpp; + + check_arg_irq(irq); + vpp = &ISR[irq - open_pic2_irq_offset]->Vector_Priority; + openpic2_clearfield(vpp, OPENPIC_MASK); + /* make sure mask gets to controller before we return to user */ + do { + mb(); /* sync is probably useless here */ + } while (openpic2_readfield(vpp, OPENPIC_MASK)); +} + +static void openpic2_disable_irq(u_int irq) +{ + volatile u_int *vpp; + u32 vp; + + check_arg_irq(irq); + vpp = &ISR[irq - open_pic2_irq_offset]->Vector_Priority; + openpic2_setfield(vpp, OPENPIC_MASK); + /* make sure mask gets to controller before we return to user */ + do { + mb(); /* sync is probably useless here */ + vp = openpic2_readfield(vpp, OPENPIC_MASK | OPENPIC_ACTIVITY); + } while((vp & OPENPIC_ACTIVITY) && !(vp & OPENPIC_MASK)); +} + + +/* + * Initialize an interrupt source (and disable it!) + * + * irq: OpenPIC interrupt number + * pri: interrupt source priority + * vec: the vector it will produce + * pol: polarity (1 for positive, 0 for negative) + * sense: 1 for level, 0 for edge + */ +static void __init +openpic2_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense) +{ + openpic2_safe_writefield(&ISR[irq]->Vector_Priority, + OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK | + OPENPIC_SENSE_MASK | OPENPIC_POLARITY_MASK, + (pri << OPENPIC_PRIORITY_SHIFT) | vec | + (pol ? OPENPIC_POLARITY_POSITIVE : + OPENPIC_POLARITY_NEGATIVE) | + (sense ? OPENPIC_SENSE_LEVEL : OPENPIC_SENSE_EDGE)); +} + +/* + * Map an interrupt source to one or more CPUs + */ +static void openpic2_mapirq(u_int irq, u_int physmask, u_int keepmask) +{ + if (ISR[irq] == 0) + return; + if (keepmask != 0) + physmask |= openpic2_read(&ISR[irq]->Destination) & keepmask; + openpic2_write(&ISR[irq]->Destination, physmask); +} + +#ifdef notused +/* + * Set the sense for an interrupt source (and disable it!) + * + * sense: 1 for level, 0 for edge + */ +static void openpic2_set_sense(u_int irq, int sense) +{ + if (ISR[irq] != 0) + openpic2_safe_writefield(&ISR[irq]->Vector_Priority, + OPENPIC_SENSE_LEVEL, + (sense ? OPENPIC_SENSE_LEVEL : 0)); +} +#endif /* notused */ + +/* No spinlocks, should not be necessary with the OpenPIC + * (1 register = 1 interrupt and we have the desc lock). + */ +static void openpic2_ack_irq(unsigned int irq_nr) +{ + openpic2_disable_irq(irq_nr); + openpic2_eoi(); +} + +static void openpic2_end_irq(unsigned int irq_nr) +{ + if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + openpic2_enable_irq(irq_nr); +} + +int +openpic2_get_irq(struct pt_regs *regs) +{ + int irq = openpic2_irq(); + + if (irq == (OPENPIC2_VEC_SPURIOUS + open_pic2_irq_offset)) + irq = -1; + return irq; +} + +#ifdef CONFIG_PM + +/* + * We implement the IRQ controller as a sysdev and put it + * to sleep at powerdown stage (the callback is named suspend, + * but it's old semantics, for the Device Model, it's really + * powerdown). The possible problem is that another sysdev that + * happens to be suspend after this one will have interrupts off, + * that may be an issue... For now, this isn't an issue on pmac + * though... + */ + +static u32 save_ipi_vp[OPENPIC_NUM_IPI]; +static u32 save_irq_src_vp[OPENPIC_MAX_SOURCES]; +static u32 save_irq_src_dest[OPENPIC_MAX_SOURCES]; +static u32 save_cpu_task_pri[OPENPIC_MAX_PROCESSORS]; +static int openpic_suspend_count; + +static void openpic2_cached_enable_irq(u_int irq) +{ + check_arg_irq(irq); + save_irq_src_vp[irq - open_pic2_irq_offset] &= ~OPENPIC_MASK; +} + +static void openpic2_cached_disable_irq(u_int irq) +{ + check_arg_irq(irq); + save_irq_src_vp[irq - open_pic2_irq_offset] |= OPENPIC_MASK; +} + +/* WARNING: Can be called directly by the cpufreq code with NULL parameter, + * we need something better to deal with that... Maybe switch to S1 for + * cpufreq changes + */ +int openpic2_suspend(struct sys_device *sysdev, u32 state) +{ + int i; + unsigned long flags; + + spin_lock_irqsave(&openpic2_setup_lock, flags); + + if (openpic_suspend_count++ > 0) { + spin_unlock_irqrestore(&openpic2_setup_lock, flags); + return 0; + } + + open_pic2.enable = openpic2_cached_enable_irq; + open_pic2.disable = openpic2_cached_disable_irq; + + for (i=0; iProcessor[i].Current_Task_Priority); + openpic2_writefield(&OpenPIC2->Processor[i].Current_Task_Priority, + OPENPIC_CURRENT_TASK_PRIORITY_MASK, 0xf); + } + + for (i=0; iGlobal.IPI_Vector_Priority(i)); + for (i=0; iVector_Priority) & ~OPENPIC_ACTIVITY; + save_irq_src_dest[i] = openpic2_read(&ISR[i]->Destination); + } + + spin_unlock_irqrestore(&openpic2_setup_lock, flags); + + return 0; +} + +/* WARNING: Can be called directly by the cpufreq code with NULL parameter, + * we need something better to deal with that... Maybe switch to S1 for + * cpufreq changes + */ +int openpic2_resume(struct sys_device *sysdev) +{ + int i; + unsigned long flags; + u32 vppmask = OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK | + OPENPIC_SENSE_MASK | OPENPIC_POLARITY_MASK | + OPENPIC_MASK; + + spin_lock_irqsave(&openpic2_setup_lock, flags); + + if ((--openpic_suspend_count) > 0) { + spin_unlock_irqrestore(&openpic2_setup_lock, flags); + return 0; + } + + openpic2_reset(); + + /* OpenPIC sometimes seem to need some time to be fully back up... */ + do { + openpic2_set_spurious(OPENPIC2_VEC_SPURIOUS+open_pic2_irq_offset); + } while(openpic2_readfield(&OpenPIC2->Global.Spurious_Vector, OPENPIC_VECTOR_MASK) + != (OPENPIC2_VEC_SPURIOUS + open_pic2_irq_offset)); + + openpic2_disable_8259_pass_through(); + + for (i=0; iGlobal.IPI_Vector_Priority(i), + save_ipi_vp[i]); + for (i=0; iDestination, save_irq_src_dest[i]); + openpic2_write(&ISR[i]->Vector_Priority, save_irq_src_vp[i]); + /* make sure mask gets to controller before we return to user */ + do { + openpic2_write(&ISR[i]->Vector_Priority, save_irq_src_vp[i]); + } while (openpic2_readfield(&ISR[i]->Vector_Priority, vppmask) + != (save_irq_src_vp[i] & vppmask)); + } + for (i=0; iProcessor[i].Current_Task_Priority, + save_cpu_task_pri[i]); + + open_pic2.enable = openpic2_enable_irq; + open_pic2.disable = openpic2_disable_irq; + + spin_unlock_irqrestore(&openpic2_setup_lock, flags); + + return 0; +} + +#endif /* CONFIG_PM */ + +/* HACK ALERT */ +static struct sysdev_class openpic2_sysclass = { + set_kset_name("openpic2"), +}; + +static struct sys_device device_openpic2 = { + .id = 0, + .cls = &openpic2_sysclass, +}; + +static struct sysdev_driver driver_openpic2 = { +#ifdef CONFIG_PM + .suspend = &openpic2_suspend, + .resume = &openpic2_resume, +#endif /* CONFIG_PM */ +}; + +static int __init init_openpic2_sysfs(void) +{ + int rc; + + if (!OpenPIC2_Addr) + return -ENODEV; + printk(KERN_DEBUG "Registering openpic2 with sysfs...\n"); + rc = sysdev_class_register(&openpic2_sysclass); + if (rc) { + printk(KERN_ERR "Failed registering openpic sys class\n"); + return -ENODEV; + } + rc = sysdev_register(&device_openpic2); + if (rc) { + printk(KERN_ERR "Failed registering openpic sys device\n"); + return -ENODEV; + } + rc = sysdev_driver_register(&openpic2_sysclass, &driver_openpic2); + if (rc) { + printk(KERN_ERR "Failed registering openpic sys driver\n"); + return -ENODEV; + } + return 0; +} + +subsys_initcall(init_openpic2_sysfs); + diff --git a/arch/ppc/syslib/open_pic_defs.h b/arch/ppc/syslib/open_pic_defs.h new file mode 100644 index 00000000000..4f05624b249 --- /dev/null +++ b/arch/ppc/syslib/open_pic_defs.h @@ -0,0 +1,292 @@ +/* + * arch/ppc/kernel/open_pic_defs.h -- OpenPIC definitions + * + * Copyright (C) 1997 Geert Uytterhoeven + * + * This file is based on the following documentation: + * + * The Open Programmable Interrupt Controller (PIC) + * Register Interface Specification Revision 1.2 + * + * Issue Date: October 1995 + * + * Issued jointly by Advanced Micro Devices and Cyrix Corporation + * + * AMD is a registered trademark of Advanced Micro Devices, Inc. + * Copyright (C) 1995, Advanced Micro Devices, Inc. and Cyrix, Inc. + * All Rights Reserved. + * + * To receive a copy of this documentation, send an email to openpic@amd.com. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + +#ifndef _LINUX_OPENPIC_H +#define _LINUX_OPENPIC_H + +#ifdef __KERNEL__ + + /* + * OpenPIC supports up to 2048 interrupt sources and up to 32 processors + */ + +#define OPENPIC_MAX_SOURCES 2048 +#define OPENPIC_MAX_PROCESSORS 32 +#define OPENPIC_MAX_ISU 16 + +#define OPENPIC_NUM_TIMERS 4 +#define OPENPIC_NUM_IPI 4 +#define OPENPIC_NUM_PRI 16 +#define OPENPIC_NUM_VECTORS 256 + + + + /* + * OpenPIC Registers are 32 bits and aligned on 128 bit boundaries + */ + +typedef struct _OpenPIC_Reg { + u_int Reg; /* Little endian! */ + char Pad[0xc]; +} OpenPIC_Reg; + + + /* + * Per Processor Registers + */ + +typedef struct _OpenPIC_Processor { + /* + * Private Shadow Registers (for SLiC backwards compatibility) + */ + u_int IPI0_Dispatch_Shadow; /* Write Only */ + char Pad1[0x4]; + u_int IPI0_Vector_Priority_Shadow; /* Read/Write */ + char Pad2[0x34]; + /* + * Interprocessor Interrupt Command Ports + */ + OpenPIC_Reg _IPI_Dispatch[OPENPIC_NUM_IPI]; /* Write Only */ + /* + * Current Task Priority Register + */ + OpenPIC_Reg _Current_Task_Priority; /* Read/Write */ + char Pad3[0x10]; + /* + * Interrupt Acknowledge Register + */ + OpenPIC_Reg _Interrupt_Acknowledge; /* Read Only */ + /* + * End of Interrupt (EOI) Register + */ + OpenPIC_Reg _EOI; /* Read/Write */ + char Pad5[0xf40]; +} OpenPIC_Processor; + + + /* + * Timer Registers + */ + +typedef struct _OpenPIC_Timer { + OpenPIC_Reg _Current_Count; /* Read Only */ + OpenPIC_Reg _Base_Count; /* Read/Write */ + OpenPIC_Reg _Vector_Priority; /* Read/Write */ + OpenPIC_Reg _Destination; /* Read/Write */ +} OpenPIC_Timer; + + + /* + * Global Registers + */ + +typedef struct _OpenPIC_Global { + /* + * Feature Reporting Registers + */ + OpenPIC_Reg _Feature_Reporting0; /* Read Only */ + OpenPIC_Reg _Feature_Reporting1; /* Future Expansion */ + /* + * Global Configuration Registers + */ + OpenPIC_Reg _Global_Configuration0; /* Read/Write */ + OpenPIC_Reg _Global_Configuration1; /* Future Expansion */ + /* + * Vendor Specific Registers + */ + OpenPIC_Reg _Vendor_Specific[4]; + /* + * Vendor Identification Register + */ + OpenPIC_Reg _Vendor_Identification; /* Read Only */ + /* + * Processor Initialization Register + */ + OpenPIC_Reg _Processor_Initialization; /* Read/Write */ + /* + * IPI Vector/Priority Registers + */ + OpenPIC_Reg _IPI_Vector_Priority[OPENPIC_NUM_IPI]; /* Read/Write */ + /* + * Spurious Vector Register + */ + OpenPIC_Reg _Spurious_Vector; /* Read/Write */ + /* + * Global Timer Registers + */ + OpenPIC_Reg _Timer_Frequency; /* Read/Write */ + OpenPIC_Timer Timer[OPENPIC_NUM_TIMERS]; + char Pad1[0xee00]; +} OpenPIC_Global; + + + /* + * Interrupt Source Registers + */ + +typedef struct _OpenPIC_Source { + OpenPIC_Reg _Vector_Priority; /* Read/Write */ + OpenPIC_Reg _Destination; /* Read/Write */ +} OpenPIC_Source, *OpenPIC_SourcePtr; + + + /* + * OpenPIC Register Map + */ + +struct OpenPIC { + char Pad1[0x1000]; + /* + * Global Registers + */ + OpenPIC_Global Global; + /* + * Interrupt Source Configuration Registers + */ + OpenPIC_Source Source[OPENPIC_MAX_SOURCES]; + /* + * Per Processor Registers + */ + OpenPIC_Processor Processor[OPENPIC_MAX_PROCESSORS]; +}; + +extern volatile struct OpenPIC __iomem *OpenPIC; + + + /* + * Current Task Priority Register + */ + +#define OPENPIC_CURRENT_TASK_PRIORITY_MASK 0x0000000f + + /* + * Who Am I Register + */ + +#define OPENPIC_WHO_AM_I_ID_MASK 0x0000001f + + /* + * Feature Reporting Register 0 + */ + +#define OPENPIC_FEATURE_LAST_SOURCE_MASK 0x07ff0000 +#define OPENPIC_FEATURE_LAST_SOURCE_SHIFT 16 +#define OPENPIC_FEATURE_LAST_PROCESSOR_MASK 0x00001f00 +#define OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT 8 +#define OPENPIC_FEATURE_VERSION_MASK 0x000000ff + + /* + * Global Configuration Register 0 + */ + +#define OPENPIC_CONFIG_RESET 0x80000000 +#define OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE 0x20000000 +#define OPENPIC_CONFIG_BASE_MASK 0x000fffff + + /* + * Global Configuration Register 1 + * This is the EICR on EPICs. + */ + +#define OPENPIC_EICR_S_CLK_MASK 0x70000000 +#define OPENPIC_EICR_SIE 0x08000000 + + /* + * Vendor Identification Register + */ + +#define OPENPIC_VENDOR_ID_STEPPING_MASK 0x00ff0000 +#define OPENPIC_VENDOR_ID_STEPPING_SHIFT 16 +#define OPENPIC_VENDOR_ID_DEVICE_ID_MASK 0x0000ff00 +#define OPENPIC_VENDOR_ID_DEVICE_ID_SHIFT 8 +#define OPENPIC_VENDOR_ID_VENDOR_ID_MASK 0x000000ff + + /* + * Vector/Priority Registers + */ + +#define OPENPIC_MASK 0x80000000 +#define OPENPIC_ACTIVITY 0x40000000 /* Read Only */ +#define OPENPIC_PRIORITY_MASK 0x000f0000 +#define OPENPIC_PRIORITY_SHIFT 16 +#define OPENPIC_VECTOR_MASK 0x000000ff + + + /* + * Interrupt Source Registers + */ + +#define OPENPIC_POLARITY_POSITIVE 0x00800000 +#define OPENPIC_POLARITY_NEGATIVE 0x00000000 +#define OPENPIC_POLARITY_MASK 0x00800000 +#define OPENPIC_SENSE_LEVEL 0x00400000 +#define OPENPIC_SENSE_EDGE 0x00000000 +#define OPENPIC_SENSE_MASK 0x00400000 + + + /* + * Timer Registers + */ + +#define OPENPIC_COUNT_MASK 0x7fffffff +#define OPENPIC_TIMER_TOGGLE 0x80000000 +#define OPENPIC_TIMER_COUNT_INHIBIT 0x80000000 + + + /* + * Aliases to make life simpler + */ + +/* Per Processor Registers */ +#define IPI_Dispatch(i) _IPI_Dispatch[i].Reg +#define Current_Task_Priority _Current_Task_Priority.Reg +#define Interrupt_Acknowledge _Interrupt_Acknowledge.Reg +#define EOI _EOI.Reg + +/* Global Registers */ +#define Feature_Reporting0 _Feature_Reporting0.Reg +#define Feature_Reporting1 _Feature_Reporting1.Reg +#define Global_Configuration0 _Global_Configuration0.Reg +#define Global_Configuration1 _Global_Configuration1.Reg +#define Vendor_Specific(i) _Vendor_Specific[i].Reg +#define Vendor_Identification _Vendor_Identification.Reg +#define Processor_Initialization _Processor_Initialization.Reg +#define IPI_Vector_Priority(i) _IPI_Vector_Priority[i].Reg +#define Spurious_Vector _Spurious_Vector.Reg +#define Timer_Frequency _Timer_Frequency.Reg + +/* Timer Registers */ +#define Current_Count _Current_Count.Reg +#define Base_Count _Base_Count.Reg +#define Vector_Priority _Vector_Priority.Reg +#define Destination _Destination.Reg + +/* Interrupt Source Registers */ +#define Vector_Priority _Vector_Priority.Reg +#define Destination _Destination.Reg + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_OPENPIC_H */ diff --git a/arch/ppc/syslib/pci_auto.c b/arch/ppc/syslib/pci_auto.c new file mode 100644 index 00000000000..d64207c2a97 --- /dev/null +++ b/arch/ppc/syslib/pci_auto.c @@ -0,0 +1,517 @@ +/* + * arch/ppc/syslib/pci_auto.c + * + * PCI autoconfiguration library + * + * Author: Matt Porter + * + * 2001 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +/* + * The CardBus support is very preliminary. Preallocating space is + * the way to go but will require some change in card services to + * make it useful. Eventually this will ensure that we can put + * multiple CB bridges behind multiple P2P bridges. For now, at + * least it ensures that we place the CB bridge BAR and assigned + * initial bus numbers. I definitely need to do something about + * the lack of 16-bit I/O support. -MDP + */ + +#include +#include +#include + +#include + +#define PCIAUTO_IDE_MODE_MASK 0x05 + +#undef DEBUG + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif /* DEBUG */ + +static int pciauto_upper_iospc; +static int pciauto_upper_memspc; + +void __init pciauto_setup_bars(struct pci_controller *hose, + int current_bus, + int pci_devfn, + int bar_limit) +{ + int bar_response, bar_size, bar_value; + int bar, addr_mask; + int * upper_limit; + int found_mem64 = 0; + + DBG("PCI Autoconfig: Found Bus %d, Device %d, Function %d\n", + current_bus, PCI_SLOT(pci_devfn), PCI_FUNC(pci_devfn) ); + + for (bar = PCI_BASE_ADDRESS_0; bar <= bar_limit; bar+=4) { + /* Tickle the BAR and get the response */ + early_write_config_dword(hose, + current_bus, + pci_devfn, + bar, + 0xffffffff); + early_read_config_dword(hose, + current_bus, + pci_devfn, + bar, + &bar_response); + + /* If BAR is not implemented go to the next BAR */ + if (!bar_response) + continue; + + /* Check the BAR type and set our address mask */ + if (bar_response & PCI_BASE_ADDRESS_SPACE) { + addr_mask = PCI_BASE_ADDRESS_IO_MASK; + upper_limit = &pciauto_upper_iospc; + DBG("PCI Autoconfig: BAR 0x%x, I/O, ", bar); + } else { + if ( (bar_response & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == + PCI_BASE_ADDRESS_MEM_TYPE_64) + found_mem64 = 1; + + addr_mask = PCI_BASE_ADDRESS_MEM_MASK; + upper_limit = &pciauto_upper_memspc; + DBG("PCI Autoconfig: BAR 0x%x, Mem ", bar); + } + + /* Calculate requested size */ + bar_size = ~(bar_response & addr_mask) + 1; + + /* Allocate a base address */ + bar_value = (*upper_limit - bar_size) & ~(bar_size - 1); + + /* Write it out and update our limit */ + early_write_config_dword(hose, + current_bus, + pci_devfn, + bar, + bar_value); + + *upper_limit = bar_value; + + /* + * If we are a 64-bit decoder then increment to the + * upper 32 bits of the bar and force it to locate + * in the lower 4GB of memory. + */ + if (found_mem64) { + bar += 4; + early_write_config_dword(hose, + current_bus, + pci_devfn, + bar, + 0x00000000); + found_mem64 = 0; + } + + DBG("size=0x%x, address=0x%x\n", + bar_size, bar_value); + } + +} + +void __init pciauto_prescan_setup_bridge(struct pci_controller *hose, + int current_bus, + int pci_devfn, + int sub_bus, + int *iosave, + int *memsave) +{ + /* Configure bus number registers */ + early_write_config_byte(hose, + current_bus, + pci_devfn, + PCI_PRIMARY_BUS, + current_bus); + early_write_config_byte(hose, + current_bus, + pci_devfn, + PCI_SECONDARY_BUS, + sub_bus + 1); + early_write_config_byte(hose, + current_bus, + pci_devfn, + PCI_SUBORDINATE_BUS, + 0xff); + + /* Round memory allocator to 1MB boundary */ + pciauto_upper_memspc &= ~(0x100000 - 1); + *memsave = pciauto_upper_memspc; + + /* Round I/O allocator to 4KB boundary */ + pciauto_upper_iospc &= ~(0x1000 - 1); + *iosave = pciauto_upper_iospc; + + /* Set up memory and I/O filter limits, assume 32-bit I/O space */ + early_write_config_word(hose, + current_bus, + pci_devfn, + PCI_MEMORY_LIMIT, + ((pciauto_upper_memspc - 1) & 0xfff00000) >> 16); + early_write_config_byte(hose, + current_bus, + pci_devfn, + PCI_IO_LIMIT, + ((pciauto_upper_iospc - 1) & 0x0000f000) >> 8); + early_write_config_word(hose, + current_bus, + pci_devfn, + PCI_IO_LIMIT_UPPER16, + ((pciauto_upper_iospc - 1) & 0xffff0000) >> 16); + + /* Zero upper 32 bits of prefetchable base/limit */ + early_write_config_dword(hose, + current_bus, + pci_devfn, + PCI_PREF_BASE_UPPER32, + 0); + early_write_config_dword(hose, + current_bus, + pci_devfn, + PCI_PREF_LIMIT_UPPER32, + 0); +} + +void __init pciauto_postscan_setup_bridge(struct pci_controller *hose, + int current_bus, + int pci_devfn, + int sub_bus, + int *iosave, + int *memsave) +{ + int cmdstat; + + /* Configure bus number registers */ + early_write_config_byte(hose, + current_bus, + pci_devfn, + PCI_SUBORDINATE_BUS, + sub_bus); + + /* + * Round memory allocator to 1MB boundary. + * If no space used, allocate minimum. + */ + pciauto_upper_memspc &= ~(0x100000 - 1); + if (*memsave == pciauto_upper_memspc) + pciauto_upper_memspc -= 0x00100000; + + early_write_config_word(hose, + current_bus, + pci_devfn, + PCI_MEMORY_BASE, + pciauto_upper_memspc >> 16); + + /* Allocate 1MB for pre-fretch */ + early_write_config_word(hose, + current_bus, + pci_devfn, + PCI_PREF_MEMORY_LIMIT, + ((pciauto_upper_memspc - 1) & 0xfff00000) >> 16); + + pciauto_upper_memspc -= 0x100000; + + early_write_config_word(hose, + current_bus, + pci_devfn, + PCI_PREF_MEMORY_BASE, + pciauto_upper_memspc >> 16); + + /* Round I/O allocator to 4KB boundary */ + pciauto_upper_iospc &= ~(0x1000 - 1); + if (*iosave == pciauto_upper_iospc) + pciauto_upper_iospc -= 0x1000; + + early_write_config_byte(hose, + current_bus, + pci_devfn, + PCI_IO_BASE, + (pciauto_upper_iospc & 0x0000f000) >> 8); + early_write_config_word(hose, + current_bus, + pci_devfn, + PCI_IO_BASE_UPPER16, + pciauto_upper_iospc >> 16); + + /* Enable memory and I/O accesses, enable bus master */ + early_read_config_dword(hose, + current_bus, + pci_devfn, + PCI_COMMAND, + &cmdstat); + early_write_config_dword(hose, + current_bus, + pci_devfn, + PCI_COMMAND, + cmdstat | + PCI_COMMAND_IO | + PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER); +} + +void __init pciauto_prescan_setup_cardbus_bridge(struct pci_controller *hose, + int current_bus, + int pci_devfn, + int sub_bus, + int *iosave, + int *memsave) +{ + /* Configure bus number registers */ + early_write_config_byte(hose, + current_bus, + pci_devfn, + PCI_PRIMARY_BUS, + current_bus); + early_write_config_byte(hose, + current_bus, + pci_devfn, + PCI_SECONDARY_BUS, + sub_bus + 1); + early_write_config_byte(hose, + current_bus, + pci_devfn, + PCI_SUBORDINATE_BUS, + 0xff); + + /* Round memory allocator to 4KB boundary */ + pciauto_upper_memspc &= ~(0x1000 - 1); + *memsave = pciauto_upper_memspc; + + /* Round I/O allocator to 4 byte boundary */ + pciauto_upper_iospc &= ~(0x4 - 1); + *iosave = pciauto_upper_iospc; + + /* Set up memory and I/O filter limits, assume 32-bit I/O space */ + early_write_config_dword(hose, + current_bus, + pci_devfn, + 0x20, + pciauto_upper_memspc - 1); + early_write_config_dword(hose, + current_bus, + pci_devfn, + 0x30, + pciauto_upper_iospc - 1); +} + +void __init pciauto_postscan_setup_cardbus_bridge(struct pci_controller *hose, + int current_bus, + int pci_devfn, + int sub_bus, + int *iosave, + int *memsave) +{ + int cmdstat; + + /* + * Configure subordinate bus number. The PCI subsystem + * bus scan will renumber buses (reserving three additional + * for this PCI<->CardBus bridge for the case where a CardBus + * adapter contains a P2P or CB2CB bridge. + */ + early_write_config_byte(hose, + current_bus, + pci_devfn, + PCI_SUBORDINATE_BUS, + sub_bus); + + /* + * Reserve an additional 4MB for mem space and 16KB for + * I/O space. This should cover any additional space + * requirement of unusual CardBus devices with + * additional bridges that can consume more address space. + * + * Although pcmcia-cs currently will reprogram bridge + * windows, the goal is to add an option to leave them + * alone and use the bridge window ranges as the regions + * that are searched for free resources upon hot-insertion + * of a device. This will allow a PCI<->CardBus bridge + * configured by this routine to happily live behind a + * P2P bridge in a system. + */ + pciauto_upper_memspc -= 0x00400000; + pciauto_upper_iospc -= 0x00004000; + + /* Round memory allocator to 4KB boundary */ + pciauto_upper_memspc &= ~(0x1000 - 1); + + early_write_config_dword(hose, + current_bus, + pci_devfn, + 0x1c, + pciauto_upper_memspc); + + /* Round I/O allocator to 4 byte boundary */ + pciauto_upper_iospc &= ~(0x4 - 1); + early_write_config_dword(hose, + current_bus, + pci_devfn, + 0x2c, + pciauto_upper_iospc); + + /* Enable memory and I/O accesses, enable bus master */ + early_read_config_dword(hose, + current_bus, + pci_devfn, + PCI_COMMAND, + &cmdstat); + early_write_config_dword(hose, + current_bus, + pci_devfn, + PCI_COMMAND, + cmdstat | + PCI_COMMAND_IO | + PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER); +} + +int __init pciauto_bus_scan(struct pci_controller *hose, int current_bus) +{ + int sub_bus, pci_devfn, pci_class, cmdstat, found_multi = 0; + unsigned short vid; + unsigned char header_type; + + /* + * Fetch our I/O and memory space upper boundaries used + * to allocated base addresses on this hose. + */ + if (current_bus == hose->first_busno) { + pciauto_upper_iospc = hose->io_space.end + 1; + pciauto_upper_memspc = hose->mem_space.end + 1; + } + + sub_bus = current_bus; + + for (pci_devfn = 0; pci_devfn < 0xff; pci_devfn++) { + /* Skip our host bridge */ + if ( (current_bus == hose->first_busno) && (pci_devfn == 0) ) + continue; + + if (PCI_FUNC(pci_devfn) && !found_multi) + continue; + + /* If config space read fails from this device, move on */ + if (early_read_config_byte(hose, + current_bus, + pci_devfn, + PCI_HEADER_TYPE, + &header_type)) + continue; + + if (!PCI_FUNC(pci_devfn)) + found_multi = header_type & 0x80; + + early_read_config_word(hose, + current_bus, + pci_devfn, + PCI_VENDOR_ID, + &vid); + + if (vid != 0xffff) { + early_read_config_dword(hose, + current_bus, + pci_devfn, + PCI_CLASS_REVISION, &pci_class); + if ( (pci_class >> 16) == PCI_CLASS_BRIDGE_PCI ) { + int iosave, memsave; + + DBG("PCI Autoconfig: Found P2P bridge, device %d\n", PCI_SLOT(pci_devfn)); + /* Allocate PCI I/O and/or memory space */ + pciauto_setup_bars(hose, + current_bus, + pci_devfn, + PCI_BASE_ADDRESS_1); + + pciauto_prescan_setup_bridge(hose, + current_bus, + pci_devfn, + sub_bus, + &iosave, + &memsave); + sub_bus = pciauto_bus_scan(hose, sub_bus+1); + pciauto_postscan_setup_bridge(hose, + current_bus, + pci_devfn, + sub_bus, + &iosave, + &memsave); + } else if ((pci_class >> 16) == PCI_CLASS_BRIDGE_CARDBUS) { + int iosave, memsave; + + DBG("PCI Autoconfig: Found CardBus bridge, device %d function %d\n", PCI_SLOT(pci_devfn), PCI_FUNC(pci_devfn)); + /* Place CardBus Socket/ExCA registers */ + pciauto_setup_bars(hose, + current_bus, + pci_devfn, + PCI_BASE_ADDRESS_0); + + pciauto_prescan_setup_cardbus_bridge(hose, + current_bus, + pci_devfn, + sub_bus, + &iosave, + &memsave); + sub_bus = pciauto_bus_scan(hose, sub_bus+1); + pciauto_postscan_setup_cardbus_bridge(hose, + current_bus, + pci_devfn, + sub_bus, + &iosave, + &memsave); + } else { + if ((pci_class >> 16) == PCI_CLASS_STORAGE_IDE) { + unsigned char prg_iface; + + early_read_config_byte(hose, + current_bus, + pci_devfn, + PCI_CLASS_PROG, + &prg_iface); + if (!(prg_iface & PCIAUTO_IDE_MODE_MASK)) { + DBG("PCI Autoconfig: Skipping legacy mode IDE controller\n"); + continue; + } + } + /* Allocate PCI I/O and/or memory space */ + pciauto_setup_bars(hose, + current_bus, + pci_devfn, + PCI_BASE_ADDRESS_5); + + /* + * Enable some standard settings + */ + early_read_config_dword(hose, + current_bus, + pci_devfn, + PCI_COMMAND, + &cmdstat); + early_write_config_dword(hose, + current_bus, + pci_devfn, + PCI_COMMAND, + cmdstat | + PCI_COMMAND_IO | + PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER); + early_write_config_byte(hose, + current_bus, + pci_devfn, + PCI_LATENCY_TIMER, + 0x80); + } + } + } + return sub_bus; +} diff --git a/arch/ppc/syslib/ppc403_pic.c b/arch/ppc/syslib/ppc403_pic.c new file mode 100644 index 00000000000..06cb0af2a58 --- /dev/null +++ b/arch/ppc/syslib/ppc403_pic.c @@ -0,0 +1,127 @@ +/* + * + * Copyright (c) 1999 Grant Erickson + * + * Module name: ppc403_pic.c + * + * Description: + * Interrupt controller driver for PowerPC 403-based processors. + */ + +/* + * The PowerPC 403 cores' Asynchronous Interrupt Controller (AIC) has + * 32 possible interrupts, a majority of which are not implemented on + * all cores. There are six configurable, external interrupt pins and + * there are eight internal interrupts for the on-chip serial port + * (SPU), DMA controller, and JTAG controller. + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +/* Function Prototypes */ + +static void ppc403_aic_enable(unsigned int irq); +static void ppc403_aic_disable(unsigned int irq); +static void ppc403_aic_disable_and_ack(unsigned int irq); + +static struct hw_interrupt_type ppc403_aic = { + "403GC AIC", + NULL, + NULL, + ppc403_aic_enable, + ppc403_aic_disable, + ppc403_aic_disable_and_ack, + 0 +}; + +int +ppc403_pic_get_irq(struct pt_regs *regs) +{ + int irq; + unsigned long bits; + + /* + * Only report the status of those interrupts that are actually + * enabled. + */ + + bits = mfdcr(DCRN_EXISR) & mfdcr(DCRN_EXIER); + + /* + * Walk through the interrupts from highest priority to lowest, and + * report the first pending interrupt found. + * We want PPC, not C bit numbering, so just subtract the ffs() + * result from 32. + */ + irq = 32 - ffs(bits); + + if (irq == NR_AIC_IRQS) + irq = -1; + + return (irq); +} + +static void +ppc403_aic_enable(unsigned int irq) +{ + int bit, word; + + bit = irq & 0x1f; + word = irq >> 5; + + ppc_cached_irq_mask[word] |= (1 << (31 - bit)); + mtdcr(DCRN_EXIER, ppc_cached_irq_mask[word]); +} + +static void +ppc403_aic_disable(unsigned int irq) +{ + int bit, word; + + bit = irq & 0x1f; + word = irq >> 5; + + ppc_cached_irq_mask[word] &= ~(1 << (31 - bit)); + mtdcr(DCRN_EXIER, ppc_cached_irq_mask[word]); +} + +static void +ppc403_aic_disable_and_ack(unsigned int irq) +{ + int bit, word; + + bit = irq & 0x1f; + word = irq >> 5; + + ppc_cached_irq_mask[word] &= ~(1 << (31 - bit)); + mtdcr(DCRN_EXIER, ppc_cached_irq_mask[word]); + mtdcr(DCRN_EXISR, (1 << (31 - bit))); +} + +void __init +ppc4xx_pic_init(void) +{ + int i; + + /* + * Disable all external interrupts until they are + * explicity requested. + */ + ppc_cached_irq_mask[0] = 0; + + mtdcr(DCRN_EXIER, ppc_cached_irq_mask[0]); + + ppc_md.get_irq = ppc403_pic_get_irq; + + for (i = 0; i < NR_IRQS; i++) + irq_desc[i].handler = &ppc403_aic; +} diff --git a/arch/ppc/syslib/ppc405_pci.c b/arch/ppc/syslib/ppc405_pci.c new file mode 100644 index 00000000000..81c83bf98df --- /dev/null +++ b/arch/ppc/syslib/ppc405_pci.c @@ -0,0 +1,177 @@ +/* + * Authors: Frank Rowand , + * Debbie Chu , or source@mvista.com + * Further modifications by Armin Kuster + * + * 2000 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + * + * Based on arch/ppc/kernel/indirect.c, Copyright (C) 1998 Gabriel Paubert. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +extern void bios_fixup(struct pci_controller *, struct pcil0_regs *); +extern int ppc405_map_irq(struct pci_dev *dev, unsigned char idsel, + unsigned char pin); + +void +ppc405_pcibios_fixup_resources(struct pci_dev *dev) +{ + int i; + unsigned long max_host_addr; + unsigned long min_host_addr; + struct resource *res; + + /* + * openbios puts some graphics cards in the same range as the host + * controller uses to map to SDRAM. Fix it. + */ + + min_host_addr = 0; + max_host_addr = PPC405_PCI_MEM_BASE - 1; + + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { + res = dev->resource + i; + if (!res->start) + continue; + if ((res->flags & IORESOURCE_MEM) && + (((res->start >= min_host_addr) + && (res->start <= max_host_addr)) + || ((res->end >= min_host_addr) + && (res->end <= max_host_addr)) + || ((res->start < min_host_addr) + && (res->end > max_host_addr)) + ) + ) { + + /* force pcibios_assign_resources() to assign a new address */ + res->end -= res->start; + res->start = 0; + } + } +} + +static int +ppc4xx_exclude_device(unsigned char bus, unsigned char devfn) +{ + /* We prevent us from seeing ourselves to avoid having + * the kernel try to remap our BAR #1 and fuck up bus + * master from external PCI devices + */ + return (bus == 0 && devfn == 0); +} + +void +ppc4xx_find_bridges(void) +{ + struct pci_controller *hose_a; + struct pcil0_regs *pcip; + unsigned int tmp_addr; + unsigned int tmp_size; + unsigned int reg_index; + unsigned int new_pmm_max = 0; + unsigned int new_pmm_min = 0; + + isa_io_base = 0; + isa_mem_base = 0; + pci_dram_offset = 0; + +#if (PSR_PCI_ARBIT_EN > 1) + /* Check if running in slave mode */ + if ((mfdcr(DCRN_CHPSR) & PSR_PCI_ARBIT_EN) == 0) { + printk("Running as PCI slave, kernel PCI disabled !\n"); + return; + } +#endif + /* Setup PCI32 hose */ + hose_a = pcibios_alloc_controller(); + if (!hose_a) + return; + setup_indirect_pci(hose_a, PPC405_PCI_CONFIG_ADDR, + PPC405_PCI_CONFIG_DATA); + + pcip = ioremap(PPC4xx_PCI_LCFG_PADDR, PAGE_SIZE); + if (pcip != NULL) { + +#if defined(CONFIG_BIOS_FIXUP) + bios_fixup(hose_a, pcip); +#endif + new_pmm_min = 0xffffffff; + for (reg_index = 0; reg_index < 3; reg_index++) { + tmp_size = in_le32(&pcip->pmm[reg_index].ma); // mask & attrs + /* test the enable bit */ + if ((tmp_size & 0x1) == 0) + continue; + tmp_addr = in_le32(&pcip->pmm[reg_index].pcila); // PCI addr + if (tmp_addr < PPC405_PCI_PHY_MEM_BASE) { + printk(KERN_DEBUG + "Disabling mapping to PCI mem addr 0x%8.8x\n", + tmp_addr); + out_le32(&pcip->pmm[reg_index].ma, tmp_size & ~1); // *_PMMOMA + continue; + } + tmp_addr = in_le32(&pcip->pmm[reg_index].la); // *_PMMOLA + if (tmp_addr < new_pmm_min) + new_pmm_min = tmp_addr; + tmp_addr = tmp_addr + + (0xffffffff - (tmp_size & 0xffffc000)); + if (tmp_addr > PPC405_PCI_UPPER_MEM) { + new_pmm_max = tmp_addr; // PPC405_PCI_UPPER_MEM + } else { + new_pmm_max = PPC405_PCI_UPPER_MEM; + } + + } // for + + iounmap(pcip); + } + + hose_a->first_busno = 0; + hose_a->last_busno = 0xff; + hose_a->pci_mem_offset = 0; + + /* Setup bridge memory/IO ranges & resources + * TODO: Handle firmwares setting up a legacy ISA mem base + */ + hose_a->io_space.start = PPC405_PCI_LOWER_IO; + hose_a->io_space.end = PPC405_PCI_UPPER_IO; + hose_a->mem_space.start = new_pmm_min; + hose_a->mem_space.end = new_pmm_max; + hose_a->io_base_phys = PPC405_PCI_PHY_IO_BASE; + hose_a->io_base_virt = ioremap(hose_a->io_base_phys, 0x10000); + hose_a->io_resource.start = 0; + hose_a->io_resource.end = PPC405_PCI_UPPER_IO - PPC405_PCI_LOWER_IO; + hose_a->io_resource.flags = IORESOURCE_IO; + hose_a->io_resource.name = "PCI I/O"; + hose_a->mem_resources[0].start = new_pmm_min; + hose_a->mem_resources[0].end = new_pmm_max; + hose_a->mem_resources[0].flags = IORESOURCE_MEM; + hose_a->mem_resources[0].name = "PCI Memory"; + isa_io_base = (int) hose_a->io_base_virt; + isa_mem_base = 0; /* ISA not implemented */ + ISA_DMA_THRESHOLD = 0x00ffffff; /* ??? ISA not implemented */ + + /* Scan busses & initial setup by pci_auto */ + hose_a->last_busno = pciauto_bus_scan(hose_a, hose_a->first_busno); + hose_a->last_busno = 0; + + /* Setup ppc_md */ + ppc_md.pcibios_fixup = NULL; + ppc_md.pci_exclude_device = ppc4xx_exclude_device; + ppc_md.pcibios_fixup_resources = ppc405_pcibios_fixup_resources; + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = ppc405_map_irq; +} diff --git a/arch/ppc/syslib/ppc4xx_dma.c b/arch/ppc/syslib/ppc4xx_dma.c new file mode 100644 index 00000000000..5015ab99afd --- /dev/null +++ b/arch/ppc/syslib/ppc4xx_dma.c @@ -0,0 +1,708 @@ +/* + * arch/ppc/kernel/ppc4xx_dma.c + * + * IBM PPC4xx DMA engine core library + * + * Copyright 2000-2004 MontaVista Software Inc. + * + * Cleaned up and converted to new DCR access + * Matt Porter + * + * Original code by Armin Kuster + * and Pete Popov + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +ppc_dma_ch_t dma_channels[MAX_PPC4xx_DMA_CHANNELS]; + +int +ppc4xx_get_dma_status(void) +{ + return (mfdcr(DCRN_DMASR)); +} + +void +ppc4xx_set_src_addr(int dmanr, phys_addr_t src_addr) +{ + if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { + printk("set_src_addr: bad channel: %d\n", dmanr); + return; + } + +#ifdef PPC4xx_DMA_64BIT + mtdcr(DCRN_DMASAH0 + dmanr*2, (u32)(src_addr >> 32)); +#else + mtdcr(DCRN_DMASA0 + dmanr*2, (u32)src_addr); +#endif +} + +void +ppc4xx_set_dst_addr(int dmanr, phys_addr_t dst_addr) +{ + if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { + printk("set_dst_addr: bad channel: %d\n", dmanr); + return; + } + +#ifdef PPC4xx_DMA_64BIT + mtdcr(DCRN_DMADAH0 + dmanr*2, (u32)(dst_addr >> 32)); +#else + mtdcr(DCRN_DMADA0 + dmanr*2, (u32)dst_addr); +#endif +} + +void +ppc4xx_enable_dma(unsigned int dmanr) +{ + unsigned int control; + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + unsigned int status_bits[] = { DMA_CS0 | DMA_TS0 | DMA_CH0_ERR, + DMA_CS1 | DMA_TS1 | DMA_CH1_ERR, + DMA_CS2 | DMA_TS2 | DMA_CH2_ERR, + DMA_CS3 | DMA_TS3 | DMA_CH3_ERR}; + + if (p_dma_ch->in_use) { + printk("enable_dma: channel %d in use\n", dmanr); + return; + } + + if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { + printk("enable_dma: bad channel: %d\n", dmanr); + return; + } + + if (p_dma_ch->mode == DMA_MODE_READ) { + /* peripheral to memory */ + ppc4xx_set_src_addr(dmanr, 0); + ppc4xx_set_dst_addr(dmanr, p_dma_ch->addr); + } else if (p_dma_ch->mode == DMA_MODE_WRITE) { + /* memory to peripheral */ + ppc4xx_set_src_addr(dmanr, p_dma_ch->addr); + ppc4xx_set_dst_addr(dmanr, 0); + } + + /* for other xfer modes, the addresses are already set */ + control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8)); + + control &= ~(DMA_TM_MASK | DMA_TD); /* clear all mode bits */ + if (p_dma_ch->mode == DMA_MODE_MM) { + /* software initiated memory to memory */ + control |= DMA_ETD_OUTPUT | DMA_TCE_ENABLE; + } + + mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control); + + /* + * Clear the CS, TS, RI bits for the channel from DMASR. This + * has been observed to happen correctly only after the mode and + * ETD/DCE bits in DMACRx are set above. Must do this before + * enabling the channel. + */ + + mtdcr(DCRN_DMASR, status_bits[dmanr]); + + /* + * For device-paced transfers, Terminal Count Enable apparently + * must be on, and this must be turned on after the mode, etc. + * bits are cleared above (at least on Redwood-6). + */ + + if ((p_dma_ch->mode == DMA_MODE_MM_DEVATDST) || + (p_dma_ch->mode == DMA_MODE_MM_DEVATSRC)) + control |= DMA_TCE_ENABLE; + + /* + * Now enable the channel. + */ + + control |= (p_dma_ch->mode | DMA_CE_ENABLE); + + mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control); + + p_dma_ch->in_use = 1; +} + +void +ppc4xx_disable_dma(unsigned int dmanr) +{ + unsigned int control; + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + + if (!p_dma_ch->in_use) { + printk("disable_dma: channel %d not in use\n", dmanr); + return; + } + + if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { + printk("disable_dma: bad channel: %d\n", dmanr); + return; + } + + control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8)); + control &= ~DMA_CE_ENABLE; + mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control); + + p_dma_ch->in_use = 0; +} + +/* + * Sets the dma mode for single DMA transfers only. + * For scatter/gather transfers, the mode is passed to the + * alloc_dma_handle() function as one of the parameters. + * + * The mode is simply saved and used later. This allows + * the driver to call set_dma_mode() and set_dma_addr() in + * any order. + * + * Valid mode values are: + * + * DMA_MODE_READ peripheral to memory + * DMA_MODE_WRITE memory to peripheral + * DMA_MODE_MM memory to memory + * DMA_MODE_MM_DEVATSRC device-paced memory to memory, device at src + * DMA_MODE_MM_DEVATDST device-paced memory to memory, device at dst + */ +int +ppc4xx_set_dma_mode(unsigned int dmanr, unsigned int mode) +{ + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + + if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { + printk("set_dma_mode: bad channel 0x%x\n", dmanr); + return DMA_STATUS_BAD_CHANNEL; + } + + p_dma_ch->mode = mode; + + return DMA_STATUS_GOOD; +} + +/* + * Sets the DMA Count register. Note that 'count' is in bytes. + * However, the DMA Count register counts the number of "transfers", + * where each transfer is equal to the bus width. Thus, count + * MUST be a multiple of the bus width. + */ +void +ppc4xx_set_dma_count(unsigned int dmanr, unsigned int count) +{ + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + +#ifdef DEBUG_4xxDMA + { + int error = 0; + switch (p_dma_ch->pwidth) { + case PW_8: + break; + case PW_16: + if (count & 0x1) + error = 1; + break; + case PW_32: + if (count & 0x3) + error = 1; + break; + case PW_64: + if (count & 0x7) + error = 1; + break; + default: + printk("set_dma_count: invalid bus width: 0x%x\n", + p_dma_ch->pwidth); + return; + } + if (error) + printk + ("Warning: set_dma_count count 0x%x bus width %d\n", + count, p_dma_ch->pwidth); + } +#endif + + count = count >> p_dma_ch->shift; + + mtdcr(DCRN_DMACT0 + (dmanr * 0x8), count); +} + +/* + * Returns the number of bytes left to be transfered. + * After a DMA transfer, this should return zero. + * Reading this while a DMA transfer is still in progress will return + * unpredictable results. + */ +int +ppc4xx_get_dma_residue(unsigned int dmanr) +{ + unsigned int count; + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + + if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { + printk("ppc4xx_get_dma_residue: bad channel 0x%x\n", dmanr); + return DMA_STATUS_BAD_CHANNEL; + } + + count = mfdcr(DCRN_DMACT0 + (dmanr * 0x8)); + + return (count << p_dma_ch->shift); +} + +/* + * Sets the DMA address for a memory to peripheral or peripheral + * to memory transfer. The address is just saved in the channel + * structure for now and used later in enable_dma(). + */ +void +ppc4xx_set_dma_addr(unsigned int dmanr, phys_addr_t addr) +{ + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + + if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { + printk("ppc4xx_set_dma_addr: bad channel: %d\n", dmanr); + return; + } + +#ifdef DEBUG_4xxDMA + { + int error = 0; + switch (p_dma_ch->pwidth) { + case PW_8: + break; + case PW_16: + if ((unsigned) addr & 0x1) + error = 1; + break; + case PW_32: + if ((unsigned) addr & 0x3) + error = 1; + break; + case PW_64: + if ((unsigned) addr & 0x7) + error = 1; + break; + default: + printk("ppc4xx_set_dma_addr: invalid bus width: 0x%x\n", + p_dma_ch->pwidth); + return; + } + if (error) + printk("Warning: ppc4xx_set_dma_addr addr 0x%x bus width %d\n", + addr, p_dma_ch->pwidth); + } +#endif + + /* save dma address and program it later after we know the xfer mode */ + p_dma_ch->addr = addr; +} + +/* + * Sets both DMA addresses for a memory to memory transfer. + * For memory to peripheral or peripheral to memory transfers + * the function set_dma_addr() should be used instead. + */ +void +ppc4xx_set_dma_addr2(unsigned int dmanr, phys_addr_t src_dma_addr, + phys_addr_t dst_dma_addr) +{ + if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { + printk("ppc4xx_set_dma_addr2: bad channel: %d\n", dmanr); + return; + } + +#ifdef DEBUG_4xxDMA + { + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + int error = 0; + switch (p_dma_ch->pwidth) { + case PW_8: + break; + case PW_16: + if (((unsigned) src_dma_addr & 0x1) || + ((unsigned) dst_dma_addr & 0x1) + ) + error = 1; + break; + case PW_32: + if (((unsigned) src_dma_addr & 0x3) || + ((unsigned) dst_dma_addr & 0x3) + ) + error = 1; + break; + case PW_64: + if (((unsigned) src_dma_addr & 0x7) || + ((unsigned) dst_dma_addr & 0x7) + ) + error = 1; + break; + default: + printk("ppc4xx_set_dma_addr2: invalid bus width: 0x%x\n", + p_dma_ch->pwidth); + return; + } + if (error) + printk + ("Warning: ppc4xx_set_dma_addr2 src 0x%x dst 0x%x bus width %d\n", + src_dma_addr, dst_dma_addr, p_dma_ch->pwidth); + } +#endif + + ppc4xx_set_src_addr(dmanr, src_dma_addr); + ppc4xx_set_dst_addr(dmanr, dst_dma_addr); +} + +/* + * Enables the channel interrupt. + * + * If performing a scatter/gatter transfer, this function + * MUST be called before calling alloc_dma_handle() and building + * the sgl list. Otherwise, interrupts will not be enabled, if + * they were previously disabled. + */ +int +ppc4xx_enable_dma_interrupt(unsigned int dmanr) +{ + unsigned int control; + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + + if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { + printk("ppc4xx_enable_dma_interrupt: bad channel: %d\n", dmanr); + return DMA_STATUS_BAD_CHANNEL; + } + + p_dma_ch->int_enable = 1; + + control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8)); + control |= DMA_CIE_ENABLE; /* Channel Interrupt Enable */ + mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control); + + return DMA_STATUS_GOOD; +} + +/* + * Disables the channel interrupt. + * + * If performing a scatter/gatter transfer, this function + * MUST be called before calling alloc_dma_handle() and building + * the sgl list. Otherwise, interrupts will not be disabled, if + * they were previously enabled. + */ +int +ppc4xx_disable_dma_interrupt(unsigned int dmanr) +{ + unsigned int control; + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + + if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { + printk("ppc4xx_disable_dma_interrupt: bad channel: %d\n", dmanr); + return DMA_STATUS_BAD_CHANNEL; + } + + p_dma_ch->int_enable = 0; + + control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8)); + control &= ~DMA_CIE_ENABLE; /* Channel Interrupt Enable */ + mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control); + + return DMA_STATUS_GOOD; +} + +/* + * Configures a DMA channel, including the peripheral bus width, if a + * peripheral is attached to the channel, the polarity of the DMAReq and + * DMAAck signals, etc. This information should really be setup by the boot + * code, since most likely the configuration won't change dynamically. + * If the kernel has to call this function, it's recommended that it's + * called from platform specific init code. The driver should not need to + * call this function. + */ +int +ppc4xx_init_dma_channel(unsigned int dmanr, ppc_dma_ch_t * p_init) +{ + unsigned int polarity; + uint32_t control = 0; + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + + DMA_MODE_READ = (unsigned long) DMA_TD; /* Peripheral to Memory */ + DMA_MODE_WRITE = 0; /* Memory to Peripheral */ + + if (!p_init) { + printk("ppc4xx_init_dma_channel: NULL p_init\n"); + return DMA_STATUS_NULL_POINTER; + } + + if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { + printk("ppc4xx_init_dma_channel: bad channel %d\n", dmanr); + return DMA_STATUS_BAD_CHANNEL; + } + +#if DCRN_POL > 0 + polarity = mfdcr(DCRN_POL); +#else + polarity = 0; +#endif + + /* Setup the control register based on the values passed to + * us in p_init. Then, over-write the control register with this + * new value. + */ + control |= SET_DMA_CONTROL; + + /* clear all polarity signals and then "or" in new signal levels */ + polarity &= ~GET_DMA_POLARITY(dmanr); + polarity |= p_init->polarity; +#if DCRN_POL > 0 + mtdcr(DCRN_POL, polarity); +#endif + mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control); + + /* save these values in our dma channel structure */ + memcpy(p_dma_ch, p_init, sizeof (ppc_dma_ch_t)); + + /* + * The peripheral width values written in the control register are: + * PW_8 0 + * PW_16 1 + * PW_32 2 + * PW_64 3 + * + * Since the DMA count register takes the number of "transfers", + * we need to divide the count sent to us in certain + * functions by the appropriate number. It so happens that our + * right shift value is equal to the peripheral width value. + */ + p_dma_ch->shift = p_init->pwidth; + + /* + * Save the control word for easy access. + */ + p_dma_ch->control = control; + + mtdcr(DCRN_DMASR, 0xffffffff); /* clear status register */ + return DMA_STATUS_GOOD; +} + +/* + * This function returns the channel configuration. + */ +int +ppc4xx_get_channel_config(unsigned int dmanr, ppc_dma_ch_t * p_dma_ch) +{ + unsigned int polarity; + unsigned int control; + + if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { + printk("ppc4xx_get_channel_config: bad channel %d\n", dmanr); + return DMA_STATUS_BAD_CHANNEL; + } + + memcpy(p_dma_ch, &dma_channels[dmanr], sizeof (ppc_dma_ch_t)); + +#if DCRN_POL > 0 + polarity = mfdcr(DCRN_POL); +#else + polarity = 0; +#endif + + p_dma_ch->polarity = polarity & GET_DMA_POLARITY(dmanr); + control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8)); + + p_dma_ch->cp = GET_DMA_PRIORITY(control); + p_dma_ch->pwidth = GET_DMA_PW(control); + p_dma_ch->psc = GET_DMA_PSC(control); + p_dma_ch->pwc = GET_DMA_PWC(control); + p_dma_ch->phc = GET_DMA_PHC(control); + p_dma_ch->ce = GET_DMA_CE_ENABLE(control); + p_dma_ch->int_enable = GET_DMA_CIE_ENABLE(control); + p_dma_ch->shift = GET_DMA_PW(control); + +#ifdef CONFIG_PPC4xx_EDMA + p_dma_ch->pf = GET_DMA_PREFETCH(control); +#else + p_dma_ch->ch_enable = GET_DMA_CH(control); + p_dma_ch->ece_enable = GET_DMA_ECE(control); + p_dma_ch->tcd_disable = GET_DMA_TCD(control); +#endif + return DMA_STATUS_GOOD; +} + +/* + * Sets the priority for the DMA channel dmanr. + * Since this is setup by the hardware init function, this function + * can be used to dynamically change the priority of a channel. + * + * Acceptable priorities: + * + * PRIORITY_LOW + * PRIORITY_MID_LOW + * PRIORITY_MID_HIGH + * PRIORITY_HIGH + * + */ +int +ppc4xx_set_channel_priority(unsigned int dmanr, unsigned int priority) +{ + unsigned int control; + + if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { + printk("ppc4xx_set_channel_priority: bad channel %d\n", dmanr); + return DMA_STATUS_BAD_CHANNEL; + } + + if ((priority != PRIORITY_LOW) && + (priority != PRIORITY_MID_LOW) && + (priority != PRIORITY_MID_HIGH) && (priority != PRIORITY_HIGH)) { + printk("ppc4xx_set_channel_priority: bad priority: 0x%x\n", priority); + } + + control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8)); + control |= SET_DMA_PRIORITY(priority); + mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control); + + return DMA_STATUS_GOOD; +} + +/* + * Returns the width of the peripheral attached to this channel. This assumes + * that someone who knows the hardware configuration, boot code or some other + * init code, already set the width. + * + * The return value is one of: + * PW_8 + * PW_16 + * PW_32 + * PW_64 + * + * The function returns 0 on error. + */ +unsigned int +ppc4xx_get_peripheral_width(unsigned int dmanr) +{ + unsigned int control; + + if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { + printk("ppc4xx_get_peripheral_width: bad channel %d\n", dmanr); + return DMA_STATUS_BAD_CHANNEL; + } + + control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8)); + + return (GET_DMA_PW(control)); +} + +/* + * Clears the channel status bits + */ +int +ppc4xx_clr_dma_status(unsigned int dmanr) +{ + if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { + printk(KERN_ERR "ppc4xx_clr_dma_status: bad channel: %d\n", dmanr); + return DMA_STATUS_BAD_CHANNEL; + } + mtdcr(DCRN_DMASR, ((u32)DMA_CH0_ERR | (u32)DMA_CS0 | (u32)DMA_TS0) >> dmanr); + return DMA_STATUS_GOOD; +} + +/* + * Enables the burst on the channel (BTEN bit in the control/count register) + * Note: + * For scatter/gather dma, this function MUST be called before the + * ppc4xx_alloc_dma_handle() func as the chan count register is copied into the + * sgl list and used as each sgl element is added. + */ +int +ppc4xx_enable_burst(unsigned int dmanr) +{ + unsigned int ctc; + if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { + printk(KERN_ERR "ppc4xx_enable_burst: bad channel: %d\n", dmanr); + return DMA_STATUS_BAD_CHANNEL; + } + ctc = mfdcr(DCRN_DMACT0 + (dmanr * 0x8)) | DMA_CTC_BTEN; + mtdcr(DCRN_DMACT0 + (dmanr * 0x8), ctc); + return DMA_STATUS_GOOD; +} +/* + * Disables the burst on the channel (BTEN bit in the control/count register) + * Note: + * For scatter/gather dma, this function MUST be called before the + * ppc4xx_alloc_dma_handle() func as the chan count register is copied into the + * sgl list and used as each sgl element is added. + */ +int +ppc4xx_disable_burst(unsigned int dmanr) +{ + unsigned int ctc; + if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { + printk(KERN_ERR "ppc4xx_disable_burst: bad channel: %d\n", dmanr); + return DMA_STATUS_BAD_CHANNEL; + } + ctc = mfdcr(DCRN_DMACT0 + (dmanr * 0x8)) &~ DMA_CTC_BTEN; + mtdcr(DCRN_DMACT0 + (dmanr * 0x8), ctc); + return DMA_STATUS_GOOD; +} +/* + * Sets the burst size (number of peripheral widths) for the channel + * (BSIZ bits in the control/count register)) + * must be one of: + * DMA_CTC_BSIZ_2 + * DMA_CTC_BSIZ_4 + * DMA_CTC_BSIZ_8 + * DMA_CTC_BSIZ_16 + * Note: + * For scatter/gather dma, this function MUST be called before the + * ppc4xx_alloc_dma_handle() func as the chan count register is copied into the + * sgl list and used as each sgl element is added. + */ +int +ppc4xx_set_burst_size(unsigned int dmanr, unsigned int bsize) +{ + unsigned int ctc; + if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { + printk(KERN_ERR "ppc4xx_set_burst_size: bad channel: %d\n", dmanr); + return DMA_STATUS_BAD_CHANNEL; + } + ctc = mfdcr(DCRN_DMACT0 + (dmanr * 0x8)) &~ DMA_CTC_BSIZ_MSK; + ctc |= (bsize & DMA_CTC_BSIZ_MSK); + mtdcr(DCRN_DMACT0 + (dmanr * 0x8), ctc); + return DMA_STATUS_GOOD; +} + +EXPORT_SYMBOL(ppc4xx_init_dma_channel); +EXPORT_SYMBOL(ppc4xx_get_channel_config); +EXPORT_SYMBOL(ppc4xx_set_channel_priority); +EXPORT_SYMBOL(ppc4xx_get_peripheral_width); +EXPORT_SYMBOL(dma_channels); +EXPORT_SYMBOL(ppc4xx_set_src_addr); +EXPORT_SYMBOL(ppc4xx_set_dst_addr); +EXPORT_SYMBOL(ppc4xx_set_dma_addr); +EXPORT_SYMBOL(ppc4xx_set_dma_addr2); +EXPORT_SYMBOL(ppc4xx_enable_dma); +EXPORT_SYMBOL(ppc4xx_disable_dma); +EXPORT_SYMBOL(ppc4xx_set_dma_mode); +EXPORT_SYMBOL(ppc4xx_set_dma_count); +EXPORT_SYMBOL(ppc4xx_get_dma_residue); +EXPORT_SYMBOL(ppc4xx_enable_dma_interrupt); +EXPORT_SYMBOL(ppc4xx_disable_dma_interrupt); +EXPORT_SYMBOL(ppc4xx_get_dma_status); +EXPORT_SYMBOL(ppc4xx_clr_dma_status); +EXPORT_SYMBOL(ppc4xx_enable_burst); +EXPORT_SYMBOL(ppc4xx_disable_burst); +EXPORT_SYMBOL(ppc4xx_set_burst_size); diff --git a/arch/ppc/syslib/ppc4xx_kgdb.c b/arch/ppc/syslib/ppc4xx_kgdb.c new file mode 100644 index 00000000000..fe8668bf813 --- /dev/null +++ b/arch/ppc/syslib/ppc4xx_kgdb.c @@ -0,0 +1,124 @@ +#include +#include +#include +#include + + + +#define LSR_DR 0x01 /* Data ready */ +#define LSR_OE 0x02 /* Overrun */ +#define LSR_PE 0x04 /* Parity error */ +#define LSR_FE 0x08 /* Framing error */ +#define LSR_BI 0x10 /* Break */ +#define LSR_THRE 0x20 /* Xmit holding register empty */ +#define LSR_TEMT 0x40 /* Xmitter empty */ +#define LSR_ERR 0x80 /* Error */ + +#include + +extern struct NS16550* COM_PORTS[]; +#ifndef NULL +#define NULL 0x00 +#endif + +static volatile struct NS16550 *kgdb_debugport = NULL; + +volatile struct NS16550 * +NS16550_init(int chan) +{ + volatile struct NS16550 *com_port; + int quot; +#ifdef BASE_BAUD + quot = BASE_BAUD / 9600; +#else + quot = 0x000c; /* 0xc = 9600 baud (on a pc) */ +#endif + + com_port = (struct NS16550 *) COM_PORTS[chan]; + + com_port->lcr = 0x00; + com_port->ier = 0xFF; + com_port->ier = 0x00; + com_port->lcr = com_port->lcr | 0x80; /* Access baud rate */ + com_port->dll = ( quot & 0x00ff ); /* 0xc = 9600 baud */ + com_port->dlm = ( quot & 0xff00 ) >> 8; + com_port->lcr = 0x03; /* 8 data, 1 stop, no parity */ + com_port->mcr = 0x00; /* RTS/DTR */ + com_port->fcr = 0x07; /* Clear & enable FIFOs */ + + return( com_port ); +} + + +void +NS16550_putc(volatile struct NS16550 *com_port, unsigned char c) +{ + while ((com_port->lsr & LSR_THRE) == 0) + ; + com_port->thr = c; + return; +} + +unsigned char +NS16550_getc(volatile struct NS16550 *com_port) +{ + while ((com_port->lsr & LSR_DR) == 0) + ; + return (com_port->rbr); +} + +unsigned char +NS16550_tstc(volatile struct NS16550 *com_port) +{ + return ((com_port->lsr & LSR_DR) != 0); +} + + +#if defined(CONFIG_KGDB_TTYS0) +#define KGDB_PORT 0 +#elif defined(CONFIG_KGDB_TTYS1) +#define KGDB_PORT 1 +#elif defined(CONFIG_KGDB_TTYS2) +#define KGDB_PORT 2 +#elif defined(CONFIG_KGDB_TTYS3) +#define KGDB_PORT 3 +#else +#error "invalid kgdb_tty port" +#endif + +void putDebugChar( unsigned char c ) +{ + if ( kgdb_debugport == NULL ) + kgdb_debugport = NS16550_init(KGDB_PORT); + NS16550_putc( kgdb_debugport, c ); +} + +int getDebugChar( void ) +{ + if (kgdb_debugport == NULL) + kgdb_debugport = NS16550_init(KGDB_PORT); + + return(NS16550_getc(kgdb_debugport)); +} + +void kgdb_interruptible(int enable) +{ + return; +} + +void putDebugString(char* str) +{ + while (*str != '\0') { + putDebugChar(*str); + str++; + } + putDebugChar('\r'); + return; +} + +void +kgdb_map_scc(void) +{ + printk("kgdb init \n"); + kgdb_debugport = NS16550_init(KGDB_PORT); +} diff --git a/arch/ppc/syslib/ppc4xx_pic.c b/arch/ppc/syslib/ppc4xx_pic.c new file mode 100644 index 00000000000..08f06dd17e7 --- /dev/null +++ b/arch/ppc/syslib/ppc4xx_pic.c @@ -0,0 +1,244 @@ +/* + * arch/ppc/syslib/ppc4xx_pic.c + * + * Interrupt controller driver for PowerPC 4xx-based processors. + * + * Eugene Surovegin or + * Copyright (c) 2004, 2005 Zultys Technologies + * + * Based on original code by + * Copyright (c) 1999 Grant Erickson + * Armin Custer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. +*/ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* See comment in include/arch-ppc/ppc4xx_pic.h + * for more info about these two variables + */ +extern struct ppc4xx_uic_settings ppc4xx_core_uic_cfg[NR_UICS] + __attribute__ ((weak)); +extern unsigned char ppc4xx_uic_ext_irq_cfg[] __attribute__ ((weak)); + +#define IRQ_MASK_UIC0(irq) (1 << (31 - (irq))) +#define IRQ_MASK_UICx(irq) (1 << (31 - ((irq) & 0x1f))) +#define IRQ_MASK_UIC1(irq) IRQ_MASK_UICx(irq) +#define IRQ_MASK_UIC2(irq) IRQ_MASK_UICx(irq) + +#define UIC_HANDLERS(n) \ +static void ppc4xx_uic##n##_enable(unsigned int irq) \ +{ \ + ppc_cached_irq_mask[n] |= IRQ_MASK_UIC##n(irq); \ + mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]); \ +} \ + \ +static void ppc4xx_uic##n##_disable(unsigned int irq) \ +{ \ + ppc_cached_irq_mask[n] &= ~IRQ_MASK_UIC##n(irq); \ + mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]); \ + ACK_UIC##n##_PARENT \ +} \ + \ +static void ppc4xx_uic##n##_ack(unsigned int irq) \ +{ \ + u32 mask = IRQ_MASK_UIC##n(irq); \ + ppc_cached_irq_mask[n] &= ~mask; \ + mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]); \ + mtdcr(DCRN_UIC_SR(UIC##n), mask); \ + ACK_UIC##n##_PARENT \ +} \ + \ +static void ppc4xx_uic##n##_end(unsigned int irq) \ +{ \ + unsigned int status = irq_desc[irq].status; \ + u32 mask = IRQ_MASK_UIC##n(irq); \ + if (status & IRQ_LEVEL) { \ + mtdcr(DCRN_UIC_SR(UIC##n), mask); \ + ACK_UIC##n##_PARENT \ + } \ + if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) { \ + ppc_cached_irq_mask[n] |= mask; \ + mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]); \ + } \ +} + +#define DECLARE_UIC(n) \ +{ \ + .typename = "UIC"#n, \ + .enable = ppc4xx_uic##n##_enable, \ + .disable = ppc4xx_uic##n##_disable, \ + .ack = ppc4xx_uic##n##_ack, \ + .end = ppc4xx_uic##n##_end, \ +} \ + +#if NR_UICS == 3 +#define ACK_UIC0_PARENT mtdcr(DCRN_UIC_SR(UICB), UICB_UIC0NC); +#define ACK_UIC1_PARENT mtdcr(DCRN_UIC_SR(UICB), UICB_UIC1NC); +#define ACK_UIC2_PARENT mtdcr(DCRN_UIC_SR(UICB), UICB_UIC2NC); +UIC_HANDLERS(0); +UIC_HANDLERS(1); +UIC_HANDLERS(2); + +static int ppc4xx_pic_get_irq(struct pt_regs *regs) +{ + u32 uicb = mfdcr(DCRN_UIC_MSR(UICB)); + if (uicb & UICB_UIC0NC) + return 32 - ffs(mfdcr(DCRN_UIC_MSR(UIC0))); + else if (uicb & UICB_UIC1NC) + return 64 - ffs(mfdcr(DCRN_UIC_MSR(UIC1))); + else if (uicb & UICB_UIC2NC) + return 96 - ffs(mfdcr(DCRN_UIC_MSR(UIC2))); + else + return -1; +} + +static void __init ppc4xx_pic_impl_init(void) +{ + /* Configure Base UIC */ + mtdcr(DCRN_UIC_CR(UICB), 0); + mtdcr(DCRN_UIC_TR(UICB), 0); + mtdcr(DCRN_UIC_PR(UICB), 0xffffffff); + mtdcr(DCRN_UIC_SR(UICB), 0xffffffff); + mtdcr(DCRN_UIC_ER(UICB), UICB_UIC0NC | UICB_UIC1NC | UICB_UIC2NC); +} + +#elif NR_UICS == 2 +#define ACK_UIC0_PARENT +#define ACK_UIC1_PARENT mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC1NC); +UIC_HANDLERS(0); +UIC_HANDLERS(1); + +static int ppc4xx_pic_get_irq(struct pt_regs *regs) +{ + u32 uic0 = mfdcr(DCRN_UIC_MSR(UIC0)); + if (uic0 & UIC0_UIC1NC) + return 64 - ffs(mfdcr(DCRN_UIC_MSR(UIC1))); + else + return uic0 ? 32 - ffs(uic0) : -1; +} + +static void __init ppc4xx_pic_impl_init(void) +{ + /* Enable cascade interrupt in UIC0 */ + ppc_cached_irq_mask[0] |= UIC0_UIC1NC; + mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC1NC); + mtdcr(DCRN_UIC_ER(UIC0), ppc_cached_irq_mask[0]); +} + +#elif NR_UICS == 1 +#define ACK_UIC0_PARENT +UIC_HANDLERS(0); + +static int ppc4xx_pic_get_irq(struct pt_regs *regs) +{ + u32 uic0 = mfdcr(DCRN_UIC_MSR(UIC0)); + return uic0 ? 32 - ffs(uic0) : -1; +} + +static inline void ppc4xx_pic_impl_init(void) +{ +} +#endif + +static struct ppc4xx_uic_impl { + struct hw_interrupt_type decl; + int base; /* Base DCR number */ +} __uic[] = { + { .decl = DECLARE_UIC(0), .base = UIC0 }, +#if NR_UICS > 1 + { .decl = DECLARE_UIC(1), .base = UIC1 }, +#if NR_UICS > 2 + { .decl = DECLARE_UIC(2), .base = UIC2 }, +#endif +#endif +}; + +static inline int is_level_sensitive(int irq) +{ + u32 tr = mfdcr(DCRN_UIC_TR(__uic[irq >> 5].base)); + return (tr & IRQ_MASK_UICx(irq)) == 0; +} + +void __init ppc4xx_pic_init(void) +{ + int i; + unsigned char *eirqs = ppc4xx_uic_ext_irq_cfg; + + for (i = 0; i < NR_UICS; ++i) { + int base = __uic[i].base; + + /* Disable everything by default */ + ppc_cached_irq_mask[i] = 0; + mtdcr(DCRN_UIC_ER(base), 0); + + /* We don't use critical interrupts */ + mtdcr(DCRN_UIC_CR(base), 0); + + /* Configure polarity and triggering */ + if (ppc4xx_core_uic_cfg) { + struct ppc4xx_uic_settings *p = ppc4xx_core_uic_cfg + i; + u32 mask = p->ext_irq_mask; + u32 pr = mfdcr(DCRN_UIC_PR(base)) & mask; + u32 tr = mfdcr(DCRN_UIC_TR(base)) & mask; + + /* "Fixed" interrupts (on-chip devices) */ + pr |= p->polarity & ~mask; + tr |= p->triggering & ~mask; + + /* Merge external IRQs settings if board port + * provided them + */ + if (eirqs && mask) { + pr &= ~mask; + tr &= ~mask; + while (mask) { + /* Extract current external IRQ mask */ + u32 eirq_mask = 1 << __ilog2(mask); + + if (!(*eirqs & IRQ_SENSE_LEVEL)) + tr |= eirq_mask; + + if (*eirqs & IRQ_POLARITY_POSITIVE) + pr |= eirq_mask; + + mask &= ~eirq_mask; + ++eirqs; + } + } + mtdcr(DCRN_UIC_PR(base), pr); + mtdcr(DCRN_UIC_TR(base), tr); + } + + /* ACK any pending interrupts to prevent false + * triggering after first enable + */ + mtdcr(DCRN_UIC_SR(base), 0xffffffff); + } + + /* Perform optional implementation specific setup + * (e.g. enable cascade interrupts for multi-UIC configurations) + */ + ppc4xx_pic_impl_init(); + + /* Attach low-level handlers */ + for (i = 0; i < (NR_UICS << 5); ++i) { + irq_desc[i].handler = &__uic[i >> 5].decl; + if (is_level_sensitive(i)) + irq_desc[i].status |= IRQ_LEVEL; + } + + ppc_md.get_irq = ppc4xx_pic_get_irq; +} diff --git a/arch/ppc/syslib/ppc4xx_pm.c b/arch/ppc/syslib/ppc4xx_pm.c new file mode 100644 index 00000000000..60a47920488 --- /dev/null +++ b/arch/ppc/syslib/ppc4xx_pm.c @@ -0,0 +1,47 @@ +/* + * Author: Armin Kuster + * + * 2002 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + * + * This an attempt to get Power Management going for the IBM 4xx processor. + * This was derived from the ppc4xx._setup.c file + */ + +#include +#include + +#include + +void __init +ppc4xx_pm_init(void) +{ + + unsigned int value = 0; + + /* turn off unused hardware to save power */ +#ifdef CONFIG_405GP + value |= CPM_DCP; /* CodePack */ +#endif + +#if !defined(CONFIG_IBM_OCP_GPIO) + value |= CPM_GPIO0; +#endif + +#if !defined(CONFIG_PPC405_I2C_ADAP) + value |= CPM_IIC0; +#ifdef CONFIG_STB03xxx + value |= CPM_IIC1; +#endif +#endif + + +#if !defined(CONFIG_405_DMA) + value |= CPM_DMA; +#endif + + mtdcr(DCRN_CPMFR, value); + +} diff --git a/arch/ppc/syslib/ppc4xx_setup.c b/arch/ppc/syslib/ppc4xx_setup.c new file mode 100644 index 00000000000..e170aebeb69 --- /dev/null +++ b/arch/ppc/syslib/ppc4xx_setup.c @@ -0,0 +1,321 @@ +/* + * + * Copyright (c) 1999-2000 Grant Erickson + * + * Copyright 2000-2001 MontaVista Software Inc. + * Completed implementation. + * Author: MontaVista Software, Inc. + * Frank Rowand + * Debbie Chu + * Further modifications by Armin Kuster + * + * Module name: ppc4xx_setup.c + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Function Prototypes */ +extern void abort(void); +extern void ppc4xx_find_bridges(void); + +extern void ppc4xx_wdt_heartbeat(void); +extern int wdt_enable; +extern unsigned long wdt_period; + +/* Global Variables */ +bd_t __res; + +void __init +ppc4xx_setup_arch(void) +{ +#if !defined(CONFIG_BDI_SWITCH) + /* + * The Abatron BDI JTAG debugger does not tolerate others + * mucking with the debug registers. + */ + mtspr(SPRN_DBCR0, (DBCR0_IDM)); + mtspr(SPRN_DBSR, 0xffffffff); +#endif + + /* Setup PCI host bridges */ +#ifdef CONFIG_PCI + ppc4xx_find_bridges(); +#endif +} + +/* + * This routine pretty-prints the platform's internal CPU clock + * frequencies into the buffer for usage in /proc/cpuinfo. + */ + +static int +ppc4xx_show_percpuinfo(struct seq_file *m, int i) +{ + seq_printf(m, "clock\t\t: %ldMHz\n", (long)__res.bi_intfreq / 1000000); + + return 0; +} + +/* + * This routine pretty-prints the platform's internal bus clock + * frequencies into the buffer for usage in /proc/cpuinfo. + */ +static int +ppc4xx_show_cpuinfo(struct seq_file *m) +{ + bd_t *bip = &__res; + + seq_printf(m, "machine\t\t: %s\n", PPC4xx_MACHINE_NAME); + seq_printf(m, "plb bus clock\t: %ldMHz\n", + (long) bip->bi_busfreq / 1000000); +#ifdef CONFIG_PCI + seq_printf(m, "pci bus clock\t: %dMHz\n", + bip->bi_pci_busfreq / 1000000); +#endif + + return 0; +} + +/* + * Return the virtual address representing the top of physical RAM. + */ +static unsigned long __init +ppc4xx_find_end_of_memory(void) +{ + return ((unsigned long) __res.bi_memsize); +} + +void __init +ppc4xx_map_io(void) +{ + io_block_mapping(PPC4xx_ONB_IO_VADDR, + PPC4xx_ONB_IO_PADDR, PPC4xx_ONB_IO_SIZE, _PAGE_IO); +#ifdef CONFIG_PCI + io_block_mapping(PPC4xx_PCI_IO_VADDR, + PPC4xx_PCI_IO_PADDR, PPC4xx_PCI_IO_SIZE, _PAGE_IO); + io_block_mapping(PPC4xx_PCI_CFG_VADDR, + PPC4xx_PCI_CFG_PADDR, PPC4xx_PCI_CFG_SIZE, _PAGE_IO); + io_block_mapping(PPC4xx_PCI_LCFG_VADDR, + PPC4xx_PCI_LCFG_PADDR, PPC4xx_PCI_LCFG_SIZE, _PAGE_IO); +#endif +} + +void __init +ppc4xx_init_IRQ(void) +{ + ppc4xx_pic_init(); +} + +static void +ppc4xx_restart(char *cmd) +{ + printk("%s\n", cmd); + abort(); +} + +static void +ppc4xx_power_off(void) +{ + printk("System Halted\n"); + local_irq_disable(); + while (1) ; +} + +static void +ppc4xx_halt(void) +{ + printk("System Halted\n"); + local_irq_disable(); + while (1) ; +} + +/* + * This routine retrieves the internal processor frequency from the board + * information structure, sets up the kernel timer decrementer based on + * that value, enables the 4xx programmable interval timer (PIT) and sets + * it up for auto-reload. + */ +static void __init +ppc4xx_calibrate_decr(void) +{ + unsigned int freq; + bd_t *bip = &__res; + +#if defined(CONFIG_WALNUT) || defined(CONFIG_ASH) || defined(CONFIG_SYCAMORE) + /* Walnut boot rom sets DCR CHCR1 (aka CPC0_CR1) bit CETE to 1 */ + mtdcr(DCRN_CHCR1, mfdcr(DCRN_CHCR1) & ~CHR1_CETE); +#endif + freq = bip->bi_tbfreq; + tb_ticks_per_jiffy = freq / HZ; + tb_to_us = mulhwu_scale_factor(freq, 1000000); + + /* Set the time base to zero. + ** At 200 Mhz, time base will rollover in ~2925 years. + */ + + mtspr(SPRN_TBWL, 0); + mtspr(SPRN_TBWU, 0); + + /* Clear any pending timer interrupts */ + + mtspr(SPRN_TSR, TSR_ENW | TSR_WIS | TSR_PIS | TSR_FIS); + mtspr(SPRN_TCR, TCR_PIE | TCR_ARE); + + /* Set the PIT reload value and just let it run. */ + mtspr(SPRN_PIT, tb_ticks_per_jiffy); +} + +/* + * IDE stuff. + * should be generic for every IDE PCI chipset + */ +#if defined(CONFIG_PCI) && defined(CONFIG_IDE) +static void +ppc4xx_ide_init_hwif_ports(hw_regs_t * hw, unsigned long data_port, + unsigned long ctrl_port, int *irq) +{ + int i; + + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; ++i) + hw->io_ports[i] = data_port + i - IDE_DATA_OFFSET; + + hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port; +} +#endif /* defined(CONFIG_PCI) && defined(CONFIG_IDE) */ + +TODC_ALLOC(); + +/* + * Input(s): + * r3 - Optional pointer to a board information structure. + * r4 - Optional pointer to the physical starting address of the init RAM + * disk. + * r5 - Optional pointer to the physical ending address of the init RAM + * disk. + * r6 - Optional pointer to the physical starting address of any kernel + * command-line parameters. + * r7 - Optional pointer to the physical ending address of any kernel + * command-line parameters. + */ +void __init +ppc4xx_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + + /* + * If we were passed in a board information, copy it into the + * residual data area. + */ + if (r3) + __res = *(bd_t *)(r3 + KERNELBASE); + +#if defined(CONFIG_BLK_DEV_INITRD) + /* + * If the init RAM disk has been configured in, and there's a valid + * starting address for it, set it up. + */ + if (r4) { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + /* Copy the kernel command line arguments to a safe place. */ + + if (r6) { + *(char *) (r7 + KERNELBASE) = 0; + strcpy(cmd_line, (char *) (r6 + KERNELBASE)); + } +#if defined(CONFIG_PPC405_WDT) +/* Look for wdt= option on command line */ + if (strstr(cmd_line, "wdt=")) { + int valid_wdt = 0; + char *p, *q; + for (q = cmd_line; (p = strstr(q, "wdt=")) != 0;) { + q = p + 4; + if (p > cmd_line && p[-1] != ' ') + continue; + wdt_period = simple_strtoul(q, &q, 0); + valid_wdt = 1; + ++q; + } + wdt_enable = valid_wdt; + } +#endif + + /* Initialize machine-dependent vectors */ + + ppc_md.setup_arch = ppc4xx_setup_arch; + ppc_md.show_percpuinfo = ppc4xx_show_percpuinfo; + ppc_md.show_cpuinfo = ppc4xx_show_cpuinfo; + ppc_md.init_IRQ = ppc4xx_init_IRQ; + + ppc_md.restart = ppc4xx_restart; + ppc_md.power_off = ppc4xx_power_off; + ppc_md.halt = ppc4xx_halt; + + ppc_md.calibrate_decr = ppc4xx_calibrate_decr; + +#ifdef CONFIG_PPC405_WDT + ppc_md.heartbeat = ppc4xx_wdt_heartbeat; +#endif + ppc_md.heartbeat_count = 0; + + ppc_md.find_end_of_memory = ppc4xx_find_end_of_memory; + ppc_md.setup_io_mappings = ppc4xx_map_io; + +#ifdef CONFIG_SERIAL_TEXT_DEBUG + ppc_md.progress = gen550_progress; +#endif + +#if defined(CONFIG_PCI) && defined(CONFIG_IDE) + ppc_ide_md.ide_init_hwif = ppc4xx_ide_init_hwif_ports; +#endif /* defined(CONFIG_PCI) && defined(CONFIG_IDE) */ +} + +/* Called from MachineCheckException */ +void platform_machine_check(struct pt_regs *regs) +{ +#if defined(DCRN_PLB0_BEAR) + printk("PLB0: BEAR= 0x%08x ACR= 0x%08x BESR= 0x%08x\n", + mfdcr(DCRN_PLB0_BEAR), mfdcr(DCRN_PLB0_ACR), + mfdcr(DCRN_PLB0_BESR)); +#endif +#if defined(DCRN_POB0_BEAR) + printk("PLB0 to OPB: BEAR= 0x%08x BESR0= 0x%08x BESR1= 0x%08x\n", + mfdcr(DCRN_POB0_BEAR), mfdcr(DCRN_POB0_BESR0), + mfdcr(DCRN_POB0_BESR1)); +#endif + +} diff --git a/arch/ppc/syslib/ppc4xx_sgdma.c b/arch/ppc/syslib/ppc4xx_sgdma.c new file mode 100644 index 00000000000..9f76e8ee39e --- /dev/null +++ b/arch/ppc/syslib/ppc4xx_sgdma.c @@ -0,0 +1,467 @@ +/* + * arch/ppc/kernel/ppc4xx_sgdma.c + * + * IBM PPC4xx DMA engine scatter/gather library + * + * Copyright 2002-2003 MontaVista Software Inc. + * + * Cleaned up and converted to new DCR access + * Matt Porter + * + * Original code by Armin Kuster + * and Pete Popov + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +void +ppc4xx_set_sg_addr(int dmanr, phys_addr_t sg_addr) +{ + if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { + printk("ppc4xx_set_sg_addr: bad channel: %d\n", dmanr); + return; + } + +#ifdef PPC4xx_DMA_64BIT + mtdcr(DCRN_ASGH0 + (dmanr * 0x8), (u32)(sg_addr >> 32)); +#endif + mtdcr(DCRN_ASG0 + (dmanr * 0x8), (u32)sg_addr); +} + +/* + * Add a new sgl descriptor to the end of a scatter/gather list + * which was created by alloc_dma_handle(). + * + * For a memory to memory transfer, both dma addresses must be + * valid. For a peripheral to memory transfer, one of the addresses + * must be set to NULL, depending on the direction of the transfer: + * memory to peripheral: set dst_addr to NULL, + * peripheral to memory: set src_addr to NULL. + */ +int +ppc4xx_add_dma_sgl(sgl_handle_t handle, phys_addr_t src_addr, phys_addr_t dst_addr, + unsigned int count) +{ + sgl_list_info_t *psgl = (sgl_list_info_t *) handle; + ppc_dma_ch_t *p_dma_ch; + + if (!handle) { + printk("ppc4xx_add_dma_sgl: null handle\n"); + return DMA_STATUS_BAD_HANDLE; + } + + if (psgl->dmanr >= MAX_PPC4xx_DMA_CHANNELS) { + printk("ppc4xx_add_dma_sgl: bad channel: %d\n", psgl->dmanr); + return DMA_STATUS_BAD_CHANNEL; + } + + p_dma_ch = &dma_channels[psgl->dmanr]; + +#ifdef DEBUG_4xxDMA + { + int error = 0; + unsigned int aligned = + (unsigned) src_addr | (unsigned) dst_addr | count; + switch (p_dma_ch->pwidth) { + case PW_8: + break; + case PW_16: + if (aligned & 0x1) + error = 1; + break; + case PW_32: + if (aligned & 0x3) + error = 1; + break; + case PW_64: + if (aligned & 0x7) + error = 1; + break; + default: + printk("ppc4xx_add_dma_sgl: invalid bus width: 0x%x\n", + p_dma_ch->pwidth); + return DMA_STATUS_GENERAL_ERROR; + } + if (error) + printk + ("Alignment warning: ppc4xx_add_dma_sgl src 0x%x dst 0x%x count 0x%x bus width var %d\n", + src_addr, dst_addr, count, p_dma_ch->pwidth); + + } +#endif + + if ((unsigned) (psgl->ptail + 1) >= ((unsigned) psgl + SGL_LIST_SIZE)) { + printk("sgl handle out of memory \n"); + return DMA_STATUS_OUT_OF_MEMORY; + } + + if (!psgl->ptail) { + psgl->phead = (ppc_sgl_t *) + ((unsigned) psgl + sizeof (sgl_list_info_t)); + psgl->phead_dma = psgl->dma_addr + sizeof(sgl_list_info_t); + psgl->ptail = psgl->phead; + psgl->ptail_dma = psgl->phead_dma; + } else { + if(p_dma_ch->int_on_final_sg) { + /* mask out all dma interrupts, except error, on tail + before adding new tail. */ + psgl->ptail->control_count &= + ~(SG_TCI_ENABLE | SG_ETI_ENABLE); + } + psgl->ptail->next = psgl->ptail_dma + sizeof(ppc_sgl_t); + psgl->ptail++; + psgl->ptail_dma += sizeof(ppc_sgl_t); + } + + psgl->ptail->control = psgl->control; + psgl->ptail->src_addr = src_addr; + psgl->ptail->dst_addr = dst_addr; + psgl->ptail->control_count = (count >> p_dma_ch->shift) | + psgl->sgl_control; + psgl->ptail->next = (uint32_t) NULL; + + return DMA_STATUS_GOOD; +} + +/* + * Enable (start) the DMA described by the sgl handle. + */ +void +ppc4xx_enable_dma_sgl(sgl_handle_t handle) +{ + sgl_list_info_t *psgl = (sgl_list_info_t *) handle; + ppc_dma_ch_t *p_dma_ch; + uint32_t sg_command; + + if (!handle) { + printk("ppc4xx_enable_dma_sgl: null handle\n"); + return; + } else if (psgl->dmanr > (MAX_PPC4xx_DMA_CHANNELS - 1)) { + printk("ppc4xx_enable_dma_sgl: bad channel in handle %d\n", + psgl->dmanr); + return; + } else if (!psgl->phead) { + printk("ppc4xx_enable_dma_sgl: sg list empty\n"); + return; + } + + p_dma_ch = &dma_channels[psgl->dmanr]; + psgl->ptail->control_count &= ~SG_LINK; /* make this the last dscrptr */ + sg_command = mfdcr(DCRN_ASGC); + + ppc4xx_set_sg_addr(psgl->dmanr, psgl->phead_dma); + + sg_command |= SSG_ENABLE(psgl->dmanr); + + mtdcr(DCRN_ASGC, sg_command); /* start transfer */ +} + +/* + * Halt an active scatter/gather DMA operation. + */ +void +ppc4xx_disable_dma_sgl(sgl_handle_t handle) +{ + sgl_list_info_t *psgl = (sgl_list_info_t *) handle; + uint32_t sg_command; + + if (!handle) { + printk("ppc4xx_enable_dma_sgl: null handle\n"); + return; + } else if (psgl->dmanr > (MAX_PPC4xx_DMA_CHANNELS - 1)) { + printk("ppc4xx_enable_dma_sgl: bad channel in handle %d\n", + psgl->dmanr); + return; + } + + sg_command = mfdcr(DCRN_ASGC); + sg_command &= ~SSG_ENABLE(psgl->dmanr); + mtdcr(DCRN_ASGC, sg_command); /* stop transfer */ +} + +/* + * Returns number of bytes left to be transferred from the entire sgl list. + * *src_addr and *dst_addr get set to the source/destination address of + * the sgl descriptor where the DMA stopped. + * + * An sgl transfer must NOT be active when this function is called. + */ +int +ppc4xx_get_dma_sgl_residue(sgl_handle_t handle, phys_addr_t * src_addr, + phys_addr_t * dst_addr) +{ + sgl_list_info_t *psgl = (sgl_list_info_t *) handle; + ppc_dma_ch_t *p_dma_ch; + ppc_sgl_t *pnext, *sgl_addr; + uint32_t count_left; + + if (!handle) { + printk("ppc4xx_get_dma_sgl_residue: null handle\n"); + return DMA_STATUS_BAD_HANDLE; + } else if (psgl->dmanr > (MAX_PPC4xx_DMA_CHANNELS - 1)) { + printk("ppc4xx_get_dma_sgl_residue: bad channel in handle %d\n", + psgl->dmanr); + return DMA_STATUS_BAD_CHANNEL; + } + + sgl_addr = (ppc_sgl_t *) __va(mfdcr(DCRN_ASG0 + (psgl->dmanr * 0x8))); + count_left = mfdcr(DCRN_DMACT0 + (psgl->dmanr * 0x8)) & SG_COUNT_MASK; + + if (!sgl_addr) { + printk("ppc4xx_get_dma_sgl_residue: sgl addr register is null\n"); + goto error; + } + + pnext = psgl->phead; + while (pnext && + ((unsigned) pnext < ((unsigned) psgl + SGL_LIST_SIZE) && + (pnext != sgl_addr)) + ) { + pnext++; + } + + if (pnext == sgl_addr) { /* found the sgl descriptor */ + + *src_addr = pnext->src_addr; + *dst_addr = pnext->dst_addr; + + /* + * Now search the remaining descriptors and add their count. + * We already have the remaining count from this descriptor in + * count_left. + */ + pnext++; + + while ((pnext != psgl->ptail) && + ((unsigned) pnext < ((unsigned) psgl + SGL_LIST_SIZE)) + ) { + count_left += pnext->control_count & SG_COUNT_MASK; + } + + if (pnext != psgl->ptail) { /* should never happen */ + printk + ("ppc4xx_get_dma_sgl_residue error (1) psgl->ptail 0x%x handle 0x%x\n", + (unsigned int) psgl->ptail, (unsigned int) handle); + goto error; + } + + /* success */ + p_dma_ch = &dma_channels[psgl->dmanr]; + return (count_left << p_dma_ch->shift); /* count in bytes */ + + } else { + /* this shouldn't happen */ + printk + ("get_dma_sgl_residue, unable to match current address 0x%x, handle 0x%x\n", + (unsigned int) sgl_addr, (unsigned int) handle); + + } + + error: + *src_addr = (phys_addr_t) NULL; + *dst_addr = (phys_addr_t) NULL; + return 0; +} + +/* + * Returns the address(es) of the buffer(s) contained in the head element of + * the scatter/gather list. The element is removed from the scatter/gather + * list and the next element becomes the head. + * + * This function should only be called when the DMA is not active. + */ +int +ppc4xx_delete_dma_sgl_element(sgl_handle_t handle, phys_addr_t * src_dma_addr, + phys_addr_t * dst_dma_addr) +{ + sgl_list_info_t *psgl = (sgl_list_info_t *) handle; + + if (!handle) { + printk("ppc4xx_delete_sgl_element: null handle\n"); + return DMA_STATUS_BAD_HANDLE; + } else if (psgl->dmanr > (MAX_PPC4xx_DMA_CHANNELS - 1)) { + printk("ppc4xx_delete_sgl_element: bad channel in handle %d\n", + psgl->dmanr); + return DMA_STATUS_BAD_CHANNEL; + } + + if (!psgl->phead) { + printk("ppc4xx_delete_sgl_element: sgl list empty\n"); + *src_dma_addr = (phys_addr_t) NULL; + *dst_dma_addr = (phys_addr_t) NULL; + return DMA_STATUS_SGL_LIST_EMPTY; + } + + *src_dma_addr = (phys_addr_t) psgl->phead->src_addr; + *dst_dma_addr = (phys_addr_t) psgl->phead->dst_addr; + + if (psgl->phead == psgl->ptail) { + /* last descriptor on the list */ + psgl->phead = NULL; + psgl->ptail = NULL; + } else { + psgl->phead++; + psgl->phead_dma += sizeof(ppc_sgl_t); + } + + return DMA_STATUS_GOOD; +} + + +/* + * Create a scatter/gather list handle. This is simply a structure which + * describes a scatter/gather list. + * + * A handle is returned in "handle" which the driver should save in order to + * be able to access this list later. A chunk of memory will be allocated + * to be used by the API for internal management purposes, including managing + * the sg list and allocating memory for the sgl descriptors. One page should + * be more than enough for that purpose. Perhaps it's a bit wasteful to use + * a whole page for a single sg list, but most likely there will be only one + * sg list per channel. + * + * Interrupt notes: + * Each sgl descriptor has a copy of the DMA control word which the DMA engine + * loads in the control register. The control word has a "global" interrupt + * enable bit for that channel. Interrupts are further qualified by a few bits + * in the sgl descriptor count register. In order to setup an sgl, we have to + * know ahead of time whether or not interrupts will be enabled at the completion + * of the transfers. Thus, enable_dma_interrupt()/disable_dma_interrupt() MUST + * be called before calling alloc_dma_handle(). If the interrupt mode will never + * change after powerup, then enable_dma_interrupt()/disable_dma_interrupt() + * do not have to be called -- interrupts will be enabled or disabled based + * on how the channel was configured after powerup by the hw_init_dma_channel() + * function. Each sgl descriptor will be setup to interrupt if an error occurs; + * however, only the last descriptor will be setup to interrupt. Thus, an + * interrupt will occur (if interrupts are enabled) only after the complete + * sgl transfer is done. + */ +int +ppc4xx_alloc_dma_handle(sgl_handle_t * phandle, unsigned int mode, unsigned int dmanr) +{ + sgl_list_info_t *psgl=NULL; + dma_addr_t dma_addr; + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + uint32_t sg_command; + uint32_t ctc_settings; + void *ret; + + if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { + printk("ppc4xx_alloc_dma_handle: invalid channel 0x%x\n", dmanr); + return DMA_STATUS_BAD_CHANNEL; + } + + if (!phandle) { + printk("ppc4xx_alloc_dma_handle: null handle pointer\n"); + return DMA_STATUS_NULL_POINTER; + } + + /* Get a page of memory, which is zeroed out by consistent_alloc() */ + ret = dma_alloc_coherent(NULL, DMA_PPC4xx_SIZE, &dma_addr, GFP_KERNEL); + if (ret != NULL) { + memset(ret, 0, DMA_PPC4xx_SIZE); + psgl = (sgl_list_info_t *) ret; + } + + if (psgl == NULL) { + *phandle = (sgl_handle_t) NULL; + return DMA_STATUS_OUT_OF_MEMORY; + } + + psgl->dma_addr = dma_addr; + psgl->dmanr = dmanr; + + /* + * Modify and save the control word. These words will be + * written to each sgl descriptor. The DMA engine then + * loads this control word into the control register + * every time it reads a new descriptor. + */ + psgl->control = p_dma_ch->control; + /* Clear all mode bits */ + psgl->control &= ~(DMA_TM_MASK | DMA_TD); + /* Save control word and mode */ + psgl->control |= (mode | DMA_CE_ENABLE); + + /* In MM mode, we must set ETD/TCE */ + if (mode == DMA_MODE_MM) + psgl->control |= DMA_ETD_OUTPUT | DMA_TCE_ENABLE; + + if (p_dma_ch->int_enable) { + /* Enable channel interrupt */ + psgl->control |= DMA_CIE_ENABLE; + } else { + psgl->control &= ~DMA_CIE_ENABLE; + } + + sg_command = mfdcr(DCRN_ASGC); + sg_command |= SSG_MASK_ENABLE(dmanr); + + /* Enable SGL control access */ + mtdcr(DCRN_ASGC, sg_command); + psgl->sgl_control = SG_ERI_ENABLE | SG_LINK; + + /* keep control count register settings */ + ctc_settings = mfdcr(DCRN_DMACT0 + (dmanr * 0x8)) + & (DMA_CTC_BSIZ_MSK | DMA_CTC_BTEN); /*burst mode settings*/ + psgl->sgl_control |= ctc_settings; + + if (p_dma_ch->int_enable) { + if (p_dma_ch->tce_enable) + psgl->sgl_control |= SG_TCI_ENABLE; + else + psgl->sgl_control |= SG_ETI_ENABLE; + } + + *phandle = (sgl_handle_t) psgl; + return DMA_STATUS_GOOD; +} + +/* + * Destroy a scatter/gather list handle that was created by alloc_dma_handle(). + * The list must be empty (contain no elements). + */ +void +ppc4xx_free_dma_handle(sgl_handle_t handle) +{ + sgl_list_info_t *psgl = (sgl_list_info_t *) handle; + + if (!handle) { + printk("ppc4xx_free_dma_handle: got NULL\n"); + return; + } else if (psgl->phead) { + printk("ppc4xx_free_dma_handle: list not empty\n"); + return; + } else if (!psgl->dma_addr) { /* should never happen */ + printk("ppc4xx_free_dma_handle: no dma address\n"); + return; + } + + dma_free_coherent(NULL, DMA_PPC4xx_SIZE, (void *) psgl, 0); +} + +EXPORT_SYMBOL(ppc4xx_alloc_dma_handle); +EXPORT_SYMBOL(ppc4xx_free_dma_handle); +EXPORT_SYMBOL(ppc4xx_add_dma_sgl); +EXPORT_SYMBOL(ppc4xx_delete_dma_sgl_element); +EXPORT_SYMBOL(ppc4xx_enable_dma_sgl); +EXPORT_SYMBOL(ppc4xx_disable_dma_sgl); +EXPORT_SYMBOL(ppc4xx_get_dma_sgl_residue); diff --git a/arch/ppc/syslib/ppc83xx_setup.c b/arch/ppc/syslib/ppc83xx_setup.c new file mode 100644 index 00000000000..c28f9d67948 --- /dev/null +++ b/arch/ppc/syslib/ppc83xx_setup.c @@ -0,0 +1,138 @@ +/* + * arch/ppc/syslib/ppc83xx_setup.c + * + * MPC83XX common board code + * + * Maintainer: Kumar Gala + * + * Copyright 2005 Freescale Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include /* for linux/serial_core.h */ +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +phys_addr_t immrbar; + +/* Return the amount of memory */ +unsigned long __init +mpc83xx_find_end_of_memory(void) +{ + bd_t *binfo; + + binfo = (bd_t *) __res; + + return binfo->bi_memsize; +} + +long __init +mpc83xx_time_init(void) +{ +#define SPCR_OFFS 0x00000110 +#define SPCR_TBEN 0x00400000 + + bd_t *binfo = (bd_t *)__res; + u32 *spcr = ioremap(binfo->bi_immr_base + SPCR_OFFS, 4); + + *spcr |= SPCR_TBEN; + + iounmap(spcr); + + return 0; +} + +/* The decrementer counts at the system (internal) clock freq divided by 4 */ +void __init +mpc83xx_calibrate_decr(void) +{ + bd_t *binfo = (bd_t *) __res; + unsigned int freq, divisor; + + freq = binfo->bi_busfreq; + divisor = 4; + tb_ticks_per_jiffy = freq / HZ / divisor; + tb_to_us = mulhwu_scale_factor(freq / divisor, 1000000); +} + +#ifdef CONFIG_SERIAL_8250 +void __init +mpc83xx_early_serial_map(void) +{ +#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) + struct uart_port serial_req; +#endif + struct plat_serial8250_port *pdata; + bd_t *binfo = (bd_t *) __res; + pdata = (struct plat_serial8250_port *) ppc_sys_get_pdata(MPC83xx_DUART); + + /* Setup serial port access */ + pdata[0].uartclk = binfo->bi_busfreq; + pdata[0].mapbase += binfo->bi_immr_base; + pdata[0].membase = ioremap(pdata[0].mapbase, 0x100); + +#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) + memset(&serial_req, 0, sizeof (serial_req)); + serial_req.iotype = SERIAL_IO_MEM; + serial_req.mapbase = pdata[0].mapbase; + serial_req.membase = pdata[0].membase; + serial_req.regshift = 0; + + gen550_init(0, &serial_req); +#endif + + pdata[1].uartclk = binfo->bi_busfreq; + pdata[1].mapbase += binfo->bi_immr_base; + pdata[1].membase = ioremap(pdata[1].mapbase, 0x100); + +#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) + /* Assume gen550_init() doesn't modify serial_req */ + serial_req.mapbase = pdata[1].mapbase; + serial_req.membase = pdata[1].membase; + + gen550_init(1, &serial_req); +#endif +} +#endif + +void +mpc83xx_restart(char *cmd) +{ + local_irq_disable(); + for(;;); +} + +void +mpc83xx_power_off(void) +{ + local_irq_disable(); + for(;;); +} + +void +mpc83xx_halt(void) +{ + local_irq_disable(); + for(;;); +} + +/* PCI SUPPORT DOES NOT EXIT, MODEL after ppc85xx_setup.c */ diff --git a/arch/ppc/syslib/ppc83xx_setup.h b/arch/ppc/syslib/ppc83xx_setup.h new file mode 100644 index 00000000000..683f179b746 --- /dev/null +++ b/arch/ppc/syslib/ppc83xx_setup.h @@ -0,0 +1,53 @@ +/* + * arch/ppc/syslib/ppc83xx_setup.h + * + * MPC83XX common board definitions + * + * Maintainer: Kumar Gala + * + * Copyright 2005 Freescale Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef __PPC_SYSLIB_PPC83XX_SETUP_H +#define __PPC_SYSLIB_PPC83XX_SETUP_H + +#include +#include +#include + +extern unsigned long mpc83xx_find_end_of_memory(void) __init; +extern long mpc83xx_time_init(void) __init; +extern void mpc83xx_calibrate_decr(void) __init; +extern void mpc83xx_early_serial_map(void) __init; +extern void mpc83xx_restart(char *cmd); +extern void mpc83xx_power_off(void); +extern void mpc83xx_halt(void); +extern void mpc83xx_setup_hose(void) __init; + +/* PCI config */ +#if 0 +#define PCI1_CFG_ADDR_OFFSET (FIXME) +#define PCI1_CFG_DATA_OFFSET (FIXME) + +#define PCI2_CFG_ADDR_OFFSET (FIXME) +#define PCI2_CFG_DATA_OFFSET (FIXME) +#endif + +/* Serial Config */ +#ifdef CONFIG_SERIAL_MANY_PORTS +#define RS_TABLE_SIZE 64 +#else +#define RS_TABLE_SIZE 2 +#endif + +#ifndef BASE_BAUD +#define BASE_BAUD 115200 +#endif + +#endif /* __PPC_SYSLIB_PPC83XX_SETUP_H */ diff --git a/arch/ppc/syslib/ppc85xx_common.c b/arch/ppc/syslib/ppc85xx_common.c new file mode 100644 index 00000000000..e83f2f8686d --- /dev/null +++ b/arch/ppc/syslib/ppc85xx_common.c @@ -0,0 +1,33 @@ +/* + * arch/ppc/syslib/ppc85xx_common.c + * + * MPC85xx support routines + * + * Maintainer: Kumar Gala + * + * Copyright 2004 Freescale Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include + +#include +#include + +/* ************************************************************************ */ +/* Return the value of CCSRBAR for the current board */ + +phys_addr_t +get_ccsrbar(void) +{ + return BOARD_CCSRBAR; +} + +EXPORT_SYMBOL(get_ccsrbar); diff --git a/arch/ppc/syslib/ppc85xx_common.h b/arch/ppc/syslib/ppc85xx_common.h new file mode 100644 index 00000000000..2c8f304441b --- /dev/null +++ b/arch/ppc/syslib/ppc85xx_common.h @@ -0,0 +1,25 @@ +/* + * arch/ppc/syslib/ppc85xx_common.h + * + * MPC85xx support routines + * + * Maintainer: Kumar Gala + * + * Copyright 2004 Freescale Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __PPC_SYSLIB_PPC85XX_COMMON_H +#define __PPC_SYSLIB_PPC85XX_COMMON_H + +#include +#include + +/* Provide access to ccsrbar for any modules, etc */ +phys_addr_t get_ccsrbar(void); + +#endif /* __PPC_SYSLIB_PPC85XX_COMMON_H */ diff --git a/arch/ppc/syslib/ppc85xx_setup.c b/arch/ppc/syslib/ppc85xx_setup.c new file mode 100644 index 00000000000..81f1968c326 --- /dev/null +++ b/arch/ppc/syslib/ppc85xx_setup.c @@ -0,0 +1,354 @@ +/* + * arch/ppc/syslib/ppc85xx_setup.c + * + * MPC85XX common board code + * + * Maintainer: Kumar Gala + * + * Copyright 2004 Freescale Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include /* for linux/serial_core.h */ +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Return the amount of memory */ +unsigned long __init +mpc85xx_find_end_of_memory(void) +{ + bd_t *binfo; + + binfo = (bd_t *) __res; + + return binfo->bi_memsize; +} + +/* The decrementer counts at the system (internal) clock freq divided by 8 */ +void __init +mpc85xx_calibrate_decr(void) +{ + bd_t *binfo = (bd_t *) __res; + unsigned int freq, divisor; + + /* get the core frequency */ + freq = binfo->bi_busfreq; + + /* The timebase is updated every 8 bus clocks, HID0[SEL_TBCLK] = 0 */ + divisor = 8; + tb_ticks_per_jiffy = freq / divisor / HZ; + tb_to_us = mulhwu_scale_factor(freq / divisor, 1000000); + + /* Set the time base to zero */ + mtspr(SPRN_TBWL, 0); + mtspr(SPRN_TBWU, 0); + + /* Clear any pending timer interrupts */ + mtspr(SPRN_TSR, TSR_ENW | TSR_WIS | TSR_DIS | TSR_FIS); + + /* Enable decrementer interrupt */ + mtspr(SPRN_TCR, TCR_DIE); +} + +#ifdef CONFIG_SERIAL_8250 +void __init +mpc85xx_early_serial_map(void) +{ +#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) + struct uart_port serial_req; +#endif + struct plat_serial8250_port *pdata; + bd_t *binfo = (bd_t *) __res; + pdata = (struct plat_serial8250_port *) ppc_sys_get_pdata(MPC85xx_DUART); + + /* Setup serial port access */ + pdata[0].uartclk = binfo->bi_busfreq; + pdata[0].mapbase += binfo->bi_immr_base; + pdata[0].membase = ioremap(pdata[0].mapbase, MPC85xx_UART0_SIZE); + +#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) + memset(&serial_req, 0, sizeof (serial_req)); + serial_req.iotype = SERIAL_IO_MEM; + serial_req.mapbase = pdata[0].mapbase; + serial_req.membase = pdata[0].membase; + serial_req.regshift = 0; + + gen550_init(0, &serial_req); +#endif + + pdata[1].uartclk = binfo->bi_busfreq; + pdata[1].mapbase += binfo->bi_immr_base; + pdata[1].membase = ioremap(pdata[1].mapbase, MPC85xx_UART0_SIZE); + +#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) + /* Assume gen550_init() doesn't modify serial_req */ + serial_req.mapbase = pdata[1].mapbase; + serial_req.membase = pdata[1].membase; + + gen550_init(1, &serial_req); +#endif +} +#endif + +void +mpc85xx_restart(char *cmd) +{ + local_irq_disable(); + abort(); +} + +void +mpc85xx_power_off(void) +{ + local_irq_disable(); + for(;;); +} + +void +mpc85xx_halt(void) +{ + local_irq_disable(); + for(;;); +} + +#ifdef CONFIG_PCI +static void __init +mpc85xx_setup_pci1(struct pci_controller *hose) +{ + volatile struct ccsr_pci *pci; + volatile struct ccsr_guts *guts; + unsigned short temps; + bd_t *binfo = (bd_t *) __res; + + pci = ioremap(binfo->bi_immr_base + MPC85xx_PCI1_OFFSET, + MPC85xx_PCI1_SIZE); + + guts = ioremap(binfo->bi_immr_base + MPC85xx_GUTS_OFFSET, + MPC85xx_GUTS_SIZE); + + early_read_config_word(hose, 0, 0, PCI_COMMAND, &temps); + temps |= PCI_COMMAND_SERR | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; + early_write_config_word(hose, 0, 0, PCI_COMMAND, temps); + +#define PORDEVSR_PCI (0x00800000) /* PCI Mode */ + if (guts->pordevsr & PORDEVSR_PCI) { + early_write_config_byte(hose, 0, 0, PCI_LATENCY_TIMER, 0x80); + } else { + /* PCI-X init */ + temps = PCI_X_CMD_MAX_SPLIT | PCI_X_CMD_MAX_READ + | PCI_X_CMD_ERO | PCI_X_CMD_DPERR_E; + early_write_config_word(hose, 0, 0, PCIX_COMMAND, temps); + } + + /* Disable all windows (except powar0 since its ignored) */ + pci->powar1 = 0; + pci->powar2 = 0; + pci->powar3 = 0; + pci->powar4 = 0; + pci->piwar1 = 0; + pci->piwar2 = 0; + pci->piwar3 = 0; + + /* Setup Phys:PCI 1:1 outbound mem window @ MPC85XX_PCI1_LOWER_MEM */ + pci->potar1 = (MPC85XX_PCI1_LOWER_MEM >> 12) & 0x000fffff; + pci->potear1 = 0x00000000; + pci->powbar1 = (MPC85XX_PCI1_LOWER_MEM >> 12) & 0x000fffff; + /* Enable, Mem R/W */ + pci->powar1 = 0x80044000 | + (__ilog2(MPC85XX_PCI1_UPPER_MEM - MPC85XX_PCI1_LOWER_MEM + 1) - 1); + + /* Setup outboud IO windows @ MPC85XX_PCI1_IO_BASE */ + pci->potar2 = 0x00000000; + pci->potear2 = 0x00000000; + pci->powbar2 = (MPC85XX_PCI1_IO_BASE >> 12) & 0x000fffff; + /* Enable, IO R/W */ + pci->powar2 = 0x80088000 | (__ilog2(MPC85XX_PCI1_IO_SIZE) - 1); + + /* Setup 2G inbound Memory Window @ 0 */ + pci->pitar1 = 0x00000000; + pci->piwbar1 = 0x00000000; + pci->piwar1 = 0xa0f5501e; /* Enable, Prefetch, Local + Mem, Snoop R/W, 2G */ +} + + +extern int mpc85xx_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin); +extern int mpc85xx_exclude_device(u_char bus, u_char devfn); + +#ifdef CONFIG_85xx_PCI2 +static void __init +mpc85xx_setup_pci2(struct pci_controller *hose) +{ + volatile struct ccsr_pci *pci; + unsigned short temps; + bd_t *binfo = (bd_t *) __res; + + pci = ioremap(binfo->bi_immr_base + MPC85xx_PCI2_OFFSET, + MPC85xx_PCI2_SIZE); + + early_read_config_word(hose, hose->bus_offset, 0, PCI_COMMAND, &temps); + temps |= PCI_COMMAND_SERR | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; + early_write_config_word(hose, hose->bus_offset, 0, PCI_COMMAND, temps); + early_write_config_byte(hose, hose->bus_offset, 0, PCI_LATENCY_TIMER, 0x80); + + /* Disable all windows (except powar0 since its ignored) */ + pci->powar1 = 0; + pci->powar2 = 0; + pci->powar3 = 0; + pci->powar4 = 0; + pci->piwar1 = 0; + pci->piwar2 = 0; + pci->piwar3 = 0; + + /* Setup Phys:PCI 1:1 outbound mem window @ MPC85XX_PCI2_LOWER_MEM */ + pci->potar1 = (MPC85XX_PCI2_LOWER_MEM >> 12) & 0x000fffff; + pci->potear1 = 0x00000000; + pci->powbar1 = (MPC85XX_PCI2_LOWER_MEM >> 12) & 0x000fffff; + /* Enable, Mem R/W */ + pci->powar1 = 0x80044000 | + (__ilog2(MPC85XX_PCI1_UPPER_MEM - MPC85XX_PCI1_LOWER_MEM + 1) - 1); + + /* Setup outboud IO windows @ MPC85XX_PCI2_IO_BASE */ + pci->potar2 = 0x00000000; + pci->potear2 = 0x00000000; + pci->powbar2 = (MPC85XX_PCI2_IO_BASE >> 12) & 0x000fffff; + /* Enable, IO R/W */ + pci->powar2 = 0x80088000 | (__ilog2(MPC85XX_PCI1_IO_SIZE) - 1); + + /* Setup 2G inbound Memory Window @ 0 */ + pci->pitar1 = 0x00000000; + pci->piwbar1 = 0x00000000; + pci->piwar1 = 0xa0f5501e; /* Enable, Prefetch, Local + Mem, Snoop R/W, 2G */ +} +#endif /* CONFIG_85xx_PCI2 */ + +int mpc85xx_pci1_last_busno = 0; + +void __init +mpc85xx_setup_hose(void) +{ + struct pci_controller *hose_a; +#ifdef CONFIG_85xx_PCI2 + struct pci_controller *hose_b; +#endif + bd_t *binfo = (bd_t *) __res; + + hose_a = pcibios_alloc_controller(); + + if (!hose_a) + return; + + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = mpc85xx_map_irq; + + hose_a->first_busno = 0; + hose_a->bus_offset = 0; + hose_a->last_busno = 0xff; + + setup_indirect_pci(hose_a, binfo->bi_immr_base + PCI1_CFG_ADDR_OFFSET, + binfo->bi_immr_base + PCI1_CFG_DATA_OFFSET); + hose_a->set_cfg_type = 1; + + mpc85xx_setup_pci1(hose_a); + + hose_a->pci_mem_offset = MPC85XX_PCI1_MEM_OFFSET; + hose_a->mem_space.start = MPC85XX_PCI1_LOWER_MEM; + hose_a->mem_space.end = MPC85XX_PCI1_UPPER_MEM; + + hose_a->io_space.start = MPC85XX_PCI1_LOWER_IO; + hose_a->io_space.end = MPC85XX_PCI1_UPPER_IO; + hose_a->io_base_phys = MPC85XX_PCI1_IO_BASE; +#ifdef CONFIG_85xx_PCI2 + isa_io_base = + (unsigned long) ioremap(MPC85XX_PCI1_IO_BASE, + MPC85XX_PCI1_IO_SIZE + + MPC85XX_PCI2_IO_SIZE); +#else + isa_io_base = + (unsigned long) ioremap(MPC85XX_PCI1_IO_BASE, + MPC85XX_PCI1_IO_SIZE); +#endif + hose_a->io_base_virt = (void *) isa_io_base; + + /* setup resources */ + pci_init_resource(&hose_a->mem_resources[0], + MPC85XX_PCI1_LOWER_MEM, + MPC85XX_PCI1_UPPER_MEM, + IORESOURCE_MEM, "PCI1 host bridge"); + + pci_init_resource(&hose_a->io_resource, + MPC85XX_PCI1_LOWER_IO, + MPC85XX_PCI1_UPPER_IO, + IORESOURCE_IO, "PCI1 host bridge"); + + ppc_md.pci_exclude_device = mpc85xx_exclude_device; + + hose_a->last_busno = pciauto_bus_scan(hose_a, hose_a->first_busno); + +#ifdef CONFIG_85xx_PCI2 + hose_b = pcibios_alloc_controller(); + + if (!hose_b) + return; + + hose_b->bus_offset = hose_a->last_busno + 1; + hose_b->first_busno = hose_a->last_busno + 1; + hose_b->last_busno = 0xff; + + setup_indirect_pci(hose_b, binfo->bi_immr_base + PCI2_CFG_ADDR_OFFSET, + binfo->bi_immr_base + PCI2_CFG_DATA_OFFSET); + hose_b->set_cfg_type = 1; + + mpc85xx_setup_pci2(hose_b); + + hose_b->pci_mem_offset = MPC85XX_PCI2_MEM_OFFSET; + hose_b->mem_space.start = MPC85XX_PCI2_LOWER_MEM; + hose_b->mem_space.end = MPC85XX_PCI2_UPPER_MEM; + + hose_b->io_space.start = MPC85XX_PCI2_LOWER_IO; + hose_b->io_space.end = MPC85XX_PCI2_UPPER_IO; + hose_b->io_base_phys = MPC85XX_PCI2_IO_BASE; + hose_b->io_base_virt = (void *) isa_io_base + MPC85XX_PCI1_IO_SIZE; + + /* setup resources */ + pci_init_resource(&hose_b->mem_resources[0], + MPC85XX_PCI2_LOWER_MEM, + MPC85XX_PCI2_UPPER_MEM, + IORESOURCE_MEM, "PCI2 host bridge"); + + pci_init_resource(&hose_b->io_resource, + MPC85XX_PCI2_LOWER_IO, + MPC85XX_PCI2_UPPER_IO, + IORESOURCE_IO, "PCI2 host bridge"); + + hose_b->last_busno = pciauto_bus_scan(hose_b, hose_b->first_busno); + + /* let board code know what the last bus number was on PCI1 */ + mpc85xx_pci1_last_busno = hose_a->last_busno; +#endif + return; +} +#endif /* CONFIG_PCI */ + + diff --git a/arch/ppc/syslib/ppc85xx_setup.h b/arch/ppc/syslib/ppc85xx_setup.h new file mode 100644 index 00000000000..6e6cfe162fa --- /dev/null +++ b/arch/ppc/syslib/ppc85xx_setup.h @@ -0,0 +1,59 @@ +/* + * arch/ppc/syslib/ppc85xx_setup.h + * + * MPC85XX common board definitions + * + * Maintainer: Kumar Gala + * + * Copyright 2004 Freescale Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef __PPC_SYSLIB_PPC85XX_SETUP_H +#define __PPC_SYSLIB_PPC85XX_SETUP_H + +#include +#include +#include + +extern unsigned long mpc85xx_find_end_of_memory(void) __init; +extern void mpc85xx_calibrate_decr(void) __init; +extern void mpc85xx_early_serial_map(void) __init; +extern void mpc85xx_restart(char *cmd); +extern void mpc85xx_power_off(void); +extern void mpc85xx_halt(void); +extern void mpc85xx_setup_hose(void) __init; + +/* PCI config */ +#define PCI1_CFG_ADDR_OFFSET (0x8000) +#define PCI1_CFG_DATA_OFFSET (0x8004) + +#define PCI2_CFG_ADDR_OFFSET (0x9000) +#define PCI2_CFG_DATA_OFFSET (0x9004) + +/* Additional register for PCI-X configuration */ +#define PCIX_NEXT_CAP 0x60 +#define PCIX_CAP_ID 0x61 +#define PCIX_COMMAND 0x62 +#define PCIX_STATUS 0x64 + +/* Serial Config */ +#ifdef CONFIG_SERIAL_MANY_PORTS +#define RS_TABLE_SIZE 64 +#else +#define RS_TABLE_SIZE 2 +#endif + +#ifndef BASE_BAUD +#define BASE_BAUD 115200 +#endif + +/* Offset of CPM register space */ +#define CPM_MAP_ADDR (CCSRBAR + MPC85xx_CPM_OFFSET) + +#endif /* __PPC_SYSLIB_PPC85XX_SETUP_H */ diff --git a/arch/ppc/syslib/ppc8xx_pic.c b/arch/ppc/syslib/ppc8xx_pic.c new file mode 100644 index 00000000000..d3b01c6c97d --- /dev/null +++ b/arch/ppc/syslib/ppc8xx_pic.c @@ -0,0 +1,130 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ppc8xx_pic.h" + +extern int cpm_get_irq(struct pt_regs *regs); + +/* The 8xx internal interrupt controller. It is usually + * the only interrupt controller. Some boards, like the MBX and + * Sandpoint have the 8259 as a secondary controller. Depending + * upon the processor type, the internal controller can have as + * few as 16 interrups or as many as 64. We could use the + * "clear_bit()" and "set_bit()" functions like other platforms, + * but they are overkill for us. + */ + +static void m8xx_mask_irq(unsigned int irq_nr) +{ + int bit, word; + + bit = irq_nr & 0x1f; + word = irq_nr >> 5; + + ppc_cached_irq_mask[word] &= ~(1 << (31-bit)); + ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask = + ppc_cached_irq_mask[word]; +} + +static void m8xx_unmask_irq(unsigned int irq_nr) +{ + int bit, word; + + bit = irq_nr & 0x1f; + word = irq_nr >> 5; + + ppc_cached_irq_mask[word] |= (1 << (31-bit)); + ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask = + ppc_cached_irq_mask[word]; +} + +static void m8xx_end_irq(unsigned int irq_nr) +{ + if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS)) + && irq_desc[irq_nr].action) { + int bit, word; + + bit = irq_nr & 0x1f; + word = irq_nr >> 5; + + ppc_cached_irq_mask[word] |= (1 << (31-bit)); + ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask = + ppc_cached_irq_mask[word]; + } +} + + +static void m8xx_mask_and_ack(unsigned int irq_nr) +{ + int bit, word; + + bit = irq_nr & 0x1f; + word = irq_nr >> 5; + + ppc_cached_irq_mask[word] &= ~(1 << (31-bit)); + ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask = + ppc_cached_irq_mask[word]; + ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sipend = 1 << (31-bit); +} + +struct hw_interrupt_type ppc8xx_pic = { + .typename = " 8xx SIU ", + .enable = m8xx_unmask_irq, + .disable = m8xx_mask_irq, + .ack = m8xx_mask_and_ack, + .end = m8xx_end_irq, +}; + +/* + * We either return a valid interrupt or -1 if there is nothing pending + */ +int +m8xx_get_irq(struct pt_regs *regs) +{ + int irq; + + /* For MPC8xx, read the SIVEC register and shift the bits down + * to get the irq number. + */ + irq = ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sivec >> 26; + + /* + * When we read the sivec without an interrupt to process, we will + * get back SIU_LEVEL7. In this case, return -1 + */ + if (irq == CPM_INTERRUPT) + irq = CPM_IRQ_OFFSET + cpm_get_irq(regs); +#if defined(CONFIG_PCI) + else if (irq == ISA_BRIDGE_INT) { + int isa_irq; + + if ((isa_irq = i8259_poll(regs)) >= 0) + irq = I8259_IRQ_OFFSET + isa_irq; + } +#endif /* CONFIG_PCI */ + else if (irq == SIU_LEVEL7) + irq = -1; + + return irq; +} + +#if defined(CONFIG_MBX) && defined(CONFIG_PCI) +/* Only the MBX uses the external 8259. This allows us to catch standard + * drivers that may mess up the internal interrupt controllers, and also + * allow them to run without modification on the MBX. + */ +void mbx_i8259_action(int irq, void *dev_id, struct pt_regs *regs) +{ + /* This interrupt handler never actually gets called. It is + * installed only to unmask the 8259 cascade interrupt in the SIU + * and to make the 8259 cascade interrupt visible in /proc/interrupts. + */ +} +#endif /* CONFIG_PCI */ diff --git a/arch/ppc/syslib/ppc8xx_pic.h b/arch/ppc/syslib/ppc8xx_pic.h new file mode 100644 index 00000000000..784935eac36 --- /dev/null +++ b/arch/ppc/syslib/ppc8xx_pic.h @@ -0,0 +1,21 @@ +#ifndef _PPC_KERNEL_PPC8xx_H +#define _PPC_KERNEL_PPC8xx_H + +#include +#include +#include + +extern struct hw_interrupt_type ppc8xx_pic; + +void m8xx_pic_init(void); +void m8xx_do_IRQ(struct pt_regs *regs, + int cpu); +int m8xx_get_irq(struct pt_regs *regs); + +#ifdef CONFIG_MBX +#include +#include +void mbx_i8259_action(int cpl, void *dev_id, struct pt_regs *regs); +#endif + +#endif /* _PPC_KERNEL_PPC8xx_H */ diff --git a/arch/ppc/syslib/ppc_sys.c b/arch/ppc/syslib/ppc_sys.c new file mode 100644 index 00000000000..87920235256 --- /dev/null +++ b/arch/ppc/syslib/ppc_sys.c @@ -0,0 +1,103 @@ +/* + * arch/ppc/syslib/ppc_sys.c + * + * PPC System library functions + * + * Maintainer: Kumar Gala + * + * Copyright 2005 Freescale Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include + +int (*ppc_sys_device_fixup) (struct platform_device * pdev); + +static int ppc_sys_inited; + +void __init identify_ppc_sys_by_id(u32 id) +{ + unsigned int i = 0; + while (1) { + if ((ppc_sys_specs[i].mask & id) == ppc_sys_specs[i].value) + break; + i++; + } + + cur_ppc_sys_spec = &ppc_sys_specs[i]; + + return; +} + +void __init identify_ppc_sys_by_name(char *name) +{ + /* TODO */ + return; +} + +/* Update all memory resources by paddr, call before platform_device_register */ +void __init +ppc_sys_fixup_mem_resource(struct platform_device *pdev, phys_addr_t paddr) +{ + int i; + for (i = 0; i < pdev->num_resources; i++) { + struct resource *r = &pdev->resource[i]; + if ((r->flags & IORESOURCE_MEM) == IORESOURCE_MEM) { + r->start += paddr; + r->end += paddr; + } + } +} + +/* Get platform_data pointer out of platform device, call before platform_device_register */ +void *__init ppc_sys_get_pdata(enum ppc_sys_devices dev) +{ + return ppc_sys_platform_devices[dev].dev.platform_data; +} + +void ppc_sys_device_remove(enum ppc_sys_devices dev) +{ + unsigned int i; + + if (ppc_sys_inited) { + platform_device_unregister(&ppc_sys_platform_devices[dev]); + } else { + if (cur_ppc_sys_spec == NULL) + return; + for (i = 0; i < cur_ppc_sys_spec->num_devices; i++) + if (cur_ppc_sys_spec->device_list[i] == dev) + cur_ppc_sys_spec->device_list[i] = -1; + } +} + +static int __init ppc_sys_init(void) +{ + unsigned int i, dev_id, ret = 0; + + BUG_ON(cur_ppc_sys_spec == NULL); + + for (i = 0; i < cur_ppc_sys_spec->num_devices; i++) { + dev_id = cur_ppc_sys_spec->device_list[i]; + if (dev_id != -1) { + if (ppc_sys_device_fixup != NULL) + ppc_sys_device_fixup(&ppc_sys_platform_devices + [dev_id]); + if (platform_device_register + (&ppc_sys_platform_devices[dev_id])) { + ret = 1; + printk(KERN_ERR + "unable to register device %d\n", + dev_id); + } + } + } + + ppc_sys_inited = 1; + return ret; +} + +subsys_initcall(ppc_sys_init); diff --git a/arch/ppc/syslib/prep_nvram.c b/arch/ppc/syslib/prep_nvram.c new file mode 100644 index 00000000000..2bcf8a16d1c --- /dev/null +++ b/arch/ppc/syslib/prep_nvram.c @@ -0,0 +1,141 @@ +/* + * arch/ppc/kernel/prep_nvram.c + * + * Copyright (C) 1998 Corey Minyard + * + * This reads the NvRAM on PReP compliant machines (generally from IBM or + * Motorola). Motorola kept the format of NvRAM in their ROM, PPCBUG, the + * same, long after they had stopped producing PReP compliant machines. So + * this code is useful in those cases as well. + * + */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static char nvramData[MAX_PREP_NVRAM]; +static NVRAM_MAP *nvram=(NVRAM_MAP *)&nvramData[0]; + +unsigned char __prep prep_nvram_read_val(int addr) +{ + outb(addr, PREP_NVRAM_AS0); + outb(addr>>8, PREP_NVRAM_AS1); + return inb(PREP_NVRAM_DATA); +} + +void __prep prep_nvram_write_val(int addr, + unsigned char val) +{ + outb(addr, PREP_NVRAM_AS0); + outb(addr>>8, PREP_NVRAM_AS1); + outb(val, PREP_NVRAM_DATA); +} + +void __init init_prep_nvram(void) +{ + unsigned char *nvp; + int i; + int nvramSize; + + /* + * The following could fail if the NvRAM were corrupt but + * we expect the boot firmware to have checked its checksum + * before boot + */ + nvp = (char *) &nvram->Header; + for (i=0; iHeader.GEAddress+nvram->Header.GELength; + if(nvramSize>MAX_PREP_NVRAM) + { + /* + * NvRAM is too large + */ + nvram->Header.GELength=0; + return; + } + + /* + * Read the remainder of the PReP NvRAM + */ + nvp = (char *) &nvram->GEArea[0]; + for (i=sizeof(HEADER); iGEArea)) < nvram->Header.GELength) + && (*cp == '\0')) + { + cp++; + } + + if ((cp - ((char *) nvram->GEArea)) < nvram->Header.GELength) { + return cp; + } else { + return NULL; + } +} diff --git a/arch/ppc/syslib/prom.c b/arch/ppc/syslib/prom.c new file mode 100644 index 00000000000..2c64ed62747 --- /dev/null +++ b/arch/ppc/syslib/prom.c @@ -0,0 +1,1447 @@ +/* + * Procedures for interfacing to the Open Firmware PROM on + * Power Macintosh computers. + * + * In particular, we are interested in the device tree + * and in using some of its services (exit, write to stdout). + * + * Paul Mackerras August 1996. + * Copyright (C) 1996 Paul Mackerras. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +struct pci_address { + unsigned a_hi; + unsigned a_mid; + unsigned a_lo; +}; + +struct pci_reg_property { + struct pci_address addr; + unsigned size_hi; + unsigned size_lo; +}; + +struct isa_reg_property { + unsigned space; + unsigned address; + unsigned size; +}; + +typedef unsigned long interpret_func(struct device_node *, unsigned long, + int, int); +static interpret_func interpret_pci_props; +static interpret_func interpret_dbdma_props; +static interpret_func interpret_isa_props; +static interpret_func interpret_macio_props; +static interpret_func interpret_root_props; + +extern char *klimit; + +/* Set for a newworld or CHRP machine */ +int use_of_interrupt_tree; +struct device_node *dflt_interrupt_controller; +int num_interrupt_controllers; + +int pmac_newworld; + +extern unsigned int rtas_entry; /* physical pointer */ + +extern struct device_node *allnodes; + +static unsigned long finish_node(struct device_node *, unsigned long, + interpret_func *, int, int); +static unsigned long finish_node_interrupts(struct device_node *, unsigned long); +static struct device_node *find_phandle(phandle); + +extern void enter_rtas(void *); +void phys_call_rtas(int, int, int, ...); + +extern char cmd_line[512]; /* XXX */ +extern boot_infos_t *boot_infos; +unsigned long dev_tree_size; + +void __openfirmware +phys_call_rtas(int service, int nargs, int nret, ...) +{ + va_list list; + union { + unsigned long words[16]; + double align; + } u; + void (*rtas)(void *, unsigned long); + int i; + + u.words[0] = service; + u.words[1] = nargs; + u.words[2] = nret; + va_start(list, nret); + for (i = 0; i < nargs; ++i) + u.words[i+3] = va_arg(list, unsigned long); + va_end(list); + + rtas = (void (*)(void *, unsigned long)) rtas_entry; + rtas(&u, rtas_data); +} + +/* + * finish_device_tree is called once things are running normally + * (i.e. with text and data mapped to the address they were linked at). + * It traverses the device tree and fills in the name, type, + * {n_}addrs and {n_}intrs fields of each node. + */ +void __init +finish_device_tree(void) +{ + unsigned long mem = (unsigned long) klimit; + struct device_node *np; + + /* All newworld pmac machines and CHRPs now use the interrupt tree */ + for (np = allnodes; np != NULL; np = np->allnext) { + if (get_property(np, "interrupt-parent", NULL)) { + use_of_interrupt_tree = 1; + break; + } + } + if (_machine == _MACH_Pmac && use_of_interrupt_tree) + pmac_newworld = 1; + +#ifdef CONFIG_BOOTX_TEXT + if (boot_infos && pmac_newworld) { + prom_print("WARNING ! BootX/miBoot booting is not supported on this machine\n"); + prom_print(" You should use an Open Firmware bootloader\n"); + } +#endif /* CONFIG_BOOTX_TEXT */ + + if (use_of_interrupt_tree) { + /* + * We want to find out here how many interrupt-controller + * nodes there are, and if we are booted from BootX, + * we need a pointer to the first (and hopefully only) + * such node. But we can't use find_devices here since + * np->name has not been set yet. -- paulus + */ + int n = 0; + char *name, *ic; + int iclen; + + for (np = allnodes; np != NULL; np = np->allnext) { + ic = get_property(np, "interrupt-controller", &iclen); + name = get_property(np, "name", NULL); + /* checking iclen makes sure we don't get a false + match on /chosen.interrupt_controller */ + if ((name != NULL + && strcmp(name, "interrupt-controller") == 0) + || (ic != NULL && iclen == 0 && strcmp(name, "AppleKiwi"))) { + if (n == 0) + dflt_interrupt_controller = np; + ++n; + } + } + num_interrupt_controllers = n; + } + + mem = finish_node(allnodes, mem, NULL, 1, 1); + dev_tree_size = mem - (unsigned long) allnodes; + klimit = (char *) mem; +} + +static unsigned long __init +finish_node(struct device_node *np, unsigned long mem_start, + interpret_func *ifunc, int naddrc, int nsizec) +{ + struct device_node *child; + int *ip; + + np->name = get_property(np, "name", NULL); + np->type = get_property(np, "device_type", NULL); + + if (!np->name) + np->name = ""; + if (!np->type) + np->type = ""; + + /* get the device addresses and interrupts */ + if (ifunc != NULL) + mem_start = ifunc(np, mem_start, naddrc, nsizec); + + if (use_of_interrupt_tree) + mem_start = finish_node_interrupts(np, mem_start); + + /* Look for #address-cells and #size-cells properties. */ + ip = (int *) get_property(np, "#address-cells", NULL); + if (ip != NULL) + naddrc = *ip; + ip = (int *) get_property(np, "#size-cells", NULL); + if (ip != NULL) + nsizec = *ip; + + if (np->parent == NULL) + ifunc = interpret_root_props; + else if (np->type == 0) + ifunc = NULL; + else if (!strcmp(np->type, "pci") || !strcmp(np->type, "vci")) + ifunc = interpret_pci_props; + else if (!strcmp(np->type, "dbdma")) + ifunc = interpret_dbdma_props; + else if (!strcmp(np->type, "mac-io") + || ifunc == interpret_macio_props) + ifunc = interpret_macio_props; + else if (!strcmp(np->type, "isa")) + ifunc = interpret_isa_props; + else if (!strcmp(np->name, "uni-n") || !strcmp(np->name, "u3")) + ifunc = interpret_root_props; + else if (!((ifunc == interpret_dbdma_props + || ifunc == interpret_macio_props) + && (!strcmp(np->type, "escc") + || !strcmp(np->type, "media-bay")))) + ifunc = NULL; + + /* if we were booted from BootX, convert the full name */ + if (boot_infos + && strncmp(np->full_name, "Devices:device-tree", 19) == 0) { + if (np->full_name[19] == 0) { + strcpy(np->full_name, "/"); + } else if (np->full_name[19] == ':') { + char *p = np->full_name + 19; + np->full_name = p; + for (; *p; ++p) + if (*p == ':') + *p = '/'; + } + } + + for (child = np->child; child != NULL; child = child->sibling) + mem_start = finish_node(child, mem_start, ifunc, + naddrc, nsizec); + + return mem_start; +} + +/* + * Find the interrupt parent of a node. + */ +static struct device_node * __init +intr_parent(struct device_node *p) +{ + phandle *parp; + + parp = (phandle *) get_property(p, "interrupt-parent", NULL); + if (parp == NULL) + return p->parent; + p = find_phandle(*parp); + if (p != NULL) + return p; + /* + * On a powermac booted with BootX, we don't get to know the + * phandles for any nodes, so find_phandle will return NULL. + * Fortunately these machines only have one interrupt controller + * so there isn't in fact any ambiguity. -- paulus + */ + if (num_interrupt_controllers == 1) + p = dflt_interrupt_controller; + return p; +} + +/* + * Find out the size of each entry of the interrupts property + * for a node. + */ +static int __init +prom_n_intr_cells(struct device_node *np) +{ + struct device_node *p; + unsigned int *icp; + + for (p = np; (p = intr_parent(p)) != NULL; ) { + icp = (unsigned int *) + get_property(p, "#interrupt-cells", NULL); + if (icp != NULL) + return *icp; + if (get_property(p, "interrupt-controller", NULL) != NULL + || get_property(p, "interrupt-map", NULL) != NULL) { + printk("oops, node %s doesn't have #interrupt-cells\n", + p->full_name); + return 1; + } + } + printk("prom_n_intr_cells failed for %s\n", np->full_name); + return 1; +} + +/* + * Map an interrupt from a device up to the platform interrupt + * descriptor. + */ +static int __init +map_interrupt(unsigned int **irq, struct device_node **ictrler, + struct device_node *np, unsigned int *ints, int nintrc) +{ + struct device_node *p, *ipar; + unsigned int *imap, *imask, *ip; + int i, imaplen, match; + int newintrc = 1, newaddrc = 1; + unsigned int *reg; + int naddrc; + + reg = (unsigned int *) get_property(np, "reg", NULL); + naddrc = prom_n_addr_cells(np); + p = intr_parent(np); + while (p != NULL) { + if (get_property(p, "interrupt-controller", NULL) != NULL) + /* this node is an interrupt controller, stop here */ + break; + imap = (unsigned int *) + get_property(p, "interrupt-map", &imaplen); + if (imap == NULL) { + p = intr_parent(p); + continue; + } + imask = (unsigned int *) + get_property(p, "interrupt-map-mask", NULL); + if (imask == NULL) { + printk("oops, %s has interrupt-map but no mask\n", + p->full_name); + return 0; + } + imaplen /= sizeof(unsigned int); + match = 0; + ipar = NULL; + while (imaplen > 0 && !match) { + /* check the child-interrupt field */ + match = 1; + for (i = 0; i < naddrc && match; ++i) + match = ((reg[i] ^ imap[i]) & imask[i]) == 0; + for (; i < naddrc + nintrc && match; ++i) + match = ((ints[i-naddrc] ^ imap[i]) & imask[i]) == 0; + imap += naddrc + nintrc; + imaplen -= naddrc + nintrc; + /* grab the interrupt parent */ + ipar = find_phandle((phandle) *imap++); + --imaplen; + if (ipar == NULL && num_interrupt_controllers == 1) + /* cope with BootX not giving us phandles */ + ipar = dflt_interrupt_controller; + if (ipar == NULL) { + printk("oops, no int parent %x in map of %s\n", + imap[-1], p->full_name); + return 0; + } + /* find the parent's # addr and intr cells */ + ip = (unsigned int *) + get_property(ipar, "#interrupt-cells", NULL); + if (ip == NULL) { + printk("oops, no #interrupt-cells on %s\n", + ipar->full_name); + return 0; + } + newintrc = *ip; + ip = (unsigned int *) + get_property(ipar, "#address-cells", NULL); + newaddrc = (ip == NULL)? 0: *ip; + imap += newaddrc + newintrc; + imaplen -= newaddrc + newintrc; + } + if (imaplen < 0) { + printk("oops, error decoding int-map on %s, len=%d\n", + p->full_name, imaplen); + return 0; + } + if (!match) { + printk("oops, no match in %s int-map for %s\n", + p->full_name, np->full_name); + return 0; + } + p = ipar; + naddrc = newaddrc; + nintrc = newintrc; + ints = imap - nintrc; + reg = ints - naddrc; + } + if (p == NULL) + printk("hmmm, int tree for %s doesn't have ctrler\n", + np->full_name); + *irq = ints; + *ictrler = p; + return nintrc; +} + +/* + * New version of finish_node_interrupts. + */ +static unsigned long __init +finish_node_interrupts(struct device_node *np, unsigned long mem_start) +{ + unsigned int *ints; + int intlen, intrcells; + int i, j, n, offset; + unsigned int *irq; + struct device_node *ic; + + ints = (unsigned int *) get_property(np, "interrupts", &intlen); + if (ints == NULL) + return mem_start; + intrcells = prom_n_intr_cells(np); + intlen /= intrcells * sizeof(unsigned int); + np->n_intrs = intlen; + np->intrs = (struct interrupt_info *) mem_start; + mem_start += intlen * sizeof(struct interrupt_info); + + for (i = 0; i < intlen; ++i) { + np->intrs[i].line = 0; + np->intrs[i].sense = 1; + n = map_interrupt(&irq, &ic, np, ints, intrcells); + if (n <= 0) + continue; + offset = 0; + /* + * On a CHRP we have an 8259 which is subordinate to + * the openpic in the interrupt tree, but we want the + * openpic's interrupt numbers offsetted, not the 8259's. + * So we apply the offset if the controller is at the + * root of the interrupt tree, i.e. has no interrupt-parent. + * This doesn't cope with the general case of multiple + * cascaded interrupt controllers, but then neither will + * irq.c at the moment either. -- paulus + * The G5 triggers that code, I add a machine test. On + * those machines, we want to offset interrupts from the + * second openpic by 128 -- BenH + */ + if (_machine != _MACH_Pmac && num_interrupt_controllers > 1 + && ic != NULL + && get_property(ic, "interrupt-parent", NULL) == NULL) + offset = 16; + else if (_machine == _MACH_Pmac && num_interrupt_controllers > 1 + && ic != NULL && ic->parent != NULL) { + char *name = get_property(ic->parent, "name", NULL); + if (name && !strcmp(name, "u3")) + offset = 128; + } + + np->intrs[i].line = irq[0] + offset; + if (n > 1) + np->intrs[i].sense = irq[1]; + if (n > 2) { + printk("hmmm, got %d intr cells for %s:", n, + np->full_name); + for (j = 0; j < n; ++j) + printk(" %d", irq[j]); + printk("\n"); + } + ints += intrcells; + } + + return mem_start; +} + +/* + * When BootX makes a copy of the device tree from the MacOS + * Name Registry, it is in the format we use but all of the pointers + * are offsets from the start of the tree. + * This procedure updates the pointers. + */ +void __init +relocate_nodes(void) +{ + unsigned long base; + struct device_node *np; + struct property *pp; + +#define ADDBASE(x) (x = (typeof (x))((x)? ((unsigned long)(x) + base): 0)) + + base = (unsigned long) boot_infos + boot_infos->deviceTreeOffset; + allnodes = (struct device_node *)(base + 4); + for (np = allnodes; np != 0; np = np->allnext) { + ADDBASE(np->full_name); + ADDBASE(np->properties); + ADDBASE(np->parent); + ADDBASE(np->child); + ADDBASE(np->sibling); + ADDBASE(np->allnext); + for (pp = np->properties; pp != 0; pp = pp->next) { + ADDBASE(pp->name); + ADDBASE(pp->value); + ADDBASE(pp->next); + } + } +} + +int +prom_n_addr_cells(struct device_node* np) +{ + int* ip; + do { + if (np->parent) + np = np->parent; + ip = (int *) get_property(np, "#address-cells", NULL); + if (ip != NULL) + return *ip; + } while (np->parent); + /* No #address-cells property for the root node, default to 1 */ + return 1; +} + +int +prom_n_size_cells(struct device_node* np) +{ + int* ip; + do { + if (np->parent) + np = np->parent; + ip = (int *) get_property(np, "#size-cells", NULL); + if (ip != NULL) + return *ip; + } while (np->parent); + /* No #size-cells property for the root node, default to 1 */ + return 1; +} + +static unsigned long __init +map_addr(struct device_node *np, unsigned long space, unsigned long addr) +{ + int na; + unsigned int *ranges; + int rlen = 0; + unsigned int type; + + type = (space >> 24) & 3; + if (type == 0) + return addr; + + while ((np = np->parent) != NULL) { + if (strcmp(np->type, "pci") != 0) + continue; + /* PCI bridge: map the address through the ranges property */ + na = prom_n_addr_cells(np); + ranges = (unsigned int *) get_property(np, "ranges", &rlen); + while ((rlen -= (na + 5) * sizeof(unsigned int)) >= 0) { + if (((ranges[0] >> 24) & 3) == type + && ranges[2] <= addr + && addr - ranges[2] < ranges[na+4]) { + /* ok, this matches, translate it */ + addr += ranges[na+2] - ranges[2]; + break; + } + ranges += na + 5; + } + } + return addr; +} + +static unsigned long __init +interpret_pci_props(struct device_node *np, unsigned long mem_start, + int naddrc, int nsizec) +{ + struct address_range *adr; + struct pci_reg_property *pci_addrs; + int i, l, *ip; + + pci_addrs = (struct pci_reg_property *) + get_property(np, "assigned-addresses", &l); + if (pci_addrs != 0 && l >= sizeof(struct pci_reg_property)) { + i = 0; + adr = (struct address_range *) mem_start; + while ((l -= sizeof(struct pci_reg_property)) >= 0) { + adr[i].space = pci_addrs[i].addr.a_hi; + adr[i].address = map_addr(np, pci_addrs[i].addr.a_hi, + pci_addrs[i].addr.a_lo); + adr[i].size = pci_addrs[i].size_lo; + ++i; + } + np->addrs = adr; + np->n_addrs = i; + mem_start += i * sizeof(struct address_range); + } + + if (use_of_interrupt_tree) + return mem_start; + + ip = (int *) get_property(np, "AAPL,interrupts", &l); + if (ip == 0 && np->parent) + ip = (int *) get_property(np->parent, "AAPL,interrupts", &l); + if (ip == 0) + ip = (int *) get_property(np, "interrupts", &l); + if (ip != 0) { + np->intrs = (struct interrupt_info *) mem_start; + np->n_intrs = l / sizeof(int); + mem_start += np->n_intrs * sizeof(struct interrupt_info); + for (i = 0; i < np->n_intrs; ++i) { + np->intrs[i].line = *ip++; + np->intrs[i].sense = 1; + } + } + + return mem_start; +} + +static unsigned long __init +interpret_dbdma_props(struct device_node *np, unsigned long mem_start, + int naddrc, int nsizec) +{ + struct reg_property *rp; + struct address_range *adr; + unsigned long base_address; + int i, l, *ip; + struct device_node *db; + + base_address = 0; + for (db = np->parent; db != NULL; db = db->parent) { + if (!strcmp(db->type, "dbdma") && db->n_addrs != 0) { + base_address = db->addrs[0].address; + break; + } + } + + rp = (struct reg_property *) get_property(np, "reg", &l); + if (rp != 0 && l >= sizeof(struct reg_property)) { + i = 0; + adr = (struct address_range *) mem_start; + while ((l -= sizeof(struct reg_property)) >= 0) { + adr[i].space = 2; + adr[i].address = rp[i].address + base_address; + adr[i].size = rp[i].size; + ++i; + } + np->addrs = adr; + np->n_addrs = i; + mem_start += i * sizeof(struct address_range); + } + + if (use_of_interrupt_tree) + return mem_start; + + ip = (int *) get_property(np, "AAPL,interrupts", &l); + if (ip == 0) + ip = (int *) get_property(np, "interrupts", &l); + if (ip != 0) { + np->intrs = (struct interrupt_info *) mem_start; + np->n_intrs = l / sizeof(int); + mem_start += np->n_intrs * sizeof(struct interrupt_info); + for (i = 0; i < np->n_intrs; ++i) { + np->intrs[i].line = *ip++; + np->intrs[i].sense = 1; + } + } + + return mem_start; +} + +static unsigned long __init +interpret_macio_props(struct device_node *np, unsigned long mem_start, + int naddrc, int nsizec) +{ + struct reg_property *rp; + struct address_range *adr; + unsigned long base_address; + int i, l, *ip; + struct device_node *db; + + base_address = 0; + for (db = np->parent; db != NULL; db = db->parent) { + if (!strcmp(db->type, "mac-io") && db->n_addrs != 0) { + base_address = db->addrs[0].address; + break; + } + } + + rp = (struct reg_property *) get_property(np, "reg", &l); + if (rp != 0 && l >= sizeof(struct reg_property)) { + i = 0; + adr = (struct address_range *) mem_start; + while ((l -= sizeof(struct reg_property)) >= 0) { + adr[i].space = 2; + adr[i].address = rp[i].address + base_address; + adr[i].size = rp[i].size; + ++i; + } + np->addrs = adr; + np->n_addrs = i; + mem_start += i * sizeof(struct address_range); + } + + if (use_of_interrupt_tree) + return mem_start; + + ip = (int *) get_property(np, "interrupts", &l); + if (ip == 0) + ip = (int *) get_property(np, "AAPL,interrupts", &l); + if (ip != 0) { + np->intrs = (struct interrupt_info *) mem_start; + np->n_intrs = l / sizeof(int); + for (i = 0; i < np->n_intrs; ++i) { + np->intrs[i].line = *ip++; + np->intrs[i].sense = 1; + } + mem_start += np->n_intrs * sizeof(struct interrupt_info); + } + + return mem_start; +} + +static unsigned long __init +interpret_isa_props(struct device_node *np, unsigned long mem_start, + int naddrc, int nsizec) +{ + struct isa_reg_property *rp; + struct address_range *adr; + int i, l, *ip; + + rp = (struct isa_reg_property *) get_property(np, "reg", &l); + if (rp != 0 && l >= sizeof(struct isa_reg_property)) { + i = 0; + adr = (struct address_range *) mem_start; + while ((l -= sizeof(struct reg_property)) >= 0) { + adr[i].space = rp[i].space; + adr[i].address = rp[i].address + + (adr[i].space? 0: _ISA_MEM_BASE); + adr[i].size = rp[i].size; + ++i; + } + np->addrs = adr; + np->n_addrs = i; + mem_start += i * sizeof(struct address_range); + } + + if (use_of_interrupt_tree) + return mem_start; + + ip = (int *) get_property(np, "interrupts", &l); + if (ip != 0) { + np->intrs = (struct interrupt_info *) mem_start; + np->n_intrs = l / (2 * sizeof(int)); + mem_start += np->n_intrs * sizeof(struct interrupt_info); + for (i = 0; i < np->n_intrs; ++i) { + np->intrs[i].line = *ip++; + np->intrs[i].sense = *ip++; + } + } + + return mem_start; +} + +static unsigned long __init +interpret_root_props(struct device_node *np, unsigned long mem_start, + int naddrc, int nsizec) +{ + struct address_range *adr; + int i, l, *ip; + unsigned int *rp; + int rpsize = (naddrc + nsizec) * sizeof(unsigned int); + + rp = (unsigned int *) get_property(np, "reg", &l); + if (rp != 0 && l >= rpsize) { + i = 0; + adr = (struct address_range *) mem_start; + while ((l -= rpsize) >= 0) { + adr[i].space = (naddrc >= 2? rp[naddrc-2]: 2); + adr[i].address = rp[naddrc - 1]; + adr[i].size = rp[naddrc + nsizec - 1]; + ++i; + rp += naddrc + nsizec; + } + np->addrs = adr; + np->n_addrs = i; + mem_start += i * sizeof(struct address_range); + } + + if (use_of_interrupt_tree) + return mem_start; + + ip = (int *) get_property(np, "AAPL,interrupts", &l); + if (ip == 0) + ip = (int *) get_property(np, "interrupts", &l); + if (ip != 0) { + np->intrs = (struct interrupt_info *) mem_start; + np->n_intrs = l / sizeof(int); + mem_start += np->n_intrs * sizeof(struct interrupt_info); + for (i = 0; i < np->n_intrs; ++i) { + np->intrs[i].line = *ip++; + np->intrs[i].sense = 1; + } + } + + return mem_start; +} + +/* + * Work out the sense (active-low level / active-high edge) + * of each interrupt from the device tree. + */ +void __init +prom_get_irq_senses(unsigned char *senses, int off, int max) +{ + struct device_node *np; + int i, j; + + /* default to level-triggered */ + memset(senses, 1, max - off); + if (!use_of_interrupt_tree) + return; + + for (np = allnodes; np != 0; np = np->allnext) { + for (j = 0; j < np->n_intrs; j++) { + i = np->intrs[j].line; + if (i >= off && i < max) { + if (np->intrs[j].sense == 1) + senses[i-off] = (IRQ_SENSE_LEVEL + | IRQ_POLARITY_NEGATIVE); + else + senses[i-off] = (IRQ_SENSE_EDGE + | IRQ_POLARITY_POSITIVE); + } + } + } +} + +/* + * Construct and return a list of the device_nodes with a given name. + */ +struct device_node * +find_devices(const char *name) +{ + struct device_node *head, **prevp, *np; + + prevp = &head; + for (np = allnodes; np != 0; np = np->allnext) { + if (np->name != 0 && strcasecmp(np->name, name) == 0) { + *prevp = np; + prevp = &np->next; + } + } + *prevp = NULL; + return head; +} + +/* + * Construct and return a list of the device_nodes with a given type. + */ +struct device_node * +find_type_devices(const char *type) +{ + struct device_node *head, **prevp, *np; + + prevp = &head; + for (np = allnodes; np != 0; np = np->allnext) { + if (np->type != 0 && strcasecmp(np->type, type) == 0) { + *prevp = np; + prevp = &np->next; + } + } + *prevp = NULL; + return head; +} + +/* + * Returns all nodes linked together + */ +struct device_node * __openfirmware +find_all_nodes(void) +{ + struct device_node *head, **prevp, *np; + + prevp = &head; + for (np = allnodes; np != 0; np = np->allnext) { + *prevp = np; + prevp = &np->next; + } + *prevp = NULL; + return head; +} + +/* Checks if the given "compat" string matches one of the strings in + * the device's "compatible" property + */ +int +device_is_compatible(struct device_node *device, const char *compat) +{ + const char* cp; + int cplen, l; + + cp = (char *) get_property(device, "compatible", &cplen); + if (cp == NULL) + return 0; + while (cplen > 0) { + if (strncasecmp(cp, compat, strlen(compat)) == 0) + return 1; + l = strlen(cp) + 1; + cp += l; + cplen -= l; + } + + return 0; +} + + +/* + * Indicates whether the root node has a given value in its + * compatible property. + */ +int +machine_is_compatible(const char *compat) +{ + struct device_node *root; + + root = find_path_device("/"); + if (root == 0) + return 0; + return device_is_compatible(root, compat); +} + +/* + * Construct and return a list of the device_nodes with a given type + * and compatible property. + */ +struct device_node * +find_compatible_devices(const char *type, const char *compat) +{ + struct device_node *head, **prevp, *np; + + prevp = &head; + for (np = allnodes; np != 0; np = np->allnext) { + if (type != NULL + && !(np->type != 0 && strcasecmp(np->type, type) == 0)) + continue; + if (device_is_compatible(np, compat)) { + *prevp = np; + prevp = &np->next; + } + } + *prevp = NULL; + return head; +} + +/* + * Find the device_node with a given full_name. + */ +struct device_node * +find_path_device(const char *path) +{ + struct device_node *np; + + for (np = allnodes; np != 0; np = np->allnext) + if (np->full_name != 0 && strcasecmp(np->full_name, path) == 0) + return np; + return NULL; +} + +/******* + * + * New implementation of the OF "find" APIs, return a refcounted + * object, call of_node_put() when done. Currently, still lacks + * locking as old implementation, this is beeing done for ppc64. + * + * Note that property management will need some locking as well, + * this isn't dealt with yet + * + *******/ + +/** + * of_find_node_by_name - Find a node by it's "name" property + * @from: The node to start searching from or NULL, the node + * you pass will not be searched, only the next one + * will; typically, you pass what the previous call + * returned. of_node_put() will be called on it + * @name: The name string to match against + * + * Returns a node pointer with refcount incremented, use + * of_node_put() on it when done. + */ +struct device_node *of_find_node_by_name(struct device_node *from, + const char *name) +{ + struct device_node *np = from ? from->allnext : allnodes; + + for (; np != 0; np = np->allnext) + if (np->name != 0 && strcasecmp(np->name, name) == 0) + break; + if (from) + of_node_put(from); + return of_node_get(np); +} + +/** + * of_find_node_by_type - Find a node by it's "device_type" property + * @from: The node to start searching from or NULL, the node + * you pass will not be searched, only the next one + * will; typically, you pass what the previous call + * returned. of_node_put() will be called on it + * @name: The type string to match against + * + * Returns a node pointer with refcount incremented, use + * of_node_put() on it when done. + */ +struct device_node *of_find_node_by_type(struct device_node *from, + const char *type) +{ + struct device_node *np = from ? from->allnext : allnodes; + + for (; np != 0; np = np->allnext) + if (np->type != 0 && strcasecmp(np->type, type) == 0) + break; + if (from) + of_node_put(from); + return of_node_get(np); +} + +/** + * of_find_compatible_node - Find a node based on type and one of the + * tokens in it's "compatible" property + * @from: The node to start searching from or NULL, the node + * you pass will not be searched, only the next one + * will; typically, you pass what the previous call + * returned. of_node_put() will be called on it + * @type: The type string to match "device_type" or NULL to ignore + * @compatible: The string to match to one of the tokens in the device + * "compatible" list. + * + * Returns a node pointer with refcount incremented, use + * of_node_put() on it when done. + */ +struct device_node *of_find_compatible_node(struct device_node *from, + const char *type, const char *compatible) +{ + struct device_node *np = from ? from->allnext : allnodes; + + for (; np != 0; np = np->allnext) { + if (type != NULL + && !(np->type != 0 && strcasecmp(np->type, type) == 0)) + continue; + if (device_is_compatible(np, compatible)) + break; + } + if (from) + of_node_put(from); + return of_node_get(np); +} + +/** + * of_find_node_by_path - Find a node matching a full OF path + * @path: The full path to match + * + * Returns a node pointer with refcount incremented, use + * of_node_put() on it when done. + */ +struct device_node *of_find_node_by_path(const char *path) +{ + struct device_node *np = allnodes; + + for (; np != 0; np = np->allnext) + if (np->full_name != 0 && strcasecmp(np->full_name, path) == 0) + break; + return of_node_get(np); +} + +/** + * of_find_all_nodes - Get next node in global list + * @prev: Previous node or NULL to start iteration + * of_node_put() will be called on it + * + * Returns a node pointer with refcount incremented, use + * of_node_put() on it when done. + */ +struct device_node *of_find_all_nodes(struct device_node *prev) +{ + return of_node_get(prev ? prev->allnext : allnodes); +} + +/** + * of_get_parent - Get a node's parent if any + * @node: Node to get parent + * + * Returns a node pointer with refcount incremented, use + * of_node_put() on it when done. + */ +struct device_node *of_get_parent(const struct device_node *node) +{ + return node ? of_node_get(node->parent) : NULL; +} + +/** + * of_get_next_child - Iterate a node childs + * @node: parent node + * @prev: previous child of the parent node, or NULL to get first + * + * Returns a node pointer with refcount incremented, use + * of_node_put() on it when done. + */ +struct device_node *of_get_next_child(const struct device_node *node, + struct device_node *prev) +{ + struct device_node *next = prev ? prev->sibling : node->child; + + for (; next != 0; next = next->sibling) + if (of_node_get(next)) + break; + if (prev) + of_node_put(prev); + return next; +} + +/** + * of_node_get - Increment refcount of a node + * @node: Node to inc refcount, NULL is supported to + * simplify writing of callers + * + * Returns the node itself or NULL if gone. Current implementation + * does nothing as we don't yet do dynamic node allocation on ppc32 + */ +struct device_node *of_node_get(struct device_node *node) +{ + return node; +} + +/** + * of_node_put - Decrement refcount of a node + * @node: Node to dec refcount, NULL is supported to + * simplify writing of callers + * + * Current implementation does nothing as we don't yet do dynamic node + * allocation on ppc32 + */ +void of_node_put(struct device_node *node) +{ +} + +/* + * Find the device_node with a given phandle. + */ +static struct device_node * __init +find_phandle(phandle ph) +{ + struct device_node *np; + + for (np = allnodes; np != 0; np = np->allnext) + if (np->node == ph) + return np; + return NULL; +} + +/* + * Find a property with a given name for a given node + * and return the value. + */ +unsigned char * +get_property(struct device_node *np, const char *name, int *lenp) +{ + struct property *pp; + + for (pp = np->properties; pp != 0; pp = pp->next) + if (pp->name != NULL && strcmp(pp->name, name) == 0) { + if (lenp != 0) + *lenp = pp->length; + return pp->value; + } + return NULL; +} + +/* + * Add a property to a node + */ +void __openfirmware +prom_add_property(struct device_node* np, struct property* prop) +{ + struct property **next = &np->properties; + + prop->next = NULL; + while (*next) + next = &(*next)->next; + *next = prop; +} + +/* I quickly hacked that one, check against spec ! */ +static inline unsigned long __openfirmware +bus_space_to_resource_flags(unsigned int bus_space) +{ + u8 space = (bus_space >> 24) & 0xf; + if (space == 0) + space = 0x02; + if (space == 0x02) + return IORESOURCE_MEM; + else if (space == 0x01) + return IORESOURCE_IO; + else { + printk(KERN_WARNING "prom.c: bus_space_to_resource_flags(), space: %x\n", + bus_space); + return 0; + } +} + +static struct resource* __openfirmware +find_parent_pci_resource(struct pci_dev* pdev, struct address_range *range) +{ + unsigned long mask; + int i; + + /* Check this one */ + mask = bus_space_to_resource_flags(range->space); + for (i=0; iresource[i].flags & mask) == mask && + pdev->resource[i].start <= range->address && + pdev->resource[i].end > range->address) { + if ((range->address + range->size - 1) > pdev->resource[i].end) { + /* Add better message */ + printk(KERN_WARNING "PCI/OF resource overlap !\n"); + return NULL; + } + break; + } + } + if (i == DEVICE_COUNT_RESOURCE) + return NULL; + return &pdev->resource[i]; +} + +/* + * Request an OF device resource. Currently handles child of PCI devices, + * or other nodes attached to the root node. Ultimately, put some + * link to resources in the OF node. + */ +struct resource* __openfirmware +request_OF_resource(struct device_node* node, int index, const char* name_postfix) +{ + struct pci_dev* pcidev; + u8 pci_bus, pci_devfn; + unsigned long iomask; + struct device_node* nd; + struct resource* parent; + struct resource *res = NULL; + int nlen, plen; + + if (index >= node->n_addrs) + goto fail; + + /* Sanity check on bus space */ + iomask = bus_space_to_resource_flags(node->addrs[index].space); + if (iomask & IORESOURCE_MEM) + parent = &iomem_resource; + else if (iomask & IORESOURCE_IO) + parent = &ioport_resource; + else + goto fail; + + /* Find a PCI parent if any */ + nd = node; + pcidev = NULL; + while(nd) { + if (!pci_device_from_OF_node(nd, &pci_bus, &pci_devfn)) + pcidev = pci_find_slot(pci_bus, pci_devfn); + if (pcidev) break; + nd = nd->parent; + } + if (pcidev) + parent = find_parent_pci_resource(pcidev, &node->addrs[index]); + if (!parent) { + printk(KERN_WARNING "request_OF_resource(%s), parent not found\n", + node->name); + goto fail; + } + + res = __request_region(parent, node->addrs[index].address, node->addrs[index].size, NULL); + if (!res) + goto fail; + nlen = strlen(node->name); + plen = name_postfix ? strlen(name_postfix) : 0; + res->name = (const char *)kmalloc(nlen+plen+1, GFP_KERNEL); + if (res->name) { + strcpy((char *)res->name, node->name); + if (plen) + strcpy((char *)res->name+nlen, name_postfix); + } + return res; +fail: + return NULL; +} + +int __openfirmware +release_OF_resource(struct device_node* node, int index) +{ + struct pci_dev* pcidev; + u8 pci_bus, pci_devfn; + unsigned long iomask, start, end; + struct device_node* nd; + struct resource* parent; + struct resource *res = NULL; + + if (index >= node->n_addrs) + return -EINVAL; + + /* Sanity check on bus space */ + iomask = bus_space_to_resource_flags(node->addrs[index].space); + if (iomask & IORESOURCE_MEM) + parent = &iomem_resource; + else if (iomask & IORESOURCE_IO) + parent = &ioport_resource; + else + return -EINVAL; + + /* Find a PCI parent if any */ + nd = node; + pcidev = NULL; + while(nd) { + if (!pci_device_from_OF_node(nd, &pci_bus, &pci_devfn)) + pcidev = pci_find_slot(pci_bus, pci_devfn); + if (pcidev) break; + nd = nd->parent; + } + if (pcidev) + parent = find_parent_pci_resource(pcidev, &node->addrs[index]); + if (!parent) { + printk(KERN_WARNING "release_OF_resource(%s), parent not found\n", + node->name); + return -ENODEV; + } + + /* Find us in the parent and its childs */ + res = parent->child; + start = node->addrs[index].address; + end = start + node->addrs[index].size - 1; + while (res) { + if (res->start == start && res->end == end && + (res->flags & IORESOURCE_BUSY)) + break; + if (res->start <= start && res->end >= end) + res = res->child; + else + res = res->sibling; + } + if (!res) + return -ENODEV; + + if (res->name) { + kfree(res->name); + res->name = NULL; + } + release_resource(res); + kfree(res); + + return 0; +} + +#if 0 +void __openfirmware +print_properties(struct device_node *np) +{ + struct property *pp; + char *cp; + int i, n; + + for (pp = np->properties; pp != 0; pp = pp->next) { + printk(KERN_INFO "%s", pp->name); + for (i = strlen(pp->name); i < 16; ++i) + printk(" "); + cp = (char *) pp->value; + for (i = pp->length; i > 0; --i, ++cp) + if ((i > 1 && (*cp < 0x20 || *cp > 0x7e)) + || (i == 1 && *cp != 0)) + break; + if (i == 0 && pp->length > 1) { + /* looks like a string */ + printk(" %s\n", (char *) pp->value); + } else { + /* dump it in hex */ + n = pp->length; + if (n > 64) + n = 64; + if (pp->length % 4 == 0) { + unsigned int *p = (unsigned int *) pp->value; + + n /= 4; + for (i = 0; i < n; ++i) { + if (i != 0 && (i % 4) == 0) + printk("\n "); + printk(" %08x", *p++); + } + } else { + unsigned char *bp = pp->value; + + for (i = 0; i < n; ++i) { + if (i != 0 && (i % 16) == 0) + printk("\n "); + printk(" %02x", *bp++); + } + } + printk("\n"); + if (pp->length > 64) + printk(" ... (length = %d)\n", + pp->length); + } + } +} +#endif + +static DEFINE_SPINLOCK(rtas_lock); + +/* this can be called after setup -- Cort */ +int __openfirmware +call_rtas(const char *service, int nargs, int nret, + unsigned long *outputs, ...) +{ + va_list list; + int i; + unsigned long s; + struct device_node *rtas; + int *tokp; + union { + unsigned long words[16]; + double align; + } u; + + rtas = find_devices("rtas"); + if (rtas == NULL) + return -1; + tokp = (int *) get_property(rtas, service, NULL); + if (tokp == NULL) { + printk(KERN_ERR "No RTAS service called %s\n", service); + return -1; + } + u.words[0] = *tokp; + u.words[1] = nargs; + u.words[2] = nret; + va_start(list, outputs); + for (i = 0; i < nargs; ++i) + u.words[i+3] = va_arg(list, unsigned long); + va_end(list); + + /* + * RTAS doesn't use floating point. + * Or at least, according to the CHRP spec we enter RTAS + * with FP disabled, and it doesn't change the FP registers. + * -- paulus. + */ + spin_lock_irqsave(&rtas_lock, s); + enter_rtas((void *)__pa(&u)); + spin_unlock_irqrestore(&rtas_lock, s); + + if (nret > 1 && outputs != NULL) + for (i = 0; i < nret-1; ++i) + outputs[i] = u.words[i+nargs+4]; + return u.words[nargs+3]; +} diff --git a/arch/ppc/syslib/prom_init.c b/arch/ppc/syslib/prom_init.c new file mode 100644 index 00000000000..2cee87137f2 --- /dev/null +++ b/arch/ppc/syslib/prom_init.c @@ -0,0 +1,1002 @@ +/* + * Note that prom_init() and anything called from prom_init() + * may be running at an address that is different from the address + * that it was linked at. References to static data items are + * handled by compiling this file with -mrelocatable-lib. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_LOGO_LINUX_CLUT224 +#include +extern const struct linux_logo logo_linux_clut224; +#endif + +/* + * Properties whose value is longer than this get excluded from our + * copy of the device tree. This way we don't waste space storing + * things like "driver,AAPL,MacOS,PowerPC" properties. But this value + * does need to be big enough to ensure that we don't lose things + * like the interrupt-map property on a PCI-PCI bridge. + */ +#define MAX_PROPERTY_LENGTH 4096 + +#ifndef FB_MAX /* avoid pulling in all of the fb stuff */ +#define FB_MAX 8 +#endif + +#define ALIGNUL(x) (((x) + sizeof(unsigned long)-1) & -sizeof(unsigned long)) + +typedef u32 prom_arg_t; + +struct prom_args { + const char *service; + int nargs; + int nret; + prom_arg_t args[10]; +}; + +struct pci_address { + unsigned a_hi; + unsigned a_mid; + unsigned a_lo; +}; + +struct pci_reg_property { + struct pci_address addr; + unsigned size_hi; + unsigned size_lo; +}; + +struct pci_range { + struct pci_address addr; + unsigned phys; + unsigned size_hi; + unsigned size_lo; +}; + +struct isa_reg_property { + unsigned space; + unsigned address; + unsigned size; +}; + +struct pci_intr_map { + struct pci_address addr; + unsigned dunno; + phandle int_ctrler; + unsigned intr; +}; + +static void prom_exit(void); +static int call_prom(const char *service, int nargs, int nret, ...); +static int call_prom_ret(const char *service, int nargs, int nret, + prom_arg_t *rets, ...); +static void prom_print_hex(unsigned int v); +static int prom_set_color(ihandle ih, int i, int r, int g, int b); +static int prom_next_node(phandle *nodep); +static unsigned long check_display(unsigned long mem); +static void setup_disp_fake_bi(ihandle dp); +static unsigned long copy_device_tree(unsigned long mem_start, + unsigned long mem_end); +static unsigned long inspect_node(phandle node, struct device_node *dad, + unsigned long mem_start, unsigned long mem_end, + struct device_node ***allnextpp); +static void prom_hold_cpus(unsigned long mem); +static void prom_instantiate_rtas(void); +static void * early_get_property(unsigned long base, unsigned long node, + char *prop); + +prom_entry prom __initdata; +ihandle prom_chosen __initdata; +ihandle prom_stdout __initdata; + +static char *prom_display_paths[FB_MAX] __initdata; +static phandle prom_display_nodes[FB_MAX] __initdata; +static unsigned int prom_num_displays __initdata; +static ihandle prom_disp_node __initdata; +char *of_stdout_device __initdata; + +unsigned int rtas_data; /* physical pointer */ +unsigned int rtas_entry; /* physical pointer */ +unsigned int rtas_size; +unsigned int old_rtas; + +boot_infos_t *boot_infos; +char *bootpath; +char *bootdevice; +struct device_node *allnodes; + +extern char *klimit; + +static void __init +prom_exit(void) +{ + struct prom_args args; + + args.service = "exit"; + args.nargs = 0; + args.nret = 0; + prom(&args); + for (;;) /* should never get here */ + ; +} + +static int __init +call_prom(const char *service, int nargs, int nret, ...) +{ + va_list list; + int i; + struct prom_args prom_args; + + prom_args.service = service; + prom_args.nargs = nargs; + prom_args.nret = nret; + va_start(list, nret); + for (i = 0; i < nargs; ++i) + prom_args.args[i] = va_arg(list, prom_arg_t); + va_end(list); + for (i = 0; i < nret; ++i) + prom_args.args[i + nargs] = 0; + prom(&prom_args); + return prom_args.args[nargs]; +} + +static int __init +call_prom_ret(const char *service, int nargs, int nret, prom_arg_t *rets, ...) +{ + va_list list; + int i; + struct prom_args prom_args; + + prom_args.service = service; + prom_args.nargs = nargs; + prom_args.nret = nret; + va_start(list, rets); + for (i = 0; i < nargs; ++i) + prom_args.args[i] = va_arg(list, int); + va_end(list); + for (i = 0; i < nret; ++i) + prom_args.args[i + nargs] = 0; + prom(&prom_args); + for (i = 1; i < nret; ++i) + rets[i-1] = prom_args.args[nargs + i]; + return prom_args.args[nargs]; +} + +void __init +prom_print(const char *msg) +{ + const char *p, *q; + + if (prom_stdout == 0) + return; + + for (p = msg; *p != 0; p = q) { + for (q = p; *q != 0 && *q != '\n'; ++q) + ; + if (q > p) + call_prom("write", 3, 1, prom_stdout, p, q - p); + if (*q != 0) { + ++q; + call_prom("write", 3, 1, prom_stdout, "\r\n", 2); + } + } +} + +static void __init +prom_print_hex(unsigned int v) +{ + char buf[16]; + int i, c; + + for (i = 0; i < 8; ++i) { + c = (v >> ((7-i)*4)) & 0xf; + c += (c >= 10)? ('a' - 10): '0'; + buf[i] = c; + } + buf[i] = ' '; + buf[i+1] = 0; + prom_print(buf); +} + +static int __init +prom_set_color(ihandle ih, int i, int r, int g, int b) +{ + return call_prom("call-method", 6, 1, "color!", ih, i, b, g, r); +} + +static int __init +prom_next_node(phandle *nodep) +{ + phandle node; + + if ((node = *nodep) != 0 + && (*nodep = call_prom("child", 1, 1, node)) != 0) + return 1; + if ((*nodep = call_prom("peer", 1, 1, node)) != 0) + return 1; + for (;;) { + if ((node = call_prom("parent", 1, 1, node)) == 0) + return 0; + if ((*nodep = call_prom("peer", 1, 1, node)) != 0) + return 1; + } +} + +#ifdef CONFIG_POWER4 +/* + * Set up a hash table with a set of entries in it to map the + * first 64MB of RAM. This is used on 64-bit machines since + * some of them don't have BATs. + */ + +static inline void make_pte(unsigned long htab, unsigned int hsize, + unsigned int va, unsigned int pa, int mode) +{ + unsigned int *pteg; + unsigned int hash, i, vsid; + + vsid = ((va >> 28) * 0x111) << 12; + hash = ((va ^ vsid) >> 5) & 0x7fff80; + pteg = (unsigned int *)(htab + (hash & (hsize - 1))); + for (i = 0; i < 8; ++i, pteg += 4) { + if ((pteg[1] & 1) == 0) { + pteg[1] = vsid | ((va >> 16) & 0xf80) | 1; + pteg[3] = pa | mode; + break; + } + } +} + +extern unsigned long _SDR1; +extern PTE *Hash; +extern unsigned long Hash_size; + +static void __init +prom_alloc_htab(void) +{ + unsigned int hsize; + unsigned long htab; + unsigned int addr; + + /* + * Because of OF bugs we can't use the "claim" client + * interface to allocate memory for the hash table. + * This code is only used on 64-bit PPCs, and the only + * 64-bit PPCs at the moment are RS/6000s, and their + * OF is based at 0xc00000 (the 12M point), so we just + * arbitrarily use the 0x800000 - 0xc00000 region for the + * hash table. + * -- paulus. + */ + hsize = 4 << 20; /* POWER4 has no BATs */ + htab = (8 << 20); + call_prom("claim", 3, 1, htab, hsize, 0); + Hash = (void *)(htab + KERNELBASE); + Hash_size = hsize; + _SDR1 = htab + __ilog2(hsize) - 18; + + /* + * Put in PTEs for the first 64MB of RAM + */ + memset((void *)htab, 0, hsize); + for (addr = 0; addr < 0x4000000; addr += 0x1000) + make_pte(htab, hsize, addr + KERNELBASE, addr, + _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX); +#if 0 /* DEBUG stuff mapping the SCC */ + make_pte(htab, hsize, 0x80013000, 0x80013000, + _PAGE_ACCESSED | _PAGE_NO_CACHE | _PAGE_GUARDED | PP_RWXX); +#endif +} +#endif /* CONFIG_POWER4 */ + + +/* + * If we have a display that we don't know how to drive, + * we will want to try to execute OF's open method for it + * later. However, OF will probably fall over if we do that + * we've taken over the MMU. + * So we check whether we will need to open the display, + * and if so, open it now. + */ +static unsigned long __init +check_display(unsigned long mem) +{ + phandle node; + ihandle ih; + int i, j; + char type[16], *path; + static unsigned char default_colors[] = { + 0x00, 0x00, 0x00, + 0x00, 0x00, 0xaa, + 0x00, 0xaa, 0x00, + 0x00, 0xaa, 0xaa, + 0xaa, 0x00, 0x00, + 0xaa, 0x00, 0xaa, + 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, + 0x55, 0x55, 0x55, + 0x55, 0x55, 0xff, + 0x55, 0xff, 0x55, + 0x55, 0xff, 0xff, + 0xff, 0x55, 0x55, + 0xff, 0x55, 0xff, + 0xff, 0xff, 0x55, + 0xff, 0xff, 0xff + }; + const unsigned char *clut; + + prom_disp_node = 0; + + for (node = 0; prom_next_node(&node); ) { + type[0] = 0; + call_prom("getprop", 4, 1, node, "device_type", + type, sizeof(type)); + if (strcmp(type, "display") != 0) + continue; + /* It seems OF doesn't null-terminate the path :-( */ + path = (char *) mem; + memset(path, 0, 256); + if (call_prom("package-to-path", 3, 1, node, path, 255) < 0) + continue; + + /* + * If this display is the device that OF is using for stdout, + * move it to the front of the list. + */ + mem += strlen(path) + 1; + i = prom_num_displays++; + if (of_stdout_device != 0 && i > 0 + && strcmp(of_stdout_device, path) == 0) { + for (; i > 0; --i) { + prom_display_paths[i] + = prom_display_paths[i-1]; + prom_display_nodes[i] + = prom_display_nodes[i-1]; + } + } + prom_display_paths[i] = path; + prom_display_nodes[i] = node; + if (i == 0) + prom_disp_node = node; + if (prom_num_displays >= FB_MAX) + break; + } + + for (j=0; j 0) { + prom_disp_node = prom_display_nodes[j]; + j--; + } else + prom_disp_node = 0; + continue; + } else { + prom_print("... ok\n"); + call_prom("setprop", 4, 1, node, "linux,opened", 0, 0); + + /* + * Setup a usable color table when the appropriate + * method is available. + * Should update this to use set-colors. + */ + clut = default_colors; + for (i = 0; i < 32; i++, clut += 3) + if (prom_set_color(ih, i, clut[0], clut[1], + clut[2]) != 0) + break; + +#ifdef CONFIG_LOGO_LINUX_CLUT224 + clut = PTRRELOC(logo_linux_clut224.clut); + for (i = 0; i < logo_linux_clut224.clutsize; + i++, clut += 3) + if (prom_set_color(ih, i + 32, clut[0], + clut[1], clut[2]) != 0) + break; +#endif /* CONFIG_LOGO_LINUX_CLUT224 */ + } + } + + if (prom_stdout) { + phandle p; + p = call_prom("instance-to-package", 1, 1, prom_stdout); + if (p && p != -1) { + type[0] = 0; + call_prom("getprop", 4, 1, p, "device_type", + type, sizeof(type)); + if (strcmp(type, "display") == 0) + call_prom("setprop", 4, 1, p, "linux,boot-display", + 0, 0); + } + } + + return ALIGNUL(mem); +} + +/* This function will enable the early boot text when doing OF booting. This + * way, xmon output should work too + */ +static void __init +setup_disp_fake_bi(ihandle dp) +{ +#ifdef CONFIG_BOOTX_TEXT + int width = 640, height = 480, depth = 8, pitch; + unsigned address; + struct pci_reg_property addrs[8]; + int i, naddrs; + char name[32]; + char *getprop = "getprop"; + + prom_print("Initializing fake screen: "); + + memset(name, 0, sizeof(name)); + call_prom(getprop, 4, 1, dp, "name", name, sizeof(name)); + name[sizeof(name)-1] = 0; + prom_print(name); + prom_print("\n"); + call_prom(getprop, 4, 1, dp, "width", &width, sizeof(width)); + call_prom(getprop, 4, 1, dp, "height", &height, sizeof(height)); + call_prom(getprop, 4, 1, dp, "depth", &depth, sizeof(depth)); + pitch = width * ((depth + 7) / 8); + call_prom(getprop, 4, 1, dp, "linebytes", + &pitch, sizeof(pitch)); + if (pitch == 1) + pitch = 0x1000; /* for strange IBM display */ + address = 0; + call_prom(getprop, 4, 1, dp, "address", + &address, sizeof(address)); + if (address == 0) { + /* look for an assigned address with a size of >= 1MB */ + naddrs = call_prom(getprop, 4, 1, dp, "assigned-addresses", + addrs, sizeof(addrs)); + naddrs /= sizeof(struct pci_reg_property); + for (i = 0; i < naddrs; ++i) { + if (addrs[i].size_lo >= (1 << 20)) { + address = addrs[i].addr.a_lo; + /* use the BE aperture if possible */ + if (addrs[i].size_lo >= (16 << 20)) + address += (8 << 20); + break; + } + } + if (address == 0) { + prom_print("Failed to get address\n"); + return; + } + } + /* kludge for valkyrie */ + if (strcmp(name, "valkyrie") == 0) + address += 0x1000; + +#ifdef CONFIG_POWER4 +#if CONFIG_TASK_SIZE > 0x80000000 +#error CONFIG_TASK_SIZE cannot be above 0x80000000 with BOOTX_TEXT on G5 +#endif + { + extern boot_infos_t disp_bi; + unsigned long va, pa, i, offset; + va = 0x90000000; + pa = address & 0xfffff000ul; + offset = address & 0x00000fff; + + for (i=0; i<0x4000; i++) { + make_pte((unsigned long)Hash - KERNELBASE, Hash_size, va, pa, + _PAGE_ACCESSED | _PAGE_NO_CACHE | + _PAGE_GUARDED | PP_RWXX); + va += 0x1000; + pa += 0x1000; + } + btext_setup_display(width, height, depth, pitch, 0x90000000 | offset); + disp_bi.dispDeviceBase = (u8 *)address; + } +#else /* CONFIG_POWER4 */ + btext_setup_display(width, height, depth, pitch, address); + btext_prepare_BAT(); +#endif /* CONFIG_POWER4 */ +#endif /* CONFIG_BOOTX_TEXT */ +} + +/* + * Make a copy of the device tree from the PROM. + */ +static unsigned long __init +copy_device_tree(unsigned long mem_start, unsigned long mem_end) +{ + phandle root; + unsigned long new_start; + struct device_node **allnextp; + + root = call_prom("peer", 1, 1, (phandle)0); + if (root == (phandle)0) { + prom_print("couldn't get device tree root\n"); + prom_exit(); + } + allnextp = &allnodes; + mem_start = ALIGNUL(mem_start); + new_start = inspect_node(root, NULL, mem_start, mem_end, &allnextp); + *allnextp = NULL; + return new_start; +} + +static unsigned long __init +inspect_node(phandle node, struct device_node *dad, + unsigned long mem_start, unsigned long mem_end, + struct device_node ***allnextpp) +{ + int l; + phandle child; + struct device_node *np; + struct property *pp, **prev_propp; + char *prev_name, *namep; + unsigned char *valp; + + np = (struct device_node *) mem_start; + mem_start += sizeof(struct device_node); + memset(np, 0, sizeof(*np)); + np->node = node; + **allnextpp = PTRUNRELOC(np); + *allnextpp = &np->allnext; + if (dad != 0) { + np->parent = PTRUNRELOC(dad); + /* we temporarily use the `next' field as `last_child'. */ + if (dad->next == 0) + dad->child = PTRUNRELOC(np); + else + dad->next->sibling = PTRUNRELOC(np); + dad->next = np; + } + + /* get and store all properties */ + prev_propp = &np->properties; + prev_name = ""; + for (;;) { + pp = (struct property *) mem_start; + namep = (char *) (pp + 1); + pp->name = PTRUNRELOC(namep); + if (call_prom("nextprop", 3, 1, node, prev_name, namep) <= 0) + break; + mem_start = ALIGNUL((unsigned long)namep + strlen(namep) + 1); + prev_name = namep; + valp = (unsigned char *) mem_start; + pp->value = PTRUNRELOC(valp); + pp->length = call_prom("getprop", 4, 1, node, namep, + valp, mem_end - mem_start); + if (pp->length < 0) + continue; +#ifdef MAX_PROPERTY_LENGTH + if (pp->length > MAX_PROPERTY_LENGTH) + continue; /* ignore this property */ +#endif + mem_start = ALIGNUL(mem_start + pp->length); + *prev_propp = PTRUNRELOC(pp); + prev_propp = &pp->next; + } + if (np->node != 0) { + /* Add a "linux,phandle" property" */ + pp = (struct property *) mem_start; + *prev_propp = PTRUNRELOC(pp); + prev_propp = &pp->next; + namep = (char *) (pp + 1); + pp->name = PTRUNRELOC(namep); + strcpy(namep, "linux,phandle"); + mem_start = ALIGNUL((unsigned long)namep + strlen(namep) + 1); + pp->value = (unsigned char *) PTRUNRELOC(&np->node); + pp->length = sizeof(np->node); + } + *prev_propp = NULL; + + /* get the node's full name */ + l = call_prom("package-to-path", 3, 1, node, + mem_start, mem_end - mem_start); + if (l >= 0) { + np->full_name = PTRUNRELOC((char *) mem_start); + *(char *)(mem_start + l) = 0; + mem_start = ALIGNUL(mem_start + l + 1); + } + + /* do all our children */ + child = call_prom("child", 1, 1, node); + while (child != 0) { + mem_start = inspect_node(child, np, mem_start, mem_end, + allnextpp); + child = call_prom("peer", 1, 1, child); + } + + return mem_start; +} + +unsigned long smp_chrp_cpu_nr __initdata = 0; + +/* + * With CHRP SMP we need to use the OF to start the other + * processors so we can't wait until smp_boot_cpus (the OF is + * trashed by then) so we have to put the processors into + * a holding pattern controlled by the kernel (not OF) before + * we destroy the OF. + * + * This uses a chunk of high memory, puts some holding pattern + * code there and sends the other processors off to there until + * smp_boot_cpus tells them to do something. We do that by using + * physical address 0x0. The holding pattern checks that address + * until its cpu # is there, when it is that cpu jumps to + * __secondary_start(). smp_boot_cpus() takes care of setting those + * values. + * + * We also use physical address 0x4 here to tell when a cpu + * is in its holding pattern code. + * + * -- Cort + * + * Note that we have to do this if we have more than one CPU, + * even if this is a UP kernel. Otherwise when we trash OF + * the other CPUs will start executing some random instructions + * and crash the system. -- paulus + */ +static void __init +prom_hold_cpus(unsigned long mem) +{ + extern void __secondary_hold(void); + unsigned long i; + int cpu; + phandle node; + char type[16], *path; + unsigned int reg; + + /* + * XXX: hack to make sure we're chrp, assume that if we're + * chrp we have a device_type property -- Cort + */ + node = call_prom("finddevice", 1, 1, "/"); + if (call_prom("getprop", 4, 1, node, + "device_type", type, sizeof(type)) <= 0) + return; + + /* copy the holding pattern code to someplace safe (0) */ + /* the holding pattern is now within the first 0x100 + bytes of the kernel image -- paulus */ + memcpy((void *)0, _stext, 0x100); + flush_icache_range(0, 0x100); + + /* look for cpus */ + *(unsigned long *)(0x0) = 0; + asm volatile("dcbf 0,%0": : "r" (0) : "memory"); + for (node = 0; prom_next_node(&node); ) { + type[0] = 0; + call_prom("getprop", 4, 1, node, "device_type", + type, sizeof(type)); + if (strcmp(type, "cpu") != 0) + continue; + path = (char *) mem; + memset(path, 0, 256); + if (call_prom("package-to-path", 3, 1, node, path, 255) < 0) + continue; + reg = -1; + call_prom("getprop", 4, 1, node, "reg", ®, sizeof(reg)); + cpu = smp_chrp_cpu_nr++; +#ifdef CONFIG_SMP + smp_hw_index[cpu] = reg; +#endif /* CONFIG_SMP */ + /* XXX: hack - don't start cpu 0, this cpu -- Cort */ + if (cpu == 0) + continue; + prom_print("starting cpu "); + prom_print(path); + *(ulong *)(0x4) = 0; + call_prom("start-cpu", 3, 0, node, + (char *)__secondary_hold - _stext, cpu); + prom_print("..."); + for ( i = 0 ; (i < 10000) && (*(ulong *)(0x4) == 0); i++ ) + ; + if (*(ulong *)(0x4) == cpu) + prom_print("ok\n"); + else { + prom_print("failed: "); + prom_print_hex(*(ulong *)0x4); + prom_print("\n"); + } + } +} + +static void __init +prom_instantiate_rtas(void) +{ + ihandle prom_rtas; + prom_arg_t result; + + prom_rtas = call_prom("finddevice", 1, 1, "/rtas"); + if (prom_rtas == -1) + return; + + rtas_size = 0; + call_prom("getprop", 4, 1, prom_rtas, + "rtas-size", &rtas_size, sizeof(rtas_size)); + prom_print("instantiating rtas"); + if (rtas_size == 0) { + rtas_data = 0; + } else { + /* + * Ask OF for some space for RTAS. + * Actually OF has bugs so we just arbitrarily + * use memory at the 6MB point. + */ + rtas_data = 6 << 20; + prom_print(" at "); + prom_print_hex(rtas_data); + } + + prom_rtas = call_prom("open", 1, 1, "/rtas"); + prom_print("..."); + rtas_entry = 0; + if (call_prom_ret("call-method", 3, 2, &result, + "instantiate-rtas", prom_rtas, rtas_data) == 0) + rtas_entry = result; + if ((rtas_entry == -1) || (rtas_entry == 0)) + prom_print(" failed\n"); + else + prom_print(" done\n"); +} + +/* + * We enter here early on, when the Open Firmware prom is still + * handling exceptions and the MMU hash table for us. + */ +unsigned long __init +prom_init(int r3, int r4, prom_entry pp) +{ + unsigned long mem; + ihandle prom_mmu; + unsigned long offset = reloc_offset(); + int i, l; + char *p, *d; + unsigned long phys; + prom_arg_t result[3]; + char model[32]; + phandle node; + int rc; + + /* Default */ + phys = (unsigned long) &_stext; + + /* First get a handle for the stdout device */ + prom = pp; + prom_chosen = call_prom("finddevice", 1, 1, "/chosen"); + if (prom_chosen == -1) + prom_exit(); + if (call_prom("getprop", 4, 1, prom_chosen, "stdout", + &prom_stdout, sizeof(prom_stdout)) <= 0) + prom_exit(); + + /* Get the full OF pathname of the stdout device */ + mem = (unsigned long) klimit + offset; + p = (char *) mem; + memset(p, 0, 256); + call_prom("instance-to-path", 3, 1, prom_stdout, p, 255); + of_stdout_device = p; + mem += strlen(p) + 1; + + /* Get the boot device and translate it to a full OF pathname. */ + p = (char *) mem; + l = call_prom("getprop", 4, 1, prom_chosen, "bootpath", p, 1<<20); + if (l > 0) { + p[l] = 0; /* should already be null-terminated */ + bootpath = PTRUNRELOC(p); + mem += l + 1; + d = (char *) mem; + *d = 0; + call_prom("canon", 3, 1, p, d, 1<<20); + bootdevice = PTRUNRELOC(d); + mem = ALIGNUL(mem + strlen(d) + 1); + } + + prom_instantiate_rtas(); + +#ifdef CONFIG_POWER4 + /* + * Find out how much memory we have and allocate a + * suitably-sized hash table. + */ + prom_alloc_htab(); +#endif + mem = check_display(mem); + + prom_print("copying OF device tree..."); + mem = copy_device_tree(mem, mem + (1<<20)); + prom_print("done\n"); + + prom_hold_cpus(mem); + + klimit = (char *) (mem - offset); + + node = call_prom("finddevice", 1, 1, "/"); + rc = call_prom("getprop", 4, 1, node, "model", model, sizeof(model)); + if (rc > 0 && !strncmp (model, "Pegasos", 7) + && strncmp (model, "Pegasos2", 8)) { + /* Pegasos 1 has a broken translate method in the OF, + * and furthermore the BATs are mapped 1:1 so the phys + * address calculated above is correct, so let's use + * it directly. + */ + } else if (offset == 0) { + /* If we are already running at 0xc0000000, we assume we were + * loaded by an OF bootloader which did set a BAT for us. + * This breaks OF translate so we force phys to be 0. + */ + prom_print("(already at 0xc0000000) phys=0\n"); + phys = 0; + } else if (call_prom("getprop", 4, 1, prom_chosen, "mmu", + &prom_mmu, sizeof(prom_mmu)) <= 0) { + prom_print(" no MMU found\n"); + } else if (call_prom_ret("call-method", 4, 4, result, "translate", + prom_mmu, &_stext, 1) != 0) { + prom_print(" (translate failed)\n"); + } else { + /* We assume the phys. address size is 3 cells */ + phys = result[2]; + } + + if (prom_disp_node != 0) + setup_disp_fake_bi(prom_disp_node); + + /* Use quiesce call to get OF to shut down any devices it's using */ + prom_print("Calling quiesce ...\n"); + call_prom("quiesce", 0, 0); + + /* Relocate various pointers which will be used once the + kernel is running at the address it was linked at. */ + for (i = 0; i < prom_num_displays; ++i) + prom_display_paths[i] = PTRUNRELOC(prom_display_paths[i]); + +#ifdef CONFIG_SERIAL_CORE_CONSOLE + /* Relocate the of stdout for console autodetection */ + of_stdout_device = PTRUNRELOC(of_stdout_device); +#endif + + prom_print("returning 0x"); + prom_print_hex(phys); + prom_print("from prom_init\n"); + prom_stdout = 0; + + return phys; +} + +/* + * early_get_property is used to access the device tree image prepared + * by BootX very early on, before the pointers in it have been relocated. + */ +static void * __init +early_get_property(unsigned long base, unsigned long node, char *prop) +{ + struct device_node *np = (struct device_node *)(base + node); + struct property *pp; + + for (pp = np->properties; pp != 0; pp = pp->next) { + pp = (struct property *) (base + (unsigned long)pp); + if (strcmp((char *)((unsigned long)pp->name + base), + prop) == 0) { + return (void *)((unsigned long)pp->value + base); + } + } + return NULL; +} + +/* Is boot-info compatible ? */ +#define BOOT_INFO_IS_COMPATIBLE(bi) ((bi)->compatible_version <= BOOT_INFO_VERSION) +#define BOOT_INFO_IS_V2_COMPATIBLE(bi) ((bi)->version >= 2) +#define BOOT_INFO_IS_V4_COMPATIBLE(bi) ((bi)->version >= 4) + +void __init +bootx_init(unsigned long r4, unsigned long phys) +{ + boot_infos_t *bi = (boot_infos_t *) r4; + unsigned long space; + unsigned long ptr, x; + char *model; + + boot_infos = PTRUNRELOC(bi); + if (!BOOT_INFO_IS_V2_COMPATIBLE(bi)) + bi->logicalDisplayBase = NULL; + +#ifdef CONFIG_BOOTX_TEXT + btext_init(bi); + + /* + * Test if boot-info is compatible. Done only in config + * CONFIG_BOOTX_TEXT since there is nothing much we can do + * with an incompatible version, except display a message + * and eventually hang the processor... + * + * I'll try to keep enough of boot-info compatible in the + * future to always allow display of this message; + */ + if (!BOOT_INFO_IS_COMPATIBLE(bi)) { + btext_drawstring(" !!! WARNING - Incompatible version of BootX !!!\n\n\n"); + btext_flushscreen(); + } +#endif /* CONFIG_BOOTX_TEXT */ + + /* New BootX enters kernel with MMU off, i/os are not allowed + here. This hack will have been done by the boostrap anyway. + */ + if (bi->version < 4) { + /* + * XXX If this is an iMac, turn off the USB controller. + */ + model = (char *) early_get_property + (r4 + bi->deviceTreeOffset, 4, "model"); + if (model + && (strcmp(model, "iMac,1") == 0 + || strcmp(model, "PowerMac1,1") == 0)) { + out_le32((unsigned *)0x80880008, 1); /* XXX */ + } + } + + /* Move klimit to enclose device tree, args, ramdisk, etc... */ + if (bi->version < 5) { + space = bi->deviceTreeOffset + bi->deviceTreeSize; + if (bi->ramDisk) + space = bi->ramDisk + bi->ramDiskSize; + } else + space = bi->totalParamsSize; + klimit = PTRUNRELOC((char *) bi + space); + + /* New BootX will have flushed all TLBs and enters kernel with + MMU switched OFF, so this should not be useful anymore. + */ + if (bi->version < 4) { + /* + * Touch each page to make sure the PTEs for them + * are in the hash table - the aim is to try to avoid + * getting DSI exceptions while copying the kernel image. + */ + for (ptr = ((unsigned long) &_stext) & PAGE_MASK; + ptr < (unsigned long)bi + space; ptr += PAGE_SIZE) + x = *(volatile unsigned long *)ptr; + } + +#ifdef CONFIG_BOOTX_TEXT + /* + * Note that after we call btext_prepare_BAT, we can't do + * prom_draw*, flushscreen or clearscreen until we turn the MMU + * on, since btext_prepare_BAT sets disp_bi.logicalDisplayBase + * to a virtual address. + */ + btext_prepare_BAT(); +#endif +} diff --git a/arch/ppc/syslib/qspan_pci.c b/arch/ppc/syslib/qspan_pci.c new file mode 100644 index 00000000000..57f4ed5e5ae --- /dev/null +++ b/arch/ppc/syslib/qspan_pci.c @@ -0,0 +1,381 @@ +/* + * QSpan pci routines. + * Most 8xx boards use the QSpan PCI bridge. The config address register + * is located 0x500 from the base of the bridge control/status registers. + * The data register is located at 0x504. + * This is a two step operation. First, the address register is written, + * then the data register is read/written as required. + * I don't know what to do about interrupts (yet). + * + * The RPX Classic implementation shares a chip select for normal + * PCI access and QSpan control register addresses. The selection is + * further selected by a bit setting in a board control register. + * Although it should happen, we disable interrupts during this operation + * to make sure some driver doesn't accidentally access the PCI while + * we have switched the chip select. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +/* + * This blows...... + * When reading the configuration space, if something does not respond + * the bus times out and we get a machine check interrupt. So, the + * good ol' exception tables come to mind to trap it and return some + * value. + * + * On an error we just return a -1, since that is what the caller wants + * returned if nothing is present. I copied this from __get_user_asm, + * with the only difference of returning -1 instead of EFAULT. + * There is an associated hack in the machine check trap code. + * + * The QSPAN is also a big endian device, that is it makes the PCI + * look big endian to us. This presents a problem for the Linux PCI + * functions, which assume little endian. For example, we see the + * first 32-bit word like this: + * ------------------------ + * | Device ID | Vendor ID | + * ------------------------ + * If we read/write as a double word, that's OK. But in our world, + * when read as a word, device ID is at location 0, not location 2 as + * the little endian PCI would believe. We have to switch bits in + * the PCI addresses given to us to get the data to/from the correct + * byte lanes. + * + * The QSPAN only supports 4 bits of "slot" in the dev_fn instead of 5. + * It always forces the MS bit to zero. Therefore, dev_fn values + * greater than 128 are returned as "no device found" errors. + * + * The QSPAN can only perform long word (32-bit) configuration cycles. + * The "offset" must have the two LS bits set to zero. Read operations + * require we read the entire word and then sort out what should be + * returned. Write operations other than long word require that we + * read the long word, update the proper word or byte, then write the + * entire long word back. + * + * PCI Bridge hack. We assume (correctly) that bus 0 is the primary + * PCI bus from the QSPAN. If we are called with a bus number other + * than zero, we create a Type 1 configuration access that a downstream + * PCI bridge will interpret. + */ + +#define __get_qspan_pci_config(x, addr, op) \ + __asm__ __volatile__( \ + "1: "op" %0,0(%1)\n" \ + " eieio\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: li %0,-1\n" \ + " b 2b\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 2\n" \ + " .long 1b,3b\n" \ + ".text" \ + : "=r"(x) : "r"(addr) : " %0") + +#define QS_CONFIG_ADDR ((volatile uint *)(PCI_CSR_ADDR + 0x500)) +#define QS_CONFIG_DATA ((volatile uint *)(PCI_CSR_ADDR + 0x504)) + +#define mk_config_addr(bus, dev, offset) \ + (((bus)<<16) | ((dev)<<8) | (offset & 0xfc)) + +#define mk_config_type1(bus, dev, offset) \ + mk_config_addr(bus, dev, offset) | 1; + +static spinlock_t pcibios_lock = SPIN_LOCK_UNLOCKED; + +int qspan_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char *val) +{ + uint temp; + u_char *cp; +#ifdef CONFIG_RPXCLASSIC + unsigned long flags; +#endif + + if ((bus > 7) || (dev_fn > 127)) { + *val = 0xff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + +#ifdef CONFIG_RPXCLASSIC + /* disable interrupts */ + spin_lock_irqsave(&pcibios_lock, flags); + *((uint *)RPX_CSR_ADDR) &= ~BCSR2_QSPACESEL; + eieio(); +#endif + + if (bus == 0) + *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); + else + *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); + __get_qspan_pci_config(temp, QS_CONFIG_DATA, "lwz"); + +#ifdef CONFIG_RPXCLASSIC + *((uint *)RPX_CSR_ADDR) |= BCSR2_QSPACESEL; + eieio(); + spin_unlock_irqrestore(&pcibios_lock, flags); +#endif + + offset ^= 0x03; + cp = ((u_char *)&temp) + (offset & 0x03); + *val = *cp; + return PCIBIOS_SUCCESSFUL; +} + +int qspan_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val) +{ + uint temp; + ushort *sp; +#ifdef CONFIG_RPXCLASSIC + unsigned long flags; +#endif + + if ((bus > 7) || (dev_fn > 127)) { + *val = 0xffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + +#ifdef CONFIG_RPXCLASSIC + /* disable interrupts */ + spin_lock_irqsave(&pcibios_lock, flags); + *((uint *)RPX_CSR_ADDR) &= ~BCSR2_QSPACESEL; + eieio(); +#endif + + if (bus == 0) + *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); + else + *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); + __get_qspan_pci_config(temp, QS_CONFIG_DATA, "lwz"); + offset ^= 0x02; + +#ifdef CONFIG_RPXCLASSIC + *((uint *)RPX_CSR_ADDR) |= BCSR2_QSPACESEL; + eieio(); + spin_unlock_irqrestore(&pcibios_lock, flags); +#endif + + sp = ((ushort *)&temp) + ((offset >> 1) & 1); + *val = *sp; + return PCIBIOS_SUCCESSFUL; +} + +int qspan_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val) +{ +#ifdef CONFIG_RPXCLASSIC + unsigned long flags; +#endif + + if ((bus > 7) || (dev_fn > 127)) { + *val = 0xffffffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + +#ifdef CONFIG_RPXCLASSIC + /* disable interrupts */ + spin_lock_irqsave(&pcibios_lock, flags); + *((uint *)RPX_CSR_ADDR) &= ~BCSR2_QSPACESEL; + eieio(); +#endif + + if (bus == 0) + *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); + else + *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); + __get_qspan_pci_config(*val, QS_CONFIG_DATA, "lwz"); + +#ifdef CONFIG_RPXCLASSIC + *((uint *)RPX_CSR_ADDR) |= BCSR2_QSPACESEL; + eieio(); + spin_unlock_irqrestore(&pcibios_lock, flags); +#endif + + return PCIBIOS_SUCCESSFUL; +} + +int qspan_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val) +{ + uint temp; + u_char *cp; +#ifdef CONFIG_RPXCLASSIC + unsigned long flags; +#endif + + if ((bus > 7) || (dev_fn > 127)) + return PCIBIOS_DEVICE_NOT_FOUND; + + qspan_pcibios_read_config_dword(bus, dev_fn, offset, &temp); + + offset ^= 0x03; + cp = ((u_char *)&temp) + (offset & 0x03); + *cp = val; + +#ifdef CONFIG_RPXCLASSIC + /* disable interrupts */ + spin_lock_irqsave(&pcibios_lock, flags); + *((uint *)RPX_CSR_ADDR) &= ~BCSR2_QSPACESEL; + eieio(); +#endif + + if (bus == 0) + *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); + else + *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); + *QS_CONFIG_DATA = temp; + +#ifdef CONFIG_RPXCLASSIC + *((uint *)RPX_CSR_ADDR) |= BCSR2_QSPACESEL; + eieio(); + spin_unlock_irqrestore(&pcibios_lock, flags); +#endif + + return PCIBIOS_SUCCESSFUL; +} + +int qspan_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val) +{ + uint temp; + ushort *sp; +#ifdef CONFIG_RPXCLASSIC + unsigned long flags; +#endif + + if ((bus > 7) || (dev_fn > 127)) + return PCIBIOS_DEVICE_NOT_FOUND; + + qspan_pcibios_read_config_dword(bus, dev_fn, offset, &temp); + + offset ^= 0x02; + sp = ((ushort *)&temp) + ((offset >> 1) & 1); + *sp = val; + +#ifdef CONFIG_RPXCLASSIC + /* disable interrupts */ + spin_lock_irqsave(&pcibios_lock, flags); + *((uint *)RPX_CSR_ADDR) &= ~BCSR2_QSPACESEL; + eieio(); +#endif + + if (bus == 0) + *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); + else + *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); + *QS_CONFIG_DATA = temp; + +#ifdef CONFIG_RPXCLASSIC + *((uint *)RPX_CSR_ADDR) |= BCSR2_QSPACESEL; + eieio(); + spin_unlock_irqrestore(&pcibios_lock, flags); +#endif + + return PCIBIOS_SUCCESSFUL; +} + +int qspan_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val) +{ +#ifdef CONFIG_RPXCLASSIC + unsigned long flags; +#endif + + if ((bus > 7) || (dev_fn > 127)) + return PCIBIOS_DEVICE_NOT_FOUND; + +#ifdef CONFIG_RPXCLASSIC + /* disable interrupts */ + spin_lock_irqsave(&pcibios_lock, flags); + *((uint *)RPX_CSR_ADDR) &= ~BCSR2_QSPACESEL; + eieio(); +#endif + + if (bus == 0) + *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); + else + *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); + *(unsigned int *)QS_CONFIG_DATA = val; + +#ifdef CONFIG_RPXCLASSIC + *((uint *)RPX_CSR_ADDR) |= BCSR2_QSPACESEL; + eieio(); + spin_unlock_irqrestore(&pcibios_lock, flags); +#endif + + return PCIBIOS_SUCCESSFUL; +} + +int qspan_pcibios_find_device(unsigned short vendor, unsigned short dev_id, + unsigned short index, unsigned char *bus_ptr, + unsigned char *dev_fn_ptr) +{ + int num, devfn; + unsigned int x, vendev; + + if (vendor == 0xffff) + return PCIBIOS_BAD_VENDOR_ID; + vendev = (dev_id << 16) + vendor; + num = 0; + for (devfn = 0; devfn < 32; devfn++) { + qspan_pcibios_read_config_dword(0, devfn<<3, PCI_VENDOR_ID, &x); + if (x == vendev) { + if (index == num) { + *bus_ptr = 0; + *dev_fn_ptr = devfn<<3; + return PCIBIOS_SUCCESSFUL; + } + ++num; + } + } + return PCIBIOS_DEVICE_NOT_FOUND; +} + +int qspan_pcibios_find_class(unsigned int class_code, unsigned short index, + unsigned char *bus_ptr, unsigned char *dev_fn_ptr) +{ + int devnr, x, num; + + num = 0; + for (devnr = 0; devnr < 32; devnr++) { + qspan_pcibios_read_config_dword(0, devnr<<3, PCI_CLASS_REVISION, &x); + if ((x>>8) == class_code) { + if (index == num) { + *bus_ptr = 0; + *dev_fn_ptr = devnr<<3; + return PCIBIOS_SUCCESSFUL; + } + ++num; + } + } + return PCIBIOS_DEVICE_NOT_FOUND; +} + +void __init +m8xx_pcibios_fixup(void)) +{ + /* Lots to do here, all board and configuration specific. */ +} + +void __init +m8xx_setup_pci_ptrs(void)) +{ + set_config_access_method(qspan); + + ppc_md.pcibios_fixup = m8xx_pcibios_fixup; +} + diff --git a/arch/ppc/syslib/todc_time.c b/arch/ppc/syslib/todc_time.c new file mode 100644 index 00000000000..1323c641c19 --- /dev/null +++ b/arch/ppc/syslib/todc_time.c @@ -0,0 +1,513 @@ +/* + * arch/ppc/syslib/todc_time.c + * + * Time of Day Clock support for the M48T35, M48T37, M48T59, and MC146818 + * Real Time Clocks/Timekeepers. + * + * Author: Mark A. Greer + * mgreer@mvista.com + * + * 2001-2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * Depending on the hardware on your board and your board design, the + * RTC/NVRAM may be accessed either directly (like normal memory) or via + * address/data registers. If your board uses the direct method, set + * 'nvram_data' to the base address of your nvram and leave 'nvram_as0' and + * 'nvram_as1' NULL. If your board uses address/data regs to access nvram, + * set 'nvram_as0' to the address of the lower byte, set 'nvram_as1' to the + * address of the upper byte (leave NULL if using mc146818), and set + * 'nvram_data' to the address of the 8-bit data register. + * + * In order to break the assumption that the RTC and NVRAM are accessed by + * the same mechanism, you need to explicitly set 'ppc_md.rtc_read_val' and + * 'ppc_md.rtc_write_val', otherwise the values of 'ppc_md.rtc_read_val' + * and 'ppc_md.rtc_write_val' will be used. + * + * Note: Even though the documentation for the various RTC chips say that it + * take up to a second before it starts updating once the 'R' bit is + * cleared, they always seem to update even though we bang on it many + * times a second. This is true, except for the Dallas Semi 1746/1747 + * (possibly others). Those chips seem to have a real problem whenever + * we set the 'R' bit before reading them, they basically stop counting. + * --MAG + */ + +/* + * 'todc_info' should be initialized in your *_setup.c file to + * point to a fully initialized 'todc_info_t' structure. + * This structure holds all the register offsets for your particular + * TODC/RTC chip. + * TODC_ALLOC()/TODC_INIT() will allocate and initialize this table for you. + */ + +#ifdef RTC_FREQ_SELECT +#undef RTC_FREQ_SELECT +#define RTC_FREQ_SELECT control_b /* Register A */ +#endif + +#ifdef RTC_CONTROL +#undef RTC_CONTROL +#define RTC_CONTROL control_a /* Register B */ +#endif + +#ifdef RTC_INTR_FLAGS +#undef RTC_INTR_FLAGS +#define RTC_INTR_FLAGS watchdog /* Register C */ +#endif + +#ifdef RTC_VALID +#undef RTC_VALID +#define RTC_VALID interrupts /* Register D */ +#endif + +/* Access routines when RTC accessed directly (like normal memory) */ +u_char +todc_direct_read_val(int addr) +{ + return readb((void __iomem *)(todc_info->nvram_data + addr)); +} + +void +todc_direct_write_val(int addr, unsigned char val) +{ + writeb(val, (void __iomem *)(todc_info->nvram_data + addr)); + return; +} + +/* Access routines for accessing m48txx type chips via addr/data regs */ +u_char +todc_m48txx_read_val(int addr) +{ + outb(addr, todc_info->nvram_as0); + outb(addr>>todc_info->as0_bits, todc_info->nvram_as1); + return inb(todc_info->nvram_data); +} + +void +todc_m48txx_write_val(int addr, unsigned char val) +{ + outb(addr, todc_info->nvram_as0); + outb(addr>>todc_info->as0_bits, todc_info->nvram_as1); + outb(val, todc_info->nvram_data); + return; +} + +/* Access routines for accessing mc146818 type chips via addr/data regs */ +u_char +todc_mc146818_read_val(int addr) +{ + outb_p(addr, todc_info->nvram_as0); + return inb_p(todc_info->nvram_data); +} + +void +todc_mc146818_write_val(int addr, unsigned char val) +{ + outb_p(addr, todc_info->nvram_as0); + outb_p(val, todc_info->nvram_data); +} + + +/* + * Routines to make RTC chips with NVRAM buried behind an addr/data pair + * have the NVRAM and clock regs appear at the same level. + * The NVRAM will appear to start at addr 0 and the clock regs will appear + * to start immediately after the NVRAM (actually, start at offset + * todc_info->nvram_size). + */ +static inline u_char +todc_read_val(int addr) +{ + u_char val; + + if (todc_info->sw_flags & TODC_FLAG_2_LEVEL_NVRAM) { + if (addr < todc_info->nvram_size) { /* NVRAM */ + ppc_md.rtc_write_val(todc_info->nvram_addr_reg, addr); + val = ppc_md.rtc_read_val(todc_info->nvram_data_reg); + } + else { /* Clock Reg */ + addr -= todc_info->nvram_size; + val = ppc_md.rtc_read_val(addr); + } + } + else { + val = ppc_md.rtc_read_val(addr); + } + + return val; +} + +static inline void +todc_write_val(int addr, u_char val) +{ + if (todc_info->sw_flags & TODC_FLAG_2_LEVEL_NVRAM) { + if (addr < todc_info->nvram_size) { /* NVRAM */ + ppc_md.rtc_write_val(todc_info->nvram_addr_reg, addr); + ppc_md.rtc_write_val(todc_info->nvram_data_reg, val); + } + else { /* Clock Reg */ + addr -= todc_info->nvram_size; + ppc_md.rtc_write_val(addr, val); + } + } + else { + ppc_md.rtc_write_val(addr, val); + } +} + +/* + * TODC routines + * + * There is some ugly stuff in that there are assumptions for the mc146818. + * + * Assumptions: + * - todc_info->control_a has the offset as mc146818 Register B reg + * - todc_info->control_b has the offset as mc146818 Register A reg + * - m48txx control reg's write enable or 'W' bit is same as + * mc146818 Register B 'SET' bit (i.e., 0x80) + * + * These assumptions were made to make the code simpler. + */ +long __init +todc_time_init(void) +{ + u_char cntl_b; + + if (!ppc_md.rtc_read_val) + ppc_md.rtc_read_val = ppc_md.nvram_read_val; + if (!ppc_md.rtc_write_val) + ppc_md.rtc_write_val = ppc_md.nvram_write_val; + + cntl_b = todc_read_val(todc_info->control_b); + + if (todc_info->rtc_type == TODC_TYPE_MC146818) { + if ((cntl_b & 0x70) != 0x20) { + printk(KERN_INFO "TODC %s %s\n", + "real-time-clock was stopped.", + "Now starting..."); + cntl_b &= ~0x70; + cntl_b |= 0x20; + } + + todc_write_val(todc_info->control_b, cntl_b); + } else if (todc_info->rtc_type == TODC_TYPE_DS17285) { + u_char mode; + + mode = todc_read_val(TODC_TYPE_DS17285_CNTL_A); + /* Make sure countdown clear is not set */ + mode &= ~0x40; + /* Enable oscillator, extended register set */ + mode |= 0x30; + todc_write_val(TODC_TYPE_DS17285_CNTL_A, mode); + + } else if (todc_info->rtc_type == TODC_TYPE_DS1501) { + u_char month; + + todc_info->enable_read = TODC_DS1501_CNTL_B_TE; + todc_info->enable_write = TODC_DS1501_CNTL_B_TE; + + month = todc_read_val(todc_info->month); + + if ((month & 0x80) == 0x80) { + printk(KERN_INFO "TODC %s %s\n", + "real-time-clock was stopped.", + "Now starting..."); + month &= ~0x80; + todc_write_val(todc_info->month, month); + } + + cntl_b &= ~TODC_DS1501_CNTL_B_TE; + todc_write_val(todc_info->control_b, cntl_b); + } else { /* must be a m48txx type */ + u_char cntl_a; + + todc_info->enable_read = TODC_MK48TXX_CNTL_A_R; + todc_info->enable_write = TODC_MK48TXX_CNTL_A_W; + + cntl_a = todc_read_val(todc_info->control_a); + + /* Check & clear STOP bit in control B register */ + if (cntl_b & TODC_MK48TXX_DAY_CB) { + printk(KERN_INFO "TODC %s %s\n", + "real-time-clock was stopped.", + "Now starting..."); + + cntl_a |= todc_info->enable_write; + cntl_b &= ~TODC_MK48TXX_DAY_CB;/* Start Oscil */ + + todc_write_val(todc_info->control_a, cntl_a); + todc_write_val(todc_info->control_b, cntl_b); + } + + /* Make sure READ & WRITE bits are cleared. */ + cntl_a &= ~(todc_info->enable_write | + todc_info->enable_read); + todc_write_val(todc_info->control_a, cntl_a); + } + + return 0; +} + +/* + * There is some ugly stuff in that there are assumptions that for a mc146818, + * the todc_info->control_a has the offset of the mc146818 Register B reg and + * that the register'ss 'SET' bit is the same as the m48txx's write enable + * bit in the control register of the m48txx (i.e., 0x80). + * + * It was done to make the code look simpler. + */ +ulong +todc_get_rtc_time(void) +{ + uint year = 0, mon = 0, day = 0, hour = 0, min = 0, sec = 0; + uint limit, i; + u_char save_control, uip = 0; + + spin_lock(&rtc_lock); + save_control = todc_read_val(todc_info->control_a); + + if (todc_info->rtc_type != TODC_TYPE_MC146818) { + limit = 1; + + switch (todc_info->rtc_type) { + case TODC_TYPE_DS1553: + case TODC_TYPE_DS1557: + case TODC_TYPE_DS1743: + case TODC_TYPE_DS1746: /* XXXX BAD HACK -> FIX */ + case TODC_TYPE_DS1747: + case TODC_TYPE_DS17285: + break; + default: + todc_write_val(todc_info->control_a, + (save_control | todc_info->enable_read)); + } + } + else { + limit = 100000000; + } + + for (i=0; irtc_type == TODC_TYPE_MC146818) { + uip = todc_read_val(todc_info->RTC_FREQ_SELECT); + } + + sec = todc_read_val(todc_info->seconds) & 0x7f; + min = todc_read_val(todc_info->minutes) & 0x7f; + hour = todc_read_val(todc_info->hours) & 0x3f; + day = todc_read_val(todc_info->day_of_month) & 0x3f; + mon = todc_read_val(todc_info->month) & 0x1f; + year = todc_read_val(todc_info->year) & 0xff; + + if (todc_info->rtc_type == TODC_TYPE_MC146818) { + uip |= todc_read_val(todc_info->RTC_FREQ_SELECT); + if ((uip & RTC_UIP) == 0) break; + } + } + + if (todc_info->rtc_type != TODC_TYPE_MC146818) { + switch (todc_info->rtc_type) { + case TODC_TYPE_DS1553: + case TODC_TYPE_DS1557: + case TODC_TYPE_DS1743: + case TODC_TYPE_DS1746: /* XXXX BAD HACK -> FIX */ + case TODC_TYPE_DS1747: + case TODC_TYPE_DS17285: + break; + default: + save_control &= ~(todc_info->enable_read); + todc_write_val(todc_info->control_a, + save_control); + } + } + spin_unlock(&rtc_lock); + + if ((todc_info->rtc_type != TODC_TYPE_MC146818) || + ((save_control & RTC_DM_BINARY) == 0) || + RTC_ALWAYS_BCD) { + + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year); + } + + year = year + 1900; + if (year < 1970) { + year += 100; + } + + return mktime(year, mon, day, hour, min, sec); +} + +int +todc_set_rtc_time(unsigned long nowtime) +{ + struct rtc_time tm; + u_char save_control, save_freq_select = 0; + + spin_lock(&rtc_lock); + to_tm(nowtime, &tm); + + save_control = todc_read_val(todc_info->control_a); + + /* Assuming MK48T59_RTC_CA_WRITE & RTC_SET are equal */ + todc_write_val(todc_info->control_a, + (save_control | todc_info->enable_write)); + save_control &= ~(todc_info->enable_write); /* in case it was set */ + + if (todc_info->rtc_type == TODC_TYPE_MC146818) { + save_freq_select = todc_read_val(todc_info->RTC_FREQ_SELECT); + todc_write_val(todc_info->RTC_FREQ_SELECT, + save_freq_select | RTC_DIV_RESET2); + } + + + tm.tm_year = (tm.tm_year - 1900) % 100; + + if ((todc_info->rtc_type != TODC_TYPE_MC146818) || + ((save_control & RTC_DM_BINARY) == 0) || + RTC_ALWAYS_BCD) { + + BIN_TO_BCD(tm.tm_sec); + BIN_TO_BCD(tm.tm_min); + BIN_TO_BCD(tm.tm_hour); + BIN_TO_BCD(tm.tm_mon); + BIN_TO_BCD(tm.tm_mday); + BIN_TO_BCD(tm.tm_year); + } + + todc_write_val(todc_info->seconds, tm.tm_sec); + todc_write_val(todc_info->minutes, tm.tm_min); + todc_write_val(todc_info->hours, tm.tm_hour); + todc_write_val(todc_info->month, tm.tm_mon); + todc_write_val(todc_info->day_of_month, tm.tm_mday); + todc_write_val(todc_info->year, tm.tm_year); + + todc_write_val(todc_info->control_a, save_control); + + if (todc_info->rtc_type == TODC_TYPE_MC146818) { + todc_write_val(todc_info->RTC_FREQ_SELECT, save_freq_select); + } + spin_unlock(&rtc_lock); + + return 0; +} + +/* + * Manipulates read bit to reliably read seconds at a high rate. + */ +static unsigned char __init todc_read_timereg(int addr) +{ + unsigned char save_control = 0, val; + + switch (todc_info->rtc_type) { + case TODC_TYPE_DS1553: + case TODC_TYPE_DS1557: + case TODC_TYPE_DS1746: /* XXXX BAD HACK -> FIX */ + case TODC_TYPE_DS1747: + case TODC_TYPE_DS17285: + case TODC_TYPE_MC146818: + break; + default: + save_control = todc_read_val(todc_info->control_a); + todc_write_val(todc_info->control_a, + (save_control | todc_info->enable_read)); + } + val = todc_read_val(addr); + + switch (todc_info->rtc_type) { + case TODC_TYPE_DS1553: + case TODC_TYPE_DS1557: + case TODC_TYPE_DS1746: /* XXXX BAD HACK -> FIX */ + case TODC_TYPE_DS1747: + case TODC_TYPE_DS17285: + case TODC_TYPE_MC146818: + break; + default: + save_control &= ~(todc_info->enable_read); + todc_write_val(todc_info->control_a, save_control); + } + + return val; +} + +/* + * This was taken from prep_setup.c + * Use the NVRAM RTC to time a second to calibrate the decrementer. + */ +void __init +todc_calibrate_decr(void) +{ + ulong freq; + ulong tbl, tbu; + long i, loop_count; + u_char sec; + + todc_time_init(); + + /* + * Actually this is bad for precision, we should have a loop in + * which we only read the seconds counter. todc_read_val writes + * the address bytes on every call and this takes a lot of time. + * Perhaps an nvram_wait_change method returning a time + * stamp with a loop count as parameter would be the solution. + */ + /* + * Need to make sure the tbl doesn't roll over so if tbu increments + * during this test, we need to do it again. + */ + loop_count = 0; + + sec = todc_read_timereg(todc_info->seconds) & 0x7f; + + do { + tbu = get_tbu(); + + for (i = 0 ; i < 10000000 ; i++) {/* may take up to 1 second */ + tbl = get_tbl(); + + if ((todc_read_timereg(todc_info->seconds) & 0x7f) != sec) { + break; + } + } + + sec = todc_read_timereg(todc_info->seconds) & 0x7f; + + for (i = 0 ; i < 10000000 ; i++) { /* Should take 1 second */ + freq = get_tbl(); + + if ((todc_read_timereg(todc_info->seconds) & 0x7f) != sec) { + break; + } + } + + freq -= tbl; + } while ((get_tbu() != tbu) && (++loop_count < 2)); + + printk("time_init: decrementer frequency = %lu.%.6lu MHz\n", + freq/1000000, freq%1000000); + + tb_ticks_per_jiffy = freq / HZ; + tb_to_us = mulhwu_scale_factor(freq, 1000000); + + return; +} diff --git a/arch/ppc/syslib/xilinx_pic.c b/arch/ppc/syslib/xilinx_pic.c new file mode 100644 index 00000000000..e0bd66f0847 --- /dev/null +++ b/arch/ppc/syslib/xilinx_pic.c @@ -0,0 +1,157 @@ +/* + * arch/ppc/syslib/xilinx_pic.c + * + * Interrupt controller driver for Xilinx Virtex-II Pro. + * + * Author: MontaVista Software, Inc. + * source@mvista.com + * + * 2002-2004 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include + +/* No one else should require these constants, so define them locally here. */ +#define ISR 0 /* Interrupt Status Register */ +#define IPR 1 /* Interrupt Pending Register */ +#define IER 2 /* Interrupt Enable Register */ +#define IAR 3 /* Interrupt Acknowledge Register */ +#define SIE 4 /* Set Interrupt Enable bits */ +#define CIE 5 /* Clear Interrupt Enable bits */ +#define IVR 6 /* Interrupt Vector Register */ +#define MER 7 /* Master Enable Register */ + +#if XPAR_XINTC_USE_DCR == 0 +static volatile u32 *intc; +#define intc_out_be32(addr, mask) out_be32((addr), (mask)) +#define intc_in_be32(addr) in_be32((addr)) +#else +#define intc XPAR_INTC_0_BASEADDR +#define intc_out_be32(addr, mask) mtdcr((addr), (mask)) +#define intc_in_be32(addr) mfdcr((addr)) +#endif + +static void +xilinx_intc_enable(unsigned int irq) +{ + unsigned long mask = (0x00000001 << (irq & 31)); + pr_debug("enable: %d\n", irq); + intc_out_be32(intc + SIE, mask); +} + +static void +xilinx_intc_disable(unsigned int irq) +{ + unsigned long mask = (0x00000001 << (irq & 31)); + pr_debug("disable: %d\n", irq); + intc_out_be32(intc + CIE, mask); +} + +static void +xilinx_intc_disable_and_ack(unsigned int irq) +{ + unsigned long mask = (0x00000001 << (irq & 31)); + pr_debug("disable_and_ack: %d\n", irq); + intc_out_be32(intc + CIE, mask); + if (!(irq_desc[irq].status & IRQ_LEVEL)) + intc_out_be32(intc + IAR, mask); /* ack edge triggered intr */ +} + +static void +xilinx_intc_end(unsigned int irq) +{ + unsigned long mask = (0x00000001 << (irq & 31)); + + pr_debug("end: %d\n", irq); + if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) { + intc_out_be32(intc + SIE, mask); + /* ack level sensitive intr */ + if (irq_desc[irq].status & IRQ_LEVEL) + intc_out_be32(intc + IAR, mask); + } +} + +static struct hw_interrupt_type xilinx_intc = { + "Xilinx Interrupt Controller", + NULL, + NULL, + xilinx_intc_enable, + xilinx_intc_disable, + xilinx_intc_disable_and_ack, + xilinx_intc_end, + 0 +}; + +int +xilinx_pic_get_irq(struct pt_regs *regs) +{ + int irq; + + /* + * NOTE: This function is the one that needs to be improved in + * order to handle multiple interrupt controllers. It currently + * is hardcoded to check for interrupts only on the first INTC. + */ + + irq = intc_in_be32(intc + IVR); + if (irq != -1) + irq = irq; + + pr_debug("get_irq: %d\n", irq); + + return (irq); +} + +void __init +ppc4xx_pic_init(void) +{ + int i; + + /* + * NOTE: The assumption here is that NR_IRQS is 32 or less + * (NR_IRQS is 32 for PowerPC 405 cores by default). + */ +#if (NR_IRQS > 32) +#error NR_IRQS > 32 not supported +#endif + +#if XPAR_XINTC_USE_DCR == 0 + intc = ioremap(XPAR_INTC_0_BASEADDR, 32); + + printk(KERN_INFO "Xilinx INTC #0 at 0x%08lX mapped to 0x%08lX\n", + (unsigned long) XPAR_INTC_0_BASEADDR, (unsigned long) intc); +#else + printk(KERN_INFO "Xilinx INTC #0 at 0x%08lX (DCR)\n", + (unsigned long) XPAR_INTC_0_BASEADDR); +#endif + + /* + * Disable all external interrupts until they are + * explicity requested. + */ + intc_out_be32(intc + IER, 0); + + /* Acknowledge any pending interrupts just in case. */ + intc_out_be32(intc + IAR, ~(u32) 0); + + /* Turn on the Master Enable. */ + intc_out_be32(intc + MER, 0x3UL); + + ppc_md.get_irq = xilinx_pic_get_irq; + + for (i = 0; i < NR_IRQS; ++i) { + irq_desc[i].handler = &xilinx_intc; + + if (XPAR_INTC_0_KIND_OF_INTR & (0x00000001 << i)) + irq_desc[i].status &= ~IRQ_LEVEL; + else + irq_desc[i].status |= IRQ_LEVEL; + } +} diff --git a/arch/ppc/xmon/Makefile b/arch/ppc/xmon/Makefile new file mode 100644 index 00000000000..9aa260b926f --- /dev/null +++ b/arch/ppc/xmon/Makefile @@ -0,0 +1,8 @@ +# Makefile for xmon + +ifdef CONFIG_8xx +obj-y := start_8xx.o +else +obj-y := start.o +endif +obj-y += xmon.o ppc-dis.o ppc-opc.o subr_prf.o setjmp.o diff --git a/arch/ppc/xmon/adb.c b/arch/ppc/xmon/adb.c new file mode 100644 index 00000000000..e91384dccca --- /dev/null +++ b/arch/ppc/xmon/adb.c @@ -0,0 +1,212 @@ +/* + * Copyright (C) 1996 Paul Mackerras. + */ +#include "nonstdio.h" +#include "privinst.h" + +#define scanhex xmon_scanhex +#define skipbl xmon_skipbl + +#define ADB_B (*(volatile unsigned char *)0xf3016000) +#define ADB_SR (*(volatile unsigned char *)0xf3017400) +#define ADB_ACR (*(volatile unsigned char *)0xf3017600) +#define ADB_IFR (*(volatile unsigned char *)0xf3017a00) + +static inline void eieio(void) { asm volatile ("eieio" : :); } + +#define N_ADB_LOG 1000 +struct adb_log { + unsigned char b; + unsigned char ifr; + unsigned char acr; + unsigned int time; +} adb_log[N_ADB_LOG]; +int n_adb_log; + +void +init_adb_log(void) +{ + adb_log[0].b = ADB_B; + adb_log[0].ifr = ADB_IFR; + adb_log[0].acr = ADB_ACR; + adb_log[0].time = get_dec(); + n_adb_log = 0; +} + +void +dump_adb_log(void) +{ + unsigned t, t0; + struct adb_log *ap; + int i; + + ap = adb_log; + t0 = ap->time; + for (i = 0; i <= n_adb_log; ++i, ++ap) { + t = t0 - ap->time; + printf("b=%x ifr=%x acr=%x at %d.%.7d\n", ap->b, ap->ifr, ap->acr, + t / 1000000000, (t % 1000000000) / 100); + } +} + +void +adb_chklog(void) +{ + struct adb_log *ap = &adb_log[n_adb_log + 1]; + + ap->b = ADB_B; + ap->ifr = ADB_IFR; + ap->acr = ADB_ACR; + if (ap->b != ap[-1].b || (ap->ifr & 4) != (ap[-1].ifr & 4) + || ap->acr != ap[-1].acr) { + ap->time = get_dec(); + ++n_adb_log; + } +} + +int +adb_bitwait(int bmask, int bval, int fmask, int fval) +{ + int i; + struct adb_log *ap; + + for (i = 10000; i > 0; --i) { + adb_chklog(); + ap = &adb_log[n_adb_log]; + if ((ap->b & bmask) == bval && (ap->ifr & fmask) == fval) + return 0; + } + return -1; +} + +int +adb_wait(void) +{ + if (adb_bitwait(0, 0, 4, 4) < 0) { + printf("adb: ready wait timeout\n"); + return -1; + } + return 0; +} + +void +adb_readin(void) +{ + int i, j; + unsigned char d[64]; + + if (ADB_B & 8) { + printf("ADB_B: %x\n", ADB_B); + return; + } + i = 0; + adb_wait(); + j = ADB_SR; + eieio(); + ADB_B &= ~0x20; + eieio(); + for (;;) { + if (adb_wait() < 0) + break; + d[i++] = ADB_SR; + eieio(); + if (ADB_B & 8) + break; + ADB_B ^= 0x10; + eieio(); + } + ADB_B |= 0x30; + if (adb_wait() == 0) + j = ADB_SR; + for (j = 0; j < i; ++j) + printf("%.2x ", d[j]); + printf("\n"); +} + +int +adb_write(unsigned char *d, int i) +{ + int j; + unsigned x; + + if ((ADB_B & 8) == 0) { + printf("r: "); + adb_readin(); + } + for (;;) { + ADB_ACR = 0x1c; + eieio(); + ADB_SR = d[0]; + eieio(); + ADB_B &= ~0x20; + eieio(); + if (ADB_B & 8) + break; + ADB_ACR = 0xc; + eieio(); + ADB_B |= 0x20; + eieio(); + adb_readin(); + } + adb_wait(); + for (j = 1; j < i; ++j) { + ADB_SR = d[j]; + eieio(); + ADB_B ^= 0x10; + eieio(); + if (adb_wait() < 0) + break; + } + ADB_ACR = 0xc; + eieio(); + x = ADB_SR; + eieio(); + ADB_B |= 0x30; + return j; +} + +void +adbcmds(void) +{ + char cmd; + unsigned rtcu, rtcl, dec, pdec, x; + int i, j; + unsigned char d[64]; + + cmd = skipbl(); + switch (cmd) { + case 't': + for (;;) { + rtcl = get_rtcl(); + rtcu = get_rtcu(); + dec = get_dec(); + printf("rtc u=%u l=%u dec=%x (%d = %d.%.7d)\n", + rtcu, rtcl, dec, pdec - dec, (pdec - dec) / 1000000000, + ((pdec - dec) % 1000000000) / 100); + pdec = dec; + if (cmd == 'x') + break; + while (xmon_read(stdin, &cmd, 1) != 1) + ; + } + break; + case 'r': + init_adb_log(); + while (adb_bitwait(8, 0, 0, 0) == 0) + adb_readin(); + break; + case 'w': + i = 0; + while (scanhex(&x)) + d[i++] = x; + init_adb_log(); + j = adb_write(d, i); + printf("sent %d bytes\n", j); + while (adb_bitwait(8, 0, 0, 0) == 0) + adb_readin(); + break; + case 'l': + dump_adb_log(); + break; + } +} diff --git a/arch/ppc/xmon/ansidecl.h b/arch/ppc/xmon/ansidecl.h new file mode 100644 index 00000000000..c9b9f0929e9 --- /dev/null +++ b/arch/ppc/xmon/ansidecl.h @@ -0,0 +1,141 @@ +/* ANSI and traditional C compatibility macros + Copyright 1991, 1992 Free Software Foundation, Inc. + This file is part of the GNU C Library. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* ANSI and traditional C compatibility macros + + ANSI C is assumed if __STDC__ is #defined. + + Macro ANSI C definition Traditional C definition + ----- ---- - ---------- ----------- - ---------- + PTR `void *' `char *' + LONG_DOUBLE `long double' `double' + VOLATILE `volatile' `' + SIGNED `signed' `' + PTRCONST `void *const' `char *' + ANSI_PROTOTYPES 1 not defined + + CONST is also defined, but is obsolete. Just use const. + + DEFUN (name, arglist, args) + + Defines function NAME. + + ARGLIST lists the arguments, separated by commas and enclosed in + parentheses. ARGLIST becomes the argument list in traditional C. + + ARGS list the arguments with their types. It becomes a prototype in + ANSI C, and the type declarations in traditional C. Arguments should + be separated with `AND'. For functions with a variable number of + arguments, the last thing listed should be `DOTS'. + + DEFUN_VOID (name) + + Defines a function NAME, which takes no arguments. + + obsolete -- EXFUN (name, (prototype)) -- obsolete. + + Replaced by PARAMS. Do not use; will disappear someday soon. + Was used in external function declarations. + In ANSI C it is `NAME PROTOTYPE' (so PROTOTYPE should be enclosed in + parentheses). In traditional C it is `NAME()'. + For a function that takes no arguments, PROTOTYPE should be `(void)'. + + PARAMS ((args)) + + We could use the EXFUN macro to handle prototype declarations, but + the name is misleading and the result is ugly. So we just define a + simple macro to handle the parameter lists, as in: + + static int foo PARAMS ((int, char)); + + This produces: `static int foo();' or `static int foo (int, char);' + + EXFUN would have done it like this: + + static int EXFUN (foo, (int, char)); + + but the function is not external...and it's hard to visually parse + the function name out of the mess. EXFUN should be considered + obsolete; new code should be written to use PARAMS. + + For example: + extern int printf PARAMS ((CONST char *format DOTS)); + int DEFUN(fprintf, (stream, format), + FILE *stream AND CONST char *format DOTS) { ... } + void DEFUN_VOID(abort) { ... } +*/ + +#ifndef _ANSIDECL_H + +#define _ANSIDECL_H 1 + + +/* Every source file includes this file, + so they will all get the switch for lint. */ +/* LINTLIBRARY */ + + +#if defined (__STDC__) || defined (_AIX) || (defined (__mips) && defined (_SYSTYPE_SVR4)) || defined(WIN32) +/* All known AIX compilers implement these things (but don't always + define __STDC__). The RISC/OS MIPS compiler defines these things + in SVR4 mode, but does not define __STDC__. */ + +#define PTR void * +#define PTRCONST void *CONST +#define LONG_DOUBLE long double + +#define AND , +#define NOARGS void +#define CONST const +#define VOLATILE volatile +#define SIGNED signed +#define DOTS , ... + +#define EXFUN(name, proto) name proto +#define DEFUN(name, arglist, args) name(args) +#define DEFUN_VOID(name) name(void) + +#define PROTO(type, name, arglist) type name arglist +#define PARAMS(paramlist) paramlist +#define ANSI_PROTOTYPES 1 + +#else /* Not ANSI C. */ + +#define PTR char * +#define PTRCONST PTR +#define LONG_DOUBLE double + +#define AND ; +#define NOARGS +#define CONST +#ifndef const /* some systems define it in header files for non-ansi mode */ +#define const +#endif +#define VOLATILE +#define SIGNED +#define DOTS + +#define EXFUN(name, proto) name() +#define DEFUN(name, arglist, args) name arglist args; +#define DEFUN_VOID(name) name() +#define PROTO(type, name, arglist) type name () +#define PARAMS(paramlist) () + +#endif /* ANSI C. */ + +#endif /* ansidecl.h */ diff --git a/arch/ppc/xmon/nonstdio.h b/arch/ppc/xmon/nonstdio.h new file mode 100644 index 00000000000..0240bc573c9 --- /dev/null +++ b/arch/ppc/xmon/nonstdio.h @@ -0,0 +1,22 @@ +typedef int FILE; +extern FILE *xmon_stdin, *xmon_stdout; +#define EOF (-1) +#define stdin xmon_stdin +#define stdout xmon_stdout +#define printf xmon_printf +#define fprintf xmon_fprintf +#define fputs xmon_fputs +#define fgets xmon_fgets +#define putchar xmon_putchar +#define getchar xmon_getchar +#define putc xmon_putc +#define getc xmon_getc +#define fopen(n, m) NULL +#define fflush(f) do {} while (0) +#define fclose(f) do {} while (0) +extern char *fgets(char *, int, void *); +extern void xmon_fprintf(void *, const char *, ...); +extern void xmon_sprintf(char *, const char *, ...); +extern void xmon_puts(char*); + +#define perror(s) printf("%s: no files!\n", (s)) diff --git a/arch/ppc/xmon/ppc-dis.c b/arch/ppc/xmon/ppc-dis.c new file mode 100644 index 00000000000..798ac1a677f --- /dev/null +++ b/arch/ppc/xmon/ppc-dis.c @@ -0,0 +1,190 @@ +/* ppc-dis.c -- Disassemble PowerPC instructions + Copyright 1994 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Cygnus Support + +This file is part of GDB, GAS, and the GNU binutils. + +GDB, GAS, and the GNU binutils are free software; you can redistribute +them and/or modify them under the terms of the GNU General Public +License as published by the Free Software Foundation; either version +2, or (at your option) any later version. + +GDB, GAS, and the GNU binutils are distributed in the hope that they +will be useful, but WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this file; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "nonstdio.h" +#include "ansidecl.h" +#include "ppc.h" + +static int print_insn_powerpc PARAMS ((FILE *, unsigned long insn, + unsigned memaddr, int dialect)); + +extern void print_address PARAMS((unsigned memaddr)); + +/* Print a big endian PowerPC instruction. For convenience, also + disassemble instructions supported by the Motorola PowerPC 601. */ + +int +print_insn_big_powerpc (FILE *out, unsigned long insn, unsigned memaddr) +{ + return print_insn_powerpc (out, insn, memaddr, + PPC_OPCODE_PPC | PPC_OPCODE_601); +} + +/* Print a PowerPC or POWER instruction. */ + +static int +print_insn_powerpc (FILE *out, unsigned long insn, unsigned memaddr, + int dialect) +{ + const struct powerpc_opcode *opcode; + const struct powerpc_opcode *opcode_end; + unsigned long op; + + /* Get the major opcode of the instruction. */ + op = PPC_OP (insn); + + /* Find the first match in the opcode table. We could speed this up + a bit by doing a binary search on the major opcode. */ + opcode_end = powerpc_opcodes + powerpc_num_opcodes; + for (opcode = powerpc_opcodes; opcode < opcode_end; opcode++) + { + unsigned long table_op; + const unsigned char *opindex; + const struct powerpc_operand *operand; + int invalid; + int need_comma; + int need_paren; + + table_op = PPC_OP (opcode->opcode); + if (op < table_op) + break; + if (op > table_op) + continue; + + if ((insn & opcode->mask) != opcode->opcode + || (opcode->flags & dialect) == 0) + continue; + + /* Make two passes over the operands. First see if any of them + have extraction functions, and, if they do, make sure the + instruction is valid. */ + invalid = 0; + for (opindex = opcode->operands; *opindex != 0; opindex++) + { + operand = powerpc_operands + *opindex; + if (operand->extract) + (*operand->extract) (insn, &invalid); + } + if (invalid) + continue; + + /* The instruction is valid. */ + fprintf(out, "%s", opcode->name); + if (opcode->operands[0] != 0) + fprintf(out, "\t"); + + /* Now extract and print the operands. */ + need_comma = 0; + need_paren = 0; + for (opindex = opcode->operands; *opindex != 0; opindex++) + { + long value; + + operand = powerpc_operands + *opindex; + + /* Operands that are marked FAKE are simply ignored. We + already made sure that the extract function considered + the instruction to be valid. */ + if ((operand->flags & PPC_OPERAND_FAKE) != 0) + continue; + + /* Extract the value from the instruction. */ + if (operand->extract) + value = (*operand->extract) (insn, (int *) 0); + else + { + value = (insn >> operand->shift) & ((1 << operand->bits) - 1); + if ((operand->flags & PPC_OPERAND_SIGNED) != 0 + && (value & (1 << (operand->bits - 1))) != 0) + value -= 1 << operand->bits; + } + + /* If the operand is optional, and the value is zero, don't + print anything. */ + if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0 + && (operand->flags & PPC_OPERAND_NEXT) == 0 + && value == 0) + continue; + + if (need_comma) + { + fprintf(out, ","); + need_comma = 0; + } + + /* Print the operand as directed by the flags. */ + if ((operand->flags & PPC_OPERAND_GPR) != 0) + fprintf(out, "r%ld", value); + else if ((operand->flags & PPC_OPERAND_FPR) != 0) + fprintf(out, "f%ld", value); + else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0) + print_address (memaddr + value); + else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0) + print_address (value & 0xffffffff); + else if ((operand->flags & PPC_OPERAND_CR) == 0 + || (dialect & PPC_OPCODE_PPC) == 0) + fprintf(out, "%ld", value); + else + { + if (operand->bits == 3) + fprintf(out, "cr%d", value); + else + { + static const char *cbnames[4] = { "lt", "gt", "eq", "so" }; + int cr; + int cc; + + cr = value >> 2; + if (cr != 0) + fprintf(out, "4*cr%d", cr); + cc = value & 3; + if (cc != 0) + { + if (cr != 0) + fprintf(out, "+"); + fprintf(out, "%s", cbnames[cc]); + } + } + } + + if (need_paren) + { + fprintf(out, ")"); + need_paren = 0; + } + + if ((operand->flags & PPC_OPERAND_PARENS) == 0) + need_comma = 1; + else + { + fprintf(out, "("); + need_paren = 1; + } + } + + /* We have found and printed an instruction; return. */ + return 4; + } + + /* We could not find a match. */ + fprintf(out, ".long 0x%lx", insn); + + return 4; +} diff --git a/arch/ppc/xmon/ppc-opc.c b/arch/ppc/xmon/ppc-opc.c new file mode 100644 index 00000000000..533a6c9973d --- /dev/null +++ b/arch/ppc/xmon/ppc-opc.c @@ -0,0 +1,2721 @@ +/* ppc-opc.c -- PowerPC opcode list + Copyright 1994 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Cygnus Support + +This file is part of GDB, GAS, and the GNU binutils. + +GDB, GAS, and the GNU binutils are free software; you can redistribute +them and/or modify them under the terms of the GNU General Public +License as published by the Free Software Foundation; either version +2, or (at your option) any later version. + +GDB, GAS, and the GNU binutils are distributed in the hope that they +will be useful, but WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this file; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include +#include "ansidecl.h" +#include "ppc.h" + +/* This file holds the PowerPC opcode table. The opcode table + includes almost all of the extended instruction mnemonics. This + permits the disassembler to use them, and simplifies the assembler + logic, at the cost of increasing the table size. The table is + strictly constant data, so the compiler should be able to put it in + the .text section. + + This file also holds the operand table. All knowledge about + inserting operands into instructions and vice-versa is kept in this + file. */ + +/* Local insertion and extraction functions. */ + +static unsigned long insert_bat PARAMS ((unsigned long, long, const char **)); +static long extract_bat PARAMS ((unsigned long, int *)); +static unsigned long insert_bba PARAMS ((unsigned long, long, const char **)); +static long extract_bba PARAMS ((unsigned long, int *)); +static unsigned long insert_bd PARAMS ((unsigned long, long, const char **)); +static long extract_bd PARAMS ((unsigned long, int *)); +static unsigned long insert_bdm PARAMS ((unsigned long, long, const char **)); +static long extract_bdm PARAMS ((unsigned long, int *)); +static unsigned long insert_bdp PARAMS ((unsigned long, long, const char **)); +static long extract_bdp PARAMS ((unsigned long, int *)); +static unsigned long insert_bo PARAMS ((unsigned long, long, const char **)); +static long extract_bo PARAMS ((unsigned long, int *)); +static unsigned long insert_boe PARAMS ((unsigned long, long, const char **)); +static long extract_boe PARAMS ((unsigned long, int *)); +static unsigned long insert_ds PARAMS ((unsigned long, long, const char **)); +static long extract_ds PARAMS ((unsigned long, int *)); +static unsigned long insert_li PARAMS ((unsigned long, long, const char **)); +static long extract_li PARAMS ((unsigned long, int *)); +static unsigned long insert_mbe PARAMS ((unsigned long, long, const char **)); +static long extract_mbe PARAMS ((unsigned long, int *)); +static unsigned long insert_mb6 PARAMS ((unsigned long, long, const char **)); +static long extract_mb6 PARAMS ((unsigned long, int *)); +static unsigned long insert_nb PARAMS ((unsigned long, long, const char **)); +static long extract_nb PARAMS ((unsigned long, int *)); +static unsigned long insert_nsi PARAMS ((unsigned long, long, const char **)); +static long extract_nsi PARAMS ((unsigned long, int *)); +static unsigned long insert_ral PARAMS ((unsigned long, long, const char **)); +static unsigned long insert_ram PARAMS ((unsigned long, long, const char **)); +static unsigned long insert_ras PARAMS ((unsigned long, long, const char **)); +static unsigned long insert_rbs PARAMS ((unsigned long, long, const char **)); +static long extract_rbs PARAMS ((unsigned long, int *)); +static unsigned long insert_sh6 PARAMS ((unsigned long, long, const char **)); +static long extract_sh6 PARAMS ((unsigned long, int *)); +static unsigned long insert_spr PARAMS ((unsigned long, long, const char **)); +static long extract_spr PARAMS ((unsigned long, int *)); +static unsigned long insert_tbr PARAMS ((unsigned long, long, const char **)); +static long extract_tbr PARAMS ((unsigned long, int *)); + +/* The operands table. + + The fields are bits, shift, signed, insert, extract, flags. */ + +const struct powerpc_operand powerpc_operands[] = +{ + /* The zero index is used to indicate the end of the list of + operands. */ +#define UNUSED (0) + { 0, 0, NULL, NULL, 0 }, + + /* The BA field in an XL form instruction. */ +#define BA (1) +#define BA_MASK (0x1f << 16) + { 5, 16, NULL, NULL, PPC_OPERAND_CR }, + + /* The BA field in an XL form instruction when it must be the same + as the BT field in the same instruction. */ +#define BAT (2) + { 5, 16, insert_bat, extract_bat, PPC_OPERAND_FAKE }, + + /* The BB field in an XL form instruction. */ +#define BB (3) +#define BB_MASK (0x1f << 11) + { 5, 11, NULL, NULL, PPC_OPERAND_CR }, + + /* The BB field in an XL form instruction when it must be the same + as the BA field in the same instruction. */ +#define BBA (4) + { 5, 11, insert_bba, extract_bba, PPC_OPERAND_FAKE }, + + /* The BD field in a B form instruction. The lower two bits are + forced to zero. */ +#define BD (5) + { 16, 0, insert_bd, extract_bd, PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when absolute addressing is + used. */ +#define BDA (6) + { 16, 0, insert_bd, extract_bd, PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the - modifier is used. + This sets the y bit of the BO field appropriately. */ +#define BDM (7) + { 16, 0, insert_bdm, extract_bdm, + PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the - modifier is used + and absolute address is used. */ +#define BDMA (8) + { 16, 0, insert_bdm, extract_bdm, + PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the + modifier is used. + This sets the y bit of the BO field appropriately. */ +#define BDP (9) + { 16, 0, insert_bdp, extract_bdp, + PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the + modifier is used + and absolute addressing is used. */ +#define BDPA (10) + { 16, 0, insert_bdp, extract_bdp, + PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED }, + + /* The BF field in an X or XL form instruction. */ +#define BF (11) + { 3, 23, NULL, NULL, PPC_OPERAND_CR }, + + /* An optional BF field. This is used for comparison instructions, + in which an omitted BF field is taken as zero. */ +#define OBF (12) + { 3, 23, NULL, NULL, PPC_OPERAND_CR | PPC_OPERAND_OPTIONAL }, + + /* The BFA field in an X or XL form instruction. */ +#define BFA (13) + { 3, 18, NULL, NULL, PPC_OPERAND_CR }, + + /* The BI field in a B form or XL form instruction. */ +#define BI (14) +#define BI_MASK (0x1f << 16) + { 5, 16, NULL, NULL, PPC_OPERAND_CR }, + + /* The BO field in a B form instruction. Certain values are + illegal. */ +#define BO (15) +#define BO_MASK (0x1f << 21) + { 5, 21, insert_bo, extract_bo, 0 }, + + /* The BO field in a B form instruction when the + or - modifier is + used. This is like the BO field, but it must be even. */ +#define BOE (16) + { 5, 21, insert_boe, extract_boe, 0 }, + + /* The BT field in an X or XL form instruction. */ +#define BT (17) + { 5, 21, NULL, NULL, PPC_OPERAND_CR }, + + /* The condition register number portion of the BI field in a B form + or XL form instruction. This is used for the extended + conditional branch mnemonics, which set the lower two bits of the + BI field. This field is optional. */ +#define CR (18) + { 3, 18, NULL, NULL, PPC_OPERAND_CR | PPC_OPERAND_OPTIONAL }, + + /* The D field in a D form instruction. This is a displacement off + a register, and implies that the next operand is a register in + parentheses. */ +#define D (19) + { 16, 0, NULL, NULL, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED }, + + /* The DS field in a DS form instruction. This is like D, but the + lower two bits are forced to zero. */ +#define DS (20) + { 16, 0, insert_ds, extract_ds, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED }, + + /* The FL1 field in a POWER SC form instruction. */ +#define FL1 (21) + { 4, 12, NULL, NULL, 0 }, + + /* The FL2 field in a POWER SC form instruction. */ +#define FL2 (22) + { 3, 2, NULL, NULL, 0 }, + + /* The FLM field in an XFL form instruction. */ +#define FLM (23) + { 8, 17, NULL, NULL, 0 }, + + /* The FRA field in an X or A form instruction. */ +#define FRA (24) +#define FRA_MASK (0x1f << 16) + { 5, 16, NULL, NULL, PPC_OPERAND_FPR }, + + /* The FRB field in an X or A form instruction. */ +#define FRB (25) +#define FRB_MASK (0x1f << 11) + { 5, 11, NULL, NULL, PPC_OPERAND_FPR }, + + /* The FRC field in an A form instruction. */ +#define FRC (26) +#define FRC_MASK (0x1f << 6) + { 5, 6, NULL, NULL, PPC_OPERAND_FPR }, + + /* The FRS field in an X form instruction or the FRT field in a D, X + or A form instruction. */ +#define FRS (27) +#define FRT (FRS) + { 5, 21, NULL, NULL, PPC_OPERAND_FPR }, + + /* The FXM field in an XFX instruction. */ +#define FXM (28) +#define FXM_MASK (0xff << 12) + { 8, 12, NULL, NULL, 0 }, + + /* The L field in a D or X form instruction. */ +#define L (29) + { 1, 21, NULL, NULL, PPC_OPERAND_OPTIONAL }, + + /* The LEV field in a POWER SC form instruction. */ +#define LEV (30) + { 7, 5, NULL, NULL, 0 }, + + /* The LI field in an I form instruction. The lower two bits are + forced to zero. */ +#define LI (31) + { 26, 0, insert_li, extract_li, PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The LI field in an I form instruction when used as an absolute + address. */ +#define LIA (32) + { 26, 0, insert_li, extract_li, PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED }, + + /* The MB field in an M form instruction. */ +#define MB (33) +#define MB_MASK (0x1f << 6) + { 5, 6, NULL, NULL, 0 }, + + /* The ME field in an M form instruction. */ +#define ME (34) +#define ME_MASK (0x1f << 1) + { 5, 1, NULL, NULL, 0 }, + + /* The MB and ME fields in an M form instruction expressed a single + operand which is a bitmask indicating which bits to select. This + is a two operand form using PPC_OPERAND_NEXT. See the + description in opcode/ppc.h for what this means. */ +#define MBE (35) + { 5, 6, NULL, NULL, PPC_OPERAND_OPTIONAL | PPC_OPERAND_NEXT }, + { 32, 0, insert_mbe, extract_mbe, 0 }, + + /* The MB or ME field in an MD or MDS form instruction. The high + bit is wrapped to the low end. */ +#define MB6 (37) +#define ME6 (MB6) +#define MB6_MASK (0x3f << 5) + { 6, 5, insert_mb6, extract_mb6, 0 }, + + /* The NB field in an X form instruction. The value 32 is stored as + 0. */ +#define NB (38) + { 6, 11, insert_nb, extract_nb, 0 }, + + /* The NSI field in a D form instruction. This is the same as the + SI field, only negated. */ +#define NSI (39) + { 16, 0, insert_nsi, extract_nsi, + PPC_OPERAND_NEGATIVE | PPC_OPERAND_SIGNED }, + + /* The RA field in an D, DS, X, XO, M, or MDS form instruction. */ +#define RA (40) +#define RA_MASK (0x1f << 16) + { 5, 16, NULL, NULL, PPC_OPERAND_GPR }, + + /* The RA field in a D or X form instruction which is an updating + load, which means that the RA field may not be zero and may not + equal the RT field. */ +#define RAL (41) + { 5, 16, insert_ral, NULL, PPC_OPERAND_GPR }, + + /* The RA field in an lmw instruction, which has special value + restrictions. */ +#define RAM (42) + { 5, 16, insert_ram, NULL, PPC_OPERAND_GPR }, + + /* The RA field in a D or X form instruction which is an updating + store or an updating floating point load, which means that the RA + field may not be zero. */ +#define RAS (43) + { 5, 16, insert_ras, NULL, PPC_OPERAND_GPR }, + + /* The RB field in an X, XO, M, or MDS form instruction. */ +#define RB (44) +#define RB_MASK (0x1f << 11) + { 5, 11, NULL, NULL, PPC_OPERAND_GPR }, + + /* The RB field in an X form instruction when it must be the same as + the RS field in the instruction. This is used for extended + mnemonics like mr. */ +#define RBS (45) + { 5, 1, insert_rbs, extract_rbs, PPC_OPERAND_FAKE }, + + /* The RS field in a D, DS, X, XFX, XS, M, MD or MDS form + instruction or the RT field in a D, DS, X, XFX or XO form + instruction. */ +#define RS (46) +#define RT (RS) +#define RT_MASK (0x1f << 21) + { 5, 21, NULL, NULL, PPC_OPERAND_GPR }, + + /* The SH field in an X or M form instruction. */ +#define SH (47) +#define SH_MASK (0x1f << 11) + { 5, 11, NULL, NULL, 0 }, + + /* The SH field in an MD form instruction. This is split. */ +#define SH6 (48) +#define SH6_MASK ((0x1f << 11) | (1 << 1)) + { 6, 1, insert_sh6, extract_sh6, 0 }, + + /* The SI field in a D form instruction. */ +#define SI (49) + { 16, 0, NULL, NULL, PPC_OPERAND_SIGNED }, + + /* The SI field in a D form instruction when we accept a wide range + of positive values. */ +#define SISIGNOPT (50) + { 16, 0, NULL, NULL, PPC_OPERAND_SIGNED | PPC_OPERAND_SIGNOPT }, + + /* The SPR field in an XFX form instruction. This is flipped--the + lower 5 bits are stored in the upper 5 and vice- versa. */ +#define SPR (51) +#define SPR_MASK (0x3ff << 11) + { 10, 11, insert_spr, extract_spr, 0 }, + + /* The BAT index number in an XFX form m[ft]ibat[lu] instruction. */ +#define SPRBAT (52) +#define SPRBAT_MASK (0x3 << 17) + { 2, 17, NULL, NULL, 0 }, + + /* The SPRG register number in an XFX form m[ft]sprg instruction. */ +#define SPRG (53) +#define SPRG_MASK (0x3 << 16) + { 2, 16, NULL, NULL, 0 }, + + /* The SR field in an X form instruction. */ +#define SR (54) + { 4, 16, NULL, NULL, 0 }, + + /* The SV field in a POWER SC form instruction. */ +#define SV (55) + { 14, 2, NULL, NULL, 0 }, + + /* The TBR field in an XFX form instruction. This is like the SPR + field, but it is optional. */ +#define TBR (56) + { 10, 11, insert_tbr, extract_tbr, PPC_OPERAND_OPTIONAL }, + + /* The TO field in a D or X form instruction. */ +#define TO (57) +#define TO_MASK (0x1f << 21) + { 5, 21, NULL, NULL, 0 }, + + /* The U field in an X form instruction. */ +#define U (58) + { 4, 12, NULL, NULL, 0 }, + + /* The UI field in a D form instruction. */ +#define UI (59) + { 16, 0, NULL, NULL, 0 }, +}; + +/* The functions used to insert and extract complicated operands. */ + +/* The BA field in an XL form instruction when it must be the same as + the BT field in the same instruction. This operand is marked FAKE. + The insertion function just copies the BT field into the BA field, + and the extraction function just checks that the fields are the + same. */ + +/*ARGSUSED*/ +static unsigned long +insert_bat(unsigned long insn, long value, const char **errmsg) +{ + return insn | (((insn >> 21) & 0x1f) << 16); +} + +static long +extract_bat(unsigned long insn, int *invalid) +{ + if (invalid != (int *) NULL + && ((insn >> 21) & 0x1f) != ((insn >> 16) & 0x1f)) + *invalid = 1; + return 0; +} + +/* The BB field in an XL form instruction when it must be the same as + the BA field in the same instruction. This operand is marked FAKE. + The insertion function just copies the BA field into the BB field, + and the extraction function just checks that the fields are the + same. */ + +/*ARGSUSED*/ +static unsigned long +insert_bba(unsigned long insn, long value, const char **errmsg) +{ + return insn | (((insn >> 16) & 0x1f) << 11); +} + +static long +extract_bba(unsigned long insn, int *invalid) +{ + if (invalid != (int *) NULL + && ((insn >> 16) & 0x1f) != ((insn >> 11) & 0x1f)) + *invalid = 1; + return 0; +} + +/* The BD field in a B form instruction. The lower two bits are + forced to zero. */ + +/*ARGSUSED*/ +static unsigned long +insert_bd(unsigned long insn, long value, const char **errmsg) +{ + return insn | (value & 0xfffc); +} + +/*ARGSUSED*/ +static long +extract_bd(unsigned long insn, int *invalid) +{ + if ((insn & 0x8000) != 0) + return (insn & 0xfffc) - 0x10000; + else + return insn & 0xfffc; +} + +/* The BD field in a B form instruction when the - modifier is used. + This modifier means that the branch is not expected to be taken. + We must set the y bit of the BO field to 1 if the offset is + negative. When extracting, we require that the y bit be 1 and that + the offset be positive, since if the y bit is 0 we just want to + print the normal form of the instruction. */ + +/*ARGSUSED*/ +static unsigned long +insert_bdm(unsigned long insn, long value, const char **errmsg) +{ + if ((value & 0x8000) != 0) + insn |= 1 << 21; + return insn | (value & 0xfffc); +} + +static long +extract_bdm(unsigned long insn, int *invalid) +{ + if (invalid != (int *) NULL + && ((insn & (1 << 21)) == 0 + || (insn & (1 << 15)) == 0)) + *invalid = 1; + if ((insn & 0x8000) != 0) + return (insn & 0xfffc) - 0x10000; + else + return insn & 0xfffc; +} + +/* The BD field in a B form instruction when the + modifier is used. + This is like BDM, above, except that the branch is expected to be + taken. */ + +/*ARGSUSED*/ +static unsigned long +insert_bdp(unsigned long insn, long value, const char **errmsg) +{ + if ((value & 0x8000) == 0) + insn |= 1 << 21; + return insn | (value & 0xfffc); +} + +static long +extract_bdp(unsigned long insn, int *invalid) +{ + if (invalid != (int *) NULL + && ((insn & (1 << 21)) == 0 + || (insn & (1 << 15)) != 0)) + *invalid = 1; + if ((insn & 0x8000) != 0) + return (insn & 0xfffc) - 0x10000; + else + return insn & 0xfffc; +} + +/* Check for legal values of a BO field. */ + +static int +valid_bo (long value) +{ + /* Certain encodings have bits that are required to be zero. These + are (z must be zero, y may be anything): + 001zy + 011zy + 1z00y + 1z01y + 1z1zz + */ + switch (value & 0x14) + { + default: + case 0: + return 1; + case 0x4: + return (value & 0x2) == 0; + case 0x10: + return (value & 0x8) == 0; + case 0x14: + return value == 0x14; + } +} + +/* The BO field in a B form instruction. Warn about attempts to set + the field to an illegal value. */ + +static unsigned long +insert_bo(unsigned long insn, long value, const char **errmsg) +{ + if (errmsg != (const char **) NULL + && ! valid_bo (value)) + *errmsg = "invalid conditional option"; + return insn | ((value & 0x1f) << 21); +} + +static long +extract_bo(unsigned long insn, int *invalid) +{ + long value; + + value = (insn >> 21) & 0x1f; + if (invalid != (int *) NULL + && ! valid_bo (value)) + *invalid = 1; + return value; +} + +/* The BO field in a B form instruction when the + or - modifier is + used. This is like the BO field, but it must be even. When + extracting it, we force it to be even. */ + +static unsigned long +insert_boe(unsigned long insn, long value, const char **errmsg) +{ + if (errmsg != (const char **) NULL) + { + if (! valid_bo (value)) + *errmsg = "invalid conditional option"; + else if ((value & 1) != 0) + *errmsg = "attempt to set y bit when using + or - modifier"; + } + return insn | ((value & 0x1f) << 21); +} + +static long +extract_boe(unsigned long insn, int *invalid) +{ + long value; + + value = (insn >> 21) & 0x1f; + if (invalid != (int *) NULL + && ! valid_bo (value)) + *invalid = 1; + return value & 0x1e; +} + +/* The DS field in a DS form instruction. This is like D, but the + lower two bits are forced to zero. */ + +/*ARGSUSED*/ +static unsigned long +insert_ds(unsigned long insn, long value, const char **errmsg) +{ + return insn | (value & 0xfffc); +} + +/*ARGSUSED*/ +static long +extract_ds(unsigned long insn, int *invalid) +{ + if ((insn & 0x8000) != 0) + return (insn & 0xfffc) - 0x10000; + else + return insn & 0xfffc; +} + +/* The LI field in an I form instruction. The lower two bits are + forced to zero. */ + +/*ARGSUSED*/ +static unsigned long +insert_li(unsigned long insn, long value, const char **errmsg) +{ + return insn | (value & 0x3fffffc); +} + +/*ARGSUSED*/ +static long +extract_li(unsigned long insn, int *invalid) +{ + if ((insn & 0x2000000) != 0) + return (insn & 0x3fffffc) - 0x4000000; + else + return insn & 0x3fffffc; +} + +/* The MB and ME fields in an M form instruction expressed as a single + operand which is itself a bitmask. The extraction function always + marks it as invalid, since we never want to recognize an + instruction which uses a field of this type. */ + +static unsigned long +insert_mbe(unsigned long insn, long value, const char **errmsg) +{ + unsigned long uval; + int mb, me; + + uval = value; + + if (uval == 0) + { + if (errmsg != (const char **) NULL) + *errmsg = "illegal bitmask"; + return insn; + } + + me = 31; + while ((uval & 1) == 0) + { + uval >>= 1; + --me; + } + + mb = me; + uval >>= 1; + while ((uval & 1) != 0) + { + uval >>= 1; + --mb; + } + + if (uval != 0) + { + if (errmsg != (const char **) NULL) + *errmsg = "illegal bitmask"; + } + + return insn | (mb << 6) | (me << 1); +} + +static long +extract_mbe(unsigned long insn, int *invalid) +{ + long ret; + int mb, me; + int i; + + if (invalid != (int *) NULL) + *invalid = 1; + + ret = 0; + mb = (insn >> 6) & 0x1f; + me = (insn >> 1) & 0x1f; + for (i = mb; i < me; i++) + ret |= 1 << (31 - i); + return ret; +} + +/* The MB or ME field in an MD or MDS form instruction. The high bit + is wrapped to the low end. */ + +/*ARGSUSED*/ +static unsigned long +insert_mb6(unsigned long insn, long value, const char **errmsg) +{ + return insn | ((value & 0x1f) << 6) | (value & 0x20); +} + +/*ARGSUSED*/ +static long +extract_mb6(unsigned long insn, int *invalid) +{ + return ((insn >> 6) & 0x1f) | (insn & 0x20); +} + +/* The NB field in an X form instruction. The value 32 is stored as + 0. */ + +static unsigned long +insert_nb(unsigned long insn, long value, const char **errmsg) +{ + if (value < 0 || value > 32) + *errmsg = "value out of range"; + if (value == 32) + value = 0; + return insn | ((value & 0x1f) << 11); +} + +/*ARGSUSED*/ +static long +extract_nb(unsigned long insn, int *invalid) +{ + long ret; + + ret = (insn >> 11) & 0x1f; + if (ret == 0) + ret = 32; + return ret; +} + +/* The NSI field in a D form instruction. This is the same as the SI + field, only negated. The extraction function always marks it as + invalid, since we never want to recognize an instruction which uses + a field of this type. */ + +/*ARGSUSED*/ +static unsigned long +insert_nsi(unsigned long insn, long value, const char **errmsg) +{ + return insn | ((- value) & 0xffff); +} + +static long +extract_nsi(unsigned long insn, int *invalid) +{ + if (invalid != (int *) NULL) + *invalid = 1; + if ((insn & 0x8000) != 0) + return - ((insn & 0xffff) - 0x10000); + else + return - (insn & 0xffff); +} + +/* The RA field in a D or X form instruction which is an updating + load, which means that the RA field may not be zero and may not + equal the RT field. */ + +static unsigned long +insert_ral(unsigned long insn, long value, const char **errmsg) +{ + if (value == 0 + || value == ((insn >> 21) & 0x1f)) + *errmsg = "invalid register operand when updating"; + return insn | ((value & 0x1f) << 16); +} + +/* The RA field in an lmw instruction, which has special value + restrictions. */ + +static unsigned long +insert_ram(unsigned long insn, long value, const char **errmsg) +{ + if (value >= ((insn >> 21) & 0x1f)) + *errmsg = "index register in load range"; + return insn | ((value & 0x1f) << 16); +} + +/* The RA field in a D or X form instruction which is an updating + store or an updating floating point load, which means that the RA + field may not be zero. */ + +static unsigned long +insert_ras(unsigned long insn, long value, const char **errmsg) +{ + if (value == 0) + *errmsg = "invalid register operand when updating"; + return insn | ((value & 0x1f) << 16); +} + +/* The RB field in an X form instruction when it must be the same as + the RS field in the instruction. This is used for extended + mnemonics like mr. This operand is marked FAKE. The insertion + function just copies the BT field into the BA field, and the + extraction function just checks that the fields are the same. */ + +/*ARGSUSED*/ +static unsigned long +insert_rbs(unsigned long insn, long value, const char **errmsg) +{ + return insn | (((insn >> 21) & 0x1f) << 11); +} + +static long +extract_rbs(unsigned long insn, int *invalid) +{ + if (invalid != (int *) NULL + && ((insn >> 21) & 0x1f) != ((insn >> 11) & 0x1f)) + *invalid = 1; + return 0; +} + +/* The SH field in an MD form instruction. This is split. */ + +/*ARGSUSED*/ +static unsigned long +insert_sh6(unsigned long insn, long value, const char **errmsg) +{ + return insn | ((value & 0x1f) << 11) | ((value & 0x20) >> 4); +} + +/*ARGSUSED*/ +static long +extract_sh6(unsigned long insn, int *invalid) +{ + return ((insn >> 11) & 0x1f) | ((insn << 4) & 0x20); +} + +/* The SPR field in an XFX form instruction. This is flipped--the + lower 5 bits are stored in the upper 5 and vice- versa. */ + +static unsigned long +insert_spr(unsigned long insn, long value, const char **errmsg) +{ + return insn | ((value & 0x1f) << 16) | ((value & 0x3e0) << 6); +} + +static long +extract_spr(unsigned long insn, int *invalid) +{ + return ((insn >> 16) & 0x1f) | ((insn >> 6) & 0x3e0); +} + +/* The TBR field in an XFX instruction. This is just like SPR, but it + is optional. When TBR is omitted, it must be inserted as 268 (the + magic number of the TB register). These functions treat 0 + (indicating an omitted optional operand) as 268. This means that + ``mftb 4,0'' is not handled correctly. This does not matter very + much, since the architecture manual does not define mftb as + accepting any values other than 268 or 269. */ + +#define TB (268) + +static unsigned long +insert_tbr(unsigned long insn, long value, const char **errmsg) +{ + if (value == 0) + value = TB; + return insn | ((value & 0x1f) << 16) | ((value & 0x3e0) << 6); +} + +static long +extract_tbr(unsigned long insn, int *invalid) +{ + long ret; + + ret = ((insn >> 16) & 0x1f) | ((insn >> 6) & 0x3e0); + if (ret == TB) + ret = 0; + return ret; +} + +/* Macros used to form opcodes. */ + +/* The main opcode. */ +#define OP(x) (((x) & 0x3f) << 26) +#define OP_MASK OP (0x3f) + +/* The main opcode combined with a trap code in the TO field of a D + form instruction. Used for extended mnemonics for the trap + instructions. */ +#define OPTO(x,to) (OP (x) | (((to) & 0x1f) << 21)) +#define OPTO_MASK (OP_MASK | TO_MASK) + +/* The main opcode combined with a comparison size bit in the L field + of a D form or X form instruction. Used for extended mnemonics for + the comparison instructions. */ +#define OPL(x,l) (OP (x) | (((l) & 1) << 21)) +#define OPL_MASK OPL (0x3f,1) + +/* An A form instruction. */ +#define A(op, xop, rc) (OP (op) | (((xop) & 0x1f) << 1) | ((rc) & 1)) +#define A_MASK A (0x3f, 0x1f, 1) + +/* An A_MASK with the FRB field fixed. */ +#define AFRB_MASK (A_MASK | FRB_MASK) + +/* An A_MASK with the FRC field fixed. */ +#define AFRC_MASK (A_MASK | FRC_MASK) + +/* An A_MASK with the FRA and FRC fields fixed. */ +#define AFRAFRC_MASK (A_MASK | FRA_MASK | FRC_MASK) + +/* A B form instruction. */ +#define B(op, aa, lk) (OP (op) | (((aa) & 1) << 1) | ((lk) & 1)) +#define B_MASK B (0x3f, 1, 1) + +/* A B form instruction setting the BO field. */ +#define BBO(op, bo, aa, lk) (B ((op), (aa), (lk)) | (((bo) & 0x1f) << 21)) +#define BBO_MASK BBO (0x3f, 0x1f, 1, 1) + +/* A BBO_MASK with the y bit of the BO field removed. This permits + matching a conditional branch regardless of the setting of the y + bit. */ +#define Y_MASK (1 << 21) +#define BBOY_MASK (BBO_MASK &~ Y_MASK) + +/* A B form instruction setting the BO field and the condition bits of + the BI field. */ +#define BBOCB(op, bo, cb, aa, lk) \ + (BBO ((op), (bo), (aa), (lk)) | (((cb) & 0x3) << 16)) +#define BBOCB_MASK BBOCB (0x3f, 0x1f, 0x3, 1, 1) + +/* A BBOCB_MASK with the y bit of the BO field removed. */ +#define BBOYCB_MASK (BBOCB_MASK &~ Y_MASK) + +/* A BBOYCB_MASK in which the BI field is fixed. */ +#define BBOYBI_MASK (BBOYCB_MASK | BI_MASK) + +/* The main opcode mask with the RA field clear. */ +#define DRA_MASK (OP_MASK | RA_MASK) + +/* A DS form instruction. */ +#define DSO(op, xop) (OP (op) | ((xop) & 0x3)) +#define DS_MASK DSO (0x3f, 3) + +/* An M form instruction. */ +#define M(op, rc) (OP (op) | ((rc) & 1)) +#define M_MASK M (0x3f, 1) + +/* An M form instruction with the ME field specified. */ +#define MME(op, me, rc) (M ((op), (rc)) | (((me) & 0x1f) << 1)) + +/* An M_MASK with the MB and ME fields fixed. */ +#define MMBME_MASK (M_MASK | MB_MASK | ME_MASK) + +/* An M_MASK with the SH and ME fields fixed. */ +#define MSHME_MASK (M_MASK | SH_MASK | ME_MASK) + +/* An MD form instruction. */ +#define MD(op, xop, rc) (OP (op) | (((xop) & 0x7) << 2) | ((rc) & 1)) +#define MD_MASK MD (0x3f, 0x7, 1) + +/* An MD_MASK with the MB field fixed. */ +#define MDMB_MASK (MD_MASK | MB6_MASK) + +/* An MD_MASK with the SH field fixed. */ +#define MDSH_MASK (MD_MASK | SH6_MASK) + +/* An MDS form instruction. */ +#define MDS(op, xop, rc) (OP (op) | (((xop) & 0xf) << 1) | ((rc) & 1)) +#define MDS_MASK MDS (0x3f, 0xf, 1) + +/* An MDS_MASK with the MB field fixed. */ +#define MDSMB_MASK (MDS_MASK | MB6_MASK) + +/* An SC form instruction. */ +#define SC(op, sa, lk) (OP (op) | (((sa) & 1) << 1) | ((lk) & 1)) +#define SC_MASK (OP_MASK | (0x3ff << 16) | (1 << 1) | 1) + +/* An X form instruction. */ +#define X(op, xop) (OP (op) | (((xop) & 0x3ff) << 1)) + +/* An X form instruction with the RC bit specified. */ +#define XRC(op, xop, rc) (X ((op), (xop)) | ((rc) & 1)) + +/* The mask for an X form instruction. */ +#define X_MASK XRC (0x3f, 0x3ff, 1) + +/* An X_MASK with the RA field fixed. */ +#define XRA_MASK (X_MASK | RA_MASK) + +/* An X_MASK with the RB field fixed. */ +#define XRB_MASK (X_MASK | RB_MASK) + +/* An X_MASK with the RT field fixed. */ +#define XRT_MASK (X_MASK | RT_MASK) + +/* An X_MASK with the RA and RB fields fixed. */ +#define XRARB_MASK (X_MASK | RA_MASK | RB_MASK) + +/* An X_MASK with the RT and RA fields fixed. */ +#define XRTRA_MASK (X_MASK | RT_MASK | RA_MASK) + +/* An X form comparison instruction. */ +#define XCMPL(op, xop, l) (X ((op), (xop)) | (((l) & 1) << 21)) + +/* The mask for an X form comparison instruction. */ +#define XCMP_MASK (X_MASK | (1 << 22)) + +/* The mask for an X form comparison instruction with the L field + fixed. */ +#define XCMPL_MASK (XCMP_MASK | (1 << 21)) + +/* An X form trap instruction with the TO field specified. */ +#define XTO(op, xop, to) (X ((op), (xop)) | (((to) & 0x1f) << 21)) +#define XTO_MASK (X_MASK | TO_MASK) + +/* An XFL form instruction. */ +#define XFL(op, xop, rc) (OP (op) | (((xop) & 0x3ff) << 1) | ((rc) & 1)) +#define XFL_MASK (XFL (0x3f, 0x3ff, 1) | (1 << 25) | (1 << 16)) + +/* An XL form instruction with the LK field set to 0. */ +#define XL(op, xop) (OP (op) | (((xop) & 0x3ff) << 1)) + +/* An XL form instruction which uses the LK field. */ +#define XLLK(op, xop, lk) (XL ((op), (xop)) | ((lk) & 1)) + +/* The mask for an XL form instruction. */ +#define XL_MASK XLLK (0x3f, 0x3ff, 1) + +/* An XL form instruction which explicitly sets the BO field. */ +#define XLO(op, bo, xop, lk) \ + (XLLK ((op), (xop), (lk)) | (((bo) & 0x1f) << 21)) +#define XLO_MASK (XL_MASK | BO_MASK) + +/* An XL form instruction which explicitly sets the y bit of the BO + field. */ +#define XLYLK(op, xop, y, lk) (XLLK ((op), (xop), (lk)) | (((y) & 1) << 21)) +#define XLYLK_MASK (XL_MASK | Y_MASK) + +/* An XL form instruction which sets the BO field and the condition + bits of the BI field. */ +#define XLOCB(op, bo, cb, xop, lk) \ + (XLO ((op), (bo), (xop), (lk)) | (((cb) & 3) << 16)) +#define XLOCB_MASK XLOCB (0x3f, 0x1f, 0x3, 0x3ff, 1) + +/* An XL_MASK or XLYLK_MASK or XLOCB_MASK with the BB field fixed. */ +#define XLBB_MASK (XL_MASK | BB_MASK) +#define XLYBB_MASK (XLYLK_MASK | BB_MASK) +#define XLBOCBBB_MASK (XLOCB_MASK | BB_MASK) + +/* An XL_MASK with the BO and BB fields fixed. */ +#define XLBOBB_MASK (XL_MASK | BO_MASK | BB_MASK) + +/* An XL_MASK with the BO, BI and BB fields fixed. */ +#define XLBOBIBB_MASK (XL_MASK | BO_MASK | BI_MASK | BB_MASK) + +/* An XO form instruction. */ +#define XO(op, xop, oe, rc) \ + (OP (op) | (((xop) & 0x1ff) << 1) | (((oe) & 1) << 10) | ((rc) & 1)) +#define XO_MASK XO (0x3f, 0x1ff, 1, 1) + +/* An XO_MASK with the RB field fixed. */ +#define XORB_MASK (XO_MASK | RB_MASK) + +/* An XS form instruction. */ +#define XS(op, xop, rc) (OP (op) | (((xop) & 0x1ff) << 2) | ((rc) & 1)) +#define XS_MASK XS (0x3f, 0x1ff, 1) + +/* A mask for the FXM version of an XFX form instruction. */ +#define XFXFXM_MASK (X_MASK | (1 << 20) | (1 << 11)) + +/* An XFX form instruction with the FXM field filled in. */ +#define XFXM(op, xop, fxm) \ + (X ((op), (xop)) | (((fxm) & 0xff) << 12)) + +/* An XFX form instruction with the SPR field filled in. */ +#define XSPR(op, xop, spr) \ + (X ((op), (xop)) | (((spr) & 0x1f) << 16) | (((spr) & 0x3e0) << 6)) +#define XSPR_MASK (X_MASK | SPR_MASK) + +/* An XFX form instruction with the SPR field filled in except for the + SPRBAT field. */ +#define XSPRBAT_MASK (XSPR_MASK &~ SPRBAT_MASK) + +/* An XFX form instruction with the SPR field filled in except for the + SPRG field. */ +#define XSPRG_MASK (XSPR_MASK &~ SPRG_MASK) + +/* The BO encodings used in extended conditional branch mnemonics. */ +#define BODNZF (0x0) +#define BODNZFP (0x1) +#define BODZF (0x2) +#define BODZFP (0x3) +#define BOF (0x4) +#define BOFP (0x5) +#define BODNZT (0x8) +#define BODNZTP (0x9) +#define BODZT (0xa) +#define BODZTP (0xb) +#define BOT (0xc) +#define BOTP (0xd) +#define BODNZ (0x10) +#define BODNZP (0x11) +#define BODZ (0x12) +#define BODZP (0x13) +#define BOU (0x14) + +/* The BI condition bit encodings used in extended conditional branch + mnemonics. */ +#define CBLT (0) +#define CBGT (1) +#define CBEQ (2) +#define CBSO (3) + +/* The TO encodings used in extended trap mnemonics. */ +#define TOLGT (0x1) +#define TOLLT (0x2) +#define TOEQ (0x4) +#define TOLGE (0x5) +#define TOLNL (0x5) +#define TOLLE (0x6) +#define TOLNG (0x6) +#define TOGT (0x8) +#define TOGE (0xc) +#define TONL (0xc) +#define TOLT (0x10) +#define TOLE (0x14) +#define TONG (0x14) +#define TONE (0x18) +#define TOU (0x1f) + +/* Smaller names for the flags so each entry in the opcodes table will + fit on a single line. */ +#undef PPC +#define PPC PPC_OPCODE_PPC +#define POWER PPC_OPCODE_POWER +#define POWER2 PPC_OPCODE_POWER2 +#define B32 PPC_OPCODE_32 +#define B64 PPC_OPCODE_64 +#define M601 PPC_OPCODE_601 + +/* The opcode table. + + The format of the opcode table is: + + NAME OPCODE MASK FLAGS { OPERANDS } + + NAME is the name of the instruction. + OPCODE is the instruction opcode. + MASK is the opcode mask; this is used to tell the disassembler + which bits in the actual opcode must match OPCODE. + FLAGS are flags indicated what processors support the instruction. + OPERANDS is the list of operands. + + The disassembler reads the table in order and prints the first + instruction which matches, so this table is sorted to put more + specific instructions before more general instructions. It is also + sorted by major opcode. */ + +const struct powerpc_opcode powerpc_opcodes[] = { +{ "tdlgti", OPTO(2,TOLGT), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdllti", OPTO(2,TOLLT), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdeqi", OPTO(2,TOEQ), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdlgei", OPTO(2,TOLGE), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdlnli", OPTO(2,TOLNL), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdllei", OPTO(2,TOLLE), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdlngi", OPTO(2,TOLNG), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdgti", OPTO(2,TOGT), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdgei", OPTO(2,TOGE), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdnli", OPTO(2,TONL), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdlti", OPTO(2,TOLT), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdlei", OPTO(2,TOLE), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdngi", OPTO(2,TONG), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdnei", OPTO(2,TONE), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdi", OP(2), OP_MASK, PPC|B64, { TO, RA, SI } }, + +{ "twlgti", OPTO(3,TOLGT), OPTO_MASK, PPC, { RA, SI } }, +{ "tlgti", OPTO(3,TOLGT), OPTO_MASK, POWER, { RA, SI } }, +{ "twllti", OPTO(3,TOLLT), OPTO_MASK, PPC, { RA, SI } }, +{ "tllti", OPTO(3,TOLLT), OPTO_MASK, POWER, { RA, SI } }, +{ "tweqi", OPTO(3,TOEQ), OPTO_MASK, PPC, { RA, SI } }, +{ "teqi", OPTO(3,TOEQ), OPTO_MASK, POWER, { RA, SI } }, +{ "twlgei", OPTO(3,TOLGE), OPTO_MASK, PPC, { RA, SI } }, +{ "tlgei", OPTO(3,TOLGE), OPTO_MASK, POWER, { RA, SI } }, +{ "twlnli", OPTO(3,TOLNL), OPTO_MASK, PPC, { RA, SI } }, +{ "tlnli", OPTO(3,TOLNL), OPTO_MASK, POWER, { RA, SI } }, +{ "twllei", OPTO(3,TOLLE), OPTO_MASK, PPC, { RA, SI } }, +{ "tllei", OPTO(3,TOLLE), OPTO_MASK, POWER, { RA, SI } }, +{ "twlngi", OPTO(3,TOLNG), OPTO_MASK, PPC, { RA, SI } }, +{ "tlngi", OPTO(3,TOLNG), OPTO_MASK, POWER, { RA, SI } }, +{ "twgti", OPTO(3,TOGT), OPTO_MASK, PPC, { RA, SI } }, +{ "tgti", OPTO(3,TOGT), OPTO_MASK, POWER, { RA, SI } }, +{ "twgei", OPTO(3,TOGE), OPTO_MASK, PPC, { RA, SI } }, +{ "tgei", OPTO(3,TOGE), OPTO_MASK, POWER, { RA, SI } }, +{ "twnli", OPTO(3,TONL), OPTO_MASK, PPC, { RA, SI } }, +{ "tnli", OPTO(3,TONL), OPTO_MASK, POWER, { RA, SI } }, +{ "twlti", OPTO(3,TOLT), OPTO_MASK, PPC, { RA, SI } }, +{ "tlti", OPTO(3,TOLT), OPTO_MASK, POWER, { RA, SI } }, +{ "twlei", OPTO(3,TOLE), OPTO_MASK, PPC, { RA, SI } }, +{ "tlei", OPTO(3,TOLE), OPTO_MASK, POWER, { RA, SI } }, +{ "twngi", OPTO(3,TONG), OPTO_MASK, PPC, { RA, SI } }, +{ "tngi", OPTO(3,TONG), OPTO_MASK, POWER, { RA, SI } }, +{ "twnei", OPTO(3,TONE), OPTO_MASK, PPC, { RA, SI } }, +{ "tnei", OPTO(3,TONE), OPTO_MASK, POWER, { RA, SI } }, +{ "twi", OP(3), OP_MASK, PPC, { TO, RA, SI } }, +{ "ti", OP(3), OP_MASK, POWER, { TO, RA, SI } }, + +{ "mulli", OP(7), OP_MASK, PPC, { RT, RA, SI } }, +{ "muli", OP(7), OP_MASK, POWER, { RT, RA, SI } }, + +{ "subfic", OP(8), OP_MASK, PPC, { RT, RA, SI } }, +{ "sfi", OP(8), OP_MASK, POWER, { RT, RA, SI } }, + +{ "dozi", OP(9), OP_MASK, POWER|M601, { RT, RA, SI } }, + +{ "cmplwi", OPL(10,0), OPL_MASK, PPC, { OBF, RA, UI } }, +{ "cmpldi", OPL(10,1), OPL_MASK, PPC|B64, { OBF, RA, UI } }, +{ "cmpli", OP(10), OP_MASK, PPC, { BF, L, RA, UI } }, +{ "cmpli", OP(10), OP_MASK, POWER, { BF, RA, UI } }, + +{ "cmpwi", OPL(11,0), OPL_MASK, PPC, { OBF, RA, SI } }, +{ "cmpdi", OPL(11,1), OPL_MASK, PPC|B64, { OBF, RA, SI } }, +{ "cmpi", OP(11), OP_MASK, PPC, { BF, L, RA, SI } }, +{ "cmpi", OP(11), OP_MASK, POWER, { BF, RA, SI } }, + +{ "addic", OP(12), OP_MASK, PPC, { RT, RA, SI } }, +{ "ai", OP(12), OP_MASK, POWER, { RT, RA, SI } }, +{ "subic", OP(12), OP_MASK, PPC, { RT, RA, NSI } }, + +{ "addic.", OP(13), OP_MASK, PPC, { RT, RA, SI } }, +{ "ai.", OP(13), OP_MASK, POWER, { RT, RA, SI } }, +{ "subic.", OP(13), OP_MASK, PPC, { RT, RA, NSI } }, + +{ "li", OP(14), DRA_MASK, PPC, { RT, SI } }, +{ "lil", OP(14), DRA_MASK, POWER, { RT, SI } }, +{ "addi", OP(14), OP_MASK, PPC, { RT, RA, SI } }, +{ "cal", OP(14), OP_MASK, POWER, { RT, D, RA } }, +{ "subi", OP(14), OP_MASK, PPC, { RT, RA, NSI } }, +{ "la", OP(14), OP_MASK, PPC, { RT, D, RA } }, + +{ "lis", OP(15), DRA_MASK, PPC, { RT, SISIGNOPT } }, +{ "liu", OP(15), DRA_MASK, POWER, { RT, SISIGNOPT } }, +{ "addis", OP(15), OP_MASK, PPC, { RT,RA,SISIGNOPT } }, +{ "cau", OP(15), OP_MASK, POWER, { RT,RA,SISIGNOPT } }, +{ "subis", OP(15), OP_MASK, PPC, { RT, RA, NSI } }, + +{ "bdnz-", BBO(16,BODNZ,0,0), BBOYBI_MASK, PPC, { BDM } }, +{ "bdnz+", BBO(16,BODNZ,0,0), BBOYBI_MASK, PPC, { BDP } }, +{ "bdnz", BBO(16,BODNZ,0,0), BBOYBI_MASK, PPC, { BD } }, +{ "bdn", BBO(16,BODNZ,0,0), BBOYBI_MASK, POWER, { BD } }, +{ "bdnzl-", BBO(16,BODNZ,0,1), BBOYBI_MASK, PPC, { BDM } }, +{ "bdnzl+", BBO(16,BODNZ,0,1), BBOYBI_MASK, PPC, { BDP } }, +{ "bdnzl", BBO(16,BODNZ,0,1), BBOYBI_MASK, PPC, { BD } }, +{ "bdnl", BBO(16,BODNZ,0,1), BBOYBI_MASK, POWER, { BD } }, +{ "bdnza-", BBO(16,BODNZ,1,0), BBOYBI_MASK, PPC, { BDMA } }, +{ "bdnza+", BBO(16,BODNZ,1,0), BBOYBI_MASK, PPC, { BDPA } }, +{ "bdnza", BBO(16,BODNZ,1,0), BBOYBI_MASK, PPC, { BDA } }, +{ "bdna", BBO(16,BODNZ,1,0), BBOYBI_MASK, POWER, { BDA } }, +{ "bdnzla-", BBO(16,BODNZ,1,1), BBOYBI_MASK, PPC, { BDMA } }, +{ "bdnzla+", BBO(16,BODNZ,1,1), BBOYBI_MASK, PPC, { BDPA } }, +{ "bdnzla", BBO(16,BODNZ,1,1), BBOYBI_MASK, PPC, { BDA } }, +{ "bdnla", BBO(16,BODNZ,1,1), BBOYBI_MASK, POWER, { BDA } }, +{ "bdz-", BBO(16,BODZ,0,0), BBOYBI_MASK, PPC, { BDM } }, +{ "bdz+", BBO(16,BODZ,0,0), BBOYBI_MASK, PPC, { BDP } }, +{ "bdz", BBO(16,BODZ,0,0), BBOYBI_MASK, PPC|POWER, { BD } }, +{ "bdzl-", BBO(16,BODZ,0,1), BBOYBI_MASK, PPC, { BDM } }, +{ "bdzl+", BBO(16,BODZ,0,1), BBOYBI_MASK, PPC, { BDP } }, +{ "bdzl", BBO(16,BODZ,0,1), BBOYBI_MASK, PPC|POWER, { BD } }, +{ "bdza-", BBO(16,BODZ,1,0), BBOYBI_MASK, PPC, { BDMA } }, +{ "bdza+", BBO(16,BODZ,1,0), BBOYBI_MASK, PPC, { BDPA } }, +{ "bdza", BBO(16,BODZ,1,0), BBOYBI_MASK, PPC|POWER, { BDA } }, +{ "bdzla-", BBO(16,BODZ,1,1), BBOYBI_MASK, PPC, { BDMA } }, +{ "bdzla+", BBO(16,BODZ,1,1), BBOYBI_MASK, PPC, { BDPA } }, +{ "bdzla", BBO(16,BODZ,1,1), BBOYBI_MASK, PPC|POWER, { BDA } }, +{ "blt-", BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "blt+", BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "blt", BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bltl-", BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bltl+", BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bltl", BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "blta-", BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "blta+", BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "blta", BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bltla-", BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bltla+", BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bltla", BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bgt-", BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bgt+", BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bgt", BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bgtl-", BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bgtl+", BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bgtl", BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bgta-", BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bgta+", BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bgta", BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bgtla-", BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bgtla+", BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bgtla", BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "beq-", BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "beq+", BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "beq", BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "beql-", BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "beql+", BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "beql", BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "beqa-", BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "beqa+", BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "beqa", BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "beqla-", BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "beqla+", BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "beqla", BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bso-", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bso+", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bso", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bsol-", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bsol+", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bsol", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bsoa-", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bsoa+", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bsoa", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bsola-", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bsola+", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bsola", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bun-", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bun+", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bun", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BD } }, +{ "bunl-", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bunl+", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bunl", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BD } }, +{ "buna-", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "buna+", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "buna", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDA } }, +{ "bunla-", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bunla+", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bunla", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDA } }, +{ "bge-", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bge+", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bge", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bgel-", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bgel+", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bgel", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bgea-", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bgea+", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bgea", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bgela-", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bgela+", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bgela", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bnl-", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bnl+", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bnl", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnll-", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bnll+", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bnll", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnla-", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnla+", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnla", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bnlla-", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnlla+", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnlla", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "ble-", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "ble+", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "ble", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "blel-", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "blel+", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "blel", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "blea-", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "blea+", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "blea", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "blela-", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "blela+", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "blela", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bng-", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bng+", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bng", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bngl-", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bngl+", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bngl", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnga-", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnga+", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnga", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bngla-", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bngla+", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bngla", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bne-", BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bne+", BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bne", BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnel-", BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bnel+", BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bnel", BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnea-", BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnea+", BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnea", BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bnela-", BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnela+", BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnela", BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bns-", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bns+", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bns", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnsl-", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bnsl+", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bnsl", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnsa-", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnsa+", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnsa", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bnsla-", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnsla+", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnsla", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bnu-", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bnu+", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bnu", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BD } }, +{ "bnul-", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bnul+", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bnul", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BD } }, +{ "bnua-", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnua+", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnua", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDA } }, +{ "bnula-", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnula+", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnula", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDA } }, +{ "bdnzt-", BBO(16,BODNZT,0,0), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdnzt+", BBO(16,BODNZT,0,0), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdnzt", BBO(16,BODNZT,0,0), BBOY_MASK, PPC, { BI, BD } }, +{ "bdnztl-", BBO(16,BODNZT,0,1), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdnztl+", BBO(16,BODNZT,0,1), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdnztl", BBO(16,BODNZT,0,1), BBOY_MASK, PPC, { BI, BD } }, +{ "bdnzta-", BBO(16,BODNZT,1,0), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdnzta+", BBO(16,BODNZT,1,0), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdnzta", BBO(16,BODNZT,1,0), BBOY_MASK, PPC, { BI, BDA } }, +{ "bdnztla-",BBO(16,BODNZT,1,1), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdnztla+",BBO(16,BODNZT,1,1), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdnztla", BBO(16,BODNZT,1,1), BBOY_MASK, PPC, { BI, BDA } }, +{ "bdnzf-", BBO(16,BODNZF,0,0), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdnzf+", BBO(16,BODNZF,0,0), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdnzf", BBO(16,BODNZF,0,0), BBOY_MASK, PPC, { BI, BD } }, +{ "bdnzfl-", BBO(16,BODNZF,0,1), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdnzfl+", BBO(16,BODNZF,0,1), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdnzfl", BBO(16,BODNZF,0,1), BBOY_MASK, PPC, { BI, BD } }, +{ "bdnzfa-", BBO(16,BODNZF,1,0), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdnzfa+", BBO(16,BODNZF,1,0), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdnzfa", BBO(16,BODNZF,1,0), BBOY_MASK, PPC, { BI, BDA } }, +{ "bdnzfla-",BBO(16,BODNZF,1,1), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdnzfla+",BBO(16,BODNZF,1,1), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdnzfla", BBO(16,BODNZF,1,1), BBOY_MASK, PPC, { BI, BDA } }, +{ "bt-", BBO(16,BOT,0,0), BBOY_MASK, PPC, { BI, BDM } }, +{ "bt+", BBO(16,BOT,0,0), BBOY_MASK, PPC, { BI, BDP } }, +{ "bt", BBO(16,BOT,0,0), BBOY_MASK, PPC, { BI, BD } }, +{ "bbt", BBO(16,BOT,0,0), BBOY_MASK, POWER, { BI, BD } }, +{ "btl-", BBO(16,BOT,0,1), BBOY_MASK, PPC, { BI, BDM } }, +{ "btl+", BBO(16,BOT,0,1), BBOY_MASK, PPC, { BI, BDP } }, +{ "btl", BBO(16,BOT,0,1), BBOY_MASK, PPC, { BI, BD } }, +{ "bbtl", BBO(16,BOT,0,1), BBOY_MASK, POWER, { BI, BD } }, +{ "bta-", BBO(16,BOT,1,0), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bta+", BBO(16,BOT,1,0), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bta", BBO(16,BOT,1,0), BBOY_MASK, PPC, { BI, BDA } }, +{ "bbta", BBO(16,BOT,1,0), BBOY_MASK, POWER, { BI, BDA } }, +{ "btla-", BBO(16,BOT,1,1), BBOY_MASK, PPC, { BI, BDMA } }, +{ "btla+", BBO(16,BOT,1,1), BBOY_MASK, PPC, { BI, BDPA } }, +{ "btla", BBO(16,BOT,1,1), BBOY_MASK, PPC, { BI, BDA } }, +{ "bbtla", BBO(16,BOT,1,1), BBOY_MASK, POWER, { BI, BDA } }, +{ "bf-", BBO(16,BOF,0,0), BBOY_MASK, PPC, { BI, BDM } }, +{ "bf+", BBO(16,BOF,0,0), BBOY_MASK, PPC, { BI, BDP } }, +{ "bf", BBO(16,BOF,0,0), BBOY_MASK, PPC, { BI, BD } }, +{ "bbf", BBO(16,BOF,0,0), BBOY_MASK, POWER, { BI, BD } }, +{ "bfl-", BBO(16,BOF,0,1), BBOY_MASK, PPC, { BI, BDM } }, +{ "bfl+", BBO(16,BOF,0,1), BBOY_MASK, PPC, { BI, BDP } }, +{ "bfl", BBO(16,BOF,0,1), BBOY_MASK, PPC, { BI, BD } }, +{ "bbfl", BBO(16,BOF,0,1), BBOY_MASK, POWER, { BI, BD } }, +{ "bfa-", BBO(16,BOF,1,0), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bfa+", BBO(16,BOF,1,0), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bfa", BBO(16,BOF,1,0), BBOY_MASK, PPC, { BI, BDA } }, +{ "bbfa", BBO(16,BOF,1,0), BBOY_MASK, POWER, { BI, BDA } }, +{ "bfla-", BBO(16,BOF,1,1), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bfla+", BBO(16,BOF,1,1), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bfla", BBO(16,BOF,1,1), BBOY_MASK, PPC, { BI, BDA } }, +{ "bbfla", BBO(16,BOF,1,1), BBOY_MASK, POWER, { BI, BDA } }, +{ "bdzt-", BBO(16,BODZT,0,0), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdzt+", BBO(16,BODZT,0,0), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdzt", BBO(16,BODZT,0,0), BBOY_MASK, PPC, { BI, BD } }, +{ "bdztl-", BBO(16,BODZT,0,1), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdztl+", BBO(16,BODZT,0,1), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdztl", BBO(16,BODZT,0,1), BBOY_MASK, PPC, { BI, BD } }, +{ "bdzta-", BBO(16,BODZT,1,0), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdzta+", BBO(16,BODZT,1,0), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdzta", BBO(16,BODZT,1,0), BBOY_MASK, PPC, { BI, BDA } }, +{ "bdztla-", BBO(16,BODZT,1,1), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdztla+", BBO(16,BODZT,1,1), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdztla", BBO(16,BODZT,1,1), BBOY_MASK, PPC, { BI, BDA } }, +{ "bdzf-", BBO(16,BODZF,0,0), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdzf+", BBO(16,BODZF,0,0), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdzf", BBO(16,BODZF,0,0), BBOY_MASK, PPC, { BI, BD } }, +{ "bdzfl-", BBO(16,BODZF,0,1), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdzfl+", BBO(16,BODZF,0,1), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdzfl", BBO(16,BODZF,0,1), BBOY_MASK, PPC, { BI, BD } }, +{ "bdzfa-", BBO(16,BODZF,1,0), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdzfa+", BBO(16,BODZF,1,0), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdzfa", BBO(16,BODZF,1,0), BBOY_MASK, PPC, { BI, BDA } }, +{ "bdzfla-", BBO(16,BODZF,1,1), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdzfla+", BBO(16,BODZF,1,1), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdzfla", BBO(16,BODZF,1,1), BBOY_MASK, PPC, { BI, BDA } }, +{ "bc-", B(16,0,0), B_MASK, PPC, { BOE, BI, BDM } }, +{ "bc+", B(16,0,0), B_MASK, PPC, { BOE, BI, BDP } }, +{ "bc", B(16,0,0), B_MASK, PPC|POWER, { BO, BI, BD } }, +{ "bcl-", B(16,0,1), B_MASK, PPC, { BOE, BI, BDM } }, +{ "bcl+", B(16,0,1), B_MASK, PPC, { BOE, BI, BDP } }, +{ "bcl", B(16,0,1), B_MASK, PPC|POWER, { BO, BI, BD } }, +{ "bca-", B(16,1,0), B_MASK, PPC, { BOE, BI, BDMA } }, +{ "bca+", B(16,1,0), B_MASK, PPC, { BOE, BI, BDPA } }, +{ "bca", B(16,1,0), B_MASK, PPC|POWER, { BO, BI, BDA } }, +{ "bcla-", B(16,1,1), B_MASK, PPC, { BOE, BI, BDMA } }, +{ "bcla+", B(16,1,1), B_MASK, PPC, { BOE, BI, BDPA } }, +{ "bcla", B(16,1,1), B_MASK, PPC|POWER, { BO, BI, BDA } }, + +{ "sc", SC(17,1,0), 0xffffffff, PPC, { 0 } }, +{ "svc", SC(17,0,0), SC_MASK, POWER, { LEV, FL1, FL2 } }, +{ "svcl", SC(17,0,1), SC_MASK, POWER, { LEV, FL1, FL2 } }, +{ "svca", SC(17,1,0), SC_MASK, POWER, { SV } }, +{ "svcla", SC(17,1,1), SC_MASK, POWER, { SV } }, + +{ "b", B(18,0,0), B_MASK, PPC|POWER, { LI } }, +{ "bl", B(18,0,1), B_MASK, PPC|POWER, { LI } }, +{ "ba", B(18,1,0), B_MASK, PPC|POWER, { LIA } }, +{ "bla", B(18,1,1), B_MASK, PPC|POWER, { LIA } }, + +{ "mcrf", XL(19,0), XLBB_MASK|(3<<21)|(3<<16), PPC|POWER, { BF, BFA } }, + +{ "blr", XLO(19,BOU,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "br", XLO(19,BOU,16,0), XLBOBIBB_MASK, POWER, { 0 } }, +{ "blrl", XLO(19,BOU,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "brl", XLO(19,BOU,16,1), XLBOBIBB_MASK, POWER, { 0 } }, +{ "bdnzlr", XLO(19,BODNZ,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdnzlr-", XLO(19,BODNZ,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdnzlr+", XLO(19,BODNZP,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdnzlrl", XLO(19,BODNZ,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdnzlrl-",XLO(19,BODNZ,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdnzlrl+",XLO(19,BODNZP,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdzlr", XLO(19,BODZ,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdzlr-", XLO(19,BODZ,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdzlr+", XLO(19,BODZP,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdzlrl", XLO(19,BODZ,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdzlrl-", XLO(19,BODZ,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdzlrl+", XLO(19,BODZP,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bltlr", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltlr-", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltlr+", XLOCB(19,BOTP,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltr", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bltlrl", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltlrl-", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltlrl+", XLOCB(19,BOTP,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltrl", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bgtlr", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtlr-", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtlr+", XLOCB(19,BOTP,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtr", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bgtlrl", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtlrl-", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtlrl+", XLOCB(19,BOTP,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtrl", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "beqlr", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqlr-", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqlr+", XLOCB(19,BOTP,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqr", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "beqlrl", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqlrl-", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqlrl+", XLOCB(19,BOTP,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqrl", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bsolr", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsolr-", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsolr+", XLOCB(19,BOTP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsor", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bsolrl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsolrl-", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsolrl+", XLOCB(19,BOTP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsorl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bunlr", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunlr-", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunlr+", XLOCB(19,BOTP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunlrl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunlrl-", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunlrl+", XLOCB(19,BOTP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgelr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgelr-", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgelr+", XLOCB(19,BOFP,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bger", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bgelrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgelrl-", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgelrl+", XLOCB(19,BOFP,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgerl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnllr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnllr-", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnllr+", XLOCB(19,BOFP,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnllrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnllrl-", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnllrl+", XLOCB(19,BOFP,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "blelr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "blelr-", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "blelr+", XLOCB(19,BOFP,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bler", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "blelrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "blelrl-", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "blelrl+", XLOCB(19,BOFP,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "blerl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnglr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnglr-", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnglr+", XLOCB(19,BOFP,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnglrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnglrl-", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnglrl+", XLOCB(19,BOFP,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnelr", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnelr-", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnelr+", XLOCB(19,BOFP,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bner", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnelrl", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnelrl-", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnelrl+", XLOCB(19,BOFP,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnerl", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnslr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnslr-", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnslr+", XLOCB(19,BOFP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnslrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnslrl-", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnslrl+", XLOCB(19,BOFP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnulr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnulr-", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnulr+", XLOCB(19,BOFP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnulrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnulrl-", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnulrl+", XLOCB(19,BOFP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "btlr", XLO(19,BOT,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "btlr-", XLO(19,BOT,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "btlr+", XLO(19,BOTP,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bbtr", XLO(19,BOT,16,0), XLBOBB_MASK, POWER, { BI } }, +{ "btlrl", XLO(19,BOT,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "btlrl-", XLO(19,BOT,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "btlrl+", XLO(19,BOTP,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bbtrl", XLO(19,BOT,16,1), XLBOBB_MASK, POWER, { BI } }, +{ "bflr", XLO(19,BOF,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bflr-", XLO(19,BOF,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bflr+", XLO(19,BOFP,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bbfr", XLO(19,BOF,16,0), XLBOBB_MASK, POWER, { BI } }, +{ "bflrl", XLO(19,BOF,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bflrl-", XLO(19,BOF,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bflrl+", XLO(19,BOFP,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bbfrl", XLO(19,BOF,16,1), XLBOBB_MASK, POWER, { BI } }, +{ "bdnztlr", XLO(19,BODNZT,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdnztlr-",XLO(19,BODNZT,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdnztlr+",XLO(19,BODNZTP,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdnztlrl",XLO(19,BODNZT,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdnztlrl-",XLO(19,BODNZT,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdnztlrl+",XLO(19,BODNZTP,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdnzflr", XLO(19,BODNZF,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdnzflr-",XLO(19,BODNZF,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdnzflr+",XLO(19,BODNZFP,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdnzflrl",XLO(19,BODNZF,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdnzflrl-",XLO(19,BODNZF,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdnzflrl+",XLO(19,BODNZFP,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdztlr", XLO(19,BODZT,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdztlr-", XLO(19,BODZT,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdztlr+", XLO(19,BODZTP,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdztlrl", XLO(19,BODZT,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdztlrl-",XLO(19,BODZT,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdztlrl+",XLO(19,BODZTP,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdzflr", XLO(19,BODZF,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdzflr-", XLO(19,BODZF,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdzflr+", XLO(19,BODZFP,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdzflrl", XLO(19,BODZF,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdzflrl-",XLO(19,BODZF,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdzflrl+",XLO(19,BODZFP,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bclr", XLLK(19,16,0), XLYBB_MASK, PPC, { BO, BI } }, +{ "bclrl", XLLK(19,16,1), XLYBB_MASK, PPC, { BO, BI } }, +{ "bclr+", XLYLK(19,16,1,0), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bclrl+", XLYLK(19,16,1,1), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bclr-", XLYLK(19,16,0,0), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bclrl-", XLYLK(19,16,0,1), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bcr", XLLK(19,16,0), XLBB_MASK, POWER, { BO, BI } }, +{ "bcrl", XLLK(19,16,1), XLBB_MASK, POWER, { BO, BI } }, + +{ "crnot", XL(19,33), XL_MASK, PPC, { BT, BA, BBA } }, +{ "crnor", XL(19,33), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "rfi", XL(19,50), 0xffffffff, PPC|POWER, { 0 } }, +{ "rfci", XL(19,51), 0xffffffff, PPC, { 0 } }, + +{ "rfsvc", XL(19,82), 0xffffffff, POWER, { 0 } }, + +{ "crandc", XL(19,129), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "isync", XL(19,150), 0xffffffff, PPC, { 0 } }, +{ "ics", XL(19,150), 0xffffffff, POWER, { 0 } }, + +{ "crclr", XL(19,193), XL_MASK, PPC, { BT, BAT, BBA } }, +{ "crxor", XL(19,193), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "crnand", XL(19,225), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "crand", XL(19,257), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "crset", XL(19,289), XL_MASK, PPC, { BT, BAT, BBA } }, +{ "creqv", XL(19,289), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "crorc", XL(19,417), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "crmove", XL(19,449), XL_MASK, PPC, { BT, BA, BBA } }, +{ "cror", XL(19,449), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "bctr", XLO(19,BOU,528,0), XLBOBIBB_MASK, PPC|POWER, { 0 } }, +{ "bctrl", XLO(19,BOU,528,1), XLBOBIBB_MASK, PPC|POWER, { 0 } }, +{ "bltctr", XLOCB(19,BOT,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltctr-", XLOCB(19,BOT,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltctr+", XLOCB(19,BOTP,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltctrl", XLOCB(19,BOT,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltctrl-",XLOCB(19,BOT,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltctrl+",XLOCB(19,BOTP,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtctr", XLOCB(19,BOT,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtctr-", XLOCB(19,BOT,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtctr+", XLOCB(19,BOTP,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtctrl", XLOCB(19,BOT,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtctrl-",XLOCB(19,BOT,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtctrl+",XLOCB(19,BOTP,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqctr", XLOCB(19,BOT,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqctr-", XLOCB(19,BOT,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqctr+", XLOCB(19,BOTP,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqctrl", XLOCB(19,BOT,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqctrl-",XLOCB(19,BOT,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqctrl+",XLOCB(19,BOTP,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsoctr", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsoctr-", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsoctr+", XLOCB(19,BOTP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsoctrl", XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsoctrl-",XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsoctrl+",XLOCB(19,BOTP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunctr", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunctr-", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunctr+", XLOCB(19,BOTP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunctrl", XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunctrl-",XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunctrl+",XLOCB(19,BOTP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgectr", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgectr-", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgectr+", XLOCB(19,BOFP,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgectrl", XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgectrl-",XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgectrl+",XLOCB(19,BOFP,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlctr", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlctr-", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlctr+", XLOCB(19,BOFP,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlctrl", XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlctrl-",XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlctrl+",XLOCB(19,BOFP,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "blectr", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "blectr-", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "blectr+", XLOCB(19,BOFP,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "blectrl", XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "blectrl-",XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "blectrl+",XLOCB(19,BOFP,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngctr", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngctr-", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngctr+", XLOCB(19,BOFP,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngctrl", XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngctrl-",XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngctrl+",XLOCB(19,BOFP,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnectr", XLOCB(19,BOF,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnectr-", XLOCB(19,BOF,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnectr+", XLOCB(19,BOFP,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnectrl", XLOCB(19,BOF,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnectrl-",XLOCB(19,BOF,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnectrl+",XLOCB(19,BOFP,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsctr", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsctr-", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsctr+", XLOCB(19,BOFP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsctrl", XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsctrl-",XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsctrl+",XLOCB(19,BOFP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnuctr", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnuctr-", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnuctr+", XLOCB(19,BOFP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnuctrl", XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnuctrl-",XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnuctrl+",XLOCB(19,BOFP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "btctr", XLO(19,BOT,528,0), XLBOBB_MASK, PPC, { BI } }, +{ "btctr-", XLO(19,BOT,528,0), XLBOBB_MASK, PPC, { BI } }, +{ "btctr+", XLO(19,BOTP,528,0), XLBOBB_MASK, PPC, { BI } }, +{ "btctrl", XLO(19,BOT,528,1), XLBOBB_MASK, PPC, { BI } }, +{ "btctrl-", XLO(19,BOT,528,1), XLBOBB_MASK, PPC, { BI } }, +{ "btctrl+", XLO(19,BOTP,528,1), XLBOBB_MASK, PPC, { BI } }, +{ "bfctr", XLO(19,BOF,528,0), XLBOBB_MASK, PPC, { BI } }, +{ "bfctr-", XLO(19,BOF,528,0), XLBOBB_MASK, PPC, { BI } }, +{ "bfctr+", XLO(19,BOFP,528,0), XLBOBB_MASK, PPC, { BI } }, +{ "bfctrl", XLO(19,BOF,528,1), XLBOBB_MASK, PPC, { BI } }, +{ "bfctrl-", XLO(19,BOF,528,1), XLBOBB_MASK, PPC, { BI } }, +{ "bfctrl+", XLO(19,BOFP,528,1), XLBOBB_MASK, PPC, { BI } }, +{ "bcctr", XLLK(19,528,0), XLYBB_MASK, PPC, { BO, BI } }, +{ "bcctr-", XLYLK(19,528,0,0), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bcctr+", XLYLK(19,528,1,0), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bcctrl", XLLK(19,528,1), XLYBB_MASK, PPC, { BO, BI } }, +{ "bcctrl-", XLYLK(19,528,0,1), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bcctrl+", XLYLK(19,528,1,1), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bcc", XLLK(19,528,0), XLBB_MASK, POWER, { BO, BI } }, +{ "bccl", XLLK(19,528,1), XLBB_MASK, POWER, { BO, BI } }, + +{ "rlwimi", M(20,0), M_MASK, PPC, { RA,RS,SH,MBE,ME } }, +{ "rlimi", M(20,0), M_MASK, POWER, { RA,RS,SH,MBE,ME } }, + +{ "rlwimi.", M(20,1), M_MASK, PPC, { RA,RS,SH,MBE,ME } }, +{ "rlimi.", M(20,1), M_MASK, POWER, { RA,RS,SH,MBE,ME } }, + +{ "rotlwi", MME(21,31,0), MMBME_MASK, PPC, { RA, RS, SH } }, +{ "clrlwi", MME(21,31,0), MSHME_MASK, PPC, { RA, RS, MB } }, +{ "rlwinm", M(21,0), M_MASK, PPC, { RA,RS,SH,MBE,ME } }, +{ "rlinm", M(21,0), M_MASK, POWER, { RA,RS,SH,MBE,ME } }, +{ "rotlwi.", MME(21,31,1), MMBME_MASK, PPC, { RA,RS,SH } }, +{ "clrlwi.", MME(21,31,1), MSHME_MASK, PPC, { RA, RS, MB } }, +{ "rlwinm.", M(21,1), M_MASK, PPC, { RA,RS,SH,MBE,ME } }, +{ "rlinm.", M(21,1), M_MASK, POWER, { RA,RS,SH,MBE,ME } }, + +{ "rlmi", M(22,0), M_MASK, POWER|M601, { RA,RS,RB,MBE,ME } }, +{ "rlmi.", M(22,1), M_MASK, POWER|M601, { RA,RS,RB,MBE,ME } }, + +{ "rotlw", MME(23,31,0), MMBME_MASK, PPC, { RA, RS, RB } }, +{ "rlwnm", M(23,0), M_MASK, PPC, { RA,RS,RB,MBE,ME } }, +{ "rlnm", M(23,0), M_MASK, POWER, { RA,RS,RB,MBE,ME } }, +{ "rotlw.", MME(23,31,1), MMBME_MASK, PPC, { RA, RS, RB } }, +{ "rlwnm.", M(23,1), M_MASK, PPC, { RA,RS,RB,MBE,ME } }, +{ "rlnm.", M(23,1), M_MASK, POWER, { RA,RS,RB,MBE,ME } }, + +{ "nop", OP(24), 0xffffffff, PPC, { 0 } }, +{ "ori", OP(24), OP_MASK, PPC, { RA, RS, UI } }, +{ "oril", OP(24), OP_MASK, POWER, { RA, RS, UI } }, + +{ "oris", OP(25), OP_MASK, PPC, { RA, RS, UI } }, +{ "oriu", OP(25), OP_MASK, POWER, { RA, RS, UI } }, + +{ "xori", OP(26), OP_MASK, PPC, { RA, RS, UI } }, +{ "xoril", OP(26), OP_MASK, POWER, { RA, RS, UI } }, + +{ "xoris", OP(27), OP_MASK, PPC, { RA, RS, UI } }, +{ "xoriu", OP(27), OP_MASK, POWER, { RA, RS, UI } }, + +{ "andi.", OP(28), OP_MASK, PPC, { RA, RS, UI } }, +{ "andil.", OP(28), OP_MASK, POWER, { RA, RS, UI } }, + +{ "andis.", OP(29), OP_MASK, PPC, { RA, RS, UI } }, +{ "andiu.", OP(29), OP_MASK, POWER, { RA, RS, UI } }, + +{ "rotldi", MD(30,0,0), MDMB_MASK, PPC|B64, { RA, RS, SH6 } }, +{ "clrldi", MD(30,0,0), MDSH_MASK, PPC|B64, { RA, RS, MB6 } }, +{ "rldicl", MD(30,0,0), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } }, +{ "rotldi.", MD(30,0,1), MDMB_MASK, PPC|B64, { RA, RS, SH6 } }, +{ "clrldi.", MD(30,0,1), MDSH_MASK, PPC|B64, { RA, RS, MB6 } }, +{ "rldicl.", MD(30,0,1), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } }, + +{ "rldicr", MD(30,1,0), MD_MASK, PPC|B64, { RA, RS, SH6, ME6 } }, +{ "rldicr.", MD(30,1,1), MD_MASK, PPC|B64, { RA, RS, SH6, ME6 } }, + +{ "rldic", MD(30,2,0), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } }, +{ "rldic.", MD(30,2,1), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } }, + +{ "rldimi", MD(30,3,0), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } }, +{ "rldimi.", MD(30,3,1), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } }, + +{ "rotld", MDS(30,8,0), MDSMB_MASK, PPC|B64, { RA, RS, RB } }, +{ "rldcl", MDS(30,8,0), MDS_MASK, PPC|B64, { RA, RS, RB, MB6 } }, +{ "rotld.", MDS(30,8,1), MDSMB_MASK, PPC|B64, { RA, RS, RB } }, +{ "rldcl.", MDS(30,8,1), MDS_MASK, PPC|B64, { RA, RS, RB, MB6 } }, + +{ "rldcr", MDS(30,9,0), MDS_MASK, PPC|B64, { RA, RS, RB, ME6 } }, +{ "rldcr.", MDS(30,9,1), MDS_MASK, PPC|B64, { RA, RS, RB, ME6 } }, + +{ "cmpw", XCMPL(31,0,0), XCMPL_MASK, PPC, { OBF, RA, RB } }, +{ "cmpd", XCMPL(31,0,1), XCMPL_MASK, PPC|B64, { OBF, RA, RB } }, +{ "cmp", X(31,0), XCMP_MASK, PPC, { BF, L, RA, RB } }, +{ "cmp", X(31,0), XCMPL_MASK, POWER, { BF, RA, RB } }, + +{ "twlgt", XTO(31,4,TOLGT), XTO_MASK, PPC, { RA, RB } }, +{ "tlgt", XTO(31,4,TOLGT), XTO_MASK, POWER, { RA, RB } }, +{ "twllt", XTO(31,4,TOLLT), XTO_MASK, PPC, { RA, RB } }, +{ "tllt", XTO(31,4,TOLLT), XTO_MASK, POWER, { RA, RB } }, +{ "tweq", XTO(31,4,TOEQ), XTO_MASK, PPC, { RA, RB } }, +{ "teq", XTO(31,4,TOEQ), XTO_MASK, POWER, { RA, RB } }, +{ "twlge", XTO(31,4,TOLGE), XTO_MASK, PPC, { RA, RB } }, +{ "tlge", XTO(31,4,TOLGE), XTO_MASK, POWER, { RA, RB } }, +{ "twlnl", XTO(31,4,TOLNL), XTO_MASK, PPC, { RA, RB } }, +{ "tlnl", XTO(31,4,TOLNL), XTO_MASK, POWER, { RA, RB } }, +{ "twlle", XTO(31,4,TOLLE), XTO_MASK, PPC, { RA, RB } }, +{ "tlle", XTO(31,4,TOLLE), XTO_MASK, POWER, { RA, RB } }, +{ "twlng", XTO(31,4,TOLNG), XTO_MASK, PPC, { RA, RB } }, +{ "tlng", XTO(31,4,TOLNG), XTO_MASK, POWER, { RA, RB } }, +{ "twgt", XTO(31,4,TOGT), XTO_MASK, PPC, { RA, RB } }, +{ "tgt", XTO(31,4,TOGT), XTO_MASK, POWER, { RA, RB } }, +{ "twge", XTO(31,4,TOGE), XTO_MASK, PPC, { RA, RB } }, +{ "tge", XTO(31,4,TOGE), XTO_MASK, POWER, { RA, RB } }, +{ "twnl", XTO(31,4,TONL), XTO_MASK, PPC, { RA, RB } }, +{ "tnl", XTO(31,4,TONL), XTO_MASK, POWER, { RA, RB } }, +{ "twlt", XTO(31,4,TOLT), XTO_MASK, PPC, { RA, RB } }, +{ "tlt", XTO(31,4,TOLT), XTO_MASK, POWER, { RA, RB } }, +{ "twle", XTO(31,4,TOLE), XTO_MASK, PPC, { RA, RB } }, +{ "tle", XTO(31,4,TOLE), XTO_MASK, POWER, { RA, RB } }, +{ "twng", XTO(31,4,TONG), XTO_MASK, PPC, { RA, RB } }, +{ "tng", XTO(31,4,TONG), XTO_MASK, POWER, { RA, RB } }, +{ "twne", XTO(31,4,TONE), XTO_MASK, PPC, { RA, RB } }, +{ "tne", XTO(31,4,TONE), XTO_MASK, POWER, { RA, RB } }, +{ "trap", XTO(31,4,TOU), 0xffffffff, PPC, { 0 } }, +{ "tw", X(31,4), X_MASK, PPC, { TO, RA, RB } }, +{ "t", X(31,4), X_MASK, POWER, { TO, RA, RB } }, + +{ "subfc", XO(31,8,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "sf", XO(31,8,0,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "subc", XO(31,8,0,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfc.", XO(31,8,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "sf.", XO(31,8,0,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "subc.", XO(31,8,0,1), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfco", XO(31,8,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "sfo", XO(31,8,1,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "subco", XO(31,8,1,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfco.", XO(31,8,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "sfo.", XO(31,8,1,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "subco.", XO(31,8,1,1), XO_MASK, PPC, { RT, RB, RA } }, + +{ "mulhdu", XO(31,9,0,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "mulhdu.", XO(31,9,0,1), XO_MASK, PPC|B64, { RT, RA, RB } }, + +{ "addc", XO(31,10,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "a", XO(31,10,0,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "addc.", XO(31,10,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "a.", XO(31,10,0,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "addco", XO(31,10,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "ao", XO(31,10,1,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "addco.", XO(31,10,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "ao.", XO(31,10,1,1), XO_MASK, POWER, { RT, RA, RB } }, + +{ "mulhwu", XO(31,11,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "mulhwu.", XO(31,11,0,1), XO_MASK, PPC, { RT, RA, RB } }, + +{ "mfcr", X(31,19), XRARB_MASK, POWER|PPC, { RT } }, + +{ "lwarx", X(31,20), X_MASK, PPC, { RT, RA, RB } }, + +{ "ldx", X(31,21), X_MASK, PPC|B64, { RT, RA, RB } }, + +{ "lwzx", X(31,23), X_MASK, PPC, { RT, RA, RB } }, +{ "lx", X(31,23), X_MASK, POWER, { RT, RA, RB } }, + +{ "slw", XRC(31,24,0), X_MASK, PPC, { RA, RS, RB } }, +{ "sl", XRC(31,24,0), X_MASK, POWER, { RA, RS, RB } }, +{ "slw.", XRC(31,24,1), X_MASK, PPC, { RA, RS, RB } }, +{ "sl.", XRC(31,24,1), X_MASK, POWER, { RA, RS, RB } }, + +{ "cntlzw", XRC(31,26,0), XRB_MASK, PPC, { RA, RS } }, +{ "cntlz", XRC(31,26,0), XRB_MASK, POWER, { RA, RS } }, +{ "cntlzw.", XRC(31,26,1), XRB_MASK, PPC, { RA, RS } }, +{ "cntlz.", XRC(31,26,1), XRB_MASK, POWER, { RA, RS } }, + +{ "sld", XRC(31,27,0), X_MASK, PPC|B64, { RA, RS, RB } }, +{ "sld.", XRC(31,27,1), X_MASK, PPC|B64, { RA, RS, RB } }, + +{ "and", XRC(31,28,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "and.", XRC(31,28,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "maskg", XRC(31,29,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "maskg.", XRC(31,29,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "cmplw", XCMPL(31,32,0), XCMPL_MASK, PPC, { OBF, RA, RB } }, +{ "cmpld", XCMPL(31,32,1), XCMPL_MASK, PPC|B64, { OBF, RA, RB } }, +{ "cmpl", X(31,32), XCMP_MASK, PPC, { BF, L, RA, RB } }, +{ "cmpl", X(31,32), XCMPL_MASK, POWER, { BF, RA, RB } }, + +{ "subf", XO(31,40,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "sub", XO(31,40,0,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "subf.", XO(31,40,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "sub.", XO(31,40,0,1), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfo", XO(31,40,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "subo", XO(31,40,1,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfo.", XO(31,40,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "subo.", XO(31,40,1,1), XO_MASK, PPC, { RT, RB, RA } }, + +{ "ldux", X(31,53), X_MASK, PPC|B64, { RT, RAL, RB } }, + +{ "dcbst", X(31,54), XRT_MASK, PPC, { RA, RB } }, + +{ "lwzux", X(31,55), X_MASK, PPC, { RT, RAL, RB } }, +{ "lux", X(31,55), X_MASK, POWER, { RT, RA, RB } }, + +{ "cntlzd", XRC(31,58,0), XRB_MASK, PPC|B64, { RA, RS } }, +{ "cntlzd.", XRC(31,58,1), XRB_MASK, PPC|B64, { RA, RS } }, + +{ "andc", XRC(31,60,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "andc.", XRC(31,60,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "tdlgt", XTO(31,68,TOLGT), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdllt", XTO(31,68,TOLLT), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdeq", XTO(31,68,TOEQ), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdlge", XTO(31,68,TOLGE), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdlnl", XTO(31,68,TOLNL), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdlle", XTO(31,68,TOLLE), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdlng", XTO(31,68,TOLNG), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdgt", XTO(31,68,TOGT), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdge", XTO(31,68,TOGE), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdnl", XTO(31,68,TONL), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdlt", XTO(31,68,TOLT), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdle", XTO(31,68,TOLE), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdng", XTO(31,68,TONG), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdne", XTO(31,68,TONE), XTO_MASK, PPC|B64, { RA, RB } }, +{ "td", X(31,68), X_MASK, PPC|B64, { TO, RA, RB } }, + +{ "mulhd", XO(31,73,0,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "mulhd.", XO(31,73,0,1), XO_MASK, PPC|B64, { RT, RA, RB } }, + +{ "mulhw", XO(31,75,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "mulhw.", XO(31,75,0,1), XO_MASK, PPC, { RT, RA, RB } }, + +{ "mfmsr", X(31,83), XRARB_MASK, PPC|POWER, { RT } }, + +{ "ldarx", X(31,84), X_MASK, PPC|B64, { RT, RA, RB } }, + +{ "dcbf", X(31,86), XRT_MASK, PPC, { RA, RB } }, + +{ "lbzx", X(31,87), X_MASK, PPC|POWER, { RT, RA, RB } }, + +{ "neg", XO(31,104,0,0), XORB_MASK, PPC|POWER, { RT, RA } }, +{ "neg.", XO(31,104,0,1), XORB_MASK, PPC|POWER, { RT, RA } }, +{ "nego", XO(31,104,1,0), XORB_MASK, PPC|POWER, { RT, RA } }, +{ "nego.", XO(31,104,1,1), XORB_MASK, PPC|POWER, { RT, RA } }, + +{ "mul", XO(31,107,0,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "mul.", XO(31,107,0,1), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "mulo", XO(31,107,1,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "mulo.", XO(31,107,1,1), XO_MASK, POWER|M601, { RT, RA, RB } }, + +{ "clf", X(31,118), XRB_MASK, POWER, { RT, RA } }, + +{ "lbzux", X(31,119), X_MASK, PPC|POWER, { RT, RAL, RB } }, + +{ "not", XRC(31,124,0), X_MASK, PPC|POWER, { RA, RS, RBS } }, +{ "nor", XRC(31,124,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "not.", XRC(31,124,1), X_MASK, PPC|POWER, { RA, RS, RBS } }, +{ "nor.", XRC(31,124,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "subfe", XO(31,136,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "sfe", XO(31,136,0,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "subfe.", XO(31,136,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "sfe.", XO(31,136,0,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "subfeo", XO(31,136,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "sfeo", XO(31,136,1,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "subfeo.", XO(31,136,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "sfeo.", XO(31,136,1,1), XO_MASK, POWER, { RT, RA, RB } }, + +{ "adde", XO(31,138,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "ae", XO(31,138,0,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "adde.", XO(31,138,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "ae.", XO(31,138,0,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "addeo", XO(31,138,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "aeo", XO(31,138,1,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "addeo.", XO(31,138,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "aeo.", XO(31,138,1,1), XO_MASK, POWER, { RT, RA, RB } }, + +{ "mtcr", XFXM(31,144,0xff), XFXFXM_MASK|FXM_MASK, PPC|POWER, { RS }}, +{ "mtcrf", X(31,144), XFXFXM_MASK, PPC|POWER, { FXM, RS } }, + +{ "mtmsr", X(31,146), XRARB_MASK, PPC|POWER, { RS } }, + +{ "stdx", X(31,149), X_MASK, PPC|B64, { RS, RA, RB } }, + +{ "stwcx.", XRC(31,150,1), X_MASK, PPC, { RS, RA, RB } }, + +{ "stwx", X(31,151), X_MASK, PPC, { RS, RA, RB } }, +{ "stx", X(31,151), X_MASK, POWER, { RS, RA, RB } }, + +{ "slq", XRC(31,152,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "slq.", XRC(31,152,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "sle", XRC(31,153,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "sle.", XRC(31,153,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "stdux", X(31,181), X_MASK, PPC|B64, { RS, RAS, RB } }, + +{ "stwux", X(31,183), X_MASK, PPC, { RS, RAS, RB } }, +{ "stux", X(31,183), X_MASK, POWER, { RS, RA, RB } }, + +{ "sliq", XRC(31,184,0), X_MASK, POWER|M601, { RA, RS, SH } }, +{ "sliq.", XRC(31,184,1), X_MASK, POWER|M601, { RA, RS, SH } }, + +{ "subfze", XO(31,200,0,0), XORB_MASK, PPC, { RT, RA } }, +{ "sfze", XO(31,200,0,0), XORB_MASK, POWER, { RT, RA } }, +{ "subfze.", XO(31,200,0,1), XORB_MASK, PPC, { RT, RA } }, +{ "sfze.", XO(31,200,0,1), XORB_MASK, POWER, { RT, RA } }, +{ "subfzeo", XO(31,200,1,0), XORB_MASK, PPC, { RT, RA } }, +{ "sfzeo", XO(31,200,1,0), XORB_MASK, POWER, { RT, RA } }, +{ "subfzeo.",XO(31,200,1,1), XORB_MASK, PPC, { RT, RA } }, +{ "sfzeo.", XO(31,200,1,1), XORB_MASK, POWER, { RT, RA } }, + +{ "addze", XO(31,202,0,0), XORB_MASK, PPC, { RT, RA } }, +{ "aze", XO(31,202,0,0), XORB_MASK, POWER, { RT, RA } }, +{ "addze.", XO(31,202,0,1), XORB_MASK, PPC, { RT, RA } }, +{ "aze.", XO(31,202,0,1), XORB_MASK, POWER, { RT, RA } }, +{ "addzeo", XO(31,202,1,0), XORB_MASK, PPC, { RT, RA } }, +{ "azeo", XO(31,202,1,0), XORB_MASK, POWER, { RT, RA } }, +{ "addzeo.", XO(31,202,1,1), XORB_MASK, PPC, { RT, RA } }, +{ "azeo.", XO(31,202,1,1), XORB_MASK, POWER, { RT, RA } }, + +{ "mtsr", X(31,210), XRB_MASK|(1<<20), PPC|POWER|B32, { SR, RS } }, + +{ "stdcx.", XRC(31,214,1), X_MASK, PPC|B64, { RS, RA, RB } }, + +{ "stbx", X(31,215), X_MASK, PPC|POWER, { RS, RA, RB } }, + +{ "sllq", XRC(31,216,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "sllq.", XRC(31,216,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "sleq", XRC(31,217,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "sleq.", XRC(31,217,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "subfme", XO(31,232,0,0), XORB_MASK, PPC, { RT, RA } }, +{ "sfme", XO(31,232,0,0), XORB_MASK, POWER, { RT, RA } }, +{ "subfme.", XO(31,232,0,1), XORB_MASK, PPC, { RT, RA } }, +{ "sfme.", XO(31,232,0,1), XORB_MASK, POWER, { RT, RA } }, +{ "subfmeo", XO(31,232,1,0), XORB_MASK, PPC, { RT, RA } }, +{ "sfmeo", XO(31,232,1,0), XORB_MASK, POWER, { RT, RA } }, +{ "subfmeo.",XO(31,232,1,1), XORB_MASK, PPC, { RT, RA } }, +{ "sfmeo.", XO(31,232,1,1), XORB_MASK, POWER, { RT, RA } }, + +{ "mulld", XO(31,233,0,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "mulld.", XO(31,233,0,1), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "mulldo", XO(31,233,1,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "mulldo.", XO(31,233,1,1), XO_MASK, PPC|B64, { RT, RA, RB } }, + +{ "addme", XO(31,234,0,0), XORB_MASK, PPC, { RT, RA } }, +{ "ame", XO(31,234,0,0), XORB_MASK, POWER, { RT, RA } }, +{ "addme.", XO(31,234,0,1), XORB_MASK, PPC, { RT, RA } }, +{ "ame.", XO(31,234,0,1), XORB_MASK, POWER, { RT, RA } }, +{ "addmeo", XO(31,234,1,0), XORB_MASK, PPC, { RT, RA } }, +{ "ameo", XO(31,234,1,0), XORB_MASK, POWER, { RT, RA } }, +{ "addmeo.", XO(31,234,1,1), XORB_MASK, PPC, { RT, RA } }, +{ "ameo.", XO(31,234,1,1), XORB_MASK, POWER, { RT, RA } }, + +{ "mullw", XO(31,235,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "muls", XO(31,235,0,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "mullw.", XO(31,235,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "muls.", XO(31,235,0,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "mullwo", XO(31,235,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "mulso", XO(31,235,1,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "mullwo.", XO(31,235,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "mulso.", XO(31,235,1,1), XO_MASK, POWER, { RT, RA, RB } }, + +{ "mtsrin", X(31,242), XRA_MASK, PPC|B32, { RS, RB } }, +{ "mtsri", X(31,242), XRA_MASK, POWER|B32, { RS, RB } }, + +{ "dcbtst", X(31,246), XRT_MASK, PPC, { RA, RB } }, + +{ "stbux", X(31,247), X_MASK, PPC|POWER, { RS, RAS, RB } }, + +{ "slliq", XRC(31,248,0), X_MASK, POWER|M601, { RA, RS, SH } }, +{ "slliq.", XRC(31,248,1), X_MASK, POWER|M601, { RA, RS, SH } }, + +{ "doz", XO(31,264,0,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "doz.", XO(31,264,0,1), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "dozo", XO(31,264,1,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "dozo.", XO(31,264,1,1), XO_MASK, POWER|M601, { RT, RA, RB } }, + +{ "add", XO(31,266,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "cax", XO(31,266,0,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "add.", XO(31,266,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "cax.", XO(31,266,0,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "addo", XO(31,266,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "caxo", XO(31,266,1,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "addo.", XO(31,266,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "caxo.", XO(31,266,1,1), XO_MASK, POWER, { RT, RA, RB } }, + +{ "lscbx", XRC(31,277,0), X_MASK, POWER|M601, { RT, RA, RB } }, +{ "lscbx.", XRC(31,277,1), X_MASK, POWER|M601, { RT, RA, RB } }, + +{ "dcbt", X(31,278), XRT_MASK, PPC, { RA, RB } }, + +{ "lhzx", X(31,279), X_MASK, PPC|POWER, { RT, RA, RB } }, + +{ "icbt", X(31,262), XRT_MASK, PPC, { RA, RB } }, + +{ "eqv", XRC(31,284,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "eqv.", XRC(31,284,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "tlbie", X(31,306), XRTRA_MASK, PPC, { RB } }, +{ "tlbi", X(31,306), XRTRA_MASK, POWER, { RB } }, + +{ "eciwx", X(31,310), X_MASK, PPC, { RT, RA, RB } }, + +{ "lhzux", X(31,311), X_MASK, PPC|POWER, { RT, RAL, RB } }, + +{ "xor", XRC(31,316,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "xor.", XRC(31,316,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "mfdcr", X(31,323), X_MASK, PPC, { RT, SPR } }, + +{ "div", XO(31,331,0,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "div.", XO(31,331,0,1), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "divo", XO(31,331,1,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "divo.", XO(31,331,1,1), XO_MASK, POWER|M601, { RT, RA, RB } }, + +{ "mfmq", XSPR(31,339,0), XSPR_MASK, POWER|M601, { RT } }, +{ "mfxer", XSPR(31,339,1), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfrtcu", XSPR(31,339,4), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfrtcl", XSPR(31,339,5), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfdec", XSPR(31,339,6), XSPR_MASK, POWER|M601, { RT } }, +{ "mflr", XSPR(31,339,8), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfctr", XSPR(31,339,9), XSPR_MASK, PPC|POWER, { RT } }, +{ "mftid", XSPR(31,339,17), XSPR_MASK, POWER, { RT } }, +{ "mfdsisr", XSPR(31,339,18), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfdar", XSPR(31,339,19), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfdec", XSPR(31,339,22), XSPR_MASK, PPC, { RT } }, +{ "mfsdr0", XSPR(31,339,24), XSPR_MASK, POWER, { RT } }, +{ "mfsdr1", XSPR(31,339,25), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfsrr0", XSPR(31,339,26), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfsrr1", XSPR(31,339,27), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfsprg", XSPR(31,339,272), XSPRG_MASK, PPC, { RT, SPRG } }, +{ "mfasr", XSPR(31,339,280), XSPR_MASK, PPC|B64, { RT } }, +{ "mfear", XSPR(31,339,282), XSPR_MASK, PPC, { RT } }, +{ "mfpvr", XSPR(31,339,287), XSPR_MASK, PPC, { RT } }, +{ "mfibatu", XSPR(31,339,528), XSPRBAT_MASK, PPC, { RT, SPRBAT } }, +{ "mfibatl", XSPR(31,339,529), XSPRBAT_MASK, PPC, { RT, SPRBAT } }, +{ "mfdbatu", XSPR(31,339,536), XSPRBAT_MASK, PPC, { RT, SPRBAT } }, +{ "mfdbatl", XSPR(31,339,537), XSPRBAT_MASK, PPC, { RT, SPRBAT } }, +{ "mfspr", X(31,339), X_MASK, PPC|POWER, { RT, SPR } }, + +{ "lwax", X(31,341), X_MASK, PPC|B64, { RT, RA, RB } }, + +{ "lhax", X(31,343), X_MASK, PPC|POWER, { RT, RA, RB } }, + +{ "dccci", X(31,454), XRT_MASK, PPC, { RA, RB } }, + +{ "abs", XO(31,360,0,0), XORB_MASK, POWER|M601, { RT, RA } }, +{ "abs.", XO(31,360,0,1), XORB_MASK, POWER|M601, { RT, RA } }, +{ "abso", XO(31,360,1,0), XORB_MASK, POWER|M601, { RT, RA } }, +{ "abso.", XO(31,360,1,1), XORB_MASK, POWER|M601, { RT, RA } }, + +{ "divs", XO(31,363,0,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "divs.", XO(31,363,0,1), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "divso", XO(31,363,1,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "divso.", XO(31,363,1,1), XO_MASK, POWER|M601, { RT, RA, RB } }, + +{ "tlbia", X(31,370), 0xffffffff, PPC, { 0 } }, + +{ "mftbu", XSPR(31,371,269), XSPR_MASK, PPC, { RT } }, +{ "mftb", X(31,371), X_MASK, PPC, { RT, TBR } }, + +{ "lwaux", X(31,373), X_MASK, PPC|B64, { RT, RAL, RB } }, + +{ "lhaux", X(31,375), X_MASK, PPC|POWER, { RT, RAL, RB } }, + +{ "sthx", X(31,407), X_MASK, PPC|POWER, { RS, RA, RB } }, + +{ "lfqx", X(31,791), X_MASK, POWER2, { FRT, RA, RB } }, + +{ "lfqux", X(31,823), X_MASK, POWER2, { FRT, RA, RB } }, + +{ "stfqx", X(31,919), X_MASK, POWER2, { FRS, RA, RB } }, + +{ "stfqux", X(31,951), X_MASK, POWER2, { FRS, RA, RB } }, + +{ "orc", XRC(31,412,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "orc.", XRC(31,412,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "sradi", XS(31,413,0), XS_MASK, PPC|B64, { RA, RS, SH6 } }, +{ "sradi.", XS(31,413,1), XS_MASK, PPC|B64, { RA, RS, SH6 } }, + +{ "slbie", X(31,434), XRTRA_MASK, PPC|B64, { RB } }, + +{ "ecowx", X(31,438), X_MASK, PPC, { RT, RA, RB } }, + +{ "sthux", X(31,439), X_MASK, PPC|POWER, { RS, RAS, RB } }, + +{ "mr", XRC(31,444,0), X_MASK, PPC|POWER, { RA, RS, RBS } }, +{ "or", XRC(31,444,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "mr.", XRC(31,444,1), X_MASK, PPC|POWER, { RA, RS, RBS } }, +{ "or.", XRC(31,444,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "mtdcr", X(31,451), X_MASK, PPC, { SPR, RS } }, + +{ "divdu", XO(31,457,0,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "divdu.", XO(31,457,0,1), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "divduo", XO(31,457,1,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "divduo.", XO(31,457,1,1), XO_MASK, PPC|B64, { RT, RA, RB } }, + +{ "divwu", XO(31,459,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwu.", XO(31,459,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwuo", XO(31,459,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwuo.", XO(31,459,1,1), XO_MASK, PPC, { RT, RA, RB } }, + +{ "mtmq", XSPR(31,467,0), XSPR_MASK, POWER|M601, { RS } }, +{ "mtxer", XSPR(31,467,1), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtlr", XSPR(31,467,8), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtctr", XSPR(31,467,9), XSPR_MASK, PPC|POWER, { RS } }, +{ "mttid", XSPR(31,467,17), XSPR_MASK, POWER, { RS } }, +{ "mtdsisr", XSPR(31,467,18), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtdar", XSPR(31,467,19), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtrtcu", XSPR(31,467,20), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtrtcl", XSPR(31,467,21), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtdec", XSPR(31,467,22), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtsdr0", XSPR(31,467,24), XSPR_MASK, POWER, { RS } }, +{ "mtsdr1", XSPR(31,467,25), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtsrr0", XSPR(31,467,26), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtsrr1", XSPR(31,467,27), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtsprg", XSPR(31,467,272), XSPRG_MASK, PPC, { SPRG, RS } }, +{ "mtasr", XSPR(31,467,280), XSPR_MASK, PPC|B64, { RS } }, +{ "mtear", XSPR(31,467,282), XSPR_MASK, PPC, { RS } }, +{ "mttbl", XSPR(31,467,284), XSPR_MASK, PPC, { RS } }, +{ "mttbu", XSPR(31,467,285), XSPR_MASK, PPC, { RS } }, +{ "mtibatu", XSPR(31,467,528), XSPRBAT_MASK, PPC, { SPRBAT, RS } }, +{ "mtibatl", XSPR(31,467,529), XSPRBAT_MASK, PPC, { SPRBAT, RS } }, +{ "mtdbatu", XSPR(31,467,536), XSPRBAT_MASK, PPC, { SPRBAT, RS } }, +{ "mtdbatl", XSPR(31,467,537), XSPRBAT_MASK, PPC, { SPRBAT, RS } }, +{ "mtspr", X(31,467), X_MASK, PPC|POWER, { SPR, RS } }, + +{ "dcbi", X(31,470), XRT_MASK, PPC, { RA, RB } }, + +{ "nand", XRC(31,476,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "nand.", XRC(31,476,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "nabs", XO(31,488,0,0), XORB_MASK, POWER|M601, { RT, RA } }, +{ "nabs.", XO(31,488,0,1), XORB_MASK, POWER|M601, { RT, RA } }, +{ "nabso", XO(31,488,1,0), XORB_MASK, POWER|M601, { RT, RA } }, +{ "nabso.", XO(31,488,1,1), XORB_MASK, POWER|M601, { RT, RA } }, + +{ "divd", XO(31,489,0,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "divd.", XO(31,489,0,1), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "divdo", XO(31,489,1,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "divdo.", XO(31,489,1,1), XO_MASK, PPC|B64, { RT, RA, RB } }, + +{ "divw", XO(31,491,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "divw.", XO(31,491,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwo", XO(31,491,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwo.", XO(31,491,1,1), XO_MASK, PPC, { RT, RA, RB } }, + +{ "slbia", X(31,498), 0xffffffff, PPC|B64, { 0 } }, + +{ "cli", X(31,502), XRB_MASK, POWER, { RT, RA } }, + +{ "mcrxr", X(31,512), XRARB_MASK|(3<<21), PPC|POWER, { BF } }, + +{ "clcs", X(31,531), XRB_MASK, POWER|M601, { RT, RA } }, + +{ "lswx", X(31,533), X_MASK, PPC, { RT, RA, RB } }, +{ "lsx", X(31,533), X_MASK, POWER, { RT, RA, RB } }, + +{ "lwbrx", X(31,534), X_MASK, PPC, { RT, RA, RB } }, +{ "lbrx", X(31,534), X_MASK, POWER, { RT, RA, RB } }, + +{ "lfsx", X(31,535), X_MASK, PPC|POWER, { FRT, RA, RB } }, + +{ "srw", XRC(31,536,0), X_MASK, PPC, { RA, RS, RB } }, +{ "sr", XRC(31,536,0), X_MASK, POWER, { RA, RS, RB } }, +{ "srw.", XRC(31,536,1), X_MASK, PPC, { RA, RS, RB } }, +{ "sr.", XRC(31,536,1), X_MASK, POWER, { RA, RS, RB } }, + +{ "rrib", XRC(31,537,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "rrib.", XRC(31,537,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "srd", XRC(31,539,0), X_MASK, PPC|B64, { RA, RS, RB } }, +{ "srd.", XRC(31,539,1), X_MASK, PPC|B64, { RA, RS, RB } }, + +{ "maskir", XRC(31,541,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "maskir.", XRC(31,541,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "tlbsync", X(31,566), 0xffffffff, PPC, { 0 } }, + +{ "lfsux", X(31,567), X_MASK, PPC|POWER, { FRT, RAS, RB } }, + +{ "mfsr", X(31,595), XRB_MASK|(1<<20), PPC|POWER|B32, { RT, SR } }, + +{ "lswi", X(31,597), X_MASK, PPC, { RT, RA, NB } }, +{ "lsi", X(31,597), X_MASK, POWER, { RT, RA, NB } }, + +{ "sync", X(31,598), 0xffffffff, PPC, { 0 } }, +{ "dcs", X(31,598), 0xffffffff, POWER, { 0 } }, + +{ "lfdx", X(31,599), X_MASK, PPC|POWER, { FRT, RA, RB } }, + +{ "mfsri", X(31,627), X_MASK, POWER, { RT, RA, RB } }, + +{ "dclst", X(31,630), XRB_MASK, POWER, { RS, RA } }, + +{ "lfdux", X(31,631), X_MASK, PPC|POWER, { FRT, RAS, RB } }, + +{ "mfsrin", X(31,659), XRA_MASK, PPC|B32, { RT, RB } }, + +{ "stswx", X(31,661), X_MASK, PPC, { RS, RA, RB } }, +{ "stsx", X(31,661), X_MASK, POWER, { RS, RA, RB } }, + +{ "stwbrx", X(31,662), X_MASK, PPC, { RS, RA, RB } }, +{ "stbrx", X(31,662), X_MASK, POWER, { RS, RA, RB } }, + +{ "stfsx", X(31,663), X_MASK, PPC|POWER, { FRS, RA, RB } }, + +{ "srq", XRC(31,664,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "srq.", XRC(31,664,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "sre", XRC(31,665,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "sre.", XRC(31,665,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "stfsux", X(31,695), X_MASK, PPC|POWER, { FRS, RAS, RB } }, + +{ "sriq", XRC(31,696,0), X_MASK, POWER|M601, { RA, RS, SH } }, +{ "sriq.", XRC(31,696,1), X_MASK, POWER|M601, { RA, RS, SH } }, + +{ "stswi", X(31,725), X_MASK, PPC, { RS, RA, NB } }, +{ "stsi", X(31,725), X_MASK, POWER, { RS, RA, NB } }, + +{ "stfdx", X(31,727), X_MASK, PPC|POWER, { FRS, RA, RB } }, + +{ "srlq", XRC(31,728,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "srlq.", XRC(31,728,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "sreq", XRC(31,729,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "sreq.", XRC(31,729,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "stfdux", X(31,759), X_MASK, PPC|POWER, { FRS, RAS, RB } }, + +{ "srliq", XRC(31,760,0), X_MASK, POWER|M601, { RA, RS, SH } }, +{ "srliq.", XRC(31,760,1), X_MASK, POWER|M601, { RA, RS, SH } }, + +{ "lhbrx", X(31,790), X_MASK, PPC|POWER, { RT, RA, RB } }, + +{ "sraw", XRC(31,792,0), X_MASK, PPC, { RA, RS, RB } }, +{ "sra", XRC(31,792,0), X_MASK, POWER, { RA, RS, RB } }, +{ "sraw.", XRC(31,792,1), X_MASK, PPC, { RA, RS, RB } }, +{ "sra.", XRC(31,792,1), X_MASK, POWER, { RA, RS, RB } }, + +{ "srad", XRC(31,794,0), X_MASK, PPC|B64, { RA, RS, RB } }, +{ "srad.", XRC(31,794,1), X_MASK, PPC|B64, { RA, RS, RB } }, + +{ "rac", X(31,818), X_MASK, POWER, { RT, RA, RB } }, + +{ "srawi", XRC(31,824,0), X_MASK, PPC, { RA, RS, SH } }, +{ "srai", XRC(31,824,0), X_MASK, POWER, { RA, RS, SH } }, +{ "srawi.", XRC(31,824,1), X_MASK, PPC, { RA, RS, SH } }, +{ "srai.", XRC(31,824,1), X_MASK, POWER, { RA, RS, SH } }, + +{ "eieio", X(31,854), 0xffffffff, PPC, { 0 } }, + +{ "sthbrx", X(31,918), X_MASK, PPC|POWER, { RS, RA, RB } }, + +{ "sraq", XRC(31,920,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "sraq.", XRC(31,920,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "srea", XRC(31,921,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "srea.", XRC(31,921,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "extsh", XRC(31,922,0), XRB_MASK, PPC, { RA, RS } }, +{ "exts", XRC(31,922,0), XRB_MASK, POWER, { RA, RS } }, +{ "extsh.", XRC(31,922,1), XRB_MASK, PPC, { RA, RS } }, +{ "exts.", XRC(31,922,1), XRB_MASK, POWER, { RA, RS } }, + +{ "sraiq", XRC(31,952,0), X_MASK, POWER|M601, { RA, RS, SH } }, +{ "sraiq.", XRC(31,952,1), X_MASK, POWER|M601, { RA, RS, SH } }, + +{ "extsb", XRC(31,954,0), XRB_MASK, PPC, { RA, RS} }, +{ "extsb.", XRC(31,954,1), XRB_MASK, PPC, { RA, RS} }, + +{ "iccci", X(31,966), XRT_MASK, PPC, { RA, RB } }, + +{ "icbi", X(31,982), XRT_MASK, PPC, { RA, RB } }, + +{ "stfiwx", X(31,983), X_MASK, PPC, { FRS, RA, RB } }, + +{ "extsw", XRC(31,986,0), XRB_MASK, PPC, { RA, RS } }, +{ "extsw.", XRC(31,986,1), XRB_MASK, PPC, { RA, RS } }, + +{ "dcbz", X(31,1014), XRT_MASK, PPC, { RA, RB } }, +{ "dclz", X(31,1014), XRT_MASK, PPC, { RA, RB } }, + +{ "lwz", OP(32), OP_MASK, PPC, { RT, D, RA } }, +{ "l", OP(32), OP_MASK, POWER, { RT, D, RA } }, + +{ "lwzu", OP(33), OP_MASK, PPC, { RT, D, RAL } }, +{ "lu", OP(33), OP_MASK, POWER, { RT, D, RA } }, + +{ "lbz", OP(34), OP_MASK, PPC|POWER, { RT, D, RA } }, + +{ "lbzu", OP(35), OP_MASK, PPC|POWER, { RT, D, RAL } }, + +{ "stw", OP(36), OP_MASK, PPC, { RS, D, RA } }, +{ "st", OP(36), OP_MASK, POWER, { RS, D, RA } }, + +{ "stwu", OP(37), OP_MASK, PPC, { RS, D, RAS } }, +{ "stu", OP(37), OP_MASK, POWER, { RS, D, RA } }, + +{ "stb", OP(38), OP_MASK, PPC|POWER, { RS, D, RA } }, + +{ "stbu", OP(39), OP_MASK, PPC|POWER, { RS, D, RAS } }, + +{ "lhz", OP(40), OP_MASK, PPC|POWER, { RT, D, RA } }, + +{ "lhzu", OP(41), OP_MASK, PPC|POWER, { RT, D, RAL } }, + +{ "lha", OP(42), OP_MASK, PPC|POWER, { RT, D, RA } }, + +{ "lhau", OP(43), OP_MASK, PPC|POWER, { RT, D, RAL } }, + +{ "sth", OP(44), OP_MASK, PPC|POWER, { RS, D, RA } }, + +{ "sthu", OP(45), OP_MASK, PPC|POWER, { RS, D, RAS } }, + +{ "lmw", OP(46), OP_MASK, PPC, { RT, D, RAM } }, +{ "lm", OP(46), OP_MASK, POWER, { RT, D, RA } }, + +{ "stmw", OP(47), OP_MASK, PPC, { RS, D, RA } }, +{ "stm", OP(47), OP_MASK, POWER, { RS, D, RA } }, + +{ "lfs", OP(48), OP_MASK, PPC|POWER, { FRT, D, RA } }, + +{ "lfsu", OP(49), OP_MASK, PPC|POWER, { FRT, D, RAS } }, + +{ "lfd", OP(50), OP_MASK, PPC|POWER, { FRT, D, RA } }, + +{ "lfdu", OP(51), OP_MASK, PPC|POWER, { FRT, D, RAS } }, + +{ "stfs", OP(52), OP_MASK, PPC|POWER, { FRS, D, RA } }, + +{ "stfsu", OP(53), OP_MASK, PPC|POWER, { FRS, D, RAS } }, + +{ "stfd", OP(54), OP_MASK, PPC|POWER, { FRS, D, RA } }, + +{ "stfdu", OP(55), OP_MASK, PPC|POWER, { FRS, D, RAS } }, + +{ "lfq", OP(56), OP_MASK, POWER2, { FRT, D, RA } }, + +{ "lfqu", OP(57), OP_MASK, POWER2, { FRT, D, RA } }, + +{ "ld", DSO(58,0), DS_MASK, PPC|B64, { RT, DS, RA } }, + +{ "ldu", DSO(58,1), DS_MASK, PPC|B64, { RT, DS, RAL } }, + +{ "lwa", DSO(58,2), DS_MASK, PPC|B64, { RT, DS, RA } }, + +{ "fdivs", A(59,18,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fdivs.", A(59,18,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, + +{ "fsubs", A(59,20,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fsubs.", A(59,20,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, + +{ "fadds", A(59,21,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fadds.", A(59,21,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, + +{ "fsqrts", A(59,22,0), AFRAFRC_MASK, PPC, { FRT, FRB } }, +{ "fsqrts.", A(59,22,1), AFRAFRC_MASK, PPC, { FRT, FRB } }, + +{ "fres", A(59,24,0), AFRAFRC_MASK, PPC, { FRT, FRB } }, +{ "fres.", A(59,24,1), AFRAFRC_MASK, PPC, { FRT, FRB } }, + +{ "fmuls", A(59,25,0), AFRB_MASK, PPC, { FRT, FRA, FRC } }, +{ "fmuls.", A(59,25,1), AFRB_MASK, PPC, { FRT, FRA, FRC } }, + +{ "fmsubs", A(59,28,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fmsubs.", A(59,28,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "fmadds", A(59,29,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fmadds.", A(59,29,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "fnmsubs", A(59,30,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnmsubs.",A(59,30,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "fnmadds", A(59,31,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnmadds.",A(59,31,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "stfq", OP(60), OP_MASK, POWER2, { FRS, D, RA } }, + +{ "stfqu", OP(61), OP_MASK, POWER2, { FRS, D, RA } }, + +{ "std", DSO(62,0), DS_MASK, PPC|B64, { RS, DS, RA } }, + +{ "stdu", DSO(62,1), DS_MASK, PPC|B64, { RS, DS, RAS } }, + +{ "fcmpu", X(63,0), X_MASK|(3<<21), PPC|POWER, { BF, FRA, FRB } }, + +{ "frsp", XRC(63,12,0), XRA_MASK, PPC|POWER, { FRT, FRB } }, +{ "frsp.", XRC(63,12,1), XRA_MASK, PPC|POWER, { FRT, FRB } }, + +{ "fctiw", XRC(63,14,0), XRA_MASK, PPC, { FRT, FRB } }, +{ "fcir", XRC(63,14,0), XRA_MASK, POWER2, { FRT, FRB } }, +{ "fctiw.", XRC(63,14,1), XRA_MASK, PPC, { FRT, FRB } }, +{ "fcir.", XRC(63,14,1), XRA_MASK, POWER2, { FRT, FRB } }, + +{ "fctiwz", XRC(63,15,0), XRA_MASK, PPC, { FRT, FRB } }, +{ "fcirz", XRC(63,15,0), XRA_MASK, POWER2, { FRT, FRB } }, +{ "fctiwz.", XRC(63,15,1), XRA_MASK, PPC, { FRT, FRB } }, +{ "fcirz.", XRC(63,15,1), XRA_MASK, POWER2, { FRT, FRB } }, + +{ "fdiv", A(63,18,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fd", A(63,18,0), AFRC_MASK, POWER, { FRT, FRA, FRB } }, +{ "fdiv.", A(63,18,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fd.", A(63,18,1), AFRC_MASK, POWER, { FRT, FRA, FRB } }, + +{ "fsub", A(63,20,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fs", A(63,20,0), AFRC_MASK, POWER, { FRT, FRA, FRB } }, +{ "fsub.", A(63,20,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fs.", A(63,20,1), AFRC_MASK, POWER, { FRT, FRA, FRB } }, + +{ "fadd", A(63,21,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fa", A(63,21,0), AFRC_MASK, POWER, { FRT, FRA, FRB } }, +{ "fadd.", A(63,21,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fa.", A(63,21,1), AFRC_MASK, POWER, { FRT, FRA, FRB } }, + +{ "fsqrt", A(63,22,0), AFRAFRC_MASK, PPC|POWER2, { FRT, FRB } }, +{ "fsqrt.", A(63,22,1), AFRAFRC_MASK, PPC|POWER2, { FRT, FRB } }, + +{ "fsel", A(63,23,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fsel.", A(63,23,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "fmul", A(63,25,0), AFRB_MASK, PPC, { FRT, FRA, FRC } }, +{ "fm", A(63,25,0), AFRB_MASK, POWER, { FRT, FRA, FRC } }, +{ "fmul.", A(63,25,1), AFRB_MASK, PPC, { FRT, FRA, FRC } }, +{ "fm.", A(63,25,1), AFRB_MASK, POWER, { FRT, FRA, FRC } }, + +{ "frsqrte", A(63,26,0), AFRAFRC_MASK, PPC, { FRT, FRB } }, +{ "frsqrte.",A(63,26,1), AFRAFRC_MASK, PPC, { FRT, FRB } }, + +{ "fmsub", A(63,28,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fms", A(63,28,0), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, +{ "fmsub.", A(63,28,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fms.", A(63,28,1), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, + +{ "fmadd", A(63,29,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fma", A(63,29,0), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, +{ "fmadd.", A(63,29,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fma.", A(63,29,1), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, + +{ "fnmsub", A(63,30,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnms", A(63,30,0), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, +{ "fnmsub.", A(63,30,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnms.", A(63,30,1), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, + +{ "fnmadd", A(63,31,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnma", A(63,31,0), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, +{ "fnmadd.", A(63,31,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnma.", A(63,31,1), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, + +{ "fcmpo", X(63,30), X_MASK|(3<<21), PPC|POWER, { BF, FRA, FRB } }, + +{ "mtfsb1", XRC(63,38,0), XRARB_MASK, PPC|POWER, { BT } }, +{ "mtfsb1.", XRC(63,38,1), XRARB_MASK, PPC|POWER, { BT } }, + +{ "fneg", XRC(63,40,0), XRA_MASK, PPC|POWER, { FRT, FRB } }, +{ "fneg.", XRC(63,40,1), XRA_MASK, PPC|POWER, { FRT, FRB } }, + +{ "mcrfs", X(63,64), XRB_MASK|(3<<21)|(3<<16), PPC|POWER, { BF, BFA } }, + +{ "mtfsb0", XRC(63,70,0), XRARB_MASK, PPC|POWER, { BT } }, +{ "mtfsb0.", XRC(63,70,1), XRARB_MASK, PPC|POWER, { BT } }, + +{ "fmr", XRC(63,72,0), XRA_MASK, PPC|POWER, { FRT, FRB } }, +{ "fmr.", XRC(63,72,1), XRA_MASK, PPC|POWER, { FRT, FRB } }, + +{ "mtfsfi", XRC(63,134,0), XRA_MASK|(3<<21)|(1<<11), PPC|POWER, { BF, U } }, +{ "mtfsfi.", XRC(63,134,1), XRA_MASK|(3<<21)|(1<<11), PPC|POWER, { BF, U } }, + +{ "fnabs", XRC(63,136,0), XRA_MASK, PPC|POWER, { FRT, FRB } }, +{ "fnabs.", XRC(63,136,1), XRA_MASK, PPC|POWER, { FRT, FRB } }, + +{ "fabs", XRC(63,264,0), XRA_MASK, PPC|POWER, { FRT, FRB } }, +{ "fabs.", XRC(63,264,1), XRA_MASK, PPC|POWER, { FRT, FRB } }, + +{ "mffs", XRC(63,583,0), XRARB_MASK, PPC|POWER, { FRT } }, +{ "mffs.", XRC(63,583,1), XRARB_MASK, PPC|POWER, { FRT } }, + +{ "mtfsf", XFL(63,711,0), XFL_MASK, PPC|POWER, { FLM, FRB } }, +{ "mtfsf.", XFL(63,711,1), XFL_MASK, PPC|POWER, { FLM, FRB } }, + +{ "fctid", XRC(63,814,0), XRA_MASK, PPC|B64, { FRT, FRB } }, +{ "fctid.", XRC(63,814,1), XRA_MASK, PPC|B64, { FRT, FRB } }, + +{ "fctidz", XRC(63,815,0), XRA_MASK, PPC|B64, { FRT, FRB } }, +{ "fctidz.", XRC(63,815,1), XRA_MASK, PPC|B64, { FRT, FRB } }, + +{ "fcfid", XRC(63,846,0), XRA_MASK, PPC|B64, { FRT, FRB } }, +{ "fcfid.", XRC(63,846,1), XRA_MASK, PPC|B64, { FRT, FRB } }, + +}; + +const int powerpc_num_opcodes = + sizeof (powerpc_opcodes) / sizeof (powerpc_opcodes[0]); + +/* The macro table. This is only used by the assembler. */ + +const struct powerpc_macro powerpc_macros[] = { +{ "extldi", 4, PPC|B64, "rldicr %0,%1,%3,(%2)-1" }, +{ "extldi.", 4, PPC|B64, "rldicr. %0,%1,%3,(%2)-1" }, +{ "extrdi", 4, PPC|B64, "rldicl %0,%1,(%2)+(%3),64-(%2)" }, +{ "extrdi.", 4, PPC|B64, "rldicl. %0,%1,(%2)+(%3),64-(%2)" }, +{ "insrdi", 4, PPC|B64, "rldimi %0,%1,64-((%2)+(%3)),%3" }, +{ "insrdi.", 4, PPC|B64, "rldimi. %0,%1,64-((%2)+(%3)),%3" }, +{ "rotrdi", 3, PPC|B64, "rldicl %0,%1,64-(%2),0" }, +{ "rotrdi.", 3, PPC|B64, "rldicl. %0,%1,64-(%2),0" }, +{ "sldi", 3, PPC|B64, "rldicr %0,%1,%2,63-(%2)" }, +{ "sldi.", 3, PPC|B64, "rldicr. %0,%1,%2,63-(%2)" }, +{ "srdi", 3, PPC|B64, "rldicl %0,%1,64-(%2),%2" }, +{ "srdi.", 3, PPC|B64, "rldicl. %0,%1,64-(%2),%2" }, +{ "clrrdi", 3, PPC|B64, "rldicr %0,%1,0,63-(%2)" }, +{ "clrrdi.", 3, PPC|B64, "rldicr. %0,%1,0,63-(%2)" }, +{ "clrlsldi",4, PPC|B64, "rldic %0,%1,%3,(%2)-(%3)" }, +{ "clrlsldi.",4, PPC|B64, "rldic. %0,%1,%3,(%2)-(%3)" }, + +{ "extlwi", 4, PPC, "rlwinm %0,%1,%3,0,(%2)-1" }, +{ "extlwi.", 4, PPC, "rlwinm. %0,%1,%3,0,(%2)-1" }, +{ "extrwi", 4, PPC, "rlwinm %0,%1,(%2)+(%3),32-(%2),31" }, +{ "extrwi.", 4, PPC, "rlwinm. %0,%1,(%2)+(%3),32-(%2),31" }, +{ "inslwi", 4, PPC, "rlwimi %0,%1,32-(%3),%3,(%2)+(%3)-1" }, +{ "inslwi.", 4, PPC, "rlwimi. %0,%1,32-(%3),%3,(%2)+(%3)-1" }, +{ "insrwi", 4, PPC, "rlwimi %0,%1,32-((%2)+(%3)),%3,(%2)+(%3)-1" }, +{ "insrwi.", 4, PPC, "rlwimi. %0,%1,32-((%2)+(%3)),%3,(%2)+(%3)-1"}, +{ "rotrwi", 3, PPC, "rlwinm %0,%1,32-(%2),0,31" }, +{ "rotrwi.", 3, PPC, "rlwinm. %0,%1,32-(%2),0,31" }, +{ "slwi", 3, PPC, "rlwinm %0,%1,%2,0,31-(%2)" }, +{ "sli", 3, POWER, "rlinm %0,%1,%2,0,31-(%2)" }, +{ "slwi.", 3, PPC, "rlwinm. %0,%1,%2,0,31-(%2)" }, +{ "sli.", 3, POWER, "rlinm. %0,%1,%2,0,31-(%2)" }, +{ "srwi", 3, PPC, "rlwinm %0,%1,32-(%2),%2,31" }, +{ "sri", 3, POWER, "rlinm %0,%1,32-(%2),%2,31" }, +{ "srwi.", 3, PPC, "rlwinm. %0,%1,32-(%2),%2,31" }, +{ "sri.", 3, POWER, "rlinm. %0,%1,32-(%2),%2,31" }, +{ "clrrwi", 3, PPC, "rlwinm %0,%1,0,0,31-(%2)" }, +{ "clrrwi.", 3, PPC, "rlwinm. %0,%1,0,0,31-(%2)" }, +{ "clrlslwi",4, PPC, "rlwinm %0,%1,%3,(%2)-(%3),31-(%3)" }, +{ "clrlslwi.",4, PPC, "rlwinm. %0,%1,%3,(%2)-(%3),31-(%3)" }, + +}; + +const int powerpc_num_macros = + sizeof (powerpc_macros) / sizeof (powerpc_macros[0]); diff --git a/arch/ppc/xmon/ppc.h b/arch/ppc/xmon/ppc.h new file mode 100644 index 00000000000..2345ecba1fe --- /dev/null +++ b/arch/ppc/xmon/ppc.h @@ -0,0 +1,240 @@ +/* ppc.h -- Header file for PowerPC opcode table + Copyright 1994 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Cygnus Support + +This file is part of GDB, GAS, and the GNU binutils. + +GDB, GAS, and the GNU binutils are free software; you can redistribute +them and/or modify them under the terms of the GNU General Public +License as published by the Free Software Foundation; either version +1, or (at your option) any later version. + +GDB, GAS, and the GNU binutils are distributed in the hope that they +will be useful, but WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this file; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef PPC_H +#define PPC_H + +/* The opcode table is an array of struct powerpc_opcode. */ + +struct powerpc_opcode +{ + /* The opcode name. */ + const char *name; + + /* The opcode itself. Those bits which will be filled in with + operands are zeroes. */ + unsigned long opcode; + + /* The opcode mask. This is used by the disassembler. This is a + mask containing ones indicating those bits which must match the + opcode field, and zeroes indicating those bits which need not + match (and are presumably filled in by operands). */ + unsigned long mask; + + /* One bit flags for the opcode. These are used to indicate which + specific processors support the instructions. The defined values + are listed below. */ + unsigned long flags; + + /* An array of operand codes. Each code is an index into the + operand table. They appear in the order which the operands must + appear in assembly code, and are terminated by a zero. */ + unsigned char operands[8]; +}; + +/* The table itself is sorted by major opcode number, and is otherwise + in the order in which the disassembler should consider + instructions. */ +extern const struct powerpc_opcode powerpc_opcodes[]; +extern const int powerpc_num_opcodes; + +/* Values defined for the flags field of a struct powerpc_opcode. */ + +/* Opcode is defined for the PowerPC architecture. */ +#define PPC_OPCODE_PPC (01) + +/* Opcode is defined for the POWER (RS/6000) architecture. */ +#define PPC_OPCODE_POWER (02) + +/* Opcode is defined for the POWER2 (Rios 2) architecture. */ +#define PPC_OPCODE_POWER2 (04) + +/* Opcode is only defined on 32 bit architectures. */ +#define PPC_OPCODE_32 (010) + +/* Opcode is only defined on 64 bit architectures. */ +#define PPC_OPCODE_64 (020) + +/* Opcode is supported by the Motorola PowerPC 601 processor. The 601 + is assumed to support all PowerPC (PPC_OPCODE_PPC) instructions, + but it also supports many additional POWER instructions. */ +#define PPC_OPCODE_601 (040) + +/* A macro to extract the major opcode from an instruction. */ +#define PPC_OP(i) (((i) >> 26) & 0x3f) + +/* The operands table is an array of struct powerpc_operand. */ + +struct powerpc_operand +{ + /* The number of bits in the operand. */ + int bits; + + /* How far the operand is left shifted in the instruction. */ + int shift; + + /* Insertion function. This is used by the assembler. To insert an + operand value into an instruction, check this field. + + If it is NULL, execute + i |= (op & ((1 << o->bits) - 1)) << o->shift; + (i is the instruction which we are filling in, o is a pointer to + this structure, and op is the opcode value; this assumes twos + complement arithmetic). + + If this field is not NULL, then simply call it with the + instruction and the operand value. It will return the new value + of the instruction. If the ERRMSG argument is not NULL, then if + the operand value is illegal, *ERRMSG will be set to a warning + string (the operand will be inserted in any case). If the + operand value is legal, *ERRMSG will be unchanged (most operands + can accept any value). */ + unsigned long (*insert) PARAMS ((unsigned long instruction, long op, + const char **errmsg)); + + /* Extraction function. This is used by the disassembler. To + extract this operand type from an instruction, check this field. + + If it is NULL, compute + op = ((i) >> o->shift) & ((1 << o->bits) - 1); + if ((o->flags & PPC_OPERAND_SIGNED) != 0 + && (op & (1 << (o->bits - 1))) != 0) + op -= 1 << o->bits; + (i is the instruction, o is a pointer to this structure, and op + is the result; this assumes twos complement arithmetic). + + If this field is not NULL, then simply call it with the + instruction value. It will return the value of the operand. If + the INVALID argument is not NULL, *INVALID will be set to + non-zero if this operand type can not actually be extracted from + this operand (i.e., the instruction does not match). If the + operand is valid, *INVALID will not be changed. */ + long (*extract) PARAMS ((unsigned long instruction, int *invalid)); + + /* One bit syntax flags. */ + unsigned long flags; +}; + +/* Elements in the table are retrieved by indexing with values from + the operands field of the powerpc_opcodes table. */ + +extern const struct powerpc_operand powerpc_operands[]; + +/* Values defined for the flags field of a struct powerpc_operand. */ + +/* This operand takes signed values. */ +#define PPC_OPERAND_SIGNED (01) + +/* This operand takes signed values, but also accepts a full positive + range of values when running in 32 bit mode. That is, if bits is + 16, it takes any value from -0x8000 to 0xffff. In 64 bit mode, + this flag is ignored. */ +#define PPC_OPERAND_SIGNOPT (02) + +/* This operand does not actually exist in the assembler input. This + is used to support extended mnemonics such as mr, for which two + operands fields are identical. The assembler should call the + insert function with any op value. The disassembler should call + the extract function, ignore the return value, and check the value + placed in the valid argument. */ +#define PPC_OPERAND_FAKE (04) + +/* The next operand should be wrapped in parentheses rather than + separated from this one by a comma. This is used for the load and + store instructions which want their operands to look like + reg,displacement(reg) + */ +#define PPC_OPERAND_PARENS (010) + +/* This operand may use the symbolic names for the CR fields, which + are + lt 0 gt 1 eq 2 so 3 un 3 + cr0 0 cr1 1 cr2 2 cr3 3 + cr4 4 cr5 5 cr6 6 cr7 7 + These may be combined arithmetically, as in cr2*4+gt. These are + only supported on the PowerPC, not the POWER. */ +#define PPC_OPERAND_CR (020) + +/* This operand names a register. The disassembler uses this to print + register names with a leading 'r'. */ +#define PPC_OPERAND_GPR (040) + +/* This operand names a floating point register. The disassembler + prints these with a leading 'f'. */ +#define PPC_OPERAND_FPR (0100) + +/* This operand is a relative branch displacement. The disassembler + prints these symbolically if possible. */ +#define PPC_OPERAND_RELATIVE (0200) + +/* This operand is an absolute branch address. The disassembler + prints these symbolically if possible. */ +#define PPC_OPERAND_ABSOLUTE (0400) + +/* This operand is optional, and is zero if omitted. This is used for + the optional BF and L fields in the comparison instructions. The + assembler must count the number of operands remaining on the line, + and the number of operands remaining for the opcode, and decide + whether this operand is present or not. The disassembler should + print this operand out only if it is not zero. */ +#define PPC_OPERAND_OPTIONAL (01000) + +/* This flag is only used with PPC_OPERAND_OPTIONAL. If this operand + is omitted, then for the next operand use this operand value plus + 1, ignoring the next operand field for the opcode. This wretched + hack is needed because the Power rotate instructions can take + either 4 or 5 operands. The disassembler should print this operand + out regardless of the PPC_OPERAND_OPTIONAL field. */ +#define PPC_OPERAND_NEXT (02000) + +/* This operand should be regarded as a negative number for the + purposes of overflow checking (i.e., the normal most negative + number is disallowed and one more than the normal most positive + number is allowed). This flag will only be set for a signed + operand. */ +#define PPC_OPERAND_NEGATIVE (04000) + +/* The POWER and PowerPC assemblers use a few macros. We keep them + with the operands table for simplicity. The macro table is an + array of struct powerpc_macro. */ + +struct powerpc_macro +{ + /* The macro name. */ + const char *name; + + /* The number of operands the macro takes. */ + unsigned int operands; + + /* One bit flags for the opcode. These are used to indicate which + specific processors support the instructions. The values are the + same as those for the struct powerpc_opcode flags field. */ + unsigned long flags; + + /* A format string to turn the macro into a normal instruction. + Each %N in the string is replaced with operand number N (zero + based). */ + const char *format; +}; + +extern const struct powerpc_macro powerpc_macros[]; +extern const int powerpc_num_macros; + +#endif /* PPC_H */ diff --git a/arch/ppc/xmon/privinst.h b/arch/ppc/xmon/privinst.h new file mode 100644 index 00000000000..93978c027ca --- /dev/null +++ b/arch/ppc/xmon/privinst.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 1996 Paul Mackerras. + */ +#include + +#define GETREG(reg) \ + static inline int get_ ## reg (void) \ + { int ret; asm volatile ("mf" #reg " %0" : "=r" (ret) :); return ret; } + +#define SETREG(reg) \ + static inline void set_ ## reg (int val) \ + { asm volatile ("mt" #reg " %0" : : "r" (val)); } + +GETREG(msr) +SETREG(msr) +GETREG(cr) + +#define GSETSPR(n, name) \ + static inline int get_ ## name (void) \ + { int ret; asm volatile ("mfspr %0," #n : "=r" (ret) : ); return ret; } \ + static inline void set_ ## name (int val) \ + { asm volatile ("mtspr " #n ",%0" : : "r" (val)); } + +GSETSPR(0, mq) +GSETSPR(1, xer) +GSETSPR(4, rtcu) +GSETSPR(5, rtcl) +GSETSPR(8, lr) +GSETSPR(9, ctr) +GSETSPR(18, dsisr) +GSETSPR(19, dar) +GSETSPR(22, dec) +GSETSPR(25, sdr1) +GSETSPR(26, srr0) +GSETSPR(27, srr1) +GSETSPR(272, sprg0) +GSETSPR(273, sprg1) +GSETSPR(274, sprg2) +GSETSPR(275, sprg3) +GSETSPR(282, ear) +GSETSPR(287, pvr) +#ifndef CONFIG_8xx +GSETSPR(528, bat0u) +GSETSPR(529, bat0l) +GSETSPR(530, bat1u) +GSETSPR(531, bat1l) +GSETSPR(532, bat2u) +GSETSPR(533, bat2l) +GSETSPR(534, bat3u) +GSETSPR(535, bat3l) +GSETSPR(1008, hid0) +GSETSPR(1009, hid1) +GSETSPR(1010, iabr) +GSETSPR(1013, dabr) +GSETSPR(1023, pir) +#else +GSETSPR(144, cmpa) +GSETSPR(145, cmpb) +GSETSPR(146, cmpc) +GSETSPR(147, cmpd) +GSETSPR(158, ictrl) +#endif + +static inline int get_sr(int n) +{ + int ret; + + asm (" mfsrin %0,%1" : "=r" (ret) : "r" (n << 28)); + return ret; +} + +static inline void set_sr(int n, int val) +{ + asm ("mtsrin %0,%1" : : "r" (val), "r" (n << 28)); +} + +static inline void store_inst(void *p) +{ + asm volatile ("dcbst 0,%0; sync; icbi 0,%0; isync" : : "r" (p)); +} + +static inline void cflush(void *p) +{ + asm volatile ("dcbf 0,%0; icbi 0,%0" : : "r" (p)); +} + +static inline void cinval(void *p) +{ + asm volatile ("dcbi 0,%0; icbi 0,%0" : : "r" (p)); +} + diff --git a/arch/ppc/xmon/setjmp.c b/arch/ppc/xmon/setjmp.c new file mode 100644 index 00000000000..28352bac2ae --- /dev/null +++ b/arch/ppc/xmon/setjmp.c @@ -0,0 +1,29 @@ +/* + * Copyright (C) 1996 Paul Mackerras. + * + * NB this file must be compiled with -O2. + */ + +int +xmon_setjmp(long *buf) +{ + asm ("mflr 0; stw 0,0(%0);" + "stw 1,4(%0); stw 2,8(%0);" + "mfcr 0; stw 0,12(%0);" + "stmw 13,16(%0)" + : : "r" (buf)); + /* XXX should save fp regs as well */ + return 0; +} + +void +xmon_longjmp(long *buf, int val) +{ + if (val == 0) + val = 1; + asm ("lmw 13,16(%0);" + "lwz 0,12(%0); mtcrf 0x38,0;" + "lwz 0,0(%0); lwz 1,4(%0); lwz 2,8(%0);" + "mtlr 0; mr 3,%1" + : : "r" (buf), "r" (val)); +} diff --git a/arch/ppc/xmon/start.c b/arch/ppc/xmon/start.c new file mode 100644 index 00000000000..507d4eeffe0 --- /dev/null +++ b/arch/ppc/xmon/start.c @@ -0,0 +1,646 @@ +/* + * Copyright (C) 1996 Paul Mackerras. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static volatile unsigned char *sccc, *sccd; +unsigned int TXRDY, RXRDY, DLAB; +static int xmon_expect(const char *str, unsigned int timeout); + +static int use_serial; +static int use_screen; +static int via_modem; +static int xmon_use_sccb; +static struct device_node *channel_node; + +#define TB_SPEED 25000000 + +static inline unsigned int readtb(void) +{ + unsigned int ret; + + asm volatile("mftb %0" : "=r" (ret) :); + return ret; +} + +void buf_access(void) +{ + if (DLAB) + sccd[3] &= ~DLAB; /* reset DLAB */ +} + +extern int adb_init(void); + +#ifdef CONFIG_PPC_CHRP +/* + * This looks in the "ranges" property for the primary PCI host bridge + * to find the physical address of the start of PCI/ISA I/O space. + * It is basically a cut-down version of pci_process_bridge_OF_ranges. + */ +static unsigned long chrp_find_phys_io_base(void) +{ + struct device_node *node; + unsigned int *ranges; + unsigned long base = CHRP_ISA_IO_BASE; + int rlen = 0; + int np; + + node = find_devices("isa"); + if (node != NULL) { + node = node->parent; + if (node == NULL || node->type == NULL + || strcmp(node->type, "pci") != 0) + node = NULL; + } + if (node == NULL) + node = find_devices("pci"); + if (node == NULL) + return base; + + ranges = (unsigned int *) get_property(node, "ranges", &rlen); + np = prom_n_addr_cells(node) + 5; + while ((rlen -= np * sizeof(unsigned int)) >= 0) { + if ((ranges[0] >> 24) == 1 && ranges[2] == 0) { + /* I/O space starting at 0, grab the phys base */ + base = ranges[np - 3]; + break; + } + ranges += np; + } + return base; +} +#endif /* CONFIG_PPC_CHRP */ + +#ifdef CONFIG_MAGIC_SYSRQ +static void sysrq_handle_xmon(int key, struct pt_regs *regs, + struct tty_struct *tty) +{ + xmon(regs); +} + +static struct sysrq_key_op sysrq_xmon_op = +{ + .handler = sysrq_handle_xmon, + .help_msg = "Xmon", + .action_msg = "Entering xmon", +}; +#endif + +void +xmon_map_scc(void) +{ +#ifdef CONFIG_PPC_MULTIPLATFORM + volatile unsigned char *base; + + if (_machine == _MACH_Pmac) { + struct device_node *np; + unsigned long addr; +#ifdef CONFIG_BOOTX_TEXT + if (!use_screen && !use_serial + && !machine_is_compatible("iMac")) { + /* see if there is a keyboard in the device tree + with a parent of type "adb" */ + for (np = find_devices("keyboard"); np; np = np->next) + if (np->parent && np->parent->type + && strcmp(np->parent->type, "adb") == 0) + break; + + /* needs to be hacked if xmon_printk is to be used + from within find_via_pmu() */ +#ifdef CONFIG_ADB_PMU + if (np != NULL && boot_text_mapped && find_via_pmu()) + use_screen = 1; +#endif +#ifdef CONFIG_ADB_CUDA + if (np != NULL && boot_text_mapped && find_via_cuda()) + use_screen = 1; +#endif + } + if (!use_screen && (np = find_devices("escc")) != NULL) { + /* + * look for the device node for the serial port + * we're using and see if it says it has a modem + */ + char *name = xmon_use_sccb? "ch-b": "ch-a"; + char *slots; + int l; + + np = np->child; + while (np != NULL && strcmp(np->name, name) != 0) + np = np->sibling; + if (np != NULL) { + /* XXX should parse this properly */ + channel_node = np; + slots = get_property(np, "slot-names", &l); + if (slots != NULL && l >= 10 + && strcmp(slots+4, "Modem") == 0) + via_modem = 1; + } + } + btext_drawstring("xmon uses "); + if (use_screen) + btext_drawstring("screen and keyboard\n"); + else { + if (via_modem) + btext_drawstring("modem on "); + btext_drawstring(xmon_use_sccb? "printer": "modem"); + btext_drawstring(" port\n"); + } + +#endif /* CONFIG_BOOTX_TEXT */ + +#ifdef CHRP_ESCC + addr = 0xc1013020; +#else + addr = 0xf3013020; +#endif + TXRDY = 4; + RXRDY = 1; + + np = find_devices("mac-io"); + if (np && np->n_addrs) + addr = np->addrs[0].address + 0x13020; + base = (volatile unsigned char *) ioremap(addr & PAGE_MASK, PAGE_SIZE); + sccc = base + (addr & ~PAGE_MASK); + sccd = sccc + 0x10; + + } else { + base = (volatile unsigned char *) isa_io_base; + if (_machine == _MACH_chrp) + base = (volatile unsigned char *) + ioremap(chrp_find_phys_io_base(), 0x1000); + + sccc = base + 0x3fd; + sccd = base + 0x3f8; + if (xmon_use_sccb) { + sccc -= 0x100; + sccd -= 0x100; + } + TXRDY = 0x20; + RXRDY = 1; + DLAB = 0x80; + } +#elif defined(CONFIG_GEMINI) + /* should already be mapped by the kernel boot */ + sccc = (volatile unsigned char *) 0xffeffb0d; + sccd = (volatile unsigned char *) 0xffeffb08; + TXRDY = 0x20; + RXRDY = 1; + DLAB = 0x80; +#elif defined(CONFIG_405GP) + sccc = (volatile unsigned char *)0xef600305; + sccd = (volatile unsigned char *)0xef600300; + TXRDY = 0x20; + RXRDY = 1; + DLAB = 0x80; +#endif /* platform */ + + register_sysrq_key('x', &sysrq_xmon_op); +} + +static int scc_initialized = 0; + +void xmon_init_scc(void); +extern void cuda_poll(void); + +static inline void do_poll_adb(void) +{ +#ifdef CONFIG_ADB_PMU + if (sys_ctrler == SYS_CTRLER_PMU) + pmu_poll_adb(); +#endif /* CONFIG_ADB_PMU */ +#ifdef CONFIG_ADB_CUDA + if (sys_ctrler == SYS_CTRLER_CUDA) + cuda_poll(); +#endif /* CONFIG_ADB_CUDA */ +} + +int +xmon_write(void *handle, void *ptr, int nb) +{ + char *p = ptr; + int i, c, ct; + +#ifdef CONFIG_SMP + static unsigned long xmon_write_lock; + int lock_wait = 1000000; + int locked; + + while ((locked = test_and_set_bit(0, &xmon_write_lock)) != 0) + if (--lock_wait == 0) + break; +#endif + +#ifdef CONFIG_BOOTX_TEXT + if (use_screen) { + /* write it on the screen */ + for (i = 0; i < nb; ++i) + btext_drawchar(*p++); + goto out; + } +#endif + if (!scc_initialized) + xmon_init_scc(); + ct = 0; + for (i = 0; i < nb; ++i) { + while ((*sccc & TXRDY) == 0) + do_poll_adb(); + c = p[i]; + if (c == '\n' && !ct) { + c = '\r'; + ct = 1; + --i; + } else { + ct = 0; + } + buf_access(); + *sccd = c; + eieio(); + } + + out: +#ifdef CONFIG_SMP + if (!locked) + clear_bit(0, &xmon_write_lock); +#endif + return nb; +} + +int xmon_wants_key; +int xmon_adb_keycode; + +#ifdef CONFIG_BOOTX_TEXT +static int xmon_adb_shiftstate; + +static unsigned char xmon_keytab[128] = + "asdfhgzxcv\000bqwer" /* 0x00 - 0x0f */ + "yt123465=97-80]o" /* 0x10 - 0x1f */ + "u[ip\rlj'k;\\,/nm." /* 0x20 - 0x2f */ + "\t `\177\0\033\0\0\0\0\0\0\0\0\0\0" /* 0x30 - 0x3f */ + "\0.\0*\0+\0\0\0\0\0/\r\0-\0" /* 0x40 - 0x4f */ + "\0\0000123456789\0\0\0"; /* 0x50 - 0x5f */ + +static unsigned char xmon_shift_keytab[128] = + "ASDFHGZXCV\000BQWER" /* 0x00 - 0x0f */ + "YT!@#$^%+(&_*)}O" /* 0x10 - 0x1f */ + "U{IP\rLJ\"K:|" /* 0x20 - 0x2f */ + "\t ~\177\0\033\0\0\0\0\0\0\0\0\0\0" /* 0x30 - 0x3f */ + "\0.\0*\0+\0\0\0\0\0/\r\0-\0" /* 0x40 - 0x4f */ + "\0\0000123456789\0\0\0"; /* 0x50 - 0x5f */ + +static int +xmon_get_adb_key(void) +{ + int k, t, on; + + xmon_wants_key = 1; + for (;;) { + xmon_adb_keycode = -1; + t = 0; + on = 0; + do { + if (--t < 0) { + on = 1 - on; + btext_drawchar(on? 0xdb: 0x20); + btext_drawchar('\b'); + t = 200000; + } + do_poll_adb(); + } while (xmon_adb_keycode == -1); + k = xmon_adb_keycode; + if (on) + btext_drawstring(" \b"); + + /* test for shift keys */ + if ((k & 0x7f) == 0x38 || (k & 0x7f) == 0x7b) { + xmon_adb_shiftstate = (k & 0x80) == 0; + continue; + } + if (k >= 0x80) + continue; /* ignore up transitions */ + k = (xmon_adb_shiftstate? xmon_shift_keytab: xmon_keytab)[k]; + if (k != 0) + break; + } + xmon_wants_key = 0; + return k; +} +#endif /* CONFIG_BOOTX_TEXT */ + +int +xmon_read(void *handle, void *ptr, int nb) +{ + char *p = ptr; + int i; + +#ifdef CONFIG_BOOTX_TEXT + if (use_screen) { + for (i = 0; i < nb; ++i) + *p++ = xmon_get_adb_key(); + return i; + } +#endif + if (!scc_initialized) + xmon_init_scc(); + for (i = 0; i < nb; ++i) { + while ((*sccc & RXRDY) == 0) + do_poll_adb(); + buf_access(); + *p++ = *sccd; + } + return i; +} + +int +xmon_read_poll(void) +{ + if ((*sccc & RXRDY) == 0) { + do_poll_adb(); + return -1; + } + buf_access(); + return *sccd; +} + +static unsigned char scc_inittab[] = { + 13, 0, /* set baud rate divisor */ + 12, 1, + 14, 1, /* baud rate gen enable, src=rtxc */ + 11, 0x50, /* clocks = br gen */ + 5, 0xea, /* tx 8 bits, assert DTR & RTS */ + 4, 0x46, /* x16 clock, 1 stop */ + 3, 0xc1, /* rx enable, 8 bits */ +}; + +void +xmon_init_scc(void) +{ + if ( _machine == _MACH_chrp ) + { + sccd[3] = 0x83; eieio(); /* LCR = 8N1 + DLAB */ + sccd[0] = 12; eieio(); /* DLL = 9600 baud */ + sccd[1] = 0; eieio(); + sccd[2] = 0; eieio(); /* FCR = 0 */ + sccd[3] = 3; eieio(); /* LCR = 8N1 */ + sccd[1] = 0; eieio(); /* IER = 0 */ + } + else if ( _machine == _MACH_Pmac ) + { + int i, x; + + if (channel_node != 0) + pmac_call_feature( + PMAC_FTR_SCC_ENABLE, + channel_node, + PMAC_SCC_ASYNC | PMAC_SCC_FLAG_XMON, 1); + printk(KERN_INFO "Serial port locked ON by debugger !\n"); + if (via_modem && channel_node != 0) { + unsigned int t0; + + pmac_call_feature( + PMAC_FTR_MODEM_ENABLE, + channel_node, 0, 1); + printk(KERN_INFO "Modem powered up by debugger !\n"); + t0 = readtb(); + while (readtb() - t0 < 3*TB_SPEED) + eieio(); + } + /* use the B channel if requested */ + if (xmon_use_sccb) { + sccc = (volatile unsigned char *) + ((unsigned long)sccc & ~0x20); + sccd = sccc + 0x10; + } + for (i = 20000; i != 0; --i) { + x = *sccc; eieio(); + } + *sccc = 9; eieio(); /* reset A or B side */ + *sccc = ((unsigned long)sccc & 0x20)? 0x80: 0x40; eieio(); + for (i = 0; i < sizeof(scc_inittab); ++i) { + *sccc = scc_inittab[i]; + eieio(); + } + } + scc_initialized = 1; + if (via_modem) { + for (;;) { + xmon_write(NULL, "ATE1V1\r", 7); + if (xmon_expect("OK", 5)) { + xmon_write(NULL, "ATA\r", 4); + if (xmon_expect("CONNECT", 40)) + break; + } + xmon_write(NULL, "+++", 3); + xmon_expect("OK", 3); + } + } +} + +#if 0 +extern int (*prom_entry)(void *); + +int +xmon_exit(void) +{ + struct prom_args { + char *service; + } args; + + for (;;) { + args.service = "exit"; + (*prom_entry)(&args); + } +} +#endif + +void *xmon_stdin; +void *xmon_stdout; +void *xmon_stderr; + +void +xmon_init(void) +{ +} + +int +xmon_putc(int c, void *f) +{ + char ch = c; + + if (c == '\n') + xmon_putc('\r', f); + return xmon_write(f, &ch, 1) == 1? c: -1; +} + +int +xmon_putchar(int c) +{ + return xmon_putc(c, xmon_stdout); +} + +int +xmon_fputs(char *str, void *f) +{ + int n = strlen(str); + + return xmon_write(f, str, n) == n? 0: -1; +} + +int +xmon_readchar(void) +{ + char ch; + + for (;;) { + switch (xmon_read(xmon_stdin, &ch, 1)) { + case 1: + return ch; + case -1: + xmon_printf("read(stdin) returned -1\r\n", 0, 0); + return -1; + } + } +} + +static char line[256]; +static char *lineptr; +static int lineleft; + +int xmon_expect(const char *str, unsigned int timeout) +{ + int c; + unsigned int t0; + + timeout *= TB_SPEED; + t0 = readtb(); + do { + lineptr = line; + for (;;) { + c = xmon_read_poll(); + if (c == -1) { + if (readtb() - t0 > timeout) + return 0; + continue; + } + if (c == '\n') + break; + if (c != '\r' && lineptr < &line[sizeof(line) - 1]) + *lineptr++ = c; + } + *lineptr = 0; + } while (strstr(line, str) == NULL); + return 1; +} + +int +xmon_getchar(void) +{ + int c; + + if (lineleft == 0) { + lineptr = line; + for (;;) { + c = xmon_readchar(); + if (c == -1 || c == 4) + break; + if (c == '\r' || c == '\n') { + *lineptr++ = '\n'; + xmon_putchar('\n'); + break; + } + switch (c) { + case 0177: + case '\b': + if (lineptr > line) { + xmon_putchar('\b'); + xmon_putchar(' '); + xmon_putchar('\b'); + --lineptr; + } + break; + case 'U' & 0x1F: + while (lineptr > line) { + xmon_putchar('\b'); + xmon_putchar(' '); + xmon_putchar('\b'); + --lineptr; + } + break; + default: + if (lineptr >= &line[sizeof(line) - 1]) + xmon_putchar('\a'); + else { + xmon_putchar(c); + *lineptr++ = c; + } + } + } + lineleft = lineptr - line; + lineptr = line; + } + if (lineleft == 0) + return -1; + --lineleft; + return *lineptr++; +} + +char * +xmon_fgets(char *str, int nb, void *f) +{ + char *p; + int c; + + for (p = str; p < str + nb - 1; ) { + c = xmon_getchar(); + if (c == -1) { + if (p == str) + return NULL; + break; + } + *p++ = c; + if (c == '\n') + break; + } + *p = 0; + return str; +} + +void +xmon_enter(void) +{ +#ifdef CONFIG_ADB_PMU + if (_machine == _MACH_Pmac) { + pmu_suspend(); + } +#endif +} + +void +xmon_leave(void) +{ +#ifdef CONFIG_ADB_PMU + if (_machine == _MACH_Pmac) { + pmu_resume(); + } +#endif +} diff --git a/arch/ppc/xmon/start_8xx.c b/arch/ppc/xmon/start_8xx.c new file mode 100644 index 00000000000..a48bd594cf6 --- /dev/null +++ b/arch/ppc/xmon/start_8xx.c @@ -0,0 +1,287 @@ +/* + * Copyright (C) 1996 Paul Mackerras. + * Copyright (C) 2000 Dan Malek. + * Quick hack of Paul's code to make XMON work on 8xx processors. Lots + * of assumptions, like the SMC1 is used, it has been initialized by the + * loader at some point, and we can just stuff and suck bytes. + * We rely upon the 8xx uart driver to support us, as the interface + * changes between boot up and operational phases of the kernel. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +extern void xmon_printf(const char *fmt, ...); +extern int xmon_8xx_write(char *str, int nb); +extern int xmon_8xx_read_poll(void); +extern int xmon_8xx_read_char(void); +void prom_drawhex(uint); +void prom_drawstring(const char *str); + +static int use_screen = 1; /* default */ + +#define TB_SPEED 25000000 + +static inline unsigned int readtb(void) +{ + unsigned int ret; + + asm volatile("mftb %0" : "=r" (ret) :); + return ret; +} + +void buf_access(void) +{ +} + +void +xmon_map_scc(void) +{ + + cpmp = (cpm8xx_t *)&(((immap_t *)IMAP_ADDR)->im_cpm); + use_screen = 0; + + prom_drawstring("xmon uses serial port\n"); +} + +static int scc_initialized = 0; + +void xmon_init_scc(void); + +int +xmon_write(void *handle, void *ptr, int nb) +{ + char *p = ptr; + int i, c, ct; + + if (!scc_initialized) + xmon_init_scc(); + + return(xmon_8xx_write(ptr, nb)); +} + +int xmon_wants_key; + +int +xmon_read(void *handle, void *ptr, int nb) +{ + char *p = ptr; + int i; + + if (!scc_initialized) + xmon_init_scc(); + + for (i = 0; i < nb; ++i) { + *p++ = xmon_8xx_read_char(); + } + return i; +} + +int +xmon_read_poll(void) +{ + return(xmon_8xx_read_poll()); +} + +void +xmon_init_scc() +{ + scc_initialized = 1; +} + +#if 0 +extern int (*prom_entry)(void *); + +int +xmon_exit(void) +{ + struct prom_args { + char *service; + } args; + + for (;;) { + args.service = "exit"; + (*prom_entry)(&args); + } +} +#endif + +void *xmon_stdin; +void *xmon_stdout; +void *xmon_stderr; + +void +xmon_init(void) +{ +} + +int +xmon_putc(int c, void *f) +{ + char ch = c; + + if (c == '\n') + xmon_putc('\r', f); + return xmon_write(f, &ch, 1) == 1? c: -1; +} + +int +xmon_putchar(int c) +{ + return xmon_putc(c, xmon_stdout); +} + +int +xmon_fputs(char *str, void *f) +{ + int n = strlen(str); + + return xmon_write(f, str, n) == n? 0: -1; +} + +int +xmon_readchar(void) +{ + char ch; + + for (;;) { + switch (xmon_read(xmon_stdin, &ch, 1)) { + case 1: + return ch; + case -1: + xmon_printf("read(stdin) returned -1\r\n", 0, 0); + return -1; + } + } +} + +static char line[256]; +static char *lineptr; +static int lineleft; + +#if 0 +int xmon_expect(const char *str, unsigned int timeout) +{ + int c; + unsigned int t0; + + timeout *= TB_SPEED; + t0 = readtb(); + do { + lineptr = line; + for (;;) { + c = xmon_read_poll(); + if (c == -1) { + if (readtb() - t0 > timeout) + return 0; + continue; + } + if (c == '\n') + break; + if (c != '\r' && lineptr < &line[sizeof(line) - 1]) + *lineptr++ = c; + } + *lineptr = 0; + } while (strstr(line, str) == NULL); + return 1; +} +#endif + +int +xmon_getchar(void) +{ + int c; + + if (lineleft == 0) { + lineptr = line; + for (;;) { + c = xmon_readchar(); + if (c == -1 || c == 4) + break; + if (c == '\r' || c == '\n') { + *lineptr++ = '\n'; + xmon_putchar('\n'); + break; + } + switch (c) { + case 0177: + case '\b': + if (lineptr > line) { + xmon_putchar('\b'); + xmon_putchar(' '); + xmon_putchar('\b'); + --lineptr; + } + break; + case 'U' & 0x1F: + while (lineptr > line) { + xmon_putchar('\b'); + xmon_putchar(' '); + xmon_putchar('\b'); + --lineptr; + } + break; + default: + if (lineptr >= &line[sizeof(line) - 1]) + xmon_putchar('\a'); + else { + xmon_putchar(c); + *lineptr++ = c; + } + } + } + lineleft = lineptr - line; + lineptr = line; + } + if (lineleft == 0) + return -1; + --lineleft; + return *lineptr++; +} + +char * +xmon_fgets(char *str, int nb, void *f) +{ + char *p; + int c; + + for (p = str; p < str + nb - 1; ) { + c = xmon_getchar(); + if (c == -1) { + if (p == str) + return 0; + break; + } + *p++ = c; + if (c == '\n') + break; + } + *p = 0; + return str; +} + +void +prom_drawhex(uint val) +{ + unsigned char buf[10]; + + int i; + for (i = 7; i >= 0; i--) + { + buf[i] = "0123456789abcdef"[val & 0x0f]; + val >>= 4; + } + buf[8] = '\0'; + xmon_fputs(buf, xmon_stdout); +} + +void +prom_drawstring(const char *str) +{ + xmon_fputs(str, xmon_stdout); +} diff --git a/arch/ppc/xmon/subr_prf.c b/arch/ppc/xmon/subr_prf.c new file mode 100644 index 00000000000..126624f3f2e --- /dev/null +++ b/arch/ppc/xmon/subr_prf.c @@ -0,0 +1,55 @@ +/* + * Written by Cort Dougan to replace the version originally used + * by Paul Mackerras, which came from NetBSD and thus had copyright + * conflicts with Linux. + * + * This file makes liberal use of the standard linux utility + * routines to reduce the size of the binary. We assume we can + * trust some parts of Linux inside the debugger. + * -- Cort (cort@cs.nmt.edu) + * + * Copyright (C) 1999 Cort Dougan. + */ + +#include +#include +#include +#include "nonstdio.h" + +extern int xmon_write(void *, void *, int); + +void +xmon_vfprintf(void *f, const char *fmt, va_list ap) +{ + static char xmon_buf[2048]; + int n; + + n = vsprintf(xmon_buf, fmt, ap); + xmon_write(f, xmon_buf, n); +} + +void +xmon_printf(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + xmon_vfprintf(stdout, fmt, ap); + va_end(ap); +} + +void +xmon_fprintf(void *f, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + xmon_vfprintf(f, fmt, ap); + va_end(ap); +} + +void +xmon_puts(char *s) +{ + xmon_write(stdout, s, strlen(s)); +} diff --git a/arch/ppc/xmon/xmon.c b/arch/ppc/xmon/xmon.c new file mode 100644 index 00000000000..8565f49b8b0 --- /dev/null +++ b/arch/ppc/xmon/xmon.c @@ -0,0 +1,2008 @@ +/* + * Routines providing a simple monitor for use on the PowerMac. + * + * Copyright (C) 1996 Paul Mackerras. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_PMAC_BACKLIGHT +#include +#endif +#include "nonstdio.h" +#include "privinst.h" + +#define scanhex xmon_scanhex +#define skipbl xmon_skipbl + +#ifdef CONFIG_SMP +static unsigned long cpus_in_xmon = 0; +static unsigned long got_xmon = 0; +static volatile int take_xmon = -1; +#endif /* CONFIG_SMP */ + +static unsigned adrs; +static int size = 1; +static unsigned ndump = 64; +static unsigned nidump = 16; +static unsigned ncsum = 4096; +static int termch; + +static u_int bus_error_jmp[100]; +#define setjmp xmon_setjmp +#define longjmp xmon_longjmp + +/* Breakpoint stuff */ +struct bpt { + unsigned address; + unsigned instr; + unsigned count; + unsigned char enabled; +}; + +#define NBPTS 16 +static struct bpt bpts[NBPTS]; +static struct bpt dabr; +static struct bpt iabr; +static unsigned bpinstr = 0x7fe00008; /* trap */ + +/* Prototypes */ +extern void (*debugger_fault_handler)(struct pt_regs *); +static int cmds(struct pt_regs *); +static int mread(unsigned, void *, int); +static int mwrite(unsigned, void *, int); +static void handle_fault(struct pt_regs *); +static void byterev(unsigned char *, int); +static void memex(void); +static int bsesc(void); +static void dump(void); +static void prdump(unsigned, int); +#ifdef __MWERKS__ +static void prndump(unsigned, int); +static int nvreadb(unsigned); +#endif +static int ppc_inst_dump(unsigned, int); +void print_address(unsigned); +static int getsp(void); +static void dump_hash_table(void); +static void backtrace(struct pt_regs *); +static void excprint(struct pt_regs *); +static void prregs(struct pt_regs *); +static void memops(int); +static void memlocate(void); +static void memzcan(void); +static void memdiffs(unsigned char *, unsigned char *, unsigned, unsigned); +int skipbl(void); +int scanhex(unsigned *valp); +static void scannl(void); +static int hexdigit(int); +void getstring(char *, int); +static void flush_input(void); +static int inchar(void); +static void take_input(char *); +/* static void openforth(void); */ +static unsigned read_spr(int); +static void write_spr(int, unsigned); +static void super_regs(void); +static void print_sysmap(void); +static void sysmap_lookup(void); +static void remove_bpts(void); +static void insert_bpts(void); +static struct bpt *at_breakpoint(unsigned pc); +static void bpt_cmds(void); +static void cacheflush(void); +#ifdef CONFIG_SMP +static void cpu_cmd(void); +#endif /* CONFIG_SMP */ +static int pretty_print_addr(unsigned long addr); +static void csum(void); +#ifdef CONFIG_BOOTX_TEXT +static void vidcmds(void); +#endif +static void bootcmds(void); +static void proccall(void); +static void printtime(void); + +extern int print_insn_big_powerpc(FILE *, unsigned long, unsigned); +extern void printf(const char *fmt, ...); +extern int putchar(int ch); +extern int setjmp(u_int *); +extern void longjmp(u_int *, int); + +extern void xmon_enter(void); +extern void xmon_leave(void); +extern char* xmon_find_symbol(unsigned long addr, unsigned long* saddr); +extern unsigned long xmon_symbol_to_addr(char* symbol); + +static unsigned start_tb[NR_CPUS][2]; +static unsigned stop_tb[NR_CPUS][2]; + +#define GETWORD(v) (((v)[0] << 24) + ((v)[1] << 16) + ((v)[2] << 8) + (v)[3]) + +#define isxdigit(c) (('0' <= (c) && (c) <= '9') \ + || ('a' <= (c) && (c) <= 'f') \ + || ('A' <= (c) && (c) <= 'F')) +#define isalnum(c) (('0' <= (c) && (c) <= '9') \ + || ('a' <= (c) && (c) <= 'z') \ + || ('A' <= (c) && (c) <= 'Z')) +#define isspace(c) (c == ' ' || c == '\t' || c == 10 || c == 13 || c == 0) + +static char *help_string = "\ +Commands:\n\ + d dump bytes\n\ + di dump instructions\n\ + df dump float values\n\ + dd dump double values\n\ + e print exception information\n\ + h dump hash table\n\ + m examine/change memory\n\ + mm move a block of memory\n\ + ms set a block of memory\n\ + md compare two blocks of memory\n\ + M print System.map\n\ + r print registers\n\ + S print special registers\n\ + t print backtrace\n\ + la lookup address in system.map\n\ + ls lookup symbol in system.map\n\ + x exit monitor\n\ +"; + +static int xmon_trace[NR_CPUS]; +#define SSTEP 1 /* stepping because of 's' command */ +#define BRSTEP 2 /* stepping over breakpoint */ + +static struct pt_regs *xmon_regs[NR_CPUS]; + +extern inline void sync(void) +{ + asm volatile("sync; isync"); +} + +extern inline void __delay(unsigned int loops) +{ + if (loops != 0) + __asm__ __volatile__("mtctr %0; 1: bdnz 1b" : : + "r" (loops) : "ctr"); +} + +static void get_tb(unsigned *p) +{ + unsigned hi, lo, hiagain; + + if ((get_pvr() >> 16) == 1) + return; + + do { + asm volatile("mftbu %0; mftb %1; mftbu %2" + : "=r" (hi), "=r" (lo), "=r" (hiagain)); + } while (hi != hiagain); + p[0] = hi; + p[1] = lo; +} + +void +xmon(struct pt_regs *excp) +{ + struct pt_regs regs; + int msr, cmd; + + get_tb(stop_tb[smp_processor_id()]); + if (excp == NULL) { + asm volatile ("stw 0,0(%0)\n\ + lwz 0,0(1)\n\ + stw 0,4(%0)\n\ + stmw 2,8(%0)" : : "b" (®s)); + regs.nip = regs.link = ((unsigned long *)regs.gpr[1])[1]; + regs.msr = get_msr(); + regs.ctr = get_ctr(); + regs.xer = get_xer(); + regs.ccr = get_cr(); + regs.trap = 0; + excp = ®s; + } + + msr = get_msr(); + set_msr(msr & ~0x8000); /* disable interrupts */ + xmon_regs[smp_processor_id()] = excp; + xmon_enter(); + excprint(excp); +#ifdef CONFIG_SMP + if (test_and_set_bit(smp_processor_id(), &cpus_in_xmon)) + for (;;) + ; + while (test_and_set_bit(0, &got_xmon)) { + if (take_xmon == smp_processor_id()) { + take_xmon = -1; + break; + } + } + /* + * XXX: breakpoints are removed while any cpu is in xmon + */ +#endif /* CONFIG_SMP */ + remove_bpts(); +#ifdef CONFIG_PMAC_BACKLIGHT + if( setjmp(bus_error_jmp) == 0 ) { + debugger_fault_handler = handle_fault; + sync(); + set_backlight_enable(1); + set_backlight_level(BACKLIGHT_MAX); + sync(); + } + debugger_fault_handler = NULL; +#endif /* CONFIG_PMAC_BACKLIGHT */ + cmd = cmds(excp); + if (cmd == 's') { + xmon_trace[smp_processor_id()] = SSTEP; + excp->msr |= 0x400; + } else if (at_breakpoint(excp->nip)) { + xmon_trace[smp_processor_id()] = BRSTEP; + excp->msr |= 0x400; + } else { + xmon_trace[smp_processor_id()] = 0; + insert_bpts(); + } + xmon_leave(); + xmon_regs[smp_processor_id()] = NULL; +#ifdef CONFIG_SMP + clear_bit(0, &got_xmon); + clear_bit(smp_processor_id(), &cpus_in_xmon); +#endif /* CONFIG_SMP */ + set_msr(msr); /* restore interrupt enable */ + get_tb(start_tb[smp_processor_id()]); +} + +irqreturn_t +xmon_irq(int irq, void *d, struct pt_regs *regs) +{ + unsigned long flags; + local_irq_save(flags); + printf("Keyboard interrupt\n"); + xmon(regs); + local_irq_restore(flags); + return IRQ_HANDLED; +} + +int +xmon_bpt(struct pt_regs *regs) +{ + struct bpt *bp; + + bp = at_breakpoint(regs->nip); + if (!bp) + return 0; + if (bp->count) { + --bp->count; + remove_bpts(); + excprint(regs); + xmon_trace[smp_processor_id()] = BRSTEP; + regs->msr |= 0x400; + } else { + xmon(regs); + } + return 1; +} + +int +xmon_sstep(struct pt_regs *regs) +{ + if (!xmon_trace[smp_processor_id()]) + return 0; + if (xmon_trace[smp_processor_id()] == BRSTEP) { + xmon_trace[smp_processor_id()] = 0; + insert_bpts(); + } else { + xmon(regs); + } + return 1; +} + +int +xmon_dabr_match(struct pt_regs *regs) +{ + if (dabr.enabled && dabr.count) { + --dabr.count; + remove_bpts(); + excprint(regs); + xmon_trace[smp_processor_id()] = BRSTEP; + regs->msr |= 0x400; + } else { + dabr.instr = regs->nip; + xmon(regs); + } + return 1; +} + +int +xmon_iabr_match(struct pt_regs *regs) +{ + if (iabr.enabled && iabr.count) { + --iabr.count; + remove_bpts(); + excprint(regs); + xmon_trace[smp_processor_id()] = BRSTEP; + regs->msr |= 0x400; + } else { + xmon(regs); + } + return 1; +} + +static struct bpt * +at_breakpoint(unsigned pc) +{ + int i; + struct bpt *bp; + + if (dabr.enabled && pc == dabr.instr) + return &dabr; + if (iabr.enabled && pc == iabr.address) + return &iabr; + bp = bpts; + for (i = 0; i < NBPTS; ++i, ++bp) + if (bp->enabled && pc == bp->address) + return bp; + return NULL; +} + +static void +insert_bpts(void) +{ + int i; + struct bpt *bp; + + bp = bpts; + for (i = 0; i < NBPTS; ++i, ++bp) { + if (!bp->enabled) + continue; + if (mread(bp->address, &bp->instr, 4) != 4 + || mwrite(bp->address, &bpinstr, 4) != 4) { + printf("Couldn't insert breakpoint at %x, disabling\n", + bp->address); + bp->enabled = 0; + } + store_inst((void *) bp->address); + } +#if !defined(CONFIG_8xx) + if (dabr.enabled) + set_dabr(dabr.address); + if (iabr.enabled) + set_iabr(iabr.address); +#endif +} + +static void +remove_bpts(void) +{ + int i; + struct bpt *bp; + unsigned instr; + +#if !defined(CONFIG_8xx) + set_dabr(0); + set_iabr(0); +#endif + bp = bpts; + for (i = 0; i < NBPTS; ++i, ++bp) { + if (!bp->enabled) + continue; + if (mread(bp->address, &instr, 4) == 4 + && instr == bpinstr + && mwrite(bp->address, &bp->instr, 4) != 4) + printf("Couldn't remove breakpoint at %x\n", + bp->address); + store_inst((void *) bp->address); + } +} + +static char *last_cmd; + +/* Command interpreting routine */ +static int +cmds(struct pt_regs *excp) +{ + int cmd; + + last_cmd = NULL; + for(;;) { +#ifdef CONFIG_SMP + printf("%d:", smp_processor_id()); +#endif /* CONFIG_SMP */ + printf("mon> "); + fflush(stdout); + flush_input(); + termch = 0; + cmd = skipbl(); + if( cmd == '\n' ) { + if (last_cmd == NULL) + continue; + take_input(last_cmd); + last_cmd = NULL; + cmd = inchar(); + } + switch (cmd) { + case 'm': + cmd = inchar(); + switch (cmd) { + case 'm': + case 's': + case 'd': + memops(cmd); + break; + case 'l': + memlocate(); + break; + case 'z': + memzcan(); + break; + default: + termch = cmd; + memex(); + } + break; + case 'd': + dump(); + break; + case 'l': + sysmap_lookup(); + break; + case 'r': + if (excp != NULL) + prregs(excp); /* print regs */ + break; + case 'e': + if (excp == NULL) + printf("No exception information\n"); + else + excprint(excp); + break; + case 'M': + print_sysmap(); + break; + case 'S': + super_regs(); + break; + case 't': + backtrace(excp); + break; + case 'f': + cacheflush(); + break; + case 'h': + dump_hash_table(); + break; + case 's': + case 'x': + case EOF: + return cmd; + case '?': + printf(help_string); + break; + default: + printf("Unrecognized command: "); + if( ' ' < cmd && cmd <= '~' ) + putchar(cmd); + else + printf("\\x%x", cmd); + printf(" (type ? for help)\n"); + break; + case 'b': + bpt_cmds(); + break; + case 'C': + csum(); + break; +#ifdef CONFIG_SMP + case 'c': + cpu_cmd(); + break; +#endif /* CONFIG_SMP */ +#ifdef CONFIG_BOOTX_TEXT + case 'v': + vidcmds(); + break; +#endif + case 'z': + bootcmds(); + break; + case 'p': + proccall(); + break; + case 'T': + printtime(); + break; + } + } +} + +extern unsigned tb_to_us; + +#define mulhwu(x,y) \ +({unsigned z; asm ("mulhwu %0,%1,%2" : "=r" (z) : "r" (x), "r" (y)); z;}) + +static void printtime(void) +{ + unsigned int delta; + + delta = stop_tb[smp_processor_id()][1] + - start_tb[smp_processor_id()][1]; + delta = mulhwu(tb_to_us, delta); + printf("%u.%06u seconds\n", delta / 1000000, delta % 1000000); +} + +static void bootcmds(void) +{ + int cmd; + + cmd = inchar(); + if (cmd == 'r') + ppc_md.restart(NULL); + else if (cmd == 'h') + ppc_md.halt(); + else if (cmd == 'p') + ppc_md.power_off(); +} + +#ifdef CONFIG_SMP +static void cpu_cmd(void) +{ + unsigned cpu; + int timeout; + int cmd; + + cmd = inchar(); + if (cmd == 'i') { + /* interrupt other cpu(s) */ + cpu = MSG_ALL_BUT_SELF; + if (scanhex(&cpu)) + smp_send_xmon_break(cpu); + return; + } + termch = cmd; + if (!scanhex(&cpu)) { + /* print cpus waiting or in xmon */ + printf("cpus stopped:"); + for (cpu = 0; cpu < NR_CPUS; ++cpu) { + if (test_bit(cpu, &cpus_in_xmon)) { + printf(" %d", cpu); + if (cpu == smp_processor_id()) + printf("*", cpu); + } + } + printf("\n"); + return; + } + /* try to switch to cpu specified */ + take_xmon = cpu; + timeout = 10000000; + while (take_xmon >= 0) { + if (--timeout == 0) { + /* yes there's a race here */ + take_xmon = -1; + printf("cpu %u didn't take control\n", cpu); + return; + } + } + /* now have to wait to be given control back */ + while (test_and_set_bit(0, &got_xmon)) { + if (take_xmon == smp_processor_id()) { + take_xmon = -1; + break; + } + } +} +#endif /* CONFIG_SMP */ + +#ifdef CONFIG_BOOTX_TEXT +extern boot_infos_t disp_bi; + +static void vidcmds(void) +{ + int c = inchar(); + unsigned int val, w; + extern int boot_text_mapped; + + if (!boot_text_mapped) + return; + if (c != '\n' && scanhex(&val)) { + switch (c) { + case 'd': + w = disp_bi.dispDeviceRowBytes + / (disp_bi.dispDeviceDepth >> 3); + disp_bi.dispDeviceDepth = val; + disp_bi.dispDeviceRowBytes = w * (val >> 3); + return; + case 'p': + disp_bi.dispDeviceRowBytes = val; + return; + case 'w': + disp_bi.dispDeviceRect[2] = val; + return; + case 'h': + disp_bi.dispDeviceRect[3] = val; + return; + } + } + printf("W = %d (0x%x) H = %d (0x%x) D = %d (0x%x) P = %d (0x%x)\n", + disp_bi.dispDeviceRect[2], disp_bi.dispDeviceRect[2], + disp_bi.dispDeviceRect[3], disp_bi.dispDeviceRect[3], + disp_bi.dispDeviceDepth, disp_bi.dispDeviceDepth, + disp_bi.dispDeviceRowBytes, disp_bi.dispDeviceRowBytes); +} +#endif /* CONFIG_BOOTX_TEXT */ + +static unsigned short fcstab[256] = { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; + +#define FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff]) + +static void +csum(void) +{ + unsigned int i; + unsigned short fcs; + unsigned char v; + + if (!scanhex(&adrs)) + return; + if (!scanhex(&ncsum)) + return; + fcs = 0xffff; + for (i = 0; i < ncsum; ++i) { + if (mread(adrs+i, &v, 1) == 0) { + printf("csum stopped at %x\n", adrs+i); + break; + } + fcs = FCS(fcs, v); + } + printf("%x\n", fcs); +} + +static void +bpt_cmds(void) +{ + int cmd; + unsigned a; + int mode, i; + struct bpt *bp; + + cmd = inchar(); + switch (cmd) { +#if !defined(CONFIG_8xx) + case 'd': + mode = 7; + cmd = inchar(); + if (cmd == 'r') + mode = 5; + else if (cmd == 'w') + mode = 6; + else + termch = cmd; + cmd = inchar(); + if (cmd == 'p') + mode &= ~4; + else + termch = cmd; + dabr.address = 0; + dabr.count = 0; + dabr.enabled = scanhex(&dabr.address); + scanhex(&dabr.count); + if (dabr.enabled) + dabr.address = (dabr.address & ~7) | mode; + break; + case 'i': + cmd = inchar(); + if (cmd == 'p') + mode = 2; + else + mode = 3; + iabr.address = 0; + iabr.count = 0; + iabr.enabled = scanhex(&iabr.address); + if (iabr.enabled) + iabr.address |= mode; + scanhex(&iabr.count); + break; +#endif + case 'c': + if (!scanhex(&a)) { + /* clear all breakpoints */ + for (i = 0; i < NBPTS; ++i) + bpts[i].enabled = 0; + iabr.enabled = 0; + dabr.enabled = 0; + printf("All breakpoints cleared\n"); + } else { + bp = at_breakpoint(a); + if (bp == 0) { + printf("No breakpoint at %x\n", a); + } else { + bp->enabled = 0; + } + } + break; + default: + termch = cmd; + if (!scanhex(&a)) { + /* print all breakpoints */ + printf("type address count\n"); + if (dabr.enabled) { + printf("data %.8x %8x [", dabr.address & ~7, + dabr.count); + if (dabr.address & 1) + printf("r"); + if (dabr.address & 2) + printf("w"); + if (!(dabr.address & 4)) + printf("p"); + printf("]\n"); + } + if (iabr.enabled) + printf("inst %.8x %8x\n", iabr.address & ~3, + iabr.count); + for (bp = bpts; bp < &bpts[NBPTS]; ++bp) + if (bp->enabled) + printf("trap %.8x %8x\n", bp->address, + bp->count); + break; + } + bp = at_breakpoint(a); + if (bp == 0) { + for (bp = bpts; bp < &bpts[NBPTS]; ++bp) + if (!bp->enabled) + break; + if (bp >= &bpts[NBPTS]) { + printf("Sorry, no free breakpoints\n"); + break; + } + } + bp->enabled = 1; + bp->address = a; + bp->count = 0; + scanhex(&bp->count); + break; + } +} + +static void +backtrace(struct pt_regs *excp) +{ + unsigned sp; + unsigned stack[2]; + struct pt_regs regs; + extern char ret_from_except, ret_from_except_full, ret_from_syscall; + + printf("backtrace:\n"); + + if (excp != NULL) + sp = excp->gpr[1]; + else + sp = getsp(); + scanhex(&sp); + scannl(); + for (; sp != 0; sp = stack[0]) { + if (mread(sp, stack, sizeof(stack)) != sizeof(stack)) + break; + pretty_print_addr(stack[1]); + printf(" "); + if (stack[1] == (unsigned) &ret_from_except + || stack[1] == (unsigned) &ret_from_except_full + || stack[1] == (unsigned) &ret_from_syscall) { + if (mread(sp+16, ®s, sizeof(regs)) != sizeof(regs)) + break; + printf("\nexception:%x [%x] %x ", regs.trap, sp+16, + regs.nip); + sp = regs.gpr[1]; + if (mread(sp, stack, sizeof(stack)) != sizeof(stack)) + break; + } + printf("\n"); + } +} + +int +getsp(void) +{ + int x; + + asm("mr %0,1" : "=r" (x) :); + return x; +} + +void +excprint(struct pt_regs *fp) +{ + int trap; + +#ifdef CONFIG_SMP + printf("cpu %d: ", smp_processor_id()); +#endif /* CONFIG_SMP */ + printf("vector: %x at pc = ", fp->trap); + pretty_print_addr(fp->nip); + printf(", lr = "); + pretty_print_addr(fp->link); + printf("\nmsr = %x, sp = %x [%x]\n", fp->msr, fp->gpr[1], fp); + trap = TRAP(fp); + if (trap == 0x300 || trap == 0x600) + printf("dar = %x, dsisr = %x\n", fp->dar, fp->dsisr); + if (current) + printf("current = %x, pid = %d, comm = %s\n", + current, current->pid, current->comm); +} + +void +prregs(struct pt_regs *fp) +{ + int n; + unsigned base; + + if (scanhex(&base)) + fp = (struct pt_regs *) base; + for (n = 0; n < 32; ++n) { + printf("R%.2d = %.8x%s", n, fp->gpr[n], + (n & 3) == 3? "\n": " "); + if (n == 12 && !FULL_REGS(fp)) { + printf("\n"); + break; + } + } + printf("pc = %.8x msr = %.8x lr = %.8x cr = %.8x\n", + fp->nip, fp->msr, fp->link, fp->ccr); + printf("ctr = %.8x xer = %.8x trap = %4x\n", + fp->ctr, fp->xer, fp->trap); +} + +void +cacheflush(void) +{ + int cmd; + unsigned nflush; + + cmd = inchar(); + if (cmd != 'i') + termch = cmd; + scanhex(&adrs); + if (termch != '\n') + termch = 0; + nflush = 1; + scanhex(&nflush); + nflush = (nflush + 31) / 32; + if (cmd != 'i') { + for (; nflush > 0; --nflush, adrs += 0x20) + cflush((void *) adrs); + } else { + for (; nflush > 0; --nflush, adrs += 0x20) + cinval((void *) adrs); + } +} + +unsigned int +read_spr(int n) +{ + unsigned int instrs[2]; + int (*code)(void); + + instrs[0] = 0x7c6002a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6); + instrs[1] = 0x4e800020; + store_inst(instrs); + store_inst(instrs+1); + code = (int (*)(void)) instrs; + return code(); +} + +void +write_spr(int n, unsigned int val) +{ + unsigned int instrs[2]; + int (*code)(unsigned int); + + instrs[0] = 0x7c6003a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6); + instrs[1] = 0x4e800020; + store_inst(instrs); + store_inst(instrs+1); + code = (int (*)(unsigned int)) instrs; + code(val); +} + +static unsigned int regno; +extern char exc_prolog; +extern char dec_exc; + +void +print_sysmap(void) +{ + extern char *sysmap; + if ( sysmap ) { + printf("System.map: \n"); + if( setjmp(bus_error_jmp) == 0 ) { + debugger_fault_handler = handle_fault; + sync(); + xmon_puts(sysmap); + sync(); + } + debugger_fault_handler = NULL; + } + else + printf("No System.map\n"); +} + +void +super_regs(void) +{ + int i, cmd; + unsigned val; + + cmd = skipbl(); + if (cmd == '\n') { + printf("msr = %x, pvr = %x\n", get_msr(), get_pvr()); + printf("sprg0-3 = %x %x %x %x\n", get_sprg0(), get_sprg1(), + get_sprg2(), get_sprg3()); + printf("srr0 = %x, srr1 = %x\n", get_srr0(), get_srr1()); +#ifdef CONFIG_PPC_STD_MMU + printf("sr0-15 ="); + for (i = 0; i < 16; ++i) + printf(" %x", get_sr(i)); + printf("\n"); +#endif + asm("mr %0,1" : "=r" (i) :); + printf("sp = %x ", i); + asm("mr %0,2" : "=r" (i) :); + printf("toc = %x\n", i); + return; + } + + scanhex(®no); + switch (cmd) { + case 'w': + val = read_spr(regno); + scanhex(&val); + write_spr(regno, val); + /* fall through */ + case 'r': + printf("spr %x = %x\n", regno, read_spr(regno)); + break; + case 's': + val = get_sr(regno); + scanhex(&val); + set_sr(regno, val); + break; + case 'm': + val = get_msr(); + scanhex(&val); + set_msr(val); + break; + } + scannl(); +} + +#ifndef CONFIG_PPC_STD_MMU +static void +dump_hash_table(void) +{ + printf("This CPU doesn't have a hash table.\n"); +} +#else + +#ifndef CONFIG_PPC64BRIDGE +static void +dump_hash_table_seg(unsigned seg, unsigned start, unsigned end) +{ + extern void *Hash; + extern unsigned long Hash_size; + unsigned *htab = Hash; + unsigned hsize = Hash_size; + unsigned v, hmask, va, last_va = 0; + int found, last_found, i; + unsigned *hg, w1, last_w2 = 0, last_va0 = 0; + + last_found = 0; + hmask = hsize / 64 - 1; + va = start; + start = (start >> 12) & 0xffff; + end = (end >> 12) & 0xffff; + for (v = start; v < end; ++v) { + found = 0; + hg = htab + (((v ^ seg) & hmask) * 16); + w1 = 0x80000000 | (seg << 7) | (v >> 10); + for (i = 0; i < 8; ++i, hg += 2) { + if (*hg == w1) { + found = 1; + break; + } + } + if (!found) { + w1 ^= 0x40; + hg = htab + ((~(v ^ seg) & hmask) * 16); + for (i = 0; i < 8; ++i, hg += 2) { + if (*hg == w1) { + found = 1; + break; + } + } + } + if (!(last_found && found && (hg[1] & ~0x180) == last_w2 + 4096)) { + if (last_found) { + if (last_va != last_va0) + printf(" ... %x", last_va); + printf("\n"); + } + if (found) { + printf("%x to %x", va, hg[1]); + last_va0 = va; + } + last_found = found; + } + if (found) { + last_w2 = hg[1] & ~0x180; + last_va = va; + } + va += 4096; + } + if (last_found) + printf(" ... %x\n", last_va); +} + +#else /* CONFIG_PPC64BRIDGE */ +static void +dump_hash_table_seg(unsigned seg, unsigned start, unsigned end) +{ + extern void *Hash; + extern unsigned long Hash_size; + unsigned *htab = Hash; + unsigned hsize = Hash_size; + unsigned v, hmask, va, last_va; + int found, last_found, i; + unsigned *hg, w1, last_w2, last_va0; + + last_found = 0; + hmask = hsize / 128 - 1; + va = start; + start = (start >> 12) & 0xffff; + end = (end >> 12) & 0xffff; + for (v = start; v < end; ++v) { + found = 0; + hg = htab + (((v ^ seg) & hmask) * 32); + w1 = 1 | (seg << 12) | ((v & 0xf800) >> 4); + for (i = 0; i < 8; ++i, hg += 4) { + if (hg[1] == w1) { + found = 1; + break; + } + } + if (!found) { + w1 ^= 2; + hg = htab + ((~(v ^ seg) & hmask) * 32); + for (i = 0; i < 8; ++i, hg += 4) { + if (hg[1] == w1) { + found = 1; + break; + } + } + } + if (!(last_found && found && (hg[3] & ~0x180) == last_w2 + 4096)) { + if (last_found) { + if (last_va != last_va0) + printf(" ... %x", last_va); + printf("\n"); + } + if (found) { + printf("%x to %x", va, hg[3]); + last_va0 = va; + } + last_found = found; + } + if (found) { + last_w2 = hg[3] & ~0x180; + last_va = va; + } + va += 4096; + } + if (last_found) + printf(" ... %x\n", last_va); +} +#endif /* CONFIG_PPC64BRIDGE */ + +static unsigned hash_ctx; +static unsigned hash_start; +static unsigned hash_end; + +static void +dump_hash_table(void) +{ + int seg; + unsigned seg_start, seg_end; + + hash_ctx = 0; + hash_start = 0; + hash_end = 0xfffff000; + scanhex(&hash_ctx); + scanhex(&hash_start); + scanhex(&hash_end); + printf("Mappings for context %x\n", hash_ctx); + seg_start = hash_start; + for (seg = hash_start >> 28; seg <= hash_end >> 28; ++seg) { + seg_end = (seg << 28) | 0x0ffff000; + if (seg_end > hash_end) + seg_end = hash_end; + dump_hash_table_seg((hash_ctx << 4) + (seg * 0x111), + seg_start, seg_end); + seg_start = seg_end + 0x1000; + } +} +#endif /* CONFIG_PPC_STD_MMU */ + +/* + * Stuff for reading and writing memory safely + */ + +int +mread(unsigned adrs, void *buf, int size) +{ + volatile int n; + char *p, *q; + + n = 0; + if( setjmp(bus_error_jmp) == 0 ){ + debugger_fault_handler = handle_fault; + sync(); + p = (char *) adrs; + q = (char *) buf; + switch (size) { + case 2: *(short *)q = *(short *)p; break; + case 4: *(int *)q = *(int *)p; break; + default: + for( ; n < size; ++n ) { + *q++ = *p++; + sync(); + } + } + sync(); + /* wait a little while to see if we get a machine check */ + __delay(200); + n = size; + } + debugger_fault_handler = NULL; + return n; +} + +int +mwrite(unsigned adrs, void *buf, int size) +{ + volatile int n; + char *p, *q; + + n = 0; + if( setjmp(bus_error_jmp) == 0 ){ + debugger_fault_handler = handle_fault; + sync(); + p = (char *) adrs; + q = (char *) buf; + switch (size) { + case 2: *(short *)p = *(short *)q; break; + case 4: *(int *)p = *(int *)q; break; + default: + for( ; n < size; ++n ) { + *p++ = *q++; + sync(); + } + } + sync(); + n = size; + } else { + printf("*** Error writing address %x\n", adrs + n); + } + debugger_fault_handler = NULL; + return n; +} + +static int fault_type; +static int fault_except; +static char *fault_chars[] = { "--", "**", "##" }; + +static void +handle_fault(struct pt_regs *regs) +{ + fault_except = TRAP(regs); + fault_type = TRAP(regs) == 0x200? 0: TRAP(regs) == 0x300? 1: 2; + longjmp(bus_error_jmp, 1); +} + +#define SWAP(a, b, t) ((t) = (a), (a) = (b), (b) = (t)) + +void +byterev(unsigned char *val, int size) +{ + int t; + + switch (size) { + case 2: + SWAP(val[0], val[1], t); + break; + case 4: + SWAP(val[0], val[3], t); + SWAP(val[1], val[2], t); + break; + } +} + +static int brev; +static int mnoread; + +void +memex(void) +{ + int cmd, inc, i, nslash; + unsigned n; + unsigned char val[4]; + + last_cmd = "m\n"; + scanhex(&adrs); + while ((cmd = skipbl()) != '\n') { + switch( cmd ){ + case 'b': size = 1; break; + case 'w': size = 2; break; + case 'l': size = 4; break; + case 'r': brev = !brev; break; + case 'n': mnoread = 1; break; + case '.': mnoread = 0; break; + } + } + if( size <= 0 ) + size = 1; + else if( size > 4 ) + size = 4; + for(;;){ + if (!mnoread) + n = mread(adrs, val, size); + printf("%.8x%c", adrs, brev? 'r': ' '); + if (!mnoread) { + if (brev) + byterev(val, size); + putchar(' '); + for (i = 0; i < n; ++i) + printf("%.2x", val[i]); + for (; i < size; ++i) + printf("%s", fault_chars[fault_type]); + } + putchar(' '); + inc = size; + nslash = 0; + for(;;){ + if( scanhex(&n) ){ + for (i = 0; i < size; ++i) + val[i] = n >> (i * 8); + if (!brev) + byterev(val, size); + mwrite(adrs, val, size); + inc = size; + } + cmd = skipbl(); + if (cmd == '\n') + break; + inc = 0; + switch (cmd) { + case '\'': + for(;;){ + n = inchar(); + if( n == '\\' ) + n = bsesc(); + else if( n == '\'' ) + break; + for (i = 0; i < size; ++i) + val[i] = n >> (i * 8); + if (!brev) + byterev(val, size); + mwrite(adrs, val, size); + adrs += size; + } + adrs -= size; + inc = size; + break; + case ',': + adrs += size; + break; + case '.': + mnoread = 0; + break; + case ';': + break; + case 'x': + case EOF: + scannl(); + return; + case 'b': + case 'v': + size = 1; + break; + case 'w': + size = 2; + break; + case 'l': + size = 4; + break; + case '^': + adrs -= size; + break; + break; + case '/': + if (nslash > 0) + adrs -= 1 << nslash; + else + nslash = 0; + nslash += 4; + adrs += 1 << nslash; + break; + case '\\': + if (nslash < 0) + adrs += 1 << -nslash; + else + nslash = 0; + nslash -= 4; + adrs -= 1 << -nslash; + break; + case 'm': + scanhex(&adrs); + break; + case 'n': + mnoread = 1; + break; + case 'r': + brev = !brev; + break; + case '<': + n = size; + scanhex(&n); + adrs -= n; + break; + case '>': + n = size; + scanhex(&n); + adrs += n; + break; + } + } + adrs += inc; + } +} + +int +bsesc(void) +{ + int c; + + c = inchar(); + switch( c ){ + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 'b': c = '\b'; break; + case 't': c = '\t'; break; + } + return c; +} + +void +dump(void) +{ + int c; + + c = inchar(); + if ((isxdigit(c) && c != 'f' && c != 'd') || c == '\n') + termch = c; + scanhex(&adrs); + if( termch != '\n') + termch = 0; + if( c == 'i' ){ + scanhex(&nidump); + if( nidump == 0 ) + nidump = 16; + adrs += ppc_inst_dump(adrs, nidump); + last_cmd = "di\n"; + } else { + scanhex(&ndump); + if( ndump == 0 ) + ndump = 64; + prdump(adrs, ndump); + adrs += ndump; + last_cmd = "d\n"; + } +} + +void +prdump(unsigned adrs, int ndump) +{ + register int n, m, c, r, nr; + unsigned char temp[16]; + + for( n = ndump; n > 0; ){ + printf("%.8x", adrs); + putchar(' '); + r = n < 16? n: 16; + nr = mread(adrs, temp, r); + adrs += nr; + for( m = 0; m < r; ++m ){ + putchar((m & 3) == 0 && m > 0? '.': ' '); + if( m < nr ) + printf("%.2x", temp[m]); + else + printf("%s", fault_chars[fault_type]); + } + for(; m < 16; ++m ) + printf(" "); + printf(" |"); + for( m = 0; m < r; ++m ){ + if( m < nr ){ + c = temp[m]; + putchar(' ' <= c && c <= '~'? c: '.'); + } else + putchar(' '); + } + n -= r; + for(; m < 16; ++m ) + putchar(' '); + printf("|\n"); + if( nr < r ) + break; + } +} + +int +ppc_inst_dump(unsigned adr, int count) +{ + int nr, dotted; + unsigned first_adr; + unsigned long inst, last_inst = 0; + unsigned char val[4]; + + dotted = 0; + for (first_adr = adr; count > 0; --count, adr += 4){ + nr = mread(adr, val, 4); + if( nr == 0 ){ + const char *x = fault_chars[fault_type]; + printf("%.8x %s%s%s%s\n", adr, x, x, x, x); + break; + } + inst = GETWORD(val); + if (adr > first_adr && inst == last_inst) { + if (!dotted) { + printf(" ...\n"); + dotted = 1; + } + continue; + } + dotted = 0; + last_inst = inst; + printf("%.8x ", adr); + printf("%.8x\t", inst); + print_insn_big_powerpc(stdout, inst, adr); /* always returns 4 */ + printf("\n"); + } + return adr - first_adr; +} + +void +print_address(unsigned addr) +{ + printf("0x%x", addr); +} + +/* + * Memory operations - move, set, print differences + */ +static unsigned mdest; /* destination address */ +static unsigned msrc; /* source address */ +static unsigned mval; /* byte value to set memory to */ +static unsigned mcount; /* # bytes to affect */ +static unsigned mdiffs; /* max # differences to print */ + +void +memops(int cmd) +{ + scanhex(&mdest); + if( termch != '\n' ) + termch = 0; + scanhex(cmd == 's'? &mval: &msrc); + if( termch != '\n' ) + termch = 0; + scanhex(&mcount); + switch( cmd ){ + case 'm': + memmove((void *)mdest, (void *)msrc, mcount); + break; + case 's': + memset((void *)mdest, mval, mcount); + break; + case 'd': + if( termch != '\n' ) + termch = 0; + scanhex(&mdiffs); + memdiffs((unsigned char *)mdest, (unsigned char *)msrc, mcount, mdiffs); + break; + } +} + +void +memdiffs(unsigned char *p1, unsigned char *p2, unsigned nb, unsigned maxpr) +{ + unsigned n, prt; + + prt = 0; + for( n = nb; n > 0; --n ) + if( *p1++ != *p2++ ) + if( ++prt <= maxpr ) + printf("%.8x %.2x # %.8x %.2x\n", (unsigned)p1 - 1, + p1[-1], (unsigned)p2 - 1, p2[-1]); + if( prt > maxpr ) + printf("Total of %d differences\n", prt); +} + +static unsigned mend; +static unsigned mask; + +void +memlocate(void) +{ + unsigned a, n; + unsigned char val[4]; + + last_cmd = "ml"; + scanhex(&mdest); + if (termch != '\n') { + termch = 0; + scanhex(&mend); + if (termch != '\n') { + termch = 0; + scanhex(&mval); + mask = ~0; + if (termch != '\n') termch = 0; + scanhex(&mask); + } + } + n = 0; + for (a = mdest; a < mend; a += 4) { + if (mread(a, val, 4) == 4 + && ((GETWORD(val) ^ mval) & mask) == 0) { + printf("%.8x: %.8x\n", a, GETWORD(val)); + if (++n >= 10) + break; + } + } +} + +static unsigned mskip = 0x1000; +static unsigned mlim = 0xffffffff; + +void +memzcan(void) +{ + unsigned char v; + unsigned a; + int ok, ook; + + scanhex(&mdest); + if (termch != '\n') termch = 0; + scanhex(&mskip); + if (termch != '\n') termch = 0; + scanhex(&mlim); + ook = 0; + for (a = mdest; a < mlim; a += mskip) { + ok = mread(a, &v, 1); + if (ok && !ook) { + printf("%.8x .. ", a); + fflush(stdout); + } else if (!ok && ook) + printf("%.8x\n", a - mskip); + ook = ok; + if (a + mskip < a) + break; + } + if (ook) + printf("%.8x\n", a - mskip); +} + +void proccall(void) +{ + unsigned int args[8]; + unsigned int ret; + int i; + typedef unsigned int (*callfunc_t)(unsigned int, unsigned int, + unsigned int, unsigned int, unsigned int, + unsigned int, unsigned int, unsigned int); + callfunc_t func; + + scanhex(&adrs); + if (termch != '\n') + termch = 0; + for (i = 0; i < 8; ++i) + args[i] = 0; + for (i = 0; i < 8; ++i) { + if (!scanhex(&args[i]) || termch == '\n') + break; + termch = 0; + } + func = (callfunc_t) adrs; + ret = 0; + if (setjmp(bus_error_jmp) == 0) { + debugger_fault_handler = handle_fault; + sync(); + ret = func(args[0], args[1], args[2], args[3], + args[4], args[5], args[6], args[7]); + sync(); + printf("return value is %x\n", ret); + } else { + printf("*** %x exception occurred\n", fault_except); + } + debugger_fault_handler = NULL; +} + +/* Input scanning routines */ +int +skipbl(void) +{ + int c; + + if( termch != 0 ){ + c = termch; + termch = 0; + } else + c = inchar(); + while( c == ' ' || c == '\t' ) + c = inchar(); + return c; +} + +#define N_PTREGS 44 +static char *regnames[N_PTREGS] = { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", + "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", + "pc", "msr", "or3", "ctr", "lr", "xer", "ccr", "mq", + "trap", "dar", "dsisr", "res" +}; + +int +scanhex(unsigned *vp) +{ + int c, d; + unsigned v; + + c = skipbl(); + if (c == '%') { + /* parse register name */ + char regname[8]; + int i; + + for (i = 0; i < sizeof(regname) - 1; ++i) { + c = inchar(); + if (!isalnum(c)) { + termch = c; + break; + } + regname[i] = c; + } + regname[i] = 0; + for (i = 0; i < N_PTREGS; ++i) { + if (strcmp(regnames[i], regname) == 0) { + unsigned *rp = (unsigned *) + xmon_regs[smp_processor_id()]; + if (rp == NULL) { + printf("regs not available\n"); + return 0; + } + *vp = rp[i]; + return 1; + } + } + printf("invalid register name '%%%s'\n", regname); + return 0; + } else if (c == '$') { + static char symname[64]; + int i; + for (i=0; i<63; i++) { + c = inchar(); + if (isspace(c)) { + termch = c; + break; + } + symname[i] = c; + } + symname[i++] = 0; + *vp = xmon_symbol_to_addr(symname); + if (!(*vp)) { + printf("unknown symbol\n"); + return 0; + } + return 1; + } + + d = hexdigit(c); + if( d == EOF ){ + termch = c; + return 0; + } + v = 0; + do { + v = (v << 4) + d; + c = inchar(); + d = hexdigit(c); + } while( d != EOF ); + termch = c; + *vp = v; + return 1; +} + +void +scannl(void) +{ + int c; + + c = termch; + termch = 0; + while( c != '\n' ) + c = inchar(); +} + +int hexdigit(int c) +{ + if( '0' <= c && c <= '9' ) + return c - '0'; + if( 'A' <= c && c <= 'F' ) + return c - ('A' - 10); + if( 'a' <= c && c <= 'f' ) + return c - ('a' - 10); + return EOF; +} + +void +getstring(char *s, int size) +{ + int c; + + c = skipbl(); + do { + if( size > 1 ){ + *s++ = c; + --size; + } + c = inchar(); + } while( c != ' ' && c != '\t' && c != '\n' ); + termch = c; + *s = 0; +} + +static char line[256]; +static char *lineptr; + +void +flush_input(void) +{ + lineptr = NULL; +} + +int +inchar(void) +{ + if (lineptr == NULL || *lineptr == 0) { + if (fgets(line, sizeof(line), stdin) == NULL) { + lineptr = NULL; + return EOF; + } + lineptr = line; + } + return *lineptr++; +} + +void +take_input(char *str) +{ + lineptr = str; +} + +void +sysmap_lookup(void) +{ + int type = inchar(); + unsigned addr; + static char tmp[64]; + char* cur; + + extern char *sysmap; + extern unsigned long sysmap_size; + if ( !sysmap || !sysmap_size ) + return; + + switch(type) { + case 'a': + if (scanhex(&addr)) { + pretty_print_addr(addr); + printf("\n"); + } + termch = 0; + break; + case 's': + getstring(tmp, 64); + if( setjmp(bus_error_jmp) == 0 ) { + debugger_fault_handler = handle_fault; + sync(); + cur = sysmap; + do { + cur = strstr(cur, tmp); + if (cur) { + static char res[64]; + char *p, *d; + p = cur; + while(p > sysmap && *p != 10) + p--; + if (*p == 10) p++; + d = res; + while(*p && p < (sysmap + sysmap_size) && *p != 10) + *(d++) = *(p++); + *(d++) = 0; + printf("%s\n", res); + cur++; + } + } while (cur); + sync(); + } + debugger_fault_handler = NULL; + termch = 0; + break; + } +} + +static int +pretty_print_addr(unsigned long addr) +{ + char *sym; + unsigned long saddr; + + printf("%08x", addr); + sym = xmon_find_symbol(addr, &saddr); + if (sym) + printf(" (%s+0x%x)", sym, addr-saddr); + return (sym != 0); +} + +char* +xmon_find_symbol(unsigned long addr, unsigned long* saddr) +{ + static char rbuffer[64]; + char *p, *ep, *limit; + unsigned long prev, next; + char* psym; + + extern char *sysmap; + extern unsigned long sysmap_size; + if ( !sysmap || !sysmap_size ) + return NULL; + + prev = 0; + psym = NULL; + p = sysmap; + limit = p + sysmap_size; + if( setjmp(bus_error_jmp) == 0 ) { + debugger_fault_handler = handle_fault; + sync(); + do { + next = simple_strtoul(p, &p, 16); + if (next > addr && prev <= addr) { + if (!psym) + goto bail; + ep = rbuffer; + p = psym; + while(*p && p < limit && *p == 32) + p++; + while(*p && p < limit && *p != 10 && (ep - rbuffer) < 63) + *(ep++) = *(p++); + *(ep++) = 0; + if (saddr) + *saddr = prev; + debugger_fault_handler = NULL; + return rbuffer; + } + prev = next; + psym = p; + while(*p && p < limit && *p != 10) + p++; + if (*p) p++; + } while(*p && p < limit && next); +bail: + sync(); + } + debugger_fault_handler = NULL; + return NULL; +} + +unsigned long +xmon_symbol_to_addr(char* symbol) +{ + char *p, *cur; + char *match = NULL; + int goodness = 0; + int result = 0; + + extern char *sysmap; + extern unsigned long sysmap_size; + if ( !sysmap || !sysmap_size ) + return 0; + + if( setjmp(bus_error_jmp) == 0 ) { + debugger_fault_handler = handle_fault; + sync(); + cur = sysmap; + while(cur) { + cur = strstr(cur, symbol); + if (cur) { + int gd = 1; + + /* best match if equal, better match if + * begins with + */ + if (cur == sysmap || *(cur-1) == ' ') { + gd++; + if (cur[strlen(symbol)] == 10) + gd++; + } + if (gd > goodness) { + match = cur; + goodness = gd; + if (gd == 3) + break; + } + cur++; + } + } + if (goodness) { + p = match; + while(p > sysmap && *p != 10) + p--; + if (*p == 10) p++; + result = simple_strtoul(p, &p, 16); + } + sync(); + } + debugger_fault_handler = NULL; + return result; +} -- cgit v1.2.3