aboutsummaryrefslogtreecommitdiff
path: root/arch/ppc
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/ppc
downloadlinux-stericsson-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.gz
Linux-2.6.12-rc2v2.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!
Diffstat (limited to 'arch/ppc')
-rw-r--r--arch/ppc/4xx_io/Makefile6
-rw-r--r--arch/ppc/4xx_io/serial_sicc.c2012
-rw-r--r--arch/ppc/8260_io/Kconfig65
-rw-r--r--arch/ppc/8260_io/Makefile6
-rw-r--r--arch/ppc/8260_io/enet.c867
-rw-r--r--arch/ppc/8260_io/fcc_enet.c2395
-rw-r--r--arch/ppc/8xx_io/Kconfig138
-rw-r--r--arch/ppc/8xx_io/Makefile10
-rw-r--r--arch/ppc/8xx_io/commproc.c464
-rw-r--r--arch/ppc/8xx_io/cs4218.h167
-rw-r--r--arch/ppc/8xx_io/cs4218_tdm.c2836
-rw-r--r--arch/ppc/8xx_io/enet.c971
-rw-r--r--arch/ppc/8xx_io/fec.c1973
-rw-r--r--arch/ppc/8xx_io/micropatch.c744
-rw-r--r--arch/ppc/Kconfig1317
-rw-r--r--arch/ppc/Kconfig.debug72
-rw-r--r--arch/ppc/Makefile138
-rw-r--r--arch/ppc/amiga/Makefile8
-rw-r--r--arch/ppc/amiga/amiga_ksyms.c1
-rw-r--r--arch/ppc/amiga/amiints.c323
-rw-r--r--arch/ppc/amiga/amisound.c1
-rw-r--r--arch/ppc/amiga/bootinfo.c80
-rw-r--r--arch/ppc/amiga/chipram.c1
-rw-r--r--arch/ppc/amiga/cia.c178
-rw-r--r--arch/ppc/amiga/config.c962
-rw-r--r--arch/ppc/amiga/ints.c160
-rw-r--r--arch/ppc/amiga/pcmcia.c1
-rw-r--r--arch/ppc/amiga/time.c58
-rw-r--r--arch/ppc/boot/Makefile34
-rw-r--r--arch/ppc/boot/common/Makefile13
-rw-r--r--arch/ppc/boot/common/bootinfo.c70
-rw-r--r--arch/ppc/boot/common/crt0.S81
-rw-r--r--arch/ppc/boot/common/misc-common.c553
-rw-r--r--arch/ppc/boot/common/ns16550.c99
-rw-r--r--arch/ppc/boot/common/serial_stub.c23
-rw-r--r--arch/ppc/boot/common/string.S150
-rw-r--r--arch/ppc/boot/common/util.S293
-rw-r--r--arch/ppc/boot/images/Makefile27
-rw-r--r--arch/ppc/boot/include/cpc700.h26
-rw-r--r--arch/ppc/boot/include/iso_font.h257
-rw-r--r--arch/ppc/boot/include/mpc10x.h65
-rw-r--r--arch/ppc/boot/include/mpsc_defs.h146
-rw-r--r--arch/ppc/boot/include/nonstdio.h34
-rw-r--r--arch/ppc/boot/include/of1275.h39
-rw-r--r--arch/ppc/boot/include/rs6000.h243
-rw-r--r--arch/ppc/boot/include/serial.h46
-rw-r--r--arch/ppc/boot/ld.script88
-rw-r--r--arch/ppc/boot/lib/Makefile23
-rw-r--r--arch/ppc/boot/lib/div64.S58
-rw-r--r--arch/ppc/boot/lib/kbd.c248
-rw-r--r--arch/ppc/boot/lib/vreset.c805
-rw-r--r--arch/ppc/boot/of1275/Makefile6
-rw-r--r--arch/ppc/boot/of1275/claim.c34
-rw-r--r--arch/ppc/boot/of1275/enter.c22
-rw-r--r--arch/ppc/boot/of1275/exit.c24
-rw-r--r--arch/ppc/boot/of1275/finddevice.c31
-rw-r--r--arch/ppc/boot/of1275/getprop.c37
-rw-r--r--arch/ppc/boot/of1275/map.c48
-rw-r--r--arch/ppc/boot/of1275/ofinit.c27
-rw-r--r--arch/ppc/boot/of1275/ofstdio.c32
-rw-r--r--arch/ppc/boot/of1275/read.c35
-rw-r--r--arch/ppc/boot/of1275/release.c30
-rw-r--r--arch/ppc/boot/of1275/write.c35
-rw-r--r--arch/ppc/boot/openfirmware/Makefile188
-rw-r--r--arch/ppc/boot/openfirmware/chrpmain.c101
-rw-r--r--arch/ppc/boot/openfirmware/coffmain.c101
-rw-r--r--arch/ppc/boot/openfirmware/common.c162
-rw-r--r--arch/ppc/boot/openfirmware/dummy.c4
-rw-r--r--arch/ppc/boot/openfirmware/misc.S67
-rw-r--r--arch/ppc/boot/openfirmware/newworldmain.c94
-rw-r--r--arch/ppc/boot/openfirmware/start.c172
-rw-r--r--arch/ppc/boot/simple/Makefile252
-rw-r--r--arch/ppc/boot/simple/chrpmap.c12
-rw-r--r--arch/ppc/boot/simple/clear.S19
-rw-r--r--arch/ppc/boot/simple/cpc700_memory.c36
-rw-r--r--arch/ppc/boot/simple/dummy.c4
-rw-r--r--arch/ppc/boot/simple/embed_config.c981
-rw-r--r--arch/ppc/boot/simple/head.S142
-rw-r--r--arch/ppc/boot/simple/iic.c214
-rw-r--r--arch/ppc/boot/simple/m8260_tty.c325
-rw-r--r--arch/ppc/boot/simple/m8xx_tty.c290
-rw-r--r--arch/ppc/boot/simple/misc-chestnut.c35
-rw-r--r--arch/ppc/boot/simple/misc-cpci690.c27
-rw-r--r--arch/ppc/boot/simple/misc-embedded.c275
-rw-r--r--arch/ppc/boot/simple/misc-ev64260.c57
-rw-r--r--arch/ppc/boot/simple/misc-katana.c37
-rw-r--r--arch/ppc/boot/simple/misc-mv64x60.c61
-rw-r--r--arch/ppc/boot/simple/misc-prep.c212
-rw-r--r--arch/ppc/boot/simple/misc-radstone_ppc7d.c26
-rw-r--r--arch/ppc/boot/simple/misc-spruce.c274
-rw-r--r--arch/ppc/boot/simple/misc.c284
-rw-r--r--arch/ppc/boot/simple/mpc10x_memory.c111
-rw-r--r--arch/ppc/boot/simple/mpc52xx_tty.c140
-rw-r--r--arch/ppc/boot/simple/mv64x60_tty.c360
-rw-r--r--arch/ppc/boot/simple/openbios.c37
-rw-r--r--arch/ppc/boot/simple/pci.c274
-rw-r--r--arch/ppc/boot/simple/pibs.c103
-rw-r--r--arch/ppc/boot/simple/prepmap.c12
-rw-r--r--arch/ppc/boot/simple/qspan_pci.c269
-rw-r--r--arch/ppc/boot/simple/relocate.S216
-rw-r--r--arch/ppc/boot/simple/rw4/ppc_40x.h664
-rw-r--r--arch/ppc/boot/simple/rw4/rw4_init.S78
-rw-r--r--arch/ppc/boot/simple/rw4/rw4_init_brd.S1125
-rw-r--r--arch/ppc/boot/simple/rw4/stb.h239
-rw-r--r--arch/ppc/boot/utils/addRamDisk.c203
-rw-r--r--arch/ppc/boot/utils/addSystemMap.c186
-rw-r--r--arch/ppc/boot/utils/addnote.c175
-rw-r--r--arch/ppc/boot/utils/elf.pl33
-rw-r--r--arch/ppc/boot/utils/hack-coff.c84
-rw-r--r--arch/ppc/boot/utils/mkbugboot.c187
-rw-r--r--arch/ppc/boot/utils/mknote.c44
-rw-r--r--arch/ppc/boot/utils/mkprep.c293
-rw-r--r--arch/ppc/boot/utils/mktree.c152
-rw-r--r--arch/ppc/configs/FADS_defconfig520
-rw-r--r--arch/ppc/configs/IVMS8_defconfig548
-rw-r--r--arch/ppc/configs/SM850_defconfig522
-rw-r--r--arch/ppc/configs/SPD823TS_defconfig520
-rw-r--r--arch/ppc/configs/TQM823L_defconfig521
-rw-r--r--arch/ppc/configs/TQM8260_defconfig499
-rw-r--r--arch/ppc/configs/TQM850L_defconfig521
-rw-r--r--arch/ppc/configs/TQM860L_defconfig549
-rw-r--r--arch/ppc/configs/adir_defconfig805
-rw-r--r--arch/ppc/configs/ads8272_defconfig582
-rw-r--r--arch/ppc/configs/apus_defconfig920
-rw-r--r--arch/ppc/configs/ash_defconfig666
-rw-r--r--arch/ppc/configs/beech_defconfig615
-rw-r--r--arch/ppc/configs/bseip_defconfig517
-rw-r--r--arch/ppc/configs/bubinga_defconfig592
-rw-r--r--arch/ppc/configs/cedar_defconfig534
-rw-r--r--arch/ppc/configs/chestnut_defconfig794
-rw-r--r--arch/ppc/configs/common_defconfig1421
-rw-r--r--arch/ppc/configs/cpci405_defconfig631
-rw-r--r--arch/ppc/configs/cpci690_defconfig686
-rw-r--r--arch/ppc/configs/ebony_defconfig585
-rw-r--r--arch/ppc/configs/ep405_defconfig572
-rw-r--r--arch/ppc/configs/est8260_defconfig491
-rw-r--r--arch/ppc/configs/ev64260_defconfig759
-rw-r--r--arch/ppc/configs/gemini_defconfig618
-rw-r--r--arch/ppc/configs/hdpu_defconfig890
-rw-r--r--arch/ppc/configs/ibmchrp_defconfig875
-rw-r--r--arch/ppc/configs/k2_defconfig680
-rw-r--r--arch/ppc/configs/katana_defconfig861
-rw-r--r--arch/ppc/configs/lite5200_defconfig436
-rw-r--r--arch/ppc/configs/lopec_defconfig814
-rw-r--r--arch/ppc/configs/luan_defconfig668
-rw-r--r--arch/ppc/configs/mbx_defconfig512
-rw-r--r--arch/ppc/configs/mcpn765_defconfig579
-rw-r--r--arch/ppc/configs/menf1_defconfig621
-rw-r--r--arch/ppc/configs/mpc834x_sys_defconfig644
-rw-r--r--arch/ppc/configs/mpc8540_ads_defconfig707
-rw-r--r--arch/ppc/configs/mpc8555_cds_defconfig718
-rw-r--r--arch/ppc/configs/mpc8560_ads_defconfig719
-rw-r--r--arch/ppc/configs/mvme5100_defconfig746
-rw-r--r--arch/ppc/configs/oak_defconfig485
-rw-r--r--arch/ppc/configs/ocotea_defconfig599
-rw-r--r--arch/ppc/configs/pcore_defconfig716
-rw-r--r--arch/ppc/configs/pmac_defconfig1523
-rw-r--r--arch/ppc/configs/power3_defconfig1034
-rw-r--r--arch/ppc/configs/pplus_defconfig720
-rw-r--r--arch/ppc/configs/prpmc750_defconfig594
-rw-r--r--arch/ppc/configs/prpmc800_defconfig656
-rw-r--r--arch/ppc/configs/radstone_ppc7d_defconfig956
-rw-r--r--arch/ppc/configs/rainier_defconfig599
-rw-r--r--arch/ppc/configs/redwood5_defconfig557
-rw-r--r--arch/ppc/configs/redwood6_defconfig535
-rw-r--r--arch/ppc/configs/redwood_defconfig540
-rw-r--r--arch/ppc/configs/rpx8260_defconfig555
-rw-r--r--arch/ppc/configs/rpxcllf_defconfig582
-rw-r--r--arch/ppc/configs/rpxlite_defconfig581
-rw-r--r--arch/ppc/configs/sandpoint_defconfig737
-rw-r--r--arch/ppc/configs/spruce_defconfig577
-rw-r--r--arch/ppc/configs/stx_gp3_defconfig972
-rw-r--r--arch/ppc/configs/sycamore_defconfig664
-rw-r--r--arch/ppc/configs/walnut_defconfig578
-rw-r--r--arch/ppc/kernel/Makefile33
-rw-r--r--arch/ppc/kernel/align.c398
-rw-r--r--arch/ppc/kernel/asm-offsets.c146
-rw-r--r--arch/ppc/kernel/bitops.c126
-rw-r--r--arch/ppc/kernel/cpu_setup_6xx.S440
-rw-r--r--arch/ppc/kernel/cpu_setup_power4.S201
-rw-r--r--arch/ppc/kernel/cputable.c922
-rw-r--r--arch/ppc/kernel/dma-mapping.c447
-rw-r--r--arch/ppc/kernel/entry.S969
-rw-r--r--arch/ppc/kernel/find_name.c48
-rw-r--r--arch/ppc/kernel/head.S1710
-rw-r--r--arch/ppc/kernel/head_44x.S753
-rw-r--r--arch/ppc/kernel/head_4xx.S1010
-rw-r--r--arch/ppc/kernel/head_8xx.S862
-rw-r--r--arch/ppc/kernel/head_booke.h340
-rw-r--r--arch/ppc/kernel/head_fsl_booke.S952
-rw-r--r--arch/ppc/kernel/idle.c100
-rw-r--r--arch/ppc/kernel/idle_6xx.S233
-rw-r--r--arch/ppc/kernel/idle_power4.S91
-rw-r--r--arch/ppc/kernel/irq.c164
-rw-r--r--arch/ppc/kernel/l2cr.S442
-rw-r--r--arch/ppc/kernel/misc.S1453
-rw-r--r--arch/ppc/kernel/module.c320
-rw-r--r--arch/ppc/kernel/pci.c1849
-rw-r--r--arch/ppc/kernel/perfmon.c93
-rw-r--r--arch/ppc/kernel/perfmon_fsl_booke.c222
-rw-r--r--arch/ppc/kernel/ppc-stub.c867
-rw-r--r--arch/ppc/kernel/ppc_htab.c467
-rw-r--r--arch/ppc/kernel/ppc_ksyms.c350
-rw-r--r--arch/ppc/kernel/process.c781
-rw-r--r--arch/ppc/kernel/ptrace.c474
-rw-r--r--arch/ppc/kernel/semaphore.c131
-rw-r--r--arch/ppc/kernel/setup.c778
-rw-r--r--arch/ppc/kernel/signal.c775
-rw-r--r--arch/ppc/kernel/smp-tbsync.c181
-rw-r--r--arch/ppc/kernel/smp.c399
-rw-r--r--arch/ppc/kernel/softemu8xx.c147
-rw-r--r--arch/ppc/kernel/swsusp.S349
-rw-r--r--arch/ppc/kernel/syscalls.c272
-rw-r--r--arch/ppc/kernel/temp.c272
-rw-r--r--arch/ppc/kernel/time.c447
-rw-r--r--arch/ppc/kernel/traps.c886
-rw-r--r--arch/ppc/kernel/vecemu.c345
-rw-r--r--arch/ppc/kernel/vector.S217
-rw-r--r--arch/ppc/kernel/vmlinux.lds.S192
-rw-r--r--arch/ppc/lib/Makefile9
-rw-r--r--arch/ppc/lib/checksum.S225
-rw-r--r--arch/ppc/lib/dec_and_lock.c46
-rw-r--r--arch/ppc/lib/div64.S58
-rw-r--r--arch/ppc/lib/locks.c190
-rw-r--r--arch/ppc/lib/rheap.c693
-rw-r--r--arch/ppc/lib/strcase.c23
-rw-r--r--arch/ppc/lib/string.S716
-rw-r--r--arch/ppc/math-emu/Makefile13
-rw-r--r--arch/ppc/math-emu/double.h129
-rw-r--r--arch/ppc/math-emu/fabs.c18
-rw-r--r--arch/ppc/math-emu/fadd.c38
-rw-r--r--arch/ppc/math-emu/fadds.c39
-rw-r--r--arch/ppc/math-emu/fcmpo.c46
-rw-r--r--arch/ppc/math-emu/fcmpu.c42
-rw-r--r--arch/ppc/math-emu/fctiw.c25
-rw-r--r--arch/ppc/math-emu/fctiwz.c32
-rw-r--r--arch/ppc/math-emu/fdiv.c53
-rw-r--r--arch/ppc/math-emu/fdivs.c55
-rw-r--r--arch/ppc/math-emu/fmadd.c48
-rw-r--r--arch/ppc/math-emu/fmadds.c49
-rw-r--r--arch/ppc/math-emu/fmr.c18
-rw-r--r--arch/ppc/math-emu/fmsub.c51
-rw-r--r--arch/ppc/math-emu/fmsubs.c52
-rw-r--r--arch/ppc/math-emu/fmul.c42
-rw-r--r--arch/ppc/math-emu/fmuls.c43
-rw-r--r--arch/ppc/math-emu/fnabs.c18
-rw-r--r--arch/ppc/math-emu/fneg.c18
-rw-r--r--arch/ppc/math-emu/fnmadd.c51
-rw-r--r--arch/ppc/math-emu/fnmadds.c52
-rw-r--r--arch/ppc/math-emu/fnmsub.c54
-rw-r--r--arch/ppc/math-emu/fnmsubs.c55
-rw-r--r--arch/ppc/math-emu/fres.c12
-rw-r--r--arch/ppc/math-emu/frsp.c25
-rw-r--r--arch/ppc/math-emu/frsqrte.c12
-rw-r--r--arch/ppc/math-emu/fsel.c38
-rw-r--r--arch/ppc/math-emu/fsqrt.c37
-rw-r--r--arch/ppc/math-emu/fsqrts.c38
-rw-r--r--arch/ppc/math-emu/fsub.c41
-rw-r--r--arch/ppc/math-emu/fsubs.c42
-rw-r--r--arch/ppc/math-emu/lfd.c19
-rw-r--r--arch/ppc/math-emu/lfs.c37
-rw-r--r--arch/ppc/math-emu/math.c485
-rw-r--r--arch/ppc/math-emu/mcrfs.c31
-rw-r--r--arch/ppc/math-emu/mffs.c17
-rw-r--r--arch/ppc/math-emu/mtfsb0.c18
-rw-r--r--arch/ppc/math-emu/mtfsb1.c18
-rw-r--r--arch/ppc/math-emu/mtfsf.c45
-rw-r--r--arch/ppc/math-emu/mtfsfi.c23
-rw-r--r--arch/ppc/math-emu/op-1.h245
-rw-r--r--arch/ppc/math-emu/op-2.h433
-rw-r--r--arch/ppc/math-emu/op-4.h297
-rw-r--r--arch/ppc/math-emu/op-common.h688
-rw-r--r--arch/ppc/math-emu/sfp-machine.h377
-rw-r--r--arch/ppc/math-emu/single.h66
-rw-r--r--arch/ppc/math-emu/soft-fp.h104
-rw-r--r--arch/ppc/math-emu/stfd.c20
-rw-r--r--arch/ppc/math-emu/stfiwx.c16
-rw-r--r--arch/ppc/math-emu/stfs.c41
-rw-r--r--arch/ppc/math-emu/types.c51
-rw-r--r--arch/ppc/math-emu/udivmodti4.c191
-rw-r--r--arch/ppc/mm/44x_mmu.c121
-rw-r--r--arch/ppc/mm/4xx_mmu.c142
-rw-r--r--arch/ppc/mm/Makefile11
-rw-r--r--arch/ppc/mm/fault.c440
-rw-r--r--arch/ppc/mm/fsl_booke_mmu.c236
-rw-r--r--arch/ppc/mm/hashtable.S642
-rw-r--r--arch/ppc/mm/init.c667
-rw-r--r--arch/ppc/mm/mem_pieces.c163
-rw-r--r--arch/ppc/mm/mem_pieces.h48
-rw-r--r--arch/ppc/mm/mmu_context.c86
-rw-r--r--arch/ppc/mm/mmu_decl.h83
-rw-r--r--arch/ppc/mm/pgtable.c471
-rw-r--r--arch/ppc/mm/ppc_mmu.c296
-rw-r--r--arch/ppc/mm/tlb.c183
-rw-r--r--arch/ppc/oprofile/Kconfig23
-rw-r--r--arch/ppc/oprofile/Makefile14
-rw-r--r--arch/ppc/oprofile/common.c161
-rw-r--r--arch/ppc/oprofile/op_impl.h45
-rw-r--r--arch/ppc/oprofile/op_model_fsl_booke.c184
-rw-r--r--arch/ppc/platforms/4xx/Kconfig247
-rw-r--r--arch/ppc/platforms/4xx/Makefile27
-rw-r--r--arch/ppc/platforms/4xx/ash.c250
-rw-r--r--arch/ppc/platforms/4xx/ash.h83
-rw-r--r--arch/ppc/platforms/4xx/bubinga.c263
-rw-r--r--arch/ppc/platforms/4xx/bubinga.h69
-rw-r--r--arch/ppc/platforms/4xx/cpci405.c84
-rw-r--r--arch/ppc/platforms/4xx/cpci405.h37
-rw-r--r--arch/ppc/platforms/4xx/ebony.c356
-rw-r--r--arch/ppc/platforms/4xx/ebony.h91
-rw-r--r--arch/ppc/platforms/4xx/ep405.c197
-rw-r--r--arch/ppc/platforms/4xx/ep405.h54
-rw-r--r--arch/ppc/platforms/4xx/ibm405ep.c143
-rw-r--r--arch/ppc/platforms/4xx/ibm405ep.h148
-rw-r--r--arch/ppc/platforms/4xx/ibm405gp.c120
-rw-r--r--arch/ppc/platforms/4xx/ibm405gp.h151
-rw-r--r--arch/ppc/platforms/4xx/ibm405gpr.c117
-rw-r--r--arch/ppc/platforms/4xx/ibm405gpr.h151
-rw-r--r--arch/ppc/platforms/4xx/ibm440gp.c164
-rw-r--r--arch/ppc/platforms/4xx/ibm440gp.h66
-rw-r--r--arch/ppc/platforms/4xx/ibm440gx.c234
-rw-r--r--arch/ppc/platforms/4xx/ibm440gx.h74
-rw-r--r--arch/ppc/platforms/4xx/ibm440sp.c131
-rw-r--r--arch/ppc/platforms/4xx/ibm440sp.h64
-rw-r--r--arch/ppc/platforms/4xx/ibmnp405h.c172
-rw-r--r--arch/ppc/platforms/4xx/ibmnp405h.h157
-rw-r--r--arch/ppc/platforms/4xx/ibmstb4.c83
-rw-r--r--arch/ppc/platforms/4xx/ibmstb4.h238
-rw-r--r--arch/ppc/platforms/4xx/ibmstbx25.c68
-rw-r--r--arch/ppc/platforms/4xx/ibmstbx25.h261
-rw-r--r--arch/ppc/platforms/4xx/luan.c387
-rw-r--r--arch/ppc/platforms/4xx/luan.h80
-rw-r--r--arch/ppc/platforms/4xx/oak.c255
-rw-r--r--arch/ppc/platforms/4xx/oak.h96
-rw-r--r--arch/ppc/platforms/4xx/oak_setup.h50
-rw-r--r--arch/ppc/platforms/4xx/ocotea.c367
-rw-r--r--arch/ppc/platforms/4xx/ocotea.h88
-rw-r--r--arch/ppc/platforms/4xx/redwood5.c110
-rw-r--r--arch/ppc/platforms/4xx/redwood5.h54
-rw-r--r--arch/ppc/platforms/4xx/redwood6.c159
-rw-r--r--arch/ppc/platforms/4xx/redwood6.h55
-rw-r--r--arch/ppc/platforms/4xx/sycamore.c278
-rw-r--r--arch/ppc/platforms/4xx/sycamore.h67
-rw-r--r--arch/ppc/platforms/4xx/virtex-ii_pro.c60
-rw-r--r--arch/ppc/platforms/4xx/virtex-ii_pro.h99
-rw-r--r--arch/ppc/platforms/4xx/walnut.c249
-rw-r--r--arch/ppc/platforms/4xx/walnut.h72
-rw-r--r--arch/ppc/platforms/4xx/xilinx_ml300.c146
-rw-r--r--arch/ppc/platforms/4xx/xilinx_ml300.h47
-rw-r--r--arch/ppc/platforms/4xx/xparameters/xparameters_ml300.h310
-rw-r--r--arch/ppc/platforms/83xx/Makefile4
-rw-r--r--arch/ppc/platforms/83xx/mpc834x_sys.c289
-rw-r--r--arch/ppc/platforms/83xx/mpc834x_sys.h51
-rw-r--r--arch/ppc/platforms/85xx/Kconfig76
-rw-r--r--arch/ppc/platforms/85xx/Makefile8
-rw-r--r--arch/ppc/platforms/85xx/mpc8540_ads.c218
-rw-r--r--arch/ppc/platforms/85xx/mpc8540_ads.h25
-rw-r--r--arch/ppc/platforms/85xx/mpc8555_cds.h26
-rw-r--r--arch/ppc/platforms/85xx/mpc8560_ads.c210
-rw-r--r--arch/ppc/platforms/85xx/mpc8560_ads.h27
-rw-r--r--arch/ppc/platforms/85xx/mpc85xx_ads_common.c225
-rw-r--r--arch/ppc/platforms/85xx/mpc85xx_ads_common.h50
-rw-r--r--arch/ppc/platforms/85xx/mpc85xx_cds_common.c467
-rw-r--r--arch/ppc/platforms/85xx/mpc85xx_cds_common.h80
-rw-r--r--arch/ppc/platforms/85xx/sbc8560.c227
-rw-r--r--arch/ppc/platforms/85xx/sbc8560.h49
-rw-r--r--arch/ppc/platforms/85xx/sbc85xx.c203
-rw-r--r--arch/ppc/platforms/85xx/sbc85xx.h55
-rw-r--r--arch/ppc/platforms/85xx/stx_gp3.c355
-rw-r--r--arch/ppc/platforms/85xx/stx_gp3.h74
-rw-r--r--arch/ppc/platforms/Makefile53
-rw-r--r--arch/ppc/platforms/adir.h95
-rw-r--r--arch/ppc/platforms/adir_pci.c247
-rw-r--r--arch/ppc/platforms/adir_pic.c130
-rw-r--r--arch/ppc/platforms/adir_setup.c210
-rw-r--r--arch/ppc/platforms/apus_pci.c208
-rw-r--r--arch/ppc/platforms/apus_pci.h34
-rw-r--r--arch/ppc/platforms/apus_setup.c815
-rw-r--r--arch/ppc/platforms/bseip.h38
-rw-r--r--arch/ppc/platforms/ccm.h28
-rw-r--r--arch/ppc/platforms/chestnut.c580
-rw-r--r--arch/ppc/platforms/chestnut.h129
-rw-r--r--arch/ppc/platforms/chrp_pci.c309
-rw-r--r--arch/ppc/platforms/chrp_pegasos_eth.c101
-rw-r--r--arch/ppc/platforms/chrp_setup.c615
-rw-r--r--arch/ppc/platforms/chrp_smp.c98
-rw-r--r--arch/ppc/platforms/chrp_time.c194
-rw-r--r--arch/ppc/platforms/cpci690.c491
-rw-r--r--arch/ppc/platforms/cpci690.h78
-rw-r--r--arch/ppc/platforms/est8260.h35
-rw-r--r--arch/ppc/platforms/ev64260.c651
-rw-r--r--arch/ppc/platforms/ev64260.h128
-rw-r--r--arch/ppc/platforms/fads.h57
-rw-r--r--arch/ppc/platforms/gemini.h168
-rw-r--r--arch/ppc/platforms/gemini_pci.c41
-rw-r--r--arch/ppc/platforms/gemini_prom.S93
-rw-r--r--arch/ppc/platforms/gemini_serial.h41
-rw-r--r--arch/ppc/platforms/gemini_setup.c584
-rw-r--r--arch/ppc/platforms/hdpu.c1062
-rw-r--r--arch/ppc/platforms/hdpu.h82
-rw-r--r--arch/ppc/platforms/hermes.h27
-rw-r--r--arch/ppc/platforms/ip860.h36
-rw-r--r--arch/ppc/platforms/ivms8.h56
-rw-r--r--arch/ppc/platforms/k2.c613
-rw-r--r--arch/ppc/platforms/k2.h82
-rw-r--r--arch/ppc/platforms/katana.c795
-rw-r--r--arch/ppc/platforms/katana.h255
-rw-r--r--arch/ppc/platforms/lantec.h21
-rw-r--r--arch/ppc/platforms/lite5200.c236
-rw-r--r--arch/ppc/platforms/lite5200.h23
-rw-r--r--arch/ppc/platforms/lopec.c411
-rw-r--r--arch/ppc/platforms/lopec.h39
-rw-r--r--arch/ppc/platforms/lwmon.h60
-rw-r--r--arch/ppc/platforms/mbx.h117
-rw-r--r--arch/ppc/platforms/mcpn765.c527
-rw-r--r--arch/ppc/platforms/mcpn765.h122
-rw-r--r--arch/ppc/platforms/mpc5200.c53
-rw-r--r--arch/ppc/platforms/mvme5100.c349
-rw-r--r--arch/ppc/platforms/mvme5100.h91
-rw-r--r--arch/ppc/platforms/pal4.h42
-rw-r--r--arch/ppc/platforms/pal4_pci.c77
-rw-r--r--arch/ppc/platforms/pal4_serial.h39
-rw-r--r--arch/ppc/platforms/pal4_setup.c175
-rw-r--r--arch/ppc/platforms/pcore.c352
-rw-r--r--arch/ppc/platforms/pcore.h39
-rw-r--r--arch/ppc/platforms/pcu_e.h28
-rw-r--r--arch/ppc/platforms/pmac_backlight.c202
-rw-r--r--arch/ppc/platforms/pmac_cache.S325
-rw-r--r--arch/ppc/platforms/pmac_cpufreq.c571
-rw-r--r--arch/ppc/platforms/pmac_feature.c2972
-rw-r--r--arch/ppc/platforms/pmac_low_i2c.c513
-rw-r--r--arch/ppc/platforms/pmac_nvram.c584
-rw-r--r--arch/ppc/platforms/pmac_pci.c1125
-rw-r--r--arch/ppc/platforms/pmac_pic.c689
-rw-r--r--arch/ppc/platforms/pmac_pic.h11
-rw-r--r--arch/ppc/platforms/pmac_setup.c745
-rw-r--r--arch/ppc/platforms/pmac_sleep.S390
-rw-r--r--arch/ppc/platforms/pmac_smp.c640
-rw-r--r--arch/ppc/platforms/pmac_time.c292
-rw-r--r--arch/ppc/platforms/powerpmc250.c383
-rw-r--r--arch/ppc/platforms/powerpmc250.h52
-rw-r--r--arch/ppc/platforms/pplus.c917
-rw-r--r--arch/ppc/platforms/pplus.h67
-rw-r--r--arch/ppc/platforms/pq2ads.c26
-rw-r--r--arch/ppc/platforms/pq2ads.h96
-rw-r--r--arch/ppc/platforms/prep_pci.c1336
-rw-r--r--arch/ppc/platforms/prep_setup.c1181
-rw-r--r--arch/ppc/platforms/prpmc750.c364
-rw-r--r--arch/ppc/platforms/prpmc750.h95
-rw-r--r--arch/ppc/platforms/prpmc800.c477
-rw-r--r--arch/ppc/platforms/prpmc800.h82
-rw-r--r--arch/ppc/platforms/radstone_ppc7d.c1452
-rw-r--r--arch/ppc/platforms/radstone_ppc7d.h434
-rw-r--r--arch/ppc/platforms/residual.c1034
-rw-r--r--arch/ppc/platforms/rpx8260.h81
-rw-r--r--arch/ppc/platforms/rpxclassic.h119
-rw-r--r--arch/ppc/platforms/rpxhiox.h41
-rw-r--r--arch/ppc/platforms/rpxlite.h96
-rw-r--r--arch/ppc/platforms/sandpoint.c742
-rw-r--r--arch/ppc/platforms/sandpoint.h80
-rw-r--r--arch/ppc/platforms/sbc82xx.c259
-rw-r--r--arch/ppc/platforms/sbc82xx.h36
-rw-r--r--arch/ppc/platforms/sbs8260.h28
-rw-r--r--arch/ppc/platforms/spd8xx.h92
-rw-r--r--arch/ppc/platforms/spruce.c325
-rw-r--r--arch/ppc/platforms/spruce.h71
-rw-r--r--arch/ppc/platforms/tqm8260.h23
-rw-r--r--arch/ppc/platforms/tqm8260_setup.c44
-rw-r--r--arch/ppc/platforms/tqm8xx.h179
-rw-r--r--arch/ppc/syslib/Makefile115
-rw-r--r--arch/ppc/syslib/btext.c861
-rw-r--r--arch/ppc/syslib/cpc700.h98
-rw-r--r--arch/ppc/syslib/cpc700_pic.c187
-rw-r--r--arch/ppc/syslib/cpc710.h83
-rw-r--r--arch/ppc/syslib/cpm2_common.c198
-rw-r--r--arch/ppc/syslib/cpm2_pic.c172
-rw-r--r--arch/ppc/syslib/cpm2_pic.h8
-rw-r--r--arch/ppc/syslib/dcr.S41
-rw-r--r--arch/ppc/syslib/gen550.h16
-rw-r--r--arch/ppc/syslib/gen550_dbg.c182
-rw-r--r--arch/ppc/syslib/gen550_kgdb.c86
-rw-r--r--arch/ppc/syslib/gt64260_pic.c328
-rw-r--r--arch/ppc/syslib/harrier.c302
-rw-r--r--arch/ppc/syslib/hawk_common.c319
-rw-r--r--arch/ppc/syslib/i8259.c211
-rw-r--r--arch/ppc/syslib/ibm440gp_common.c76
-rw-r--r--arch/ppc/syslib/ibm440gp_common.h35
-rw-r--r--arch/ppc/syslib/ibm440gx_common.c270
-rw-r--r--arch/ppc/syslib/ibm440gx_common.h57
-rw-r--r--arch/ppc/syslib/ibm440sp_common.c71
-rw-r--r--arch/ppc/syslib/ibm440sp_common.h25
-rw-r--r--arch/ppc/syslib/ibm44x_common.c193
-rw-r--r--arch/ppc/syslib/ibm44x_common.h42
-rw-r--r--arch/ppc/syslib/ibm_ocp.c9
-rw-r--r--arch/ppc/syslib/indirect_pci.c135
-rw-r--r--arch/ppc/syslib/ipic.c646
-rw-r--r--arch/ppc/syslib/ipic.h49
-rw-r--r--arch/ppc/syslib/m8260_pci.c194
-rw-r--r--arch/ppc/syslib/m8260_pci.h76
-rw-r--r--arch/ppc/syslib/m8260_pci_erratum9.c473
-rw-r--r--arch/ppc/syslib/m8260_setup.c264
-rw-r--r--arch/ppc/syslib/m8xx_setup.c433
-rw-r--r--arch/ppc/syslib/m8xx_wdt.c99
-rw-r--r--arch/ppc/syslib/m8xx_wdt.h16
-rw-r--r--arch/ppc/syslib/mpc10x_common.c510
-rw-r--r--arch/ppc/syslib/mpc52xx_devices.c318
-rw-r--r--arch/ppc/syslib/mpc52xx_pci.c235
-rw-r--r--arch/ppc/syslib/mpc52xx_pci.h139
-rw-r--r--arch/ppc/syslib/mpc52xx_pic.c257
-rw-r--r--arch/ppc/syslib/mpc52xx_setup.c230
-rw-r--r--arch/ppc/syslib/mpc52xx_sys.c38
-rw-r--r--arch/ppc/syslib/mpc83xx_devices.c237
-rw-r--r--arch/ppc/syslib/mpc83xx_sys.c100
-rw-r--r--arch/ppc/syslib/mpc85xx_devices.c552
-rw-r--r--arch/ppc/syslib/mpc85xx_sys.c118
-rw-r--r--arch/ppc/syslib/mv64360_pic.c426
-rw-r--r--arch/ppc/syslib/mv64x60.c2392
-rw-r--r--arch/ppc/syslib/mv64x60_dbg.c123
-rw-r--r--arch/ppc/syslib/mv64x60_win.c1168
-rw-r--r--arch/ppc/syslib/ocp.c485
-rw-r--r--arch/ppc/syslib/of_device.c273
-rw-r--r--arch/ppc/syslib/open_pic.c1083
-rw-r--r--arch/ppc/syslib/open_pic2.c716
-rw-r--r--arch/ppc/syslib/open_pic_defs.h292
-rw-r--r--arch/ppc/syslib/pci_auto.c517
-rw-r--r--arch/ppc/syslib/ppc403_pic.c127
-rw-r--r--arch/ppc/syslib/ppc405_pci.c177
-rw-r--r--arch/ppc/syslib/ppc4xx_dma.c708
-rw-r--r--arch/ppc/syslib/ppc4xx_kgdb.c124
-rw-r--r--arch/ppc/syslib/ppc4xx_pic.c244
-rw-r--r--arch/ppc/syslib/ppc4xx_pm.c47
-rw-r--r--arch/ppc/syslib/ppc4xx_setup.c321
-rw-r--r--arch/ppc/syslib/ppc4xx_sgdma.c467
-rw-r--r--arch/ppc/syslib/ppc83xx_setup.c138
-rw-r--r--arch/ppc/syslib/ppc83xx_setup.h53
-rw-r--r--arch/ppc/syslib/ppc85xx_common.c33
-rw-r--r--arch/ppc/syslib/ppc85xx_common.h25
-rw-r--r--arch/ppc/syslib/ppc85xx_setup.c354
-rw-r--r--arch/ppc/syslib/ppc85xx_setup.h59
-rw-r--r--arch/ppc/syslib/ppc8xx_pic.c130
-rw-r--r--arch/ppc/syslib/ppc8xx_pic.h21
-rw-r--r--arch/ppc/syslib/ppc_sys.c103
-rw-r--r--arch/ppc/syslib/prep_nvram.c141
-rw-r--r--arch/ppc/syslib/prom.c1447
-rw-r--r--arch/ppc/syslib/prom_init.c1002
-rw-r--r--arch/ppc/syslib/qspan_pci.c381
-rw-r--r--arch/ppc/syslib/todc_time.c513
-rw-r--r--arch/ppc/syslib/xilinx_pic.c157
-rw-r--r--arch/ppc/xmon/Makefile8
-rw-r--r--arch/ppc/xmon/adb.c212
-rw-r--r--arch/ppc/xmon/ansidecl.h141
-rw-r--r--arch/ppc/xmon/nonstdio.h22
-rw-r--r--arch/ppc/xmon/ppc-dis.c190
-rw-r--r--arch/ppc/xmon/ppc-opc.c2721
-rw-r--r--arch/ppc/xmon/ppc.h240
-rw-r--r--arch/ppc/xmon/privinst.h91
-rw-r--r--arch/ppc/xmon/setjmp.c29
-rw-r--r--arch/ppc/xmon/start.c646
-rw-r--r--arch/ppc/xmon/start_8xx.c287
-rw-r--r--arch/ppc/xmon/subr_prf.c55
-rw-r--r--arch/ppc/xmon/xmon.c2008
560 files changed, 174909 insertions, 0 deletions
diff --git a/arch/ppc/4xx_io/Makefile b/arch/ppc/4xx_io/Makefile
new file mode 100644
index 000000000000..6a8cd575f382
--- /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 000000000000..e95c48d57571
--- /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 <yangyud@cn.ibm.com>
+ * Yi Ge <geyi@cn.ibm.com>
+ *
+ * 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 <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/circ_buf.h>
+#include <linux/serial.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/bitops.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/serial.h>
+
+
+#include <linux/serialP.h>
+
+
+/* -----------------------------------------------------------------------------
+ * 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 000000000000..ea9651e2dd6a
--- /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 000000000000..971f292c5d48
--- /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 000000000000..ac6d55fe2235
--- /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 <asm/commproc.h> 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 <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/bitops.h>
+
+#include <asm/immap_cpm2.h>
+#include <asm/pgtable.h>
+#include <asm/mpc8260.h>
+#include <asm/uaccess.h>
+#include <asm/cpm2.h>
+#include <asm/irq.h>
+
+/*
+ * 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; i<dev->mc_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; i<TX_RING_SIZE; i++) {
+
+ /* Initialize the BD for every fragment in the page.
+ */
+ bdp->cbd_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; i<CPM_ENET_RX_PAGES; i++) {
+
+ /* Allocate a page.
+ */
+ mem_addr = __get_free_page(GFP_KERNEL);
+ /* BUG: no check for failure */
+
+ /* Initialize the BD for every fragment in the page.
+ */
+ for (j=0; j<CPM_ENET_RX_FRPPG; j++) {
+ bdp->cbd_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 000000000000..2086c6ad1147
--- /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 <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mii.h>
+#include <linux/workqueue.h>
+#include <linux/bitops.h>
+
+#include <asm/immap_cpm2.h>
+#include <asm/pgtable.h>
+#include <asm/mpc8260.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/signal.h>
+
+/* 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 <asm/cpm2.h>
+
+/* 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; i<dev->mc_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; i<TX_RING_SIZE; i++) {
+
+ /* Initialize the BD for every fragment in the page.
+ */
+ bdp->cbd_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; i<FCC_ENET_RX_PAGES; i++) {
+
+ /* Allocate a page.
+ */
+ mem_addr = __get_free_page(GFP_KERNEL);
+
+ /* Initialize the BD for every fragment in the page.
+ */
+ for (j=0; j<FCC_ENET_RX_FRPPG; j++) {
+ bdp->cbd_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; i<TX_RING_SIZE; i++) {
+ bdp->cbd_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 000000000000..9e2227ec3b34
--- /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 000000000000..d8760181fe99
--- /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 000000000000..0cc2e7a9cb11
--- /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 <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/dma-mapping.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <asm/mpc8xx.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/8xx_immap.h>
+#include <asm/commproc.h>
+#include <asm/io.h>
+#include <asm/tlbflush.h>
+#include <asm/rheap.h>
+
+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 000000000000..a3c38c5a5db2
--- /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 <linux/types.h>
+#include <linux/config.h>
+
+#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 000000000000..89fe0ceeaa40
--- /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 <linux/module.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/major.h>
+#include <linux/config.h>
+#include <linux/fcntl.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/sound.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/pgtable.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+/* Should probably do something different with this path name.....
+ * Actually, I should just stop using it...
+ */
+#include "cs4218.h"
+#include <linux/soundcard.h>
+
+#include <asm/mpc8xx.h>
+#include <asm/8xx_immap.h>
+#include <asm/commproc.h>
+
+#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; i<numBufs; i++) {
+ bdp->cbd_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; i<numReadBufs; i++) {
+ bdp->cbd_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; i<bcnt; i++) {
+ outbyte = *obuf++;
+ inbyte = 0;
+ for (bits=0; bits<8; bits++) {
+ eieio();
+ cp->cp_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 000000000000..4ea7158e5062
--- /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 <asm/commproc.h> 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 <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/dma-mapping.h>
+#include <linux/bitops.h>
+
+#include <asm/8xx_immap.h>
+#include <asm/pgtable.h>
+#include <asm/mpc8xx.h>
+#include <asm/uaccess.h>
+#include <asm/commproc.h>
+
+/*
+ * 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 <asm/commproc.h>.
+ */
+
+/* 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; i<dev->mc_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; i<TX_RING_SIZE; i++) {
+
+ /* Initialize the BD for every fragment in the page.
+ */
+ bdp->cbd_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; i<CPM_ENET_RX_PAGES; i++) {
+
+ /* Allocate a page.
+ */
+ ba = (unsigned char *)dma_alloc_coherent(NULL, PAGE_SIZE,
+ &mem_addr, GFP_KERNEL);
+ /* BUG: no check for failure */
+
+ /* Initialize the BD for every fragment in the page.
+ */
+ for (j=0; j<CPM_ENET_RX_FRPPG; j++) {
+ bdp->cbd_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 000000000000..0730392dcc20
--- /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 <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/bitops.h>
+#ifdef CONFIG_FEC_PACKETHOOK
+#include <linux/pkthook.h>
+#endif
+
+#include <asm/8xx_immap.h>
+#include <asm/pgtable.h>
+#include <asm/mpc8xx.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/commproc.h>
+
+#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; i<dev->mc_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; i<FEC_ENET_RX_PAGES; i++) {
+
+ /* Allocate a page.
+ */
+ ba = (unsigned char *)consistent_alloc(GFP_KERNEL, PAGE_SIZE, &mem_addr);
+ /* BUG: no check for failure */
+
+ /* Initialize the BD for every fragment in the page.
+ */
+ for (j=0; j<FEC_ENET_RX_FRPPG; j++) {
+ bdp->cbd_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; i<NMII-1; i++)
+ mii_cmds[i].mii_next = &mii_cmds[i+1];
+ mii_free = mii_cmds;
+#endif /* CONFIG_USE_MDIO */
+
+ /* Configure all of port D for MII.
+ */
+ immap->im_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; i<RX_RING_SIZE; i++) {
+
+ /* Initialize the BD for every fragment in the page.
+ */
+ bdp->cbd_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; i<TX_RING_SIZE; i++) {
+
+ /* Initialize the BD for every fragment in the page.
+ */
+ bdp->cbd_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 000000000000..312af0776c31
--- /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 <linux/config.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <asm/irq.h>
+#include <asm/mpc8xx.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/8xx_immap.h>
+#include <asm/commproc.h>
+
+/*
+ * 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 000000000000..813c6c9a6d99
--- /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: <http://www.denx.de/PDF/TQM8xxLHWM201.pdf>
+ - starter kit: <http://www.denx.de/PDF/STK8xxLHWM201.pdf>
+ - images: <http://www.denx.de/embedded-ppc-en.html>
+
+ FPS850L:
+ FingerPrint Sensor System (based on TQM850L)
+ Manufacturer: IKENDI AG, <http://www.ikendi.com/>
+ 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, <http://www.speech-design.de/>
+ Date of Release: Mid 2000 (?)
+ End of life: -
+ URL: <http://www.speech-design.de/>
+ 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, <http://www.speech-design.de/>
+ Date of Release: December 2000 (?)
+ End of life: -
+ URL: <http://www.speech-design.de/>
+
+ IVML24:
+ MPC860 based board used in the "Integrated Voice Mail System",
+ Large Version (24 voice channels)
+ Manufacturer: Speech Design, <http://www.speech-design.de/>
+ Date of Release: March 2001 (?)
+ End of life: -
+ URL: <http://www.speech-design.de/>
+
+ SM850:
+ Service Module (based on TQM850L)
+ Manufacturer: Dependable Computer Systems, <http://www.decomsys.com/>
+ Date of Release: end 2000 (?)
+ End of life: mid 2001 (?)
+ URL: <http://www.tz-mikroelektronik.de/ServiceModule/index.html>
+
+ HERMES:
+ Hermes-Pro ISDN/LAN router with integrated 8 x hub
+ Manufacturer: Multidata Gesellschaft fur Datentechnik und Informatik
+ <http://www.multidata.de/>
+ Date of Release: 2000 (?)
+ End of life: -
+ URL: <http://www.multidata.de/english/products/hpro.htm>
+
+ IP860:
+ VMEBus IP (Industry Pack) carrier board with MPC860
+ Manufacturer: MicroSys GmbH, <http://www.microsys.de/>
+ Date of Release: ?
+ End of life: -
+ URL: <http://www.microsys.de/html/ip860.html>
+
+ PCU_E:
+ PCU = Peripheral Controller Unit, Extended
+ Manufacturer: Siemens AG, ICN (Information and Communication Networks)
+ <http://www.siemens.de/page/1,3771,224315-1-999_2_226207-0,00.html>
+ 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
+ <http://www.denx.de/PDF/TQM8xxLHWM201.pdf>, and
+ <http://www.denx.de/PDF/STK8xxLHWM201.pdf>, and an image at
+ <http://www.denx.de/embedded-ppc-en.html>.
+
+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
+ <http://www.denx.de/PDF/TQM8xxLHWM201.pdf>, and
+ <http://www.denx.de/PDF/STK8xxLHWM201.pdf>, and an image at
+ <http://www.denx.de/embedded-ppc-en.html>.
+
+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
+ <http://www.denx.de/PDF/TQM8xxLHWM201.pdf>, and
+ <http://www.denx.de/PDF/STK8xxLHWM201.pdf>, and an image at
+ <http://www.denx.de/embedded-ppc-en.html>.
+
+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
+ <http://www.denx.de/PDF/TQM8xxLHWM201.pdf>, and
+ <http://www.denx.de/PDF/STK8xxLHWM201.pdf>, and an image at
+ <http://www.denx.de/embedded-ppc-en.html>.
+
+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
+ <http://www.speech-design.de/>.
+
+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 <http://www.speech-design.de/>.
+
+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 <http://www.speech-design.de/>.
+
+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 <http://www.decomsys.com/>.
+
+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:
+ <http://www.synergymicro.com/PressRel/97_10_15.html>.
+
+ Select APUS if configuring for a PowerUP Amiga. More information is
+ available at: <http://linux-apus.sourceforge.net/>.
+
+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:
+ <http://linux-apus.sourceforge.net/>.
+
+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:
+ <http://www.synergymicro.com/PressRel/97_10_15.html>.
+
+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
+ <http://www.windriver.com/>, 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: <http://www.windriver.com/>
+
+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: <http://www.denx.de/PDF/TQM82xx_SPEC_Rev005.pdf>
+
+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 <http://www.hisoft.co.uk/>, 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 000000000000..d2e1eea8e8e4
--- /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 <http://kgdb.sourceforge.net/> 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 000000000000..73cbdda5b597
--- /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 000000000000..59fec0a3ac8e
--- /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 000000000000..ec74e5b7a1ce
--- /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 000000000000..91195e2ce38d
--- /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 <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel_stat.h>
+#include <linux/init.h>
+
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/traps.h>
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+#include <asm/amipcmcia.h>
+
+#ifdef CONFIG_APUS
+#include <asm/amigappc.h>
+#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 000000000000..2b86cbef79f6
--- /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 000000000000..e2e965661d03
--- /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 <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/init.h>
+
+#include <asm/setup.h>
+#include <asm/bootinfo.h>
+
+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 000000000000..e6ab3c6b223c
--- /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 000000000000..ad961465b6cb
--- /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 <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel_stat.h>
+#include <linux/init.h>
+
+#include <asm/irq.h>
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+
+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 000000000000..af881d7454dd
--- /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 <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/kd.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#ifdef CONFIG_ZORRO
+#include <linux/zorro.h>
+#endif
+
+#include <asm/bootinfo.h>
+#include <asm/setup.h>
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+#include <asm/irq.h>
+#include <asm/machdep.h>
+#include <asm/io.h>
+
+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