From 1c6a4b6b1c09e7938210280ee98df7503365d0da Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Mon, 25 Oct 2010 16:10:07 +0200 Subject: [PATCH] --- yaml --- r: 216738 b: refs/heads/master c: 238ec4efeee4461d5cff2ed3e5a15a3ab850959b h: refs/heads/master v: v3 --- [refs] | 2 +- trunk/Documentation/ABI/obsolete/dv1394 | 9 + trunk/Documentation/ABI/removed/dv1394 | 14 - trunk/Documentation/ABI/removed/raw1394 | 15 - .../ABI/removed/raw1394_legacy_isochronous | 16 + trunk/Documentation/ABI/removed/video1394 | 16 - .../ABI/testing/sysfs-devices-system-ibm-rtl | 22 - .../feature-removal-schedule.txt | 10 + .../sound/alsa/ALSA-Configuration.txt | 82 +- trunk/Documentation/sound/alsa/HD-Audio.txt | 8 +- trunk/MAINTAINERS | 22 +- trunk/arch/arm/mach-davinci/devices.c | 13 - trunk/arch/arm/mach-ep93xx/core.c | 40 - .../mach-ep93xx/include/mach/ep93xx-regs.h | 1 - .../arm/mach-ep93xx/include/mach/platform.h | 1 - trunk/arch/arm/mach-ep93xx/simone.c | 1 - trunk/arch/arm/mach-kirkwood/common.c | 6 - trunk/arch/arm/mach-omap1/devices.c | 26 - .../arm/mach-omap2/board-rx51-peripherals.c | 17 +- .../arm/mach-omap2/board-zoom-peripherals.c | 12 - trunk/arch/arm/mach-omap2/board-zoom2.c | 36 +- trunk/arch/arm/mach-omap2/devices.c | 39 - .../arm/mach-omap2/include/mach/board-zoom.h | 2 - trunk/arch/arm/mach-pxa/devices.c | 25 - trunk/arch/arm/mach-pxa/devices.h | 6 - trunk/arch/arm/mach-pxa/pxa27x.c | 4 - trunk/arch/arm/mach-pxa/pxa3xx.c | 5 - trunk/arch/arm/mach-pxa/zylonite.c | 11 - trunk/arch/arm/mach-s3c64xx/dev-audio.c | 20 +- trunk/arch/arm/mach-s3c64xx/mach-smdk6410.c | 1 - trunk/arch/arm/mach-ux500/board-mop500.c | 129 - trunk/arch/arm/mach-ux500/clock.c | 1 - trunk/arch/arm/mach-ux500/devices-db8500.c | 20 - .../arm/mach-ux500/include/mach/devices.h | 1 - trunk/arch/arm/mach-ux500/pins-db8500.h | 32 +- .../arch/arm/plat-nomadik/include/plat/ske.h | 50 - trunk/arch/arm/plat-omap/include/plat/mcbsp.h | 7 - .../arm/plat-omap/include/plat/omap4-keypad.h | 14 - trunk/arch/arm/plat-s3c24xx/devs.c | 34 +- .../arch/arm/plat-samsung/include/plat/devs.h | 2 - trunk/arch/m68k/include/asm/cacheflush_no.h | 2 +- trunk/arch/m68k/include/asm/coldfire.h | 4 +- trunk/arch/m68k/include/asm/gpio.h | 7 +- trunk/arch/m68k/include/asm/m548xgpt.h | 88 - trunk/arch/m68k/include/asm/m548xsim.h | 55 - trunk/arch/m68k/include/asm/mcfcache.h | 2 +- trunk/arch/m68k/include/asm/mcfsim.h | 2 - trunk/arch/m68k/include/asm/mcfslt.h | 44 - trunk/arch/m68k/include/asm/mcfuart.h | 9 +- trunk/arch/m68k/kernel/asm-offsets.c | 12 + trunk/arch/m68knommu/Kconfig | 11 +- trunk/arch/m68knommu/Makefile | 3 - trunk/arch/m68knommu/kernel/.gitignore | 1 - trunk/arch/m68knommu/kernel/asm-offsets.c | 11 +- trunk/arch/m68knommu/kernel/ptrace.c | 47 +- trunk/arch/m68knommu/kernel/setup.c | 3 + trunk/arch/m68knommu/kernel/time.c | 13 +- trunk/arch/m68knommu/kernel/traps.c | 26 +- trunk/arch/m68knommu/platform/5206/Makefile | 4 +- trunk/arch/m68knommu/platform/5206e/Makefile | 4 +- trunk/arch/m68knommu/platform/520x/Makefile | 4 +- trunk/arch/m68knommu/platform/523x/Makefile | 4 +- trunk/arch/m68knommu/platform/5249/Makefile | 4 +- trunk/arch/m68knommu/platform/5272/Makefile | 4 +- trunk/arch/m68knommu/platform/5272/config.c | 16 - trunk/arch/m68knommu/platform/5272/intc.c | 60 +- trunk/arch/m68knommu/platform/527x/Makefile | 4 +- trunk/arch/m68knommu/platform/528x/Makefile | 4 +- trunk/arch/m68knommu/platform/5307/Makefile | 4 +- trunk/arch/m68knommu/platform/532x/Makefile | 4 +- trunk/arch/m68knommu/platform/5407/Makefile | 4 +- trunk/arch/m68knommu/platform/548x/Makefile | 18 - trunk/arch/m68knommu/platform/548x/config.c | 115 - trunk/arch/m68knommu/platform/68328/entry.S | 36 +- .../arch/m68knommu/platform/68328/head-de2.S | 6 + .../arch/m68knommu/platform/68328/head-ram.S | 27 + trunk/arch/m68knommu/platform/68328/ints.c | 6 +- trunk/arch/m68knommu/platform/68360/entry.S | 13 +- trunk/arch/m68knommu/platform/68360/ints.c | 6 +- .../arch/m68knommu/platform/68VZ328/config.c | 5 + .../arch/m68knommu/platform/coldfire/Makefile | 5 +- .../arch/m68knommu/platform/coldfire/entry.S | 4 +- .../arch/m68knommu/platform/coldfire/intc-2.c | 53 +- .../m68knommu/platform/coldfire/intc-simr.c | 10 +- trunk/arch/m68knommu/platform/coldfire/intc.c | 8 +- .../m68knommu/platform/coldfire/sltimers.c | 145 - trunk/arch/microblaze/kernel/prom.c | 5 + trunk/arch/mips/Kconfig | 7 - .../mips/alchemy/devboards/db1200/platform.c | 6 - trunk/arch/mips/include/asm/irq.h | 5 - trunk/arch/mips/include/asm/prom.h | 31 - trunk/arch/mips/kernel/Makefile | 2 - trunk/arch/mips/kernel/mips-mt-fpaff.c | 2 +- trunk/arch/mips/kernel/prom.c | 112 - trunk/arch/mips/kernel/setup.c | 2 - trunk/arch/powerpc/boot/dts/mpc8610_hpcd.dts | 1 - trunk/arch/powerpc/configs/mpc85xx_defconfig | 3 - .../powerpc/configs/mpc85xx_smp_defconfig | 3 - .../include/asm/{fsl_guts.h => immap_86xx.h} | 111 +- trunk/arch/powerpc/kernel/ibmebus.c | 11 +- trunk/arch/powerpc/kernel/legacy_serial.c | 22 +- trunk/arch/powerpc/kernel/prom.c | 12 +- trunk/arch/powerpc/platforms/85xx/p1022_ds.c | 211 +- trunk/arch/s390/include/asm/pgtable.h | 22 +- trunk/arch/s390/mm/init.c | 49 +- trunk/arch/sh/Kconfig | 6 +- trunk/arch/sh/boards/Kconfig | 18 - trunk/arch/sh/boards/Makefile | 2 - trunk/arch/sh/boards/board-sh2007.c | 133 - trunk/arch/sh/boards/board-sh7757lcr.c | 374 -- trunk/arch/sh/boards/mach-ecovec24/setup.c | 6 +- trunk/arch/sh/boards/mach-sdk7786/Makefile | 5 +- trunk/arch/sh/boards/mach-sdk7786/gpio.c | 49 - trunk/arch/sh/boards/mach-sdk7786/setup.c | 54 +- trunk/arch/sh/boards/mach-sdk7786/sram.c | 72 - trunk/arch/sh/boards/mach-x3proto/Makefile | 2 - trunk/arch/sh/boards/mach-x3proto/gpio.c | 135 - trunk/arch/sh/boards/mach-x3proto/ilsel.c | 18 +- trunk/arch/sh/boards/mach-x3proto/setup.c | 132 +- trunk/arch/sh/boot/compressed/head_32.S | 4 +- trunk/arch/sh/cchips/hd6446x/Makefile | 2 +- trunk/arch/sh/configs/ap325rxa_defconfig | 1 + trunk/arch/sh/configs/cayman_defconfig | 1 + trunk/arch/sh/configs/dreamcast_defconfig | 1 + .../sh/configs/ecovec24-romimage_defconfig | 1 + trunk/arch/sh/configs/edosk7760_defconfig | 1 + trunk/arch/sh/configs/espt_defconfig | 1 + trunk/arch/sh/configs/hp6xx_defconfig | 1 + .../sh/configs/kfr2r09-romimage_defconfig | 1 + trunk/arch/sh/configs/kfr2r09_defconfig | 1 + trunk/arch/sh/configs/landisk_defconfig | 1 + trunk/arch/sh/configs/lboxre2_defconfig | 1 + trunk/arch/sh/configs/magicpanelr2_defconfig | 1 + trunk/arch/sh/configs/microdev_defconfig | 1 + trunk/arch/sh/configs/migor_defconfig | 1 + trunk/arch/sh/configs/polaris_defconfig | 1 + trunk/arch/sh/configs/r7780mp_defconfig | 1 + trunk/arch/sh/configs/r7785rp_defconfig | 1 + trunk/arch/sh/configs/rts7751r2d1_defconfig | 1 + .../arch/sh/configs/rts7751r2dplus_defconfig | 1 + trunk/arch/sh/configs/sdk7780_defconfig | 1 + trunk/arch/sh/configs/se7343_defconfig | 1 + trunk/arch/sh/configs/se7712_defconfig | 1 + trunk/arch/sh/configs/se7721_defconfig | 1 + trunk/arch/sh/configs/se7722_defconfig | 1 + trunk/arch/sh/configs/se7724_defconfig | 1 + trunk/arch/sh/configs/se7750_defconfig | 1 + trunk/arch/sh/configs/se7751_defconfig | 1 + trunk/arch/sh/configs/se7780_defconfig | 1 + trunk/arch/sh/configs/sh03_defconfig | 1 + trunk/arch/sh/configs/sh2007_defconfig | 212 - trunk/arch/sh/configs/sh7710voipgw_defconfig | 1 + trunk/arch/sh/configs/sh7757lcr_defconfig | 85 - trunk/arch/sh/configs/sh7763rdp_defconfig | 1 + trunk/arch/sh/configs/sh7785lcr_defconfig | 1 + trunk/arch/sh/configs/shx3_defconfig | 1 + trunk/arch/sh/configs/snapgear_defconfig | 1 + trunk/arch/sh/configs/systemh_defconfig | 1 + trunk/arch/sh/configs/titan_defconfig | 1 + trunk/arch/sh/configs/ul2_defconfig | 1 + trunk/arch/sh/drivers/dma/dma-api.c | 4 +- trunk/arch/sh/drivers/pci/Makefile | 1 - trunk/arch/sh/drivers/pci/fixups-sdk7786.c | 67 - trunk/arch/sh/drivers/pci/ops-sh4.c | 11 +- trunk/arch/sh/drivers/pci/ops-sh7786.c | 69 +- trunk/arch/sh/drivers/pci/pci-sh7751.c | 2 +- trunk/arch/sh/drivers/pci/pci-sh7780.c | 2 +- trunk/arch/sh/drivers/pci/pci-sh7780.h | 6 + trunk/arch/sh/drivers/pci/pci.c | 43 +- trunk/arch/sh/drivers/pci/pcie-sh7786.c | 251 +- trunk/arch/sh/drivers/pci/pcie-sh7786.h | 56 +- trunk/arch/sh/include/asm/Kbuild | 2 - trunk/arch/sh/include/asm/elf.h | 27 +- trunk/arch/sh/include/asm/fixmap.h | 4 +- trunk/arch/sh/include/asm/gpio.h | 6 +- .../{mach-x3proto/mach => asm}/ilsel.h | 0 trunk/arch/sh/include/asm/irq.h | 2 +- trunk/arch/sh/include/asm/kprobes.h | 1 + trunk/arch/sh/include/asm/pci.h | 2 - trunk/arch/sh/include/asm/processor_32.h | 3 + trunk/arch/sh/include/asm/processor_64.h | 3 + trunk/arch/sh/include/asm/ptrace.h | 169 +- trunk/arch/sh/include/asm/ptrace_32.h | 83 - trunk/arch/sh/include/asm/ptrace_64.h | 20 - trunk/arch/sh/include/asm/sizes.h | 1 - trunk/arch/sh/include/asm/sram.h | 38 - trunk/arch/sh/include/asm/system.h | 2 + trunk/arch/sh/include/asm/system_32.h | 15 +- trunk/arch/sh/include/asm/tlbflush.h | 2 - trunk/arch/sh/include/asm/unistd_32.h | 27 +- trunk/arch/sh/include/asm/unistd_64.h | 5 +- .../arch/sh/include/cpu-sh3/cpu/mmu_context.h | 1 - trunk/arch/sh/include/cpu-sh4/cpu/freq.h | 4 +- trunk/arch/sh/include/cpu-sh4/cpu/sh7757.h | 301 +- trunk/arch/sh/include/cpu-sh4/cpu/shx3.h | 64 - .../arch/sh/include/mach-common/mach/sh2007.h | 117 - .../arch/sh/include/mach-sdk7786/mach/fpga.h | 26 +- .../sh/include/mach-x3proto/mach/hardware.h | 12 - trunk/arch/sh/kernel/Makefile | 8 +- trunk/arch/sh/kernel/clkdev.c | 4 +- trunk/arch/sh/kernel/cpu/sh4/probe.c | 2 +- trunk/arch/sh/kernel/cpu/sh4a/Makefile | 5 +- trunk/arch/sh/kernel/cpu/sh4a/clock-sh7757.c | 199 +- trunk/arch/sh/kernel/cpu/sh4a/clock-shx3.c | 225 +- trunk/arch/sh/kernel/cpu/sh4a/intc-shx3.c | 34 - trunk/arch/sh/kernel/cpu/sh4a/perf_event.c | 20 +- trunk/arch/sh/kernel/cpu/sh4a/pinmux-sh7757.c | 1582 +++---- trunk/arch/sh/kernel/cpu/sh4a/pinmux-shx3.c | 587 --- trunk/arch/sh/kernel/cpu/sh4a/setup-sh7722.c | 2 +- trunk/arch/sh/kernel/cpu/sh4a/setup-sh7724.c | 66 - trunk/arch/sh/kernel/cpu/sh4a/setup-sh7757.c | 222 +- trunk/arch/sh/kernel/cpu/sh4a/setup-sh7786.c | 126 +- trunk/arch/sh/kernel/cpu/sh4a/setup-shx3.c | 42 +- trunk/arch/sh/kernel/head_32.S | 2 +- trunk/arch/sh/kernel/io_trapped.c | 2 +- trunk/arch/sh/kernel/irq.c | 2 - trunk/arch/sh/kernel/kdebugfs.c | 16 - trunk/arch/sh/kernel/kprobes.c | 100 +- trunk/arch/sh/kernel/ptrace.c | 33 - trunk/arch/sh/kernel/ptrace_32.c | 27 - trunk/arch/sh/kernel/ptrace_64.c | 88 +- trunk/arch/sh/kernel/reboot.c | 4 - trunk/arch/sh/kernel/setup.c | 22 +- trunk/arch/sh/kernel/syscalls_32.S | 22 - trunk/arch/sh/kernel/syscalls_64.S | 3 - trunk/arch/sh/kernel/traps_32.c | 29 +- trunk/arch/sh/kernel/traps_64.c | 11 +- trunk/arch/sh/lib/Makefile | 2 +- trunk/arch/sh/math-emu/math.c | 3 - trunk/arch/sh/mm/Kconfig | 4 - trunk/arch/sh/mm/Makefile | 3 +- trunk/arch/sh/mm/asids-debugfs.c | 2 +- trunk/arch/sh/mm/cache-debugfs.c | 10 +- trunk/arch/sh/mm/consistent.c | 3 +- trunk/arch/sh/mm/init.c | 52 +- trunk/arch/sh/mm/nommu.c | 4 - trunk/arch/sh/mm/pmb.c | 35 +- trunk/arch/sh/mm/sram.c | 34 - trunk/arch/sh/mm/tlb-debugfs.c | 11 +- trunk/arch/sh/mm/tlbflush_32.c | 16 - trunk/arch/sh/mm/tlbflush_64.c | 5 - trunk/arch/sh/tools/mach-types | 2 - trunk/arch/sparc/Kconfig | 1 - trunk/arch/sparc/include/asm/Kbuild | 1 + trunk/arch/sparc/include/asm/floppy_32.h | 3 +- trunk/arch/sparc/include/asm/openprom.h | 16 +- trunk/arch/sparc/include/asm/oplib_32.h | 44 +- trunk/arch/sparc/include/asm/oplib_64.h | 39 +- trunk/arch/sparc/include/asm/prom.h | 5 +- trunk/arch/sparc/kernel/auxio_32.c | 4 +- trunk/arch/sparc/kernel/btext.c | 4 +- trunk/arch/sparc/kernel/devices.c | 23 +- trunk/arch/sparc/kernel/leon_kernel.c | 2 +- trunk/arch/sparc/kernel/pcic.c | 4 +- trunk/arch/sparc/kernel/prom.h | 6 + trunk/arch/sparc/kernel/prom_common.c | 202 +- trunk/arch/sparc/kernel/setup_64.c | 2 +- trunk/arch/sparc/kernel/starfire.c | 2 +- trunk/arch/sparc/kernel/tadpole.c | 2 +- trunk/arch/sparc/mm/init_64.c | 2 +- trunk/arch/sparc/mm/srmmu.c | 8 +- trunk/arch/sparc/mm/sun4c.c | 2 +- trunk/arch/sparc/prom/init_32.c | 2 +- trunk/arch/sparc/prom/init_64.c | 4 +- trunk/arch/sparc/prom/memory.c | 3 +- trunk/arch/sparc/prom/misc_64.c | 6 +- trunk/arch/sparc/prom/ranges.c | 6 +- trunk/arch/sparc/prom/tree_32.c | 58 +- trunk/arch/sparc/prom/tree_64.c | 62 +- trunk/arch/x86/include/asm/olpc.h | 2 - trunk/block/blk-core.c | 24 +- trunk/block/blk-merge.c | 2 +- trunk/block/blk.h | 4 - trunk/block/genhd.c | 14 + trunk/drivers/Makefile | 3 +- trunk/drivers/base/platform.c | 1 - trunk/drivers/block/xsysace.c | 3 +- trunk/drivers/char/keyboard.c | 31 +- trunk/drivers/char/sysrq.c | 15 +- trunk/drivers/clocksource/sh_cmt.c | 12 +- trunk/drivers/firewire/Kconfig | 5 + trunk/drivers/firewire/Makefile | 1 - trunk/drivers/gpio/xilinx_gpio.c | 6 +- trunk/drivers/hid/hid-core.c | 5 - trunk/drivers/hid/hid-ids.h | 4 - trunk/drivers/hid/hid-input.c | 108 +- trunk/drivers/hwmon/Kconfig | 20 + trunk/drivers/hwmon/Makefile | 1 + trunk/drivers/{platform/x86 => hwmon}/hdaps.c | 2 +- trunk/drivers/i2c/busses/i2c-sh7760.c | 4 +- trunk/drivers/i2c/busses/i2c-sh_mobile.c | 23 +- trunk/drivers/ieee1394/Kconfig | 182 + trunk/drivers/ieee1394/Makefile | 18 + trunk/drivers/ieee1394/config_roms.c | 194 + trunk/drivers/ieee1394/config_roms.h | 19 + trunk/drivers/ieee1394/csr.c | 843 ++++ trunk/drivers/ieee1394/csr.h | 99 + trunk/drivers/ieee1394/csr1212.c | 1467 ++++++ trunk/drivers/ieee1394/csr1212.h | 383 ++ trunk/drivers/ieee1394/dma.c | 289 ++ trunk/drivers/ieee1394/dma.h | 89 + trunk/drivers/ieee1394/dv1394-private.h | 587 +++ trunk/drivers/ieee1394/dv1394.c | 2584 +++++++++++ trunk/drivers/ieee1394/dv1394.h | 305 ++ trunk/drivers/ieee1394/eth1394.c | 1720 +++++++ trunk/drivers/ieee1394/eth1394.h | 234 + trunk/drivers/ieee1394/highlevel.c | 691 +++ trunk/drivers/ieee1394/highlevel.h | 141 + trunk/drivers/ieee1394/hosts.c | 249 ++ trunk/drivers/ieee1394/hosts.h | 201 + trunk/drivers/ieee1394/ieee1394-ioctl.h | 106 + trunk/drivers/ieee1394/ieee1394.h | 220 + trunk/drivers/ieee1394/ieee1394_core.c | 1380 ++++++ trunk/drivers/ieee1394/ieee1394_core.h | 172 + trunk/drivers/ieee1394/ieee1394_hotplug.h | 19 + .../drivers/ieee1394/ieee1394_transactions.c | 595 +++ .../drivers/ieee1394/ieee1394_transactions.h | 40 + trunk/drivers/ieee1394/ieee1394_types.h | 69 + .../init_ohci1394_dma.c | 76 +- trunk/drivers/ieee1394/iso.c | 568 +++ trunk/drivers/ieee1394/iso.h | 195 + trunk/drivers/ieee1394/nodemgr.c | 1901 ++++++++ trunk/drivers/ieee1394/nodemgr.h | 186 + trunk/drivers/ieee1394/ohci1394.c | 3590 +++++++++++++++ trunk/drivers/ieee1394/ohci1394.h | 453 ++ trunk/drivers/ieee1394/pcilynx.c | 1554 +++++++ trunk/drivers/ieee1394/pcilynx.h | 468 ++ trunk/drivers/ieee1394/raw1394-private.h | 81 + trunk/drivers/ieee1394/raw1394.c | 3096 +++++++++++++ trunk/drivers/ieee1394/raw1394.h | 191 + trunk/drivers/ieee1394/sbp2.c | 2138 +++++++++ trunk/drivers/ieee1394/sbp2.h | 346 ++ trunk/drivers/ieee1394/video1394.c | 1528 +++++++ trunk/drivers/ieee1394/video1394.h | 67 + trunk/drivers/input/evdev.c | 100 +- trunk/drivers/input/gameport/emu10k1-gp.c | 44 +- trunk/drivers/input/gameport/fm801-gp.c | 10 +- trunk/drivers/input/input.c | 198 +- trunk/drivers/input/keyboard/Kconfig | 19 - trunk/drivers/input/keyboard/Makefile | 2 - trunk/drivers/input/keyboard/adp5588-keys.c | 2 +- trunk/drivers/input/keyboard/hil_kbd.c | 2 - .../input/keyboard/nomadik-ske-keypad.c | 408 -- trunk/drivers/input/keyboard/omap4-keypad.c | 318 -- trunk/drivers/input/keyboard/twl4030_keypad.c | 7 +- trunk/drivers/input/misc/Kconfig | 10 - trunk/drivers/input/misc/Makefile | 1 - trunk/drivers/input/misc/ab8500-ponkey.c | 157 - trunk/drivers/input/misc/ati_remote2.c | 93 +- trunk/drivers/input/misc/powermate.c | 2 +- trunk/drivers/input/misc/twl4030-vibra.c | 4 +- trunk/drivers/input/mouse/elantech.c | 2 +- trunk/drivers/input/mouse/psmouse-base.c | 4 +- trunk/drivers/input/mouse/synaptics.c | 38 +- trunk/drivers/input/mouse/synaptics.h | 2 - trunk/drivers/input/mouse/trackpoint.c | 2 +- trunk/drivers/input/mousedev.c | 2 +- trunk/drivers/input/serio/Kconfig | 9 - trunk/drivers/input/serio/Makefile | 1 - trunk/drivers/input/serio/i8042.c | 2 +- trunk/drivers/input/serio/ps2mult.c | 318 -- trunk/drivers/input/serio/serio.c | 125 +- trunk/drivers/input/sparse-keymap.c | 81 +- trunk/drivers/input/tablet/Kconfig | 11 - trunk/drivers/input/tablet/Makefile | 1 - trunk/drivers/input/tablet/hanwang.c | 446 -- trunk/drivers/input/tablet/wacom.h | 1 - trunk/drivers/input/tablet/wacom_sys.c | 77 +- trunk/drivers/input/tablet/wacom_wac.c | 253 +- trunk/drivers/input/tablet/wacom_wac.h | 15 - trunk/drivers/input/touchscreen/Kconfig | 34 - trunk/drivers/input/touchscreen/Makefile | 3 - trunk/drivers/input/touchscreen/ad7877.c | 144 +- trunk/drivers/input/touchscreen/ads7846.c | 886 ++-- trunk/drivers/input/touchscreen/bu21013_ts.c | 648 --- .../drivers/input/touchscreen/cy8ctmg110_ts.c | 4 +- .../input/touchscreen/hp680_ts_input.c | 6 +- .../input/touchscreen/intel-mid-touch.c | 687 --- trunk/drivers/input/touchscreen/lpc32xx_ts.c | 411 -- trunk/drivers/input/touchscreen/s3c2410_ts.c | 2 +- trunk/drivers/input/touchscreen/stmpe-ts.c | 11 +- trunk/drivers/input/touchscreen/tps6507x-ts.c | 3 +- trunk/drivers/input/touchscreen/tsc2007.c | 2 +- trunk/drivers/input/touchscreen/wacom_w8001.c | 185 +- trunk/drivers/input/touchscreen/wm97xx-core.c | 18 +- trunk/drivers/media/IR/ir-keytable.c | 393 +- trunk/drivers/mfd/ab8500-core.c | 20 - trunk/drivers/mfd/sh_mobile_sdhi.c | 2 +- trunk/drivers/mfd/twl-core.c | 6 +- trunk/drivers/mfd/twl4030-codec.c | 8 +- trunk/drivers/mtd/devices/m25p80.c | 7 - trunk/drivers/mtd/maps/physmap_of.c | 2 +- trunk/drivers/mtd/ofpart.c | 4 +- trunk/drivers/of/Kconfig | 5 +- trunk/drivers/of/Makefile | 1 - trunk/drivers/of/address.c | 2 +- trunk/drivers/of/base.c | 4 +- trunk/drivers/of/device.c | 27 +- trunk/drivers/of/fdt.c | 2 + trunk/drivers/of/irq.c | 39 - trunk/drivers/of/of_i2c.c | 1 - trunk/drivers/of/pdt.c | 276 -- trunk/drivers/of/platform.c | 34 +- trunk/drivers/platform/x86/Kconfig | 53 +- trunk/drivers/platform/x86/Makefile | 6 +- trunk/drivers/platform/x86/acer-wmi.c | 2 +- trunk/drivers/platform/x86/asus-laptop.c | 171 +- trunk/drivers/platform/x86/dell-laptop.c | 77 - trunk/drivers/platform/x86/dell-wmi.c | 256 +- trunk/drivers/platform/x86/eeepc-laptop.c | 16 +- trunk/drivers/platform/x86/eeepc-wmi.c | 56 - trunk/drivers/platform/x86/hp-wmi.c | 172 +- trunk/drivers/platform/x86/ibm_rtl.c | 341 -- .../x86/{ideapad-laptop.c => ideapad_acpi.c} | 238 +- trunk/drivers/platform/x86/intel_pmic_gpio.c | 26 +- trunk/drivers/platform/x86/intel_scu_ipc.c | 1 - trunk/drivers/platform/x86/panasonic-laptop.c | 194 +- trunk/drivers/platform/x86/topstar-laptop.c | 161 +- trunk/drivers/platform/x86/toshiba_acpi.c | 191 +- trunk/drivers/platform/x86/wmi.c | 308 +- trunk/drivers/platform/x86/xo1-rfkill.c | 85 - trunk/drivers/sbus/char/jsflash.c | 2 +- trunk/drivers/serial/68328serial.h | 5 + trunk/drivers/serial/of_serial.c | 12 +- trunk/drivers/serial/sh-sci.h | 17 +- trunk/drivers/sh/Kconfig | 25 +- trunk/drivers/sh/Makefile | 7 +- trunk/drivers/sh/{clk/cpg.c => clk-cpg.c} | 11 +- trunk/drivers/sh/{clk/core.c => clk.c} | 203 +- trunk/drivers/sh/clk/Makefile | 3 - trunk/drivers/sh/intc.c | 1390 ++++++ trunk/drivers/sh/intc/Kconfig | 35 - trunk/drivers/sh/intc/Makefile | 5 - trunk/drivers/sh/intc/access.c | 237 - trunk/drivers/sh/intc/balancing.c | 97 - trunk/drivers/sh/intc/chip.c | 215 - trunk/drivers/sh/intc/core.c | 469 -- trunk/drivers/sh/intc/dynamic.c | 135 - trunk/drivers/sh/intc/handle.c | 307 -- trunk/drivers/sh/intc/internals.h | 186 - trunk/drivers/sh/intc/userimask.c | 83 - trunk/drivers/sh/intc/virq-debugfs.c | 64 - trunk/drivers/sh/intc/virq.c | 255 -- trunk/drivers/sh/pfc.c | 17 +- trunk/drivers/staging/Kconfig | 2 + trunk/drivers/staging/Makefile | 1 + .../drivers/staging/mrst-touchscreen/Kconfig | 7 + .../drivers/staging/mrst-touchscreen/Makefile | 3 + trunk/drivers/staging/mrst-touchscreen/TODO | 2 + .../mrst-touchscreen/intel-mid-touch.c | 864 ++++ trunk/drivers/staging/xgifb/TODO | 2 +- trunk/drivers/usb/host/r8a66597-hcd.c | 4 +- trunk/drivers/uwb/Kconfig | 20 +- trunk/drivers/uwb/Makefile | 1 + trunk/drivers/uwb/i1480/Makefile | 1 + trunk/drivers/uwb/i1480/i1480-wlp.h | 200 + trunk/drivers/uwb/i1480/i1480u-wlp/Makefile | 8 + .../drivers/uwb/i1480/i1480u-wlp/i1480u-wlp.h | 283 ++ trunk/drivers/uwb/i1480/i1480u-wlp/lc.c | 424 ++ trunk/drivers/uwb/i1480/i1480u-wlp/netdev.c | 331 ++ trunk/drivers/uwb/i1480/i1480u-wlp/rx.c | 474 ++ trunk/drivers/uwb/i1480/i1480u-wlp/sysfs.c | 407 ++ trunk/drivers/uwb/i1480/i1480u-wlp/tx.c | 584 +++ trunk/drivers/uwb/wlp/Makefile | 10 + trunk/drivers/uwb/wlp/driver.c | 43 + trunk/drivers/uwb/wlp/eda.c | 415 ++ trunk/drivers/uwb/wlp/messages.c | 1798 ++++++++ trunk/drivers/uwb/wlp/sysfs.c | 708 +++ trunk/drivers/uwb/wlp/txrx.c | 354 ++ trunk/drivers/uwb/wlp/wlp-internal.h | 224 + trunk/drivers/uwb/wlp/wlp-lc.c | 560 +++ trunk/drivers/uwb/wlp/wss-lc.c | 959 ++++ trunk/drivers/video/Kconfig | 3 - trunk/drivers/video/aty/atyfb_base.c | 3 +- trunk/drivers/video/sh_mobile_hdmi.c | 84 +- trunk/drivers/video/sh_mobile_lcdcfb.c | 6 +- trunk/fs/coda/cache.c | 17 +- trunk/fs/coda/cnode.c | 19 +- trunk/fs/coda/dir.c | 155 +- trunk/fs/coda/file.c | 31 +- trunk/fs/coda/inode.c | 61 +- trunk/fs/coda/pioctl.c | 22 +- trunk/fs/coda/psdev.c | 41 +- trunk/fs/coda/symlink.c | 3 + trunk/fs/coda/upcall.c | 89 +- trunk/fs/partitions/check.c | 12 + trunk/include/linux/blkdev.h | 1 + trunk/include/linux/coda_fs_i.h | 13 +- trunk/include/linux/coda_linux.h | 6 +- trunk/include/linux/coda_psdev.h | 4 +- trunk/include/linux/elevator.h | 2 + trunk/include/linux/gameport.h | 4 +- trunk/include/linux/genhd.h | 1 + trunk/include/linux/i2c/twl.h | 6 +- trunk/include/linux/input.h | 57 +- trunk/include/linux/input/bu21013.h | 44 - trunk/include/linux/of_device.h | 13 +- trunk/include/linux/of_fdt.h | 2 +- trunk/include/linux/of_irq.h | 4 - trunk/include/linux/of_pdt.h | 45 - trunk/include/linux/pci_ids.h | 7 - trunk/include/linux/serio.h | 10 +- trunk/include/linux/sh_clk.h | 17 +- trunk/include/linux/sh_intc.h | 16 +- trunk/include/linux/sh_pfc.h | 1 - trunk/include/linux/wlp.h | 736 +++ trunk/include/media/rc-map.h | 2 +- trunk/include/sound/core.h | 2 +- trunk/include/sound/emu10k1.h | 2 - trunk/include/sound/jack.h | 5 +- trunk/include/sound/max98088.h | 50 - trunk/include/sound/pcm.h | 1 - trunk/include/sound/sh_fsi.h | 3 + trunk/include/sound/soc-dai.h | 98 +- trunk/include/sound/soc-dapm.h | 18 +- trunk/include/sound/soc-of-simple.h | 25 + trunk/include/sound/soc.h | 245 +- trunk/include/sound/tlv.h | 4 +- trunk/include/sound/tlv320aic3x.h | 43 +- trunk/include/sound/wm8962.h | 32 - trunk/include/video/sh_mobile_hdmi.h | 16 - trunk/sound/core/init.c | 9 +- trunk/sound/core/oss/mixer_oss.c | 34 +- trunk/sound/core/pcm.c | 3 +- trunk/sound/core/pcm_lib.c | 14 +- trunk/sound/core/pcm_native.c | 4 +- trunk/sound/drivers/Kconfig | 19 - trunk/sound/drivers/Makefile | 2 - trunk/sound/drivers/aloop.c | 1258 ------ trunk/sound/drivers/virmidi.c | 2 +- trunk/sound/i2c/other/ak4xxx-adda.c | 2 +- trunk/sound/isa/Kconfig | 36 +- trunk/sound/isa/Makefile | 4 +- trunk/sound/isa/ad1816a/ad1816a.c | 2 +- trunk/sound/isa/azt2320.c | 2 +- trunk/sound/isa/galaxy/Makefile | 10 - trunk/sound/isa/galaxy/azt1605.c | 91 - trunk/sound/isa/galaxy/azt2316.c | 111 - trunk/sound/isa/galaxy/galaxy.c | 652 --- trunk/sound/isa/gus/gusmax.c | 4 +- trunk/sound/isa/sb/sb8.c | 2 +- trunk/sound/isa/sgalaxy.c | 369 ++ trunk/sound/oss/Kconfig | 8 + trunk/sound/oss/Makefile | 1 + trunk/sound/oss/au1550_ac97.c | 48 +- trunk/sound/oss/dmasound/dmasound_core.c | 41 +- trunk/sound/oss/msnd_pinnacle.c | 15 +- trunk/sound/oss/sh_dac_audio.c | 325 ++ trunk/sound/oss/soundcard.c | 43 +- trunk/sound/oss/swarm_cs4297a.c | 20 +- trunk/sound/oss/vwsnd.c | 30 +- trunk/sound/pci/Kconfig | 17 +- trunk/sound/pci/au88x0/au88x0_mixer.c | 2 +- trunk/sound/pci/ca0106/ca0106.h | 5 +- trunk/sound/pci/ca0106/ca0106_main.c | 136 +- trunk/sound/pci/ca0106/ca0106_mixer.c | 93 +- trunk/sound/pci/emu10k1/emumpu401.c | 2 +- trunk/sound/pci/hda/Kconfig | 39 +- trunk/sound/pci/hda/Makefile | 15 +- trunk/sound/pci/hda/hda_codec.c | 271 +- trunk/sound/pci/hda/hda_codec.h | 13 - trunk/sound/pci/hda/hda_eld.c | 7 + trunk/sound/pci/hda/hda_generic.c | 41 +- trunk/sound/pci/hda/hda_intel.c | 101 +- trunk/sound/pci/hda/hda_local.h | 51 +- trunk/sound/pci/hda/patch_analog.c | 48 +- trunk/sound/pci/hda/patch_atihdmi.c | 224 + trunk/sound/pci/hda/patch_ca0110.c | 10 +- trunk/sound/pci/hda/patch_cirrus.c | 94 +- trunk/sound/pci/hda/patch_conexant.c | 651 +-- trunk/sound/pci/hda/patch_hdmi.c | 797 +--- trunk/sound/pci/hda/patch_intelhdmi.c | 220 + trunk/sound/pci/hda/patch_nvhdmi.c | 608 +++ trunk/sound/pci/hda/patch_realtek.c | 908 ++-- trunk/sound/pci/hda/patch_sigmatel.c | 379 +- trunk/sound/pci/hda/patch_via.c | 587 ++- trunk/sound/pci/ice1712/delta.c | 10 - trunk/sound/pci/ice1712/delta.h | 4 +- trunk/sound/pci/ice1712/pontis.c | 6 +- trunk/sound/pci/ice1712/prodigy192.c | 2 +- trunk/sound/pci/oxygen/oxygen.c | 4 +- trunk/sound/pci/oxygen/oxygen.h | 1 - trunk/sound/pci/oxygen/oxygen_lib.c | 55 +- trunk/sound/pci/oxygen/oxygen_mixer.c | 5 +- trunk/sound/pci/oxygen/oxygen_pcm.c | 12 +- trunk/sound/pci/oxygen/oxygen_regs.h | 10 +- trunk/sound/pci/oxygen/virtuoso.c | 5 +- trunk/sound/pci/oxygen/xonar_cs43xx.c | 8 + trunk/sound/pci/oxygen/xonar_pcm179x.c | 29 +- trunk/sound/pci/oxygen/xonar_wm87x6.c | 121 +- trunk/sound/pci/rme96.c | 8 +- trunk/sound/pci/rme9652/hdsp.c | 8 +- trunk/sound/ppc/tumbler.c | 2 +- trunk/sound/soc/atmel/atmel-pcm.c | 59 +- trunk/sound/soc/atmel/atmel-pcm.h | 3 + trunk/sound/soc/atmel/atmel_ssc_dai.c | 148 +- trunk/sound/soc/atmel/atmel_ssc_dai.h | 3 +- trunk/sound/soc/atmel/playpaq_wm8510.c | 65 +- trunk/sound/soc/atmel/sam9g20_wm8731.c | 59 +- trunk/sound/soc/atmel/snd-soc-afeb9260.c | 35 +- trunk/sound/soc/au1x/db1200.c | 39 +- trunk/sound/soc/au1x/dbdma2.c | 95 +- trunk/sound/soc/au1x/psc-ac97.c | 71 +- trunk/sound/soc/au1x/psc-i2s.c | 53 +- trunk/sound/soc/au1x/psc.h | 10 +- trunk/sound/soc/blackfin/bf5xx-ac97-pcm.c | 43 +- trunk/sound/soc/blackfin/bf5xx-ac97-pcm.h | 3 + trunk/sound/soc/blackfin/bf5xx-ac97.c | 41 +- trunk/sound/soc/blackfin/bf5xx-ac97.h | 2 + trunk/sound/soc/blackfin/bf5xx-ad1836.c | 23 +- trunk/sound/soc/blackfin/bf5xx-ad193x.c | 23 +- trunk/sound/soc/blackfin/bf5xx-ad1980.c | 19 +- trunk/sound/soc/blackfin/bf5xx-ad73311.c | 22 +- trunk/sound/soc/blackfin/bf5xx-i2s-pcm.c | 44 +- trunk/sound/soc/blackfin/bf5xx-i2s-pcm.h | 3 + trunk/sound/soc/blackfin/bf5xx-i2s.c | 45 +- trunk/sound/soc/blackfin/bf5xx-i2s.h | 14 + trunk/sound/soc/blackfin/bf5xx-ssm2602.c | 38 +- trunk/sound/soc/blackfin/bf5xx-tdm-pcm.c | 43 +- trunk/sound/soc/blackfin/bf5xx-tdm-pcm.h | 3 + trunk/sound/soc/blackfin/bf5xx-tdm.c | 15 +- trunk/sound/soc/blackfin/bf5xx-tdm.h | 2 + trunk/sound/soc/codecs/88pm860x-codec.c | 1486 ------ trunk/sound/soc/codecs/88pm860x-codec.h | 97 - trunk/sound/soc/codecs/Kconfig | 24 - trunk/sound/soc/codecs/Makefile | 12 - trunk/sound/soc/codecs/ac97.c | 125 +- trunk/sound/soc/codecs/ac97.h | 19 + trunk/sound/soc/codecs/ad1836.c | 191 +- trunk/sound/soc/codecs/ad1836.h | 2 + trunk/sound/soc/codecs/ad193x.c | 217 +- trunk/sound/soc/codecs/ad193x.h | 3 + trunk/sound/soc/codecs/ad1980.c | 113 +- trunk/sound/soc/codecs/ad1980.h | 3 + trunk/sound/soc/codecs/ad73311.c | 66 +- trunk/sound/soc/codecs/ad73311.h | 2 + trunk/sound/soc/codecs/ads117x.c | 72 +- trunk/sound/soc/codecs/ads117x.h | 4 +- trunk/sound/soc/codecs/ak4104.c | 149 +- trunk/sound/soc/codecs/ak4104.h | 7 + trunk/sound/soc/codecs/ak4535.c | 236 +- trunk/sound/soc/codecs/ak4535.h | 8 + trunk/sound/soc/codecs/ak4642.c | 235 +- trunk/sound/soc/codecs/ak4642.h | 20 + trunk/sound/soc/codecs/ak4671.c | 140 +- trunk/sound/soc/codecs/ak4671.h | 3 + trunk/sound/soc/codecs/cq93vc.c | 132 +- trunk/sound/soc/codecs/cq93vc.h | 29 + trunk/sound/soc/codecs/cs4270.c | 394 +- trunk/sound/soc/codecs/cs4270.h | 28 + trunk/sound/soc/codecs/cs42l51.c | 295 +- trunk/sound/soc/codecs/cs42l51.h | 2 + trunk/sound/soc/codecs/cx20442.c | 173 +- trunk/sound/soc/codecs/cx20442.h | 2 + trunk/sound/soc/codecs/da7210.c | 163 +- trunk/sound/soc/codecs/da7210.h | 24 + trunk/sound/soc/codecs/jz4740.c | 116 +- trunk/sound/soc/codecs/jz4740.h | 20 + trunk/sound/soc/codecs/max98088.c | 2097 --------- trunk/sound/soc/codecs/max98088.h | 193 - trunk/sound/soc/codecs/pcm3008.c | 92 +- trunk/sound/soc/codecs/pcm3008.h | 3 + trunk/sound/soc/codecs/spdif_transciever.c | 102 +- trunk/sound/soc/codecs/spdif_transciever.h | 18 + trunk/sound/soc/codecs/ssm2602.c | 218 +- trunk/sound/soc/codecs/ssm2602.h | 3 + trunk/sound/soc/codecs/stac9766.c | 118 +- trunk/sound/soc/codecs/stac9766.h | 4 + trunk/sound/soc/codecs/tlv320aic23.c | 182 +- trunk/sound/soc/codecs/tlv320aic23.h | 3 + trunk/sound/soc/codecs/tlv320aic26.c | 180 +- trunk/sound/soc/codecs/tlv320aic26.h | 3 + trunk/sound/soc/codecs/tlv320aic3x.c | 1230 +++-- trunk/sound/soc/codecs/tlv320aic3x.h | 100 +- trunk/sound/soc/codecs/tlv320dac33.c | 276 +- trunk/sound/soc/codecs/tlv320dac33.h | 3 + trunk/sound/soc/codecs/tpa6130a2.c | 28 +- trunk/sound/soc/codecs/twl4030.c | 228 +- trunk/sound/soc/codecs/twl4030.h | 55 + trunk/sound/soc/codecs/twl6040.c | 170 +- trunk/sound/soc/codecs/twl6040.h | 3 + trunk/sound/soc/codecs/uda134x.c | 154 +- trunk/sound/soc/codecs/uda134x.h | 3 + trunk/sound/soc/codecs/uda1380.c | 331 +- trunk/sound/soc/codecs/uda1380.h | 3 + trunk/sound/soc/codecs/wl1273.c | 528 --- trunk/sound/soc/codecs/wl1273.h | 101 - trunk/sound/soc/codecs/wm2000.h | 3 + trunk/sound/soc/codecs/wm8350.c | 231 +- trunk/sound/soc/codecs/wm8350.h | 3 + trunk/sound/soc/codecs/wm8400.c | 181 +- trunk/sound/soc/codecs/wm8400.h | 3 + trunk/sound/soc/codecs/wm8510.c | 288 +- trunk/sound/soc/codecs/wm8510.h | 3 + trunk/sound/soc/codecs/wm8523.c | 177 +- trunk/sound/soc/codecs/wm8523.h | 3 + trunk/sound/soc/codecs/wm8580.c | 321 +- trunk/sound/soc/codecs/wm8580.h | 17 +- trunk/sound/soc/codecs/wm8711.c | 203 +- trunk/sound/soc/codecs/wm8711.h | 3 + trunk/sound/soc/codecs/wm8727.c | 106 +- trunk/sound/soc/codecs/wm8727.h | 21 + trunk/sound/soc/codecs/wm8728.c | 289 +- trunk/sound/soc/codecs/wm8728.h | 9 + trunk/sound/soc/codecs/wm8731.c | 245 +- trunk/sound/soc/codecs/wm8731.h | 7 +- trunk/sound/soc/codecs/wm8741.c | 378 +- trunk/sound/soc/codecs/wm8741.h | 3 + trunk/sound/soc/codecs/wm8750.c | 264 +- trunk/sound/soc/codecs/wm8750.h | 9 + trunk/sound/soc/codecs/wm8753.c | 404 +- trunk/sound/soc/codecs/wm8753.h | 3 + trunk/sound/soc/codecs/wm8776.c | 248 +- trunk/sound/soc/codecs/wm8776.h | 3 + trunk/sound/soc/codecs/wm8804.c | 833 ---- trunk/sound/soc/codecs/wm8804.h | 61 - trunk/sound/soc/codecs/wm8900.c | 246 +- trunk/sound/soc/codecs/wm8900.h | 3 + trunk/sound/soc/codecs/wm8903.c | 265 +- trunk/sound/soc/codecs/wm8903.h | 3 + trunk/sound/soc/codecs/wm8904.c | 208 +- trunk/sound/soc/codecs/wm8904.h | 3 + trunk/sound/soc/codecs/wm8940.c | 199 +- trunk/sound/soc/codecs/wm8940.h | 2 + trunk/sound/soc/codecs/wm8955.c | 181 +- trunk/sound/soc/codecs/wm8955.h | 3 + trunk/sound/soc/codecs/wm8960.c | 209 +- trunk/sound/soc/codecs/wm8960.h | 3 + trunk/sound/soc/codecs/wm8961.c | 237 +- trunk/sound/soc/codecs/wm8961.h | 3 + trunk/sound/soc/codecs/wm8962.c | 3977 ----------------- trunk/sound/soc/codecs/wm8962.h | 3780 ---------------- trunk/sound/soc/codecs/wm8971.c | 247 +- trunk/sound/soc/codecs/wm8971.h | 8 + trunk/sound/soc/codecs/wm8974.c | 167 +- trunk/sound/soc/codecs/wm8974.h | 3 + trunk/sound/soc/codecs/wm8978.c | 190 +- trunk/sound/soc/codecs/wm8978.h | 3 + trunk/sound/soc/codecs/wm8985.c | 1192 ----- trunk/sound/soc/codecs/wm8985.h | 1045 ----- trunk/sound/soc/codecs/wm8988.c | 261 +- trunk/sound/soc/codecs/wm8988.h | 3 + trunk/sound/soc/codecs/wm8990.c | 223 +- trunk/sound/soc/codecs/wm8990.h | 8 + trunk/sound/soc/codecs/wm8993.c | 304 +- trunk/sound/soc/codecs/wm8993.h | 3 + trunk/sound/soc/codecs/wm8994.c | 3410 +++++++------- trunk/sound/soc/codecs/wm8994.h | 3 + trunk/sound/soc/codecs/wm9081.c | 208 +- trunk/sound/soc/codecs/wm9081.h | 3 + trunk/sound/soc/codecs/wm9090.c | 183 +- trunk/sound/soc/codecs/wm9090.h | 2 + trunk/sound/soc/codecs/wm9705.c | 116 +- trunk/sound/soc/codecs/wm9705.h | 3 + trunk/sound/soc/codecs/wm9712.c | 124 +- trunk/sound/soc/codecs/wm9712.h | 3 + trunk/sound/soc/codecs/wm9713.c | 131 +- trunk/sound/soc/codecs/wm9713.h | 3 + trunk/sound/soc/davinci/davinci-evm.c | 109 +- trunk/sound/soc/davinci/davinci-i2s.c | 50 +- trunk/sound/soc/davinci/davinci-i2s.h | 2 + trunk/sound/soc/davinci/davinci-mcasp.c | 34 +- trunk/sound/soc/davinci/davinci-mcasp.h | 2 + trunk/sound/soc/davinci/davinci-pcm.c | 45 +- trunk/sound/soc/davinci/davinci-pcm.h | 3 + trunk/sound/soc/davinci/davinci-sffsdr.c | 29 +- trunk/sound/soc/davinci/davinci-vcif.c | 25 +- trunk/sound/soc/davinci/davinci-vcif.h | 28 + trunk/sound/soc/ep93xx/Kconfig | 16 +- trunk/sound/soc/ep93xx/Makefile | 4 - trunk/sound/soc/ep93xx/ep93xx-ac97.c | 468 -- trunk/sound/soc/ep93xx/ep93xx-i2s.c | 34 +- trunk/sound/soc/ep93xx/ep93xx-i2s.h | 18 + trunk/sound/soc/ep93xx/ep93xx-pcm.c | 37 +- trunk/sound/soc/ep93xx/ep93xx-pcm.h | 2 + trunk/sound/soc/ep93xx/simone.c | 89 - trunk/sound/soc/ep93xx/snappercl15.c | 24 +- trunk/sound/soc/fsl/Kconfig | 27 +- trunk/sound/soc/fsl/Makefile | 11 +- trunk/sound/soc/fsl/efika-audio-fabric.c | 20 +- trunk/sound/soc/fsl/fsl_dma.c | 458 +- trunk/sound/soc/fsl/fsl_dma.h | 20 + trunk/sound/soc/fsl/fsl_ssi.c | 298 +- trunk/sound/soc/fsl/fsl_ssi.h | 26 + trunk/sound/soc/fsl/mpc5200_dma.c | 66 +- trunk/sound/soc/fsl/mpc5200_dma.h | 5 + trunk/sound/soc/fsl/mpc5200_psc_ac97.c | 34 +- trunk/sound/soc/fsl/mpc5200_psc_ac97.h | 2 + trunk/sound/soc/fsl/mpc5200_psc_i2s.c | 19 +- trunk/sound/soc/fsl/mpc8610_hpcd.c | 660 +-- trunk/sound/soc/fsl/p1022_ds.c | 591 --- trunk/sound/soc/fsl/pcm030-audio-fabric.c | 21 +- trunk/sound/soc/fsl/soc-of-simple.c | 172 + trunk/sound/soc/imx/Kconfig | 16 - trunk/sound/soc/imx/Makefile | 10 +- trunk/sound/soc/imx/eukrea-tlv320.c | 16 +- trunk/sound/soc/imx/imx-pcm-dma-mx2.c | 43 +- trunk/sound/soc/imx/imx-pcm-fiq.c | 68 +- trunk/sound/soc/imx/imx-ssi.c | 148 +- trunk/sound/soc/imx/imx-ssi.h | 7 +- trunk/sound/soc/imx/phycore-ac97.c | 19 +- trunk/sound/soc/imx/wm1133-ev1.c | 27 +- trunk/sound/soc/jz4740/jz4740-i2s.c | 104 +- trunk/sound/soc/jz4740/jz4740-i2s.h | 2 + trunk/sound/soc/jz4740/jz4740-pcm.c | 18 +- trunk/sound/soc/jz4740/jz4740-pcm.h | 2 + trunk/sound/soc/jz4740/qi_lb60.c | 25 +- trunk/sound/soc/kirkwood/kirkwood-dma.c | 69 +- trunk/sound/soc/kirkwood/kirkwood-dma.h | 17 + trunk/sound/soc/kirkwood/kirkwood-i2s.c | 55 +- trunk/sound/soc/kirkwood/kirkwood-i2s.h | 17 + trunk/sound/soc/kirkwood/kirkwood-openrd.c | 24 +- trunk/sound/soc/nuc900/nuc900-ac97.c | 13 +- trunk/sound/soc/nuc900/nuc900-audio.c | 16 +- trunk/sound/soc/nuc900/nuc900-audio.h | 4 + trunk/sound/soc/nuc900/nuc900-pcm.c | 38 +- trunk/sound/soc/omap/am3517evm.c | 29 +- trunk/sound/soc/omap/ams-delta.c | 98 +- trunk/sound/soc/omap/igep0020.c | 26 +- trunk/sound/soc/omap/mcpdm.c | 19 +- trunk/sound/soc/omap/mcpdm.h | 2 - trunk/sound/soc/omap/n810.c | 42 +- trunk/sound/soc/omap/omap-mcbsp.c | 125 +- trunk/sound/soc/omap/omap-mcbsp.h | 2 + trunk/sound/soc/omap/omap-mcpdm.c | 71 +- trunk/sound/soc/omap/omap-mcpdm.h | 29 + trunk/sound/soc/omap/omap-pcm.c | 47 +- trunk/sound/soc/omap/omap-pcm.h | 2 + trunk/sound/soc/omap/omap2evm.c | 29 +- trunk/sound/soc/omap/omap3beagle.c | 27 +- trunk/sound/soc/omap/omap3evm.c | 34 +- trunk/sound/soc/omap/omap3pandora.c | 36 +- trunk/sound/soc/omap/osk5912.c | 24 +- trunk/sound/soc/omap/overo.c | 22 +- trunk/sound/soc/omap/rx51.c | 41 +- trunk/sound/soc/omap/sdp3430.c | 60 +- trunk/sound/soc/omap/sdp4430.c | 27 +- trunk/sound/soc/omap/zoom2.c | 68 +- trunk/sound/soc/pxa/Kconfig | 18 - trunk/sound/soc/pxa/Makefile | 4 - trunk/sound/soc/pxa/corgi.c | 28 +- trunk/sound/soc/pxa/e740_wm9705.c | 29 +- trunk/sound/soc/pxa/e750_wm9705.c | 26 +- trunk/sound/soc/pxa/e800_wm9712.c | 26 +- trunk/sound/soc/pxa/em-x270.c | 22 +- trunk/sound/soc/pxa/imote2.c | 20 +- trunk/sound/soc/pxa/magician.c | 35 +- trunk/sound/soc/pxa/mioa701_wm9713.c | 33 +- trunk/sound/soc/pxa/palm27x.c | 27 +- trunk/sound/soc/pxa/poodle.c | 29 +- trunk/sound/soc/pxa/pxa-ssp.c | 174 +- trunk/sound/soc/pxa/pxa-ssp.h | 2 + trunk/sound/soc/pxa/pxa2xx-ac97.c | 46 +- trunk/sound/soc/pxa/pxa2xx-ac97.h | 2 + trunk/sound/soc/pxa/pxa2xx-i2s.c | 91 +- trunk/sound/soc/pxa/pxa2xx-i2s.h | 2 + trunk/sound/soc/pxa/pxa2xx-pcm.c | 46 +- trunk/sound/soc/pxa/pxa2xx-pcm.h | 19 + trunk/sound/soc/pxa/raumfeld.c | 114 +- trunk/sound/soc/pxa/saarb.c | 200 - trunk/sound/soc/pxa/spitz.c | 26 +- trunk/sound/soc/pxa/tavorevb3.c | 200 - trunk/sound/soc/pxa/tosa.c | 27 +- trunk/sound/soc/pxa/z2.c | 26 +- trunk/sound/soc/pxa/zylonite.c | 40 +- trunk/sound/soc/s3c24xx/Kconfig | 37 - trunk/sound/soc/s3c24xx/Makefile | 10 - trunk/sound/soc/s3c24xx/aquila_wm8994.c | 295 -- trunk/sound/soc/s3c24xx/goni_wm8994.c | 298 -- trunk/sound/soc/s3c24xx/jive_wm8750.c | 23 +- trunk/sound/soc/s3c24xx/ln2440sbc_alc650.c | 17 +- .../sound/soc/s3c24xx/neo1973_gta02_wm8753.c | 58 +- trunk/sound/soc/s3c24xx/neo1973_wm8753.c | 37 +- trunk/sound/soc/s3c24xx/rx1950_uda1380.c | 333 -- trunk/sound/soc/s3c24xx/s3c-ac97.c | 36 +- trunk/sound/soc/s3c24xx/s3c-ac97.h | 2 + trunk/sound/soc/s3c24xx/s3c-dma.c | 46 +- trunk/sound/soc/s3c24xx/s3c-dma.h | 1 + trunk/sound/soc/s3c24xx/s3c-i2s-v2.c | 50 +- trunk/sound/soc/s3c24xx/s3c-i2s-v2.h | 13 +- trunk/sound/soc/s3c24xx/s3c-pcm.c | 54 +- trunk/sound/soc/s3c24xx/s3c-pcm.h | 3 +- trunk/sound/soc/s3c24xx/s3c2412-i2s.c | 54 +- trunk/sound/soc/s3c24xx/s3c2412-i2s.h | 2 + trunk/sound/soc/s3c24xx/s3c24xx-i2s.c | 40 +- trunk/sound/soc/s3c24xx/s3c24xx-i2s.h | 2 + trunk/sound/soc/s3c24xx/s3c24xx_simtec.c | 15 +- trunk/sound/soc/s3c24xx/s3c24xx_simtec.h | 4 +- .../sound/soc/s3c24xx/s3c24xx_simtec_hermes.c | 25 +- .../soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c | 21 +- trunk/sound/soc/s3c24xx/s3c24xx_uda134x.c | 21 +- trunk/sound/soc/s3c24xx/s3c64xx-i2s-v4.c | 135 +- trunk/sound/soc/s3c24xx/s3c64xx-i2s.c | 206 +- trunk/sound/soc/s3c24xx/s3c64xx-i2s.h | 3 +- trunk/sound/soc/s3c24xx/smartq_wm8987.c | 15 +- trunk/sound/soc/s3c24xx/smdk2443_wm9710.c | 17 +- trunk/sound/soc/s3c24xx/smdk64xx_wm8580.c | 68 +- trunk/sound/soc/s3c24xx/smdk_spdif.c | 223 - trunk/sound/soc/s3c24xx/smdk_wm9713.c | 42 +- trunk/sound/soc/s3c24xx/spdif.c | 501 --- trunk/sound/soc/s3c24xx/spdif.h | 19 - trunk/sound/soc/s6000/s6000-i2s.c | 56 +- trunk/sound/soc/s6000/s6000-i2s.h | 2 + trunk/sound/soc/s6000/s6000-pcm.c | 100 +- trunk/sound/soc/s6000/s6000-pcm.h | 2 + trunk/sound/soc/s6000/s6105-ipcam.c | 31 +- trunk/sound/soc/sh/Kconfig | 11 +- trunk/sound/soc/sh/Makefile | 2 - trunk/sound/soc/sh/dma-sh7760.c | 53 +- trunk/sound/soc/sh/fsi-ak4642.c | 31 +- trunk/sound/soc/sh/fsi-da7210.c | 24 +- trunk/sound/soc/sh/fsi-hdmi.c | 60 - trunk/sound/soc/sh/fsi.c | 606 ++- trunk/sound/soc/sh/hac.c | 46 +- trunk/sound/soc/sh/migor.c | 29 +- trunk/sound/soc/sh/sh7760-ac97.c | 25 +- trunk/sound/soc/sh/siu.h | 6 +- trunk/sound/soc/sh/siu_dai.c | 97 +- trunk/sound/soc/sh/siu_pcm.c | 34 +- trunk/sound/soc/sh/ssi.c | 55 +- trunk/sound/soc/soc-cache.c | 198 +- trunk/sound/soc/soc-core.c | 1821 +++----- trunk/sound/soc/soc-dapm.c | 88 +- trunk/sound/soc/soc-jack.c | 21 +- trunk/sound/soc/txx9/txx9aclc-ac97.c | 55 +- trunk/sound/soc/txx9/txx9aclc-generic.c | 24 +- trunk/sound/soc/txx9/txx9aclc.c | 141 +- trunk/sound/soc/txx9/txx9aclc.h | 13 +- trunk/sound/synth/emux/emux_hwdep.c | 3 - trunk/sound/usb/Kconfig | 2 - trunk/sound/usb/caiaq/audio.c | 175 +- trunk/sound/usb/caiaq/control.c | 208 +- trunk/sound/usb/caiaq/device.c | 10 +- trunk/sound/usb/caiaq/device.h | 6 +- trunk/sound/usb/caiaq/input.c | 248 +- trunk/sound/usb/card.c | 31 +- trunk/sound/usb/endpoint.c | 2 + trunk/sound/usb/helper.c | 17 +- trunk/sound/usb/midi.c | 16 +- trunk/sound/usb/mixer.c | 9 +- trunk/sound/usb/mixer_quirks.c | 1 - trunk/sound/usb/pcm.c | 4 +- trunk/sound/usb/proc.c | 2 +- trunk/sound/usb/quirks-table.h | 184 +- trunk/sound/usb/quirks.c | 2 +- trunk/sound/usb/urb.c | 2 +- trunk/sound/usb/usbaudio.h | 2 +- trunk/sound/usb/usx2y/usx2yhwdeppcm.c | 6 +- 948 files changed, 64285 insertions(+), 54159 deletions(-) create mode 100644 trunk/Documentation/ABI/obsolete/dv1394 delete mode 100644 trunk/Documentation/ABI/removed/dv1394 delete mode 100644 trunk/Documentation/ABI/removed/raw1394 create mode 100644 trunk/Documentation/ABI/removed/raw1394_legacy_isochronous delete mode 100644 trunk/Documentation/ABI/removed/video1394 delete mode 100644 trunk/Documentation/ABI/testing/sysfs-devices-system-ibm-rtl delete mode 100644 trunk/arch/arm/plat-nomadik/include/plat/ske.h delete mode 100644 trunk/arch/arm/plat-omap/include/plat/omap4-keypad.h delete mode 100644 trunk/arch/m68k/include/asm/m548xgpt.h delete mode 100644 trunk/arch/m68k/include/asm/m548xsim.h delete mode 100644 trunk/arch/m68k/include/asm/mcfslt.h delete mode 100644 trunk/arch/m68knommu/kernel/.gitignore delete mode 100644 trunk/arch/m68knommu/platform/548x/Makefile delete mode 100644 trunk/arch/m68knommu/platform/548x/config.c delete mode 100644 trunk/arch/m68knommu/platform/coldfire/sltimers.c delete mode 100644 trunk/arch/mips/include/asm/prom.h delete mode 100644 trunk/arch/mips/kernel/prom.c rename trunk/arch/powerpc/include/asm/{fsl_guts.h => immap_86xx.h} (66%) delete mode 100644 trunk/arch/sh/boards/board-sh2007.c delete mode 100644 trunk/arch/sh/boards/board-sh7757lcr.c delete mode 100644 trunk/arch/sh/boards/mach-sdk7786/gpio.c delete mode 100644 trunk/arch/sh/boards/mach-sdk7786/sram.c delete mode 100644 trunk/arch/sh/boards/mach-x3proto/gpio.c delete mode 100644 trunk/arch/sh/configs/sh2007_defconfig delete mode 100644 trunk/arch/sh/configs/sh7757lcr_defconfig delete mode 100644 trunk/arch/sh/drivers/pci/fixups-sdk7786.c rename trunk/arch/sh/include/{mach-x3proto/mach => asm}/ilsel.h (100%) delete mode 100644 trunk/arch/sh/include/asm/ptrace_32.h delete mode 100644 trunk/arch/sh/include/asm/ptrace_64.h delete mode 100644 trunk/arch/sh/include/asm/sram.h delete mode 100644 trunk/arch/sh/include/cpu-sh4/cpu/shx3.h delete mode 100644 trunk/arch/sh/include/mach-common/mach/sh2007.h delete mode 100644 trunk/arch/sh/include/mach-x3proto/mach/hardware.h delete mode 100644 trunk/arch/sh/kernel/cpu/sh4a/intc-shx3.c delete mode 100644 trunk/arch/sh/kernel/cpu/sh4a/pinmux-shx3.c delete mode 100644 trunk/arch/sh/kernel/kdebugfs.c delete mode 100644 trunk/arch/sh/kernel/ptrace.c delete mode 100644 trunk/arch/sh/mm/sram.c rename trunk/drivers/{platform/x86 => hwmon}/hdaps.c (99%) create mode 100644 trunk/drivers/ieee1394/Kconfig create mode 100644 trunk/drivers/ieee1394/Makefile create mode 100644 trunk/drivers/ieee1394/config_roms.c create mode 100644 trunk/drivers/ieee1394/config_roms.h create mode 100644 trunk/drivers/ieee1394/csr.c create mode 100644 trunk/drivers/ieee1394/csr.h create mode 100644 trunk/drivers/ieee1394/csr1212.c create mode 100644 trunk/drivers/ieee1394/csr1212.h create mode 100644 trunk/drivers/ieee1394/dma.c create mode 100644 trunk/drivers/ieee1394/dma.h create mode 100644 trunk/drivers/ieee1394/dv1394-private.h create mode 100644 trunk/drivers/ieee1394/dv1394.c create mode 100644 trunk/drivers/ieee1394/dv1394.h create mode 100644 trunk/drivers/ieee1394/eth1394.c create mode 100644 trunk/drivers/ieee1394/eth1394.h create mode 100644 trunk/drivers/ieee1394/highlevel.c create mode 100644 trunk/drivers/ieee1394/highlevel.h create mode 100644 trunk/drivers/ieee1394/hosts.c create mode 100644 trunk/drivers/ieee1394/hosts.h create mode 100644 trunk/drivers/ieee1394/ieee1394-ioctl.h create mode 100644 trunk/drivers/ieee1394/ieee1394.h create mode 100644 trunk/drivers/ieee1394/ieee1394_core.c create mode 100644 trunk/drivers/ieee1394/ieee1394_core.h create mode 100644 trunk/drivers/ieee1394/ieee1394_hotplug.h create mode 100644 trunk/drivers/ieee1394/ieee1394_transactions.c create mode 100644 trunk/drivers/ieee1394/ieee1394_transactions.h create mode 100644 trunk/drivers/ieee1394/ieee1394_types.h rename trunk/drivers/{firewire => ieee1394}/init_ohci1394_dma.c (84%) create mode 100644 trunk/drivers/ieee1394/iso.c create mode 100644 trunk/drivers/ieee1394/iso.h create mode 100644 trunk/drivers/ieee1394/nodemgr.c create mode 100644 trunk/drivers/ieee1394/nodemgr.h create mode 100644 trunk/drivers/ieee1394/ohci1394.c create mode 100644 trunk/drivers/ieee1394/ohci1394.h create mode 100644 trunk/drivers/ieee1394/pcilynx.c create mode 100644 trunk/drivers/ieee1394/pcilynx.h create mode 100644 trunk/drivers/ieee1394/raw1394-private.h create mode 100644 trunk/drivers/ieee1394/raw1394.c create mode 100644 trunk/drivers/ieee1394/raw1394.h create mode 100644 trunk/drivers/ieee1394/sbp2.c create mode 100644 trunk/drivers/ieee1394/sbp2.h create mode 100644 trunk/drivers/ieee1394/video1394.c create mode 100644 trunk/drivers/ieee1394/video1394.h delete mode 100644 trunk/drivers/input/keyboard/nomadik-ske-keypad.c delete mode 100644 trunk/drivers/input/keyboard/omap4-keypad.c delete mode 100644 trunk/drivers/input/misc/ab8500-ponkey.c delete mode 100644 trunk/drivers/input/serio/ps2mult.c delete mode 100644 trunk/drivers/input/tablet/hanwang.c delete mode 100644 trunk/drivers/input/touchscreen/bu21013_ts.c delete mode 100644 trunk/drivers/input/touchscreen/intel-mid-touch.c delete mode 100644 trunk/drivers/input/touchscreen/lpc32xx_ts.c delete mode 100644 trunk/drivers/of/pdt.c delete mode 100644 trunk/drivers/platform/x86/ibm_rtl.c rename trunk/drivers/platform/x86/{ideapad-laptop.c => ideapad_acpi.c} (53%) delete mode 100644 trunk/drivers/platform/x86/xo1-rfkill.c rename trunk/drivers/sh/{clk/cpg.c => clk-cpg.c} (96%) rename trunk/drivers/sh/{clk/core.c => clk.c} (74%) delete mode 100644 trunk/drivers/sh/clk/Makefile create mode 100644 trunk/drivers/sh/intc.c delete mode 100644 trunk/drivers/sh/intc/Kconfig delete mode 100644 trunk/drivers/sh/intc/Makefile delete mode 100644 trunk/drivers/sh/intc/access.c delete mode 100644 trunk/drivers/sh/intc/balancing.c delete mode 100644 trunk/drivers/sh/intc/chip.c delete mode 100644 trunk/drivers/sh/intc/core.c delete mode 100644 trunk/drivers/sh/intc/dynamic.c delete mode 100644 trunk/drivers/sh/intc/handle.c delete mode 100644 trunk/drivers/sh/intc/internals.h delete mode 100644 trunk/drivers/sh/intc/userimask.c delete mode 100644 trunk/drivers/sh/intc/virq-debugfs.c delete mode 100644 trunk/drivers/sh/intc/virq.c create mode 100644 trunk/drivers/staging/mrst-touchscreen/Kconfig create mode 100644 trunk/drivers/staging/mrst-touchscreen/Makefile create mode 100644 trunk/drivers/staging/mrst-touchscreen/TODO create mode 100644 trunk/drivers/staging/mrst-touchscreen/intel-mid-touch.c create mode 100644 trunk/drivers/uwb/i1480/i1480-wlp.h create mode 100644 trunk/drivers/uwb/i1480/i1480u-wlp/Makefile create mode 100644 trunk/drivers/uwb/i1480/i1480u-wlp/i1480u-wlp.h create mode 100644 trunk/drivers/uwb/i1480/i1480u-wlp/lc.c create mode 100644 trunk/drivers/uwb/i1480/i1480u-wlp/netdev.c create mode 100644 trunk/drivers/uwb/i1480/i1480u-wlp/rx.c create mode 100644 trunk/drivers/uwb/i1480/i1480u-wlp/sysfs.c create mode 100644 trunk/drivers/uwb/i1480/i1480u-wlp/tx.c create mode 100644 trunk/drivers/uwb/wlp/Makefile create mode 100644 trunk/drivers/uwb/wlp/driver.c create mode 100644 trunk/drivers/uwb/wlp/eda.c create mode 100644 trunk/drivers/uwb/wlp/messages.c create mode 100644 trunk/drivers/uwb/wlp/sysfs.c create mode 100644 trunk/drivers/uwb/wlp/txrx.c create mode 100644 trunk/drivers/uwb/wlp/wlp-internal.h create mode 100644 trunk/drivers/uwb/wlp/wlp-lc.c create mode 100644 trunk/drivers/uwb/wlp/wss-lc.c delete mode 100644 trunk/include/linux/input/bu21013.h delete mode 100644 trunk/include/linux/of_pdt.h create mode 100644 trunk/include/linux/wlp.h delete mode 100644 trunk/include/sound/max98088.h create mode 100644 trunk/include/sound/soc-of-simple.h delete mode 100644 trunk/include/sound/wm8962.h delete mode 100644 trunk/sound/drivers/aloop.c delete mode 100644 trunk/sound/isa/galaxy/Makefile delete mode 100644 trunk/sound/isa/galaxy/azt1605.c delete mode 100644 trunk/sound/isa/galaxy/azt2316.c delete mode 100644 trunk/sound/isa/galaxy/galaxy.c create mode 100644 trunk/sound/isa/sgalaxy.c create mode 100644 trunk/sound/oss/sh_dac_audio.c create mode 100644 trunk/sound/pci/hda/patch_atihdmi.c create mode 100644 trunk/sound/pci/hda/patch_intelhdmi.c create mode 100644 trunk/sound/pci/hda/patch_nvhdmi.c create mode 100644 trunk/sound/soc/blackfin/bf5xx-i2s.h delete mode 100644 trunk/sound/soc/codecs/88pm860x-codec.c delete mode 100644 trunk/sound/soc/codecs/88pm860x-codec.h create mode 100644 trunk/sound/soc/codecs/ac97.h create mode 100644 trunk/sound/soc/codecs/ak4104.h create mode 100644 trunk/sound/soc/codecs/ak4642.h create mode 100644 trunk/sound/soc/codecs/cq93vc.h create mode 100644 trunk/sound/soc/codecs/cs4270.h create mode 100644 trunk/sound/soc/codecs/da7210.h create mode 100644 trunk/sound/soc/codecs/jz4740.h delete mode 100644 trunk/sound/soc/codecs/max98088.c delete mode 100644 trunk/sound/soc/codecs/max98088.h create mode 100644 trunk/sound/soc/codecs/spdif_transciever.h create mode 100644 trunk/sound/soc/codecs/twl4030.h delete mode 100644 trunk/sound/soc/codecs/wl1273.c delete mode 100644 trunk/sound/soc/codecs/wl1273.h create mode 100644 trunk/sound/soc/codecs/wm8727.h delete mode 100644 trunk/sound/soc/codecs/wm8804.c delete mode 100644 trunk/sound/soc/codecs/wm8804.h delete mode 100644 trunk/sound/soc/codecs/wm8962.c delete mode 100644 trunk/sound/soc/codecs/wm8962.h delete mode 100644 trunk/sound/soc/codecs/wm8985.c delete mode 100644 trunk/sound/soc/codecs/wm8985.h create mode 100644 trunk/sound/soc/davinci/davinci-vcif.h delete mode 100644 trunk/sound/soc/ep93xx/ep93xx-ac97.c create mode 100644 trunk/sound/soc/ep93xx/ep93xx-i2s.h delete mode 100644 trunk/sound/soc/ep93xx/simone.c delete mode 100644 trunk/sound/soc/fsl/p1022_ds.c create mode 100644 trunk/sound/soc/fsl/soc-of-simple.c create mode 100644 trunk/sound/soc/kirkwood/kirkwood-dma.h create mode 100644 trunk/sound/soc/kirkwood/kirkwood-i2s.h create mode 100644 trunk/sound/soc/omap/omap-mcpdm.h create mode 100644 trunk/sound/soc/pxa/pxa2xx-pcm.h delete mode 100644 trunk/sound/soc/pxa/saarb.c delete mode 100644 trunk/sound/soc/pxa/tavorevb3.c delete mode 100644 trunk/sound/soc/s3c24xx/aquila_wm8994.c delete mode 100644 trunk/sound/soc/s3c24xx/goni_wm8994.c delete mode 100644 trunk/sound/soc/s3c24xx/rx1950_uda1380.c delete mode 100644 trunk/sound/soc/s3c24xx/smdk_spdif.c delete mode 100644 trunk/sound/soc/s3c24xx/spdif.c delete mode 100644 trunk/sound/soc/s3c24xx/spdif.h delete mode 100644 trunk/sound/soc/sh/fsi-hdmi.c diff --git a/[refs] b/[refs] index 2978049199d9..471ef6d507c2 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: 33081adf8b89d5a716d7e1c60171768d39795b39 +refs/heads/master: 238ec4efeee4461d5cff2ed3e5a15a3ab850959b diff --git a/trunk/Documentation/ABI/obsolete/dv1394 b/trunk/Documentation/ABI/obsolete/dv1394 new file mode 100644 index 000000000000..2ee36864ca10 --- /dev/null +++ b/trunk/Documentation/ABI/obsolete/dv1394 @@ -0,0 +1,9 @@ +What: dv1394 (a.k.a. "OHCI-DV I/O support" for FireWire) +Contact: linux1394-devel@lists.sourceforge.net +Description: + New application development should use raw1394 + userspace libraries + instead, notably libiec61883 which is functionally equivalent. + +Users: + ffmpeg/libavformat (used by a variety of media players) + dvgrab v1.x (replaced by dvgrab2 on top of raw1394 and resp. libraries) diff --git a/trunk/Documentation/ABI/removed/dv1394 b/trunk/Documentation/ABI/removed/dv1394 deleted file mode 100644 index c2310b6676f4..000000000000 --- a/trunk/Documentation/ABI/removed/dv1394 +++ /dev/null @@ -1,14 +0,0 @@ -What: dv1394 (a.k.a. "OHCI-DV I/O support" for FireWire) -Date: May 2010 (scheduled), finally removed in kernel v2.6.37 -Contact: linux1394-devel@lists.sourceforge.net -Description: - /dev/dv1394/* were character device files, one for each FireWire - controller and for NTSC and PAL respectively, from which DV data - could be received by read() or transmitted by write(). A few - ioctl()s allowed limited control. - This special-purpose interface has been superseded by libraw1394 + - libiec61883 which are functionally equivalent, support HDV, and - transparently work on top of the newer firewire kernel drivers. - -Users: - ffmpeg/libavformat (if configured for DV1394) diff --git a/trunk/Documentation/ABI/removed/raw1394 b/trunk/Documentation/ABI/removed/raw1394 deleted file mode 100644 index 490aa1efc4ae..000000000000 --- a/trunk/Documentation/ABI/removed/raw1394 +++ /dev/null @@ -1,15 +0,0 @@ -What: raw1394 (a.k.a. "Raw IEEE1394 I/O support" for FireWire) -Date: May 2010 (scheduled), finally removed in kernel v2.6.37 -Contact: linux1394-devel@lists.sourceforge.net -Description: - /dev/raw1394 was a character device file that allowed low-level - access to FireWire buses. Its major drawbacks were its inability - to implement sensible device security policies, and its low level - of abstraction that required userspace clients do duplicate much - of the kernel's ieee1394 core functionality. - Replaced by /dev/fw*, i.e. the ABI of - firewire-core. - -Users: - libraw1394 (works with firewire-cdev too, transparent to library ABI - users) diff --git a/trunk/Documentation/ABI/removed/raw1394_legacy_isochronous b/trunk/Documentation/ABI/removed/raw1394_legacy_isochronous new file mode 100644 index 000000000000..1b629622d883 --- /dev/null +++ b/trunk/Documentation/ABI/removed/raw1394_legacy_isochronous @@ -0,0 +1,16 @@ +What: legacy isochronous ABI of raw1394 (1st generation iso ABI) +Date: June 2007 (scheduled), removed in kernel v2.6.23 +Contact: linux1394-devel@lists.sourceforge.net +Description: + The two request types RAW1394_REQ_ISO_SEND, RAW1394_REQ_ISO_LISTEN have + been deprecated for quite some time. They are very inefficient as they + come with high interrupt load and several layers of callbacks for each + packet. Because of these deficiencies, the video1394 and dv1394 drivers + and the 3rd-generation isochronous ABI in raw1394 (rawiso) were created. + +Users: + libraw1394 users via the long deprecated API raw1394_iso_write, + raw1394_start_iso_write, raw1394_start_iso_rcv, raw1394_stop_iso_rcv + + libdc1394, which optionally uses these old libraw1394 calls + alternatively to the more efficient video1394 ABI diff --git a/trunk/Documentation/ABI/removed/video1394 b/trunk/Documentation/ABI/removed/video1394 deleted file mode 100644 index c39c25aee77b..000000000000 --- a/trunk/Documentation/ABI/removed/video1394 +++ /dev/null @@ -1,16 +0,0 @@ -What: video1394 (a.k.a. "OHCI-1394 Video support" for FireWire) -Date: May 2010 (scheduled), finally removed in kernel v2.6.37 -Contact: linux1394-devel@lists.sourceforge.net -Description: - /dev/video1394/* were character device files, one for each FireWire - controller, which were used for isochronous I/O. It was added as an - alternative to raw1394's isochronous I/O functionality which had - performance issues in its first generation. Any video1394 user had - to use raw1394 + libraw1394 too because video1394 did not provide - asynchronous I/O for device discovery and configuration. - Replaced by /dev/fw*, i.e. the ABI of - firewire-core. - -Users: - libdc1394 (works with firewire-cdev too, transparent to library ABI - users) diff --git a/trunk/Documentation/ABI/testing/sysfs-devices-system-ibm-rtl b/trunk/Documentation/ABI/testing/sysfs-devices-system-ibm-rtl deleted file mode 100644 index b82deeaec314..000000000000 --- a/trunk/Documentation/ABI/testing/sysfs-devices-system-ibm-rtl +++ /dev/null @@ -1,22 +0,0 @@ -What: state -Date: Sep 2010 -KernelVersion: 2.6.37 -Contact: Vernon Mauery -Description: The state file allows a means by which to change in and - out of Premium Real-Time Mode (PRTM), as well as the - ability to query the current state. - 0 => PRTM off - 1 => PRTM enabled -Users: The ibm-prtm userspace daemon uses this interface. - - -What: version -Date: Sep 2010 -KernelVersion: 2.6.37 -Contact: Vernon Mauery -Description: The version file provides a means by which to query - the RTL table version that lives in the Extended - BIOS Data Area (EBDA). -Users: The ibm-prtm userspace daemon uses this interface. - - diff --git a/trunk/Documentation/feature-removal-schedule.txt b/trunk/Documentation/feature-removal-schedule.txt index e833c8c81e69..9961f1564d22 100644 --- a/trunk/Documentation/feature-removal-schedule.txt +++ b/trunk/Documentation/feature-removal-schedule.txt @@ -502,6 +502,16 @@ Who: Thomas Gleixner ---------------------------- +What: old ieee1394 subsystem (CONFIG_IEEE1394) +When: 2.6.37 +Files: drivers/ieee1394/ except init_ohci1394_dma.c +Why: superseded by drivers/firewire/ (CONFIG_FIREWIRE) which offers more + features, better performance, and better security, all with smaller + and more modern code base +Who: Stefan Richter + +---------------------------- + What: The acpi_sleep=s4_nonvs command line option When: 2.6.37 Files: arch/x86/kernel/acpi/sleep.c diff --git a/trunk/Documentation/sound/alsa/ALSA-Configuration.txt b/trunk/Documentation/sound/alsa/ALSA-Configuration.txt index d0eb696d32e8..7f4dcebda9c6 100644 --- a/trunk/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/trunk/Documentation/sound/alsa/ALSA-Configuration.txt @@ -300,74 +300,6 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. control correctly. If you have problems regarding this, try another ALSA compliant mixer (alsamixer works). - Module snd-azt1605 - ------------------ - - Module for Aztech Sound Galaxy soundcards based on the Aztech AZT1605 - chipset. - - port - port # for BASE (0x220,0x240,0x260,0x280) - wss_port - port # for WSS (0x530,0x604,0xe80,0xf40) - irq - IRQ # for WSS (7,9,10,11) - dma1 - DMA # for WSS playback (0,1,3) - dma2 - DMA # for WSS capture (0,1), -1 = disabled (default) - mpu_port - port # for MPU-401 UART (0x300,0x330), -1 = disabled (default) - mpu_irq - IRQ # for MPU-401 UART (3,5,7,9), -1 = disabled (default) - fm_port - port # for OPL3 (0x388), -1 = disabled (default) - - This module supports multiple cards. It does not support autoprobe: port, - wss_port, irq and dma1 have to be specified. The other values are - optional. - - "port" needs to match the BASE ADDRESS jumper on the card (0x220 or 0x240) - or the value stored in the card's EEPROM for cards that have an EEPROM and - their "CONFIG MODE" jumper set to "EEPROM SETTING". The other values can - be choosen freely from the options enumerated above. - - If dma2 is specified and different from dma1, the card will operate in - full-duplex mode. When dma1=3, only dma2=0 is valid and the only way to - enable capture since only channels 0 and 1 are available for capture. - - Generic settings are "port=0x220 wss_port=0x530 irq=10 dma1=1 dma2=0 - mpu_port=0x330 mpu_irq=9 fm_port=0x388". - - Whatever IRQ and DMA channels you pick, be sure to reserve them for - legacy ISA in your BIOS. - - Module snd-azt2316 - ------------------ - - Module for Aztech Sound Galaxy soundcards based on the Aztech AZT2316 - chipset. - - port - port # for BASE (0x220,0x240,0x260,0x280) - wss_port - port # for WSS (0x530,0x604,0xe80,0xf40) - irq - IRQ # for WSS (7,9,10,11) - dma1 - DMA # for WSS playback (0,1,3) - dma2 - DMA # for WSS capture (0,1), -1 = disabled (default) - mpu_port - port # for MPU-401 UART (0x300,0x330), -1 = disabled (default) - mpu_irq - IRQ # for MPU-401 UART (5,7,9,10), -1 = disabled (default) - fm_port - port # for OPL3 (0x388), -1 = disabled (default) - - This module supports multiple cards. It does not support autoprobe: port, - wss_port, irq and dma1 have to be specified. The other values are - optional. - - "port" needs to match the BASE ADDRESS jumper on the card (0x220 or 0x240) - or the value stored in the card's EEPROM for cards that have an EEPROM and - their "CONFIG MODE" jumper set to "EEPROM SETTING". The other values can - be choosen freely from the options enumerated above. - - If dma2 is specified and different from dma1, the card will operate in - full-duplex mode. When dma1=3, only dma2=0 is valid and the only way to - enable capture since only channels 0 and 1 are available for capture. - - Generic settings are "port=0x220 wss_port=0x530 irq=10 dma1=1 dma2=0 - mpu_port=0x330 mpu_irq=9 fm_port=0x388". - - Whatever IRQ and DMA channels you pick, be sure to reserve them for - legacy ISA in your BIOS. - Module snd-aw2 -------------- @@ -1709,6 +1641,20 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. This card is also known as Audio Excel DSP 16 or Zoltrix AV302. + Module snd-sgalaxy + ------------------ + + Module for Aztech Sound Galaxy sound card. + + sbport - Port # for SB16 interface (0x220,0x240) + wssport - Port # for WSS interface (0x530,0xe80,0xf40,0x604) + irq - IRQ # (7,9,10,11) + dma1 - DMA # + + This module supports multiple cards. + + The power-management is supported. + Module snd-sscape ----------------- diff --git a/trunk/Documentation/sound/alsa/HD-Audio.txt b/trunk/Documentation/sound/alsa/HD-Audio.txt index c82beb007634..278cc2122ea0 100644 --- a/trunk/Documentation/sound/alsa/HD-Audio.txt +++ b/trunk/Documentation/sound/alsa/HD-Audio.txt @@ -57,11 +57,9 @@ dead. However, this detection isn't perfect on some devices. In such a case, you can change the default method via `position_fix` option. `position_fix=1` means to use LPIB method explicitly. -`position_fix=2` means to use the position-buffer. -`position_fix=3` means to use a combination of both methods, needed -for some VIA and ATI controllers. 0 is the default value for all other -controllers, the automatic check and fallback to LPIB as described in -the above. If you get a problem of repeated sounds, this option might +`position_fix=2` means to use the position-buffer. 0 is the default +value, the automatic check and fallback to LPIB as described in the +above. If you get a problem of repeated sounds, this option might help. In addition to that, every controller is known to be broken regarding diff --git a/trunk/MAINTAINERS b/trunk/MAINTAINERS index 8f72c90aec0f..69aa8fe060b3 100644 --- a/trunk/MAINTAINERS +++ b/trunk/MAINTAINERS @@ -1562,8 +1562,9 @@ F: net/ceph F: include/linux/ceph CERTIFIED WIRELESS USB (WUSB) SUBSYSTEM: +M: David Vrabel L: linux-usb@vger.kernel.org -S: Orphan +S: Supported F: Documentation/usb/WUSB-Design-overview.txt F: Documentation/usb/wusb-cbaf F: drivers/usb/host/hwa-hc.c @@ -2645,10 +2646,10 @@ F: drivers/net/greth* HARD DRIVE ACTIVE PROTECTION SYSTEM (HDAPS) DRIVER M: Frank Seidel -L: platform-driver-x86@vger.kernel.org +L: lm-sensors@lm-sensors.org W: http://www.kernel.org/pub/linux/kernel/people/fseidel/hdaps/ S: Maintained -F: drivers/platform/x86/hdaps.c +F: drivers/hwmon/hdaps.c HWPOISON MEMORY FAILURE HANDLING M: Andi Kleen @@ -5990,9 +5991,13 @@ F: Documentation/filesystems/ufs.txt F: fs/ufs/ ULTRA-WIDEBAND (UWB) SUBSYSTEM: +M: David Vrabel L: linux-usb@vger.kernel.org -S: Orphan +S: Supported F: drivers/uwb/ +X: drivers/uwb/wlp/ +X: drivers/uwb/i1480/i1480u-wlp/ +X: drivers/uwb/i1480/i1480-wlp.h F: include/linux/uwb.h F: include/linux/uwb/ @@ -6528,6 +6533,15 @@ F: include/linux/wimax/debug.h F: include/net/wimax.h F: net/wimax/ +WIMEDIA LLC PROTOCOL (WLP) SUBSYSTEM +M: David Vrabel +L: netdev@vger.kernel.org +S: Maintained +F: include/linux/wlp.h +F: drivers/uwb/wlp/ +F: drivers/uwb/i1480/i1480u-wlp/ +F: drivers/uwb/i1480/i1480-wlp.h + WISTRON LAPTOP BUTTON DRIVER M: Miloslav Trmac S: Maintained diff --git a/trunk/arch/arm/mach-davinci/devices.c b/trunk/arch/arm/mach-davinci/devices.c index de40e9c787e1..8b7201e4c79c 100644 --- a/trunk/arch/arm/mach-davinci/devices.c +++ b/trunk/arch/arm/mach-davinci/devices.c @@ -295,18 +295,6 @@ static void davinci_init_wdt(void) /*-------------------------------------------------------------------------*/ -struct platform_device davinci_pcm_device = { - .name = "davinci-pcm-audio", - .id = -1, -}; - -static void davinci_init_pcm(void) -{ - platform_device_register(&davinci_pcm_device); -} - -/*-------------------------------------------------------------------------*/ - struct davinci_timer_instance davinci_timer_instance[2] = { { .base = DAVINCI_TIMER0_BASE, @@ -327,7 +315,6 @@ static int __init davinci_init_devices(void) /* please keep these calls, and their implementations above, * in alphabetical order so they're easier to sort through. */ - davinci_init_pcm(); davinci_init_wdt(); return 0; diff --git a/trunk/arch/arm/mach-ep93xx/core.c b/trunk/arch/arm/mach-ep93xx/core.c index ffdf87be2958..4cb55d3902ff 100644 --- a/trunk/arch/arm/mach-ep93xx/core.c +++ b/trunk/arch/arm/mach-ep93xx/core.c @@ -776,15 +776,9 @@ static struct platform_device ep93xx_i2s_device = { .resource = ep93xx_i2s_resource, }; -static struct platform_device ep93xx_pcm_device = { - .name = "ep93xx-pcm-audio", - .id = -1, -}; - void __init ep93xx_register_i2s(void) { platform_device_register(&ep93xx_i2s_device); - platform_device_register(&ep93xx_pcm_device); } #define EP93XX_SYSCON_DEVCFG_I2S_MASK (EP93XX_SYSCON_DEVCFG_I2SONSSP | \ @@ -832,40 +826,6 @@ void ep93xx_i2s_release(void) } EXPORT_SYMBOL(ep93xx_i2s_release); -/************************************************************************* - * EP93xx AC97 audio peripheral handling - *************************************************************************/ -static struct resource ep93xx_ac97_resources[] = { - { - .start = EP93XX_AAC_PHYS_BASE, - .end = EP93XX_AAC_PHYS_BASE + 0xb0 - 1, - .flags = IORESOURCE_MEM, - }, - { - .start = IRQ_EP93XX_AACINTR, - .end = IRQ_EP93XX_AACINTR, - .flags = IORESOURCE_IRQ, - }, -}; - -static struct platform_device ep93xx_ac97_device = { - .name = "ep93xx-ac97", - .id = -1, - .num_resources = ARRAY_SIZE(ep93xx_ac97_resources), - .resource = ep93xx_ac97_resources, -}; - -void __init ep93xx_register_ac97(void) -{ - /* - * Make sure that the AC97 pins are not used by I2S. - */ - ep93xx_devcfg_clear_bits(EP93XX_SYSCON_DEVCFG_I2SONAC97); - - platform_device_register(&ep93xx_ac97_device); - platform_device_register(&ep93xx_pcm_device); -} - extern void ep93xx_gpio_init(void); void __init ep93xx_init_devices(void) diff --git a/trunk/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h b/trunk/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h index 9ac4d1055097..c54b3e56ba63 100644 --- a/trunk/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h +++ b/trunk/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h @@ -105,7 +105,6 @@ #define EP93XX_GPIO_B_INT_STATUS EP93XX_GPIO_REG(0xbc) #define EP93XX_GPIO_EEDRIVE EP93XX_GPIO_REG(0xc8) -#define EP93XX_AAC_PHYS_BASE EP93XX_APB_PHYS(0x00080000) #define EP93XX_AAC_BASE EP93XX_APB_IOMEM(0x00080000) #define EP93XX_SPI_PHYS_BASE EP93XX_APB_PHYS(0x000a0000) diff --git a/trunk/arch/arm/mach-ep93xx/include/mach/platform.h b/trunk/arch/arm/mach-ep93xx/include/mach/platform.h index 50660455b1d8..3330b36d79e6 100644 --- a/trunk/arch/arm/mach-ep93xx/include/mach/platform.h +++ b/trunk/arch/arm/mach-ep93xx/include/mach/platform.h @@ -61,7 +61,6 @@ void ep93xx_keypad_release_gpio(struct platform_device *pdev); void ep93xx_register_i2s(void); int ep93xx_i2s_acquire(unsigned i2s_pins, unsigned i2s_config); void ep93xx_i2s_release(void); -void ep93xx_register_ac97(void); void ep93xx_init_devices(void); extern struct sys_timer ep93xx_timer; diff --git a/trunk/arch/arm/mach-ep93xx/simone.c b/trunk/arch/arm/mach-ep93xx/simone.c index d96dc1c5da20..f22ce8db7947 100644 --- a/trunk/arch/arm/mach-ep93xx/simone.c +++ b/trunk/arch/arm/mach-ep93xx/simone.c @@ -61,7 +61,6 @@ static void __init simone_init_machine(void) ep93xx_register_fb(&simone_fb_info); ep93xx_register_i2c(&simone_i2c_gpio_data, simone_i2c_board_info, ARRAY_SIZE(simone_i2c_board_info)); - ep93xx_register_ac97(); } MACHINE_START(SIM_ONE, "Simplemachines Sim.One Board") diff --git a/trunk/arch/arm/mach-kirkwood/common.c b/trunk/arch/arm/mach-kirkwood/common.c index 51ff23b72d3a..1c82d4290dad 100644 --- a/trunk/arch/arm/mach-kirkwood/common.c +++ b/trunk/arch/arm/mach-kirkwood/common.c @@ -903,16 +903,10 @@ static struct platform_device kirkwood_i2s_device = { }, }; -static struct platform_device kirkwood_pcm_device = { - .name = "kirkwood-pcm-audio", - .id = -1, -}; - void __init kirkwood_audio_init(void) { kirkwood_clk_ctrl |= CGC_AUDIO; platform_device_register(&kirkwood_i2s_device); - platform_device_register(&kirkwood_pcm_device); } /***************************************************************************** diff --git a/trunk/arch/arm/mach-omap1/devices.c b/trunk/arch/arm/mach-omap1/devices.c index b583121b04b9..aa0725608fb1 100644 --- a/trunk/arch/arm/mach-omap1/devices.c +++ b/trunk/arch/arm/mach-omap1/devices.c @@ -25,7 +25,6 @@ #include #include #include -#include /*-------------------------------------------------------------------------*/ @@ -196,30 +195,6 @@ static inline void omap_init_spi100k(void) static inline void omap_init_sti(void) {} -#if defined(CONFIG_SND_SOC) || defined(CONFIG_SND_SOC_MODULE) - -static struct platform_device omap_pcm = { - .name = "omap-pcm-audio", - .id = -1, -}; - -OMAP_MCBSP_PLATFORM_DEVICE(1); -OMAP_MCBSP_PLATFORM_DEVICE(2); -OMAP_MCBSP_PLATFORM_DEVICE(3); - -static void omap_init_audio(void) -{ - platform_device_register(&omap_mcbsp1); - platform_device_register(&omap_mcbsp2); - if (!cpu_is_omap7xx()) - platform_device_register(&omap_mcbsp3); - platform_device_register(&omap_pcm); -} - -#else -static inline void omap_init_audio(void) {} -#endif - /*-------------------------------------------------------------------------*/ /* @@ -252,7 +227,6 @@ static int __init omap1_init_devices(void) omap_init_rtc(); omap_init_spi100k(); omap_init_sti(); - omap_init_audio(); return 0; } diff --git a/trunk/arch/arm/mach-omap2/board-rx51-peripherals.c b/trunk/arch/arm/mach-omap2/board-rx51-peripherals.c index 63d786bccb67..ce28a851dcd3 100644 --- a/trunk/arch/arm/mach-omap2/board-rx51-peripherals.c +++ b/trunk/arch/arm/mach-omap2/board-rx51-peripherals.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include @@ -690,6 +689,7 @@ static struct twl4030_power_data rx51_t2scripts_data __initdata = { }; + static struct twl4030_platform_data rx51_twldata __initdata = { .irq_base = TWL4030_IRQ_BASE, .irq_end = TWL4030_IRQ_END, @@ -710,6 +710,10 @@ static struct twl4030_platform_data rx51_twldata __initdata = { .vio = &rx51_vio, }; +static struct aic3x_pdata rx51_aic3x_data __initdata = { + .gpio_reset = 60, +}; + static struct tpa6130a2_platform_data rx51_tpa6130a2_data __initdata = { .id = TPA6130A2, .power_gpio = 98, @@ -724,17 +728,6 @@ static struct i2c_board_info __initdata rx51_peripherals_i2c_board_info_1[] = { }, }; -/* Audio setup data */ -static struct aic3x_setup_data rx51_aic34_setup = { - .gpio_func[0] = AIC3X_GPIO1_FUNC_DISABLED, - .gpio_func[1] = AIC3X_GPIO2_FUNC_DIGITAL_MIC_INPUT, -}; - -static struct aic3x_pdata rx51_aic3x_data = { - .setup = &rx51_aic34_setup, - .gpio_reset = 60, -}; - static struct i2c_board_info __initdata rx51_peripherals_i2c_board_info_2[] = { { I2C_BOARD_INFO("tlv320aic3x", 0x18), diff --git a/trunk/arch/arm/mach-omap2/board-zoom-peripherals.c b/trunk/arch/arm/mach-omap2/board-zoom-peripherals.c index bc8232845d7a..189a6d1600b2 100644 --- a/trunk/arch/arm/mach-omap2/board-zoom-peripherals.c +++ b/trunk/arch/arm/mach-omap2/board-zoom-peripherals.c @@ -26,8 +26,6 @@ #include #include -#include - #include "mux.h" #include "hsmmc.h" @@ -240,11 +238,6 @@ static int zoom_twl_gpio_setup(struct device *dev, return 0; } -/* EXTMUTE callback function */ -void zoom2_set_hs_extmute(int mute) -{ - gpio_set_value(ZOOM2_HEADSET_EXTMUTE_GPIO, mute); -} static int zoom_batt_table[] = { /* 0 C*/ @@ -314,11 +307,6 @@ static struct i2c_board_info __initdata zoom_i2c_boardinfo[] = { static int __init omap_i2c_init(void) { - if (machine_is_omap_zoom2()) { - zoom_audio_data.ramp_delay_value = 3; /* 161 ms */ - zoom_audio_data.hs_extmute = 1; - zoom_audio_data.set_hs_extmute = zoom2_set_hs_extmute; - } omap_register_i2c_bus(1, 2400, zoom_i2c_boardinfo, ARRAY_SIZE(zoom_i2c_boardinfo)); omap_register_i2c_bus(2, 400, NULL, 0); diff --git a/trunk/arch/arm/mach-omap2/board-zoom2.c b/trunk/arch/arm/mach-omap2/board-zoom2.c index 4ccbc32386a0..24bbd0def64f 100644 --- a/trunk/arch/arm/mach-omap2/board-zoom2.c +++ b/trunk/arch/arm/mach-omap2/board-zoom2.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include @@ -35,6 +34,41 @@ static void __init omap_zoom2_init_irq(void) omap_gpio_init(); } +/* REVISIT: These audio entries can be removed once MFD code is merged */ +#if 0 + +static struct twl4030_madc_platform_data zoom2_madc_data = { + .irq_line = 1, +}; + +static struct twl4030_codec_audio_data zoom2_audio_data = { + .audio_mclk = 26000000, +}; + +static struct twl4030_codec_data zoom2_codec_data = { + .audio_mclk = 26000000, + .audio = &zoom2_audio_data, +}; + +static struct twl4030_platform_data zoom2_twldata = { + .irq_base = TWL4030_IRQ_BASE, + .irq_end = TWL4030_IRQ_END, + + /* platform_data for children goes here */ + .bci = &zoom2_bci_data, + .madc = &zoom2_madc_data, + .usb = &zoom2_usb_data, + .gpio = &zoom2_gpio_data, + .keypad = &zoom2_kp_twl4030_data, + .codec = &zoom2_codec_data, + .vmmc1 = &zoom2_vmmc1, + .vmmc2 = &zoom2_vmmc2, + .vsim = &zoom2_vsim, + +}; + +#endif + #ifdef CONFIG_OMAP_MUX static struct omap_board_mux board_mux[] __initdata = { /* WLAN IRQ - GPIO 162 */ diff --git a/trunk/arch/arm/mach-omap2/devices.c b/trunk/arch/arm/mach-omap2/devices.c index c5cf1ba08a6f..b27e7cbb3f29 100644 --- a/trunk/arch/arm/mach-omap2/devices.c +++ b/trunk/arch/arm/mach-omap2/devices.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include @@ -236,43 +235,6 @@ static inline void omap_init_mbox(void) { } static inline void omap_init_sti(void) {} -#if defined(CONFIG_SND_SOC) || defined(CONFIG_SND_SOC_MODULE) - -static struct platform_device omap_pcm = { - .name = "omap-pcm-audio", - .id = -1, -}; - -/* - * OMAP2420 has 2 McBSP ports - * OMAP2430 has 5 McBSP ports - * OMAP3 has 5 McBSP ports - * OMAP4 has 4 McBSP ports - */ -OMAP_MCBSP_PLATFORM_DEVICE(1); -OMAP_MCBSP_PLATFORM_DEVICE(2); -OMAP_MCBSP_PLATFORM_DEVICE(3); -OMAP_MCBSP_PLATFORM_DEVICE(4); -OMAP_MCBSP_PLATFORM_DEVICE(5); - -static void omap_init_audio(void) -{ - platform_device_register(&omap_mcbsp1); - platform_device_register(&omap_mcbsp2); - if (cpu_is_omap243x() || cpu_is_omap34xx() || cpu_is_omap44xx()) { - platform_device_register(&omap_mcbsp3); - platform_device_register(&omap_mcbsp4); - } - if (cpu_is_omap243x() || cpu_is_omap34xx()) - platform_device_register(&omap_mcbsp5); - - platform_device_register(&omap_pcm); -} - -#else -static inline void omap_init_audio(void) {} -#endif - #if defined(CONFIG_SPI_OMAP24XX) || defined(CONFIG_SPI_OMAP24XX_MODULE) #include @@ -955,7 +917,6 @@ static int __init omap2_init_devices(void) * in alphabetical order so they're easier to sort through. */ omap_hsmmc_reset(); - omap_init_audio(); omap_init_camera(); omap_init_mbox(); omap_init_mcspi(); diff --git a/trunk/arch/arm/mach-omap2/include/mach/board-zoom.h b/trunk/arch/arm/mach-omap2/include/mach/board-zoom.h index 80591fda8f8f..3af69d2c3dcd 100644 --- a/trunk/arch/arm/mach-omap2/include/mach/board-zoom.h +++ b/trunk/arch/arm/mach-omap2/include/mach/board-zoom.h @@ -9,5 +9,3 @@ extern void __init board_nand_init(struct mtd_partition *, u8 nr_parts, u8 cs); extern int __init zoom_debugboard_init(void); extern void __init zoom_peripherals_init(void); - -#define ZOOM2_HEADSET_EXTMUTE_GPIO 153 diff --git a/trunk/arch/arm/mach-pxa/devices.c b/trunk/arch/arm/mach-pxa/devices.c index aaa1166df964..08b410343870 100644 --- a/trunk/arch/arm/mach-pxa/devices.c +++ b/trunk/arch/arm/mach-pxa/devices.c @@ -382,31 +382,6 @@ struct platform_device pxa_device_i2s = { .num_resources = ARRAY_SIZE(pxai2s_resources), }; -struct platform_device pxa_device_asoc_ssp1 = { - .name = "pxa-ssp-dai", - .id = 0, -}; - -struct platform_device pxa_device_asoc_ssp2= { - .name = "pxa-ssp-dai", - .id = 1, -}; - -struct platform_device pxa_device_asoc_ssp3 = { - .name = "pxa-ssp-dai", - .id = 2, -}; - -struct platform_device pxa_device_asoc_ssp4 = { - .name = "pxa-ssp-dai", - .id = 3, -}; - -struct platform_device pxa_device_asoc_platform = { - .name = "pxa-pcm-audio", - .id = -1, -}; - static u64 pxaficp_dmamask = ~(u32)0; struct platform_device pxa_device_ficp = { diff --git a/trunk/arch/arm/mach-pxa/devices.h b/trunk/arch/arm/mach-pxa/devices.h index 2fd5a8b35757..715e8bd02e24 100644 --- a/trunk/arch/arm/mach-pxa/devices.h +++ b/trunk/arch/arm/mach-pxa/devices.h @@ -39,10 +39,4 @@ extern struct platform_device pxa3xx_device_i2c_power; extern struct platform_device pxa3xx_device_gcu; -extern struct platform_device pxa_device_asoc_platform; -extern struct platform_device pxa_device_asoc_ssp1; -extern struct platform_device pxa_device_asoc_ssp2; -extern struct platform_device pxa_device_asoc_ssp3; -extern struct platform_device pxa_device_asoc_ssp4; - void __init pxa_register_device(struct platform_device *dev, void *data); diff --git a/trunk/arch/arm/mach-pxa/pxa27x.c b/trunk/arch/arm/mach-pxa/pxa27x.c index d1fbf29d561c..12e5b9f01e6f 100644 --- a/trunk/arch/arm/mach-pxa/pxa27x.c +++ b/trunk/arch/arm/mach-pxa/pxa27x.c @@ -385,10 +385,6 @@ static struct platform_device *devices[] __initdata = { &pxa27x_device_udc, &pxa_device_pmu, &pxa_device_i2s, - &pxa_device_asoc_ssp1, - &pxa_device_asoc_ssp2, - &pxa_device_asoc_ssp3, - &pxa_device_asoc_platform, &sa1100_device_rtc, &pxa_device_rtc, &pxa27x_device_ssp1, diff --git a/trunk/arch/arm/mach-pxa/pxa3xx.c b/trunk/arch/arm/mach-pxa/pxa3xx.c index d1c747cdacf8..c85c3a7abd31 100644 --- a/trunk/arch/arm/mach-pxa/pxa3xx.c +++ b/trunk/arch/arm/mach-pxa/pxa3xx.c @@ -593,11 +593,6 @@ static struct platform_device *devices[] __initdata = { &pxa27x_device_udc, &pxa_device_pmu, &pxa_device_i2s, - &pxa_device_asoc_ssp1, - &pxa_device_asoc_ssp2, - &pxa_device_asoc_ssp3, - &pxa_device_asoc_ssp4, - &pxa_device_asoc_platform, &sa1100_device_rtc, &pxa_device_rtc, &pxa27x_device_ssp1, diff --git a/trunk/arch/arm/mach-pxa/zylonite.c b/trunk/arch/arm/mach-pxa/zylonite.c index 702f7a68e87d..f25fb6245bd7 100644 --- a/trunk/arch/arm/mach-pxa/zylonite.c +++ b/trunk/arch/arm/mach-pxa/zylonite.c @@ -45,16 +45,6 @@ int wm9713_irq; int lcd_id; int lcd_orientation; -struct platform_device pxa_device_wm9713_audio = { - .name = "wm9713-codec", - .id = -1, -}; - -static void __init zylonite_init_wm9713_audio(void) -{ - platform_device_register(&pxa_device_wm9713_audio); -} - static struct resource smc91x_resources[] = { [0] = { .start = ZYLONITE_ETH_PHYS + 0x300, @@ -418,7 +408,6 @@ static void __init zylonite_init(void) zylonite_init_nand(); zylonite_init_leds(); zylonite_init_ohci(); - zylonite_init_wm9713_audio(); } MACHINE_START(ZYLONITE, "PXA3xx Platform Development Kit (aka Zylonite)") diff --git a/trunk/arch/arm/mach-s3c64xx/dev-audio.c b/trunk/arch/arm/mach-s3c64xx/dev-audio.c index 3838335f125b..9648fbc36eec 100644 --- a/trunk/arch/arm/mach-s3c64xx/dev-audio.c +++ b/trunk/arch/arm/mach-s3c64xx/dev-audio.c @@ -43,10 +43,8 @@ static int s3c64xx_i2sv3_cfg_gpio(struct platform_device *pdev) s3c_gpio_cfgpin(S3C64XX_GPE(2), S3C64XX_GPE2_I2S1_LRCLK); s3c_gpio_cfgpin(S3C64XX_GPE(3), S3C64XX_GPE3_I2S1_DI); s3c_gpio_cfgpin(S3C64XX_GPE(4), S3C64XX_GPE4_I2S1_D0); - break; default: - printk(KERN_DEBUG "Invalid I2S Controller number: %d\n", - pdev->id); + printk(KERN_DEBUG "Invalid I2S Controller number!"); return -EINVAL; } @@ -186,8 +184,7 @@ static int s3c64xx_pcm_cfg_gpio(struct platform_device *pdev) s3c_gpio_cfgpin(S3C64XX_GPE(4), S3C64XX_GPE4_PCM1_SOUT); break; default: - printk(KERN_DEBUG "Invalid PCM Controller number: %d\n", - pdev->id); + printk(KERN_DEBUG "Invalid PCM Controller number!"); return -EINVAL; } @@ -336,16 +333,3 @@ void __init s3c64xx_ac97_setup_gpio(int num) else s3c_ac97_pdata.cfg_gpio = s3c64xx_ac97_cfg_gpe; } - -static u64 s3c_device_audio_dmamask = 0xffffffffUL; - -struct platform_device s3c_device_pcm = { - .name = "s3c24xx-pcm-audio", - .id = -1, - .dev = { - .dma_mask = &s3c_device_audio_dmamask, - .coherent_dma_mask = 0xffffffffUL - } -}; -EXPORT_SYMBOL(s3c_device_pcm); - diff --git a/trunk/arch/arm/mach-s3c64xx/mach-smdk6410.c b/trunk/arch/arm/mach-s3c64xx/mach-smdk6410.c index 77488facfe4c..ec8865c03a19 100644 --- a/trunk/arch/arm/mach-s3c64xx/mach-smdk6410.c +++ b/trunk/arch/arm/mach-s3c64xx/mach-smdk6410.c @@ -283,7 +283,6 @@ static struct platform_device *smdk6410_devices[] __initdata = { &s3c_device_fb, &s3c_device_ohci, &s3c_device_usb_hsotg, - &s3c_device_pcm, &s3c64xx_device_iisv4, &samsung_device_keypad, diff --git a/trunk/arch/arm/mach-ux500/board-mop500.c b/trunk/arch/arm/mach-ux500/board-mop500.c index cac83a694880..fcb587f825cc 100644 --- a/trunk/arch/arm/mach-ux500/board-mop500.c +++ b/trunk/arch/arm/mach-ux500/board-mop500.c @@ -18,14 +18,12 @@ #include #include #include -#include #include #include #include #include -#include #include #include @@ -51,24 +49,6 @@ static pin_cfg_t mop500_pins[] = { GPIO11_I2C2_SCL, GPIO229_I2C3_SDA, GPIO230_I2C3_SCL, - - /* SKE keypad */ - GPIO153_KP_I7, - GPIO154_KP_I6, - GPIO155_KP_I5, - GPIO156_KP_I4, - GPIO157_KP_O7, - GPIO158_KP_O6, - GPIO159_KP_O5, - GPIO160_KP_O4, - GPIO161_KP_I3, - GPIO162_KP_I2, - GPIO163_KP_I1, - GPIO164_KP_I0, - GPIO165_KP_O3, - GPIO166_KP_O2, - GPIO167_KP_O1, - GPIO168_KP_O0, }; static void ab4500_spi_cs_control(u32 command) @@ -168,120 +148,12 @@ static struct amba_device *amba_devs[] __initdata = { &u8500_ssp0_device, }; -static const unsigned int ux500_keymap[] = { - KEY(2, 5, KEY_END), - KEY(4, 1, KEY_POWER), - KEY(3, 5, KEY_VOLUMEDOWN), - KEY(1, 3, KEY_3), - KEY(5, 2, KEY_RIGHT), - KEY(5, 0, KEY_9), - - KEY(0, 5, KEY_MENU), - KEY(7, 6, KEY_ENTER), - KEY(4, 5, KEY_0), - KEY(6, 7, KEY_2), - KEY(3, 4, KEY_UP), - KEY(3, 3, KEY_DOWN), - - KEY(6, 4, KEY_SEND), - KEY(6, 2, KEY_BACK), - KEY(4, 2, KEY_VOLUMEUP), - KEY(5, 5, KEY_1), - KEY(4, 3, KEY_LEFT), - KEY(3, 2, KEY_7), -}; - -static const struct matrix_keymap_data ux500_keymap_data = { - .keymap = ux500_keymap, - .keymap_size = ARRAY_SIZE(ux500_keymap), -}; - -/* - * Nomadik SKE keypad - */ -#define ROW_PIN_I0 164 -#define ROW_PIN_I1 163 -#define ROW_PIN_I2 162 -#define ROW_PIN_I3 161 -#define ROW_PIN_I4 156 -#define ROW_PIN_I5 155 -#define ROW_PIN_I6 154 -#define ROW_PIN_I7 153 -#define COL_PIN_O0 168 -#define COL_PIN_O1 167 -#define COL_PIN_O2 166 -#define COL_PIN_O3 165 -#define COL_PIN_O4 160 -#define COL_PIN_O5 159 -#define COL_PIN_O6 158 -#define COL_PIN_O7 157 - -#define SKE_KPD_MAX_ROWS 8 -#define SKE_KPD_MAX_COLS 8 - -static int ske_kp_rows[] = { - ROW_PIN_I0, ROW_PIN_I1, ROW_PIN_I2, ROW_PIN_I3, - ROW_PIN_I4, ROW_PIN_I5, ROW_PIN_I6, ROW_PIN_I7, -}; - -/* - * ske_set_gpio_row: request and set gpio rows - */ -static int ske_set_gpio_row(int gpio) -{ - int ret; - - ret = gpio_request(gpio, "ske-kp"); - if (ret < 0) { - pr_err("ske_set_gpio_row: gpio request failed\n"); - return ret; - } - - ret = gpio_direction_output(gpio, 1); - if (ret < 0) { - pr_err("ske_set_gpio_row: gpio direction failed\n"); - gpio_free(gpio); - } - - return ret; -} - -/* - * ske_kp_init - enable the gpio configuration - */ -static int ske_kp_init(void) -{ - int ret, i; - - for (i = 0; i < SKE_KPD_MAX_ROWS; i++) { - ret = ske_set_gpio_row(ske_kp_rows[i]); - if (ret < 0) { - pr_err("ske_kp_init: failed init\n"); - return ret; - } - } - - return 0; -} - -static struct ske_keypad_platform_data ske_keypad_board = { - .init = ske_kp_init, - .keymap_data = &ux500_keymap_data, - .no_autorepeat = true, - .krow = SKE_KPD_MAX_ROWS, /* 8x8 matrix */ - .kcol = SKE_KPD_MAX_COLS, - .debounce_ms = 40, /* in millsecs */ -}; - - - /* add any platform devices here - TODO */ static struct platform_device *platform_devs[] __initdata = { &u8500_i2c0_device, &ux500_i2c1_device, &ux500_i2c2_device, &ux500_i2c3_device, - &ux500_ske_keypad_device, }; static void __init u8500_init_machine(void) @@ -296,7 +168,6 @@ static void __init u8500_init_machine(void) ux500_i2c1_device.dev.platform_data = &u8500_i2c1_data; ux500_i2c2_device.dev.platform_data = &u8500_i2c2_data; ux500_i2c3_device.dev.platform_data = &u8500_i2c3_data; - ux500_ske_keypad_device.dev.platform_data = &ske_keypad_board; u8500_ssp0_device.dev.platform_data = &ssp0_platform_data; diff --git a/trunk/arch/arm/mach-ux500/clock.c b/trunk/arch/arm/mach-ux500/clock.c index 1675047daf20..d8ab7f184fe4 100644 --- a/trunk/arch/arm/mach-ux500/clock.c +++ b/trunk/arch/arm/mach-ux500/clock.c @@ -477,7 +477,6 @@ static struct clk_lookup u8500_common_clks[] = { CLK(sdi5, "sdi5", NULL), CLK(uart2, "uart2", NULL), CLK(ske, "ske", NULL), - CLK(ske, "nmk-ske-keypad", NULL), CLK(sdi2, "sdi2", NULL), CLK(i2c0, "nmk-i2c.0", NULL), CLK(fsmc, "fsmc", NULL), diff --git a/trunk/arch/arm/mach-ux500/devices-db8500.c b/trunk/arch/arm/mach-ux500/devices-db8500.c index cbbe69a76a7c..40032fecbc16 100644 --- a/trunk/arch/arm/mach-ux500/devices-db8500.c +++ b/trunk/arch/arm/mach-ux500/devices-db8500.c @@ -292,23 +292,3 @@ void dma40_u8500ed_fixup(void) dma40_resources[1].start = U8500_DMA_LCPA_BASE_ED; dma40_resources[1].end = U8500_DMA_LCPA_BASE_ED + 2 * SZ_1K - 1; } - -struct resource keypad_resources[] = { - [0] = { - .start = U8500_SKE_BASE, - .end = U8500_SKE_BASE + SZ_4K - 1, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_DB8500_KB, - .end = IRQ_DB8500_KB, - .flags = IORESOURCE_IRQ, - }, -}; - -struct platform_device ux500_ske_keypad_device = { - .name = "nmk-ske-keypad", - .id = -1, - .num_resources = ARRAY_SIZE(keypad_resources), - .resource = keypad_resources, -}; diff --git a/trunk/arch/arm/mach-ux500/include/mach/devices.h b/trunk/arch/arm/mach-ux500/include/mach/devices.h index b91a4d1211a2..33a120c2e82e 100644 --- a/trunk/arch/arm/mach-ux500/include/mach/devices.h +++ b/trunk/arch/arm/mach-ux500/include/mach/devices.h @@ -26,7 +26,6 @@ extern struct platform_device ux500_i2c3_device; extern struct platform_device u8500_i2c0_device; extern struct platform_device u8500_i2c4_device; extern struct platform_device u8500_dma40_device; -extern struct platform_device ux500_ske_keypad_device; extern struct amba_device u8500_sdi0_device; extern struct amba_device u8500_sdi1_device; diff --git a/trunk/arch/arm/mach-ux500/pins-db8500.h b/trunk/arch/arm/mach-ux500/pins-db8500.h index f923764ee16c..66f8761cc823 100644 --- a/trunk/arch/arm/mach-ux500/pins-db8500.h +++ b/trunk/arch/arm/mach-ux500/pins-db8500.h @@ -459,82 +459,82 @@ #define GPIO152_KP_O9 PIN_CFG(152, ALT_C) #define GPIO153_GPIO PIN_CFG(153, GPIO) -#define GPIO153_KP_I7 PIN_CFG_PULL(153, ALT_A, DOWN) +#define GPIO153_KP_I7 PIN_CFG(153, ALT_A) #define GPIO153_LCD_D24 PIN_CFG(153, ALT_B) #define GPIO153_U2_RXD PIN_CFG(153, ALT_C) #define GPIO154_GPIO PIN_CFG(154, GPIO) -#define GPIO154_KP_I6 PIN_CFG_PULL(154, ALT_A, DOWN) +#define GPIO154_KP_I6 PIN_CFG(154, ALT_A) #define GPIO154_LCD_D25 PIN_CFG(154, ALT_B) #define GPIO154_U2_TXD PIN_CFG(154, ALT_C) #define GPIO155_GPIO PIN_CFG(155, GPIO) -#define GPIO155_KP_I5 PIN_CFG_PULL(155, ALT_A, DOWN) +#define GPIO155_KP_I5 PIN_CFG(155, ALT_A) #define GPIO155_LCD_D26 PIN_CFG(155, ALT_B) #define GPIO155_STMAPE_CLK PIN_CFG(155, ALT_C) #define GPIO156_GPIO PIN_CFG(156, GPIO) -#define GPIO156_KP_I4 PIN_CFG_PULL(156, ALT_A, DOWN) +#define GPIO156_KP_I4 PIN_CFG(156, ALT_A) #define GPIO156_LCD_D27 PIN_CFG(156, ALT_B) #define GPIO156_STMAPE_DAT3 PIN_CFG(156, ALT_C) #define GPIO157_GPIO PIN_CFG(157, GPIO) -#define GPIO157_KP_O7 PIN_CFG_PULL(157, ALT_A, UP) +#define GPIO157_KP_O7 PIN_CFG(157, ALT_A) #define GPIO157_LCD_D28 PIN_CFG(157, ALT_B) #define GPIO157_STMAPE_DAT2 PIN_CFG(157, ALT_C) #define GPIO158_GPIO PIN_CFG(158, GPIO) -#define GPIO158_KP_O6 PIN_CFG_PULL(158, ALT_A, UP) +#define GPIO158_KP_O6 PIN_CFG(158, ALT_A) #define GPIO158_LCD_D29 PIN_CFG(158, ALT_B) #define GPIO158_STMAPE_DAT1 PIN_CFG(158, ALT_C) #define GPIO159_GPIO PIN_CFG(159, GPIO) -#define GPIO159_KP_O5 PIN_CFG_PULL(159, ALT_A, UP) +#define GPIO159_KP_O5 PIN_CFG(159, ALT_A) #define GPIO159_LCD_D30 PIN_CFG(159, ALT_B) #define GPIO159_STMAPE_DAT0 PIN_CFG(159, ALT_C) #define GPIO160_GPIO PIN_CFG(160, GPIO) -#define GPIO160_KP_O4 PIN_CFG_PULL(160, ALT_A, UP) +#define GPIO160_KP_O4 PIN_CFG(160, ALT_A) #define GPIO160_LCD_D31 PIN_CFG(160, ALT_B) #define GPIO160_NONE PIN_CFG(160, ALT_C) #define GPIO161_GPIO PIN_CFG(161, GPIO) -#define GPIO161_KP_I3 PIN_CFG_PULL(161, ALT_A, DOWN) +#define GPIO161_KP_I3 PIN_CFG(161, ALT_A) #define GPIO161_LCD_D32 PIN_CFG(161, ALT_B) #define GPIO161_UARTMOD_RXD PIN_CFG(161, ALT_C) #define GPIO162_GPIO PIN_CFG(162, GPIO) -#define GPIO162_KP_I2 PIN_CFG_PULL(162, ALT_A, DOWN) +#define GPIO162_KP_I2 PIN_CFG(162, ALT_A) #define GPIO162_LCD_D33 PIN_CFG(162, ALT_B) #define GPIO162_UARTMOD_TXD PIN_CFG(162, ALT_C) #define GPIO163_GPIO PIN_CFG(163, GPIO) -#define GPIO163_KP_I1 PIN_CFG_PULL(163, ALT_A, DOWN) +#define GPIO163_KP_I1 PIN_CFG(163, ALT_A) #define GPIO163_LCD_D34 PIN_CFG(163, ALT_B) #define GPIO163_STMMOD_CLK PIN_CFG(163, ALT_C) #define GPIO164_GPIO PIN_CFG(164, GPIO) -#define GPIO164_KP_I0 PIN_CFG_PULL(164, ALT_A, UP) +#define GPIO164_KP_I0 PIN_CFG(164, ALT_A) #define GPIO164_LCD_D35 PIN_CFG(164, ALT_B) #define GPIO164_STMMOD_DAT3 PIN_CFG(164, ALT_C) #define GPIO165_GPIO PIN_CFG(165, GPIO) -#define GPIO165_KP_O3 PIN_CFG_PULL(165, ALT_A, UP) +#define GPIO165_KP_O3 PIN_CFG(165, ALT_A) #define GPIO165_LCD_D36 PIN_CFG(165, ALT_B) #define GPIO165_STMMOD_DAT2 PIN_CFG(165, ALT_C) #define GPIO166_GPIO PIN_CFG(166, GPIO) -#define GPIO166_KP_O2 PIN_CFG_PULL(166, ALT_A, UP) +#define GPIO166_KP_O2 PIN_CFG(166, ALT_A) #define GPIO166_LCD_D37 PIN_CFG(166, ALT_B) #define GPIO166_STMMOD_DAT1 PIN_CFG(166, ALT_C) #define GPIO167_GPIO PIN_CFG(167, GPIO) -#define GPIO167_KP_O1 PIN_CFG_PULL(167, ALT_A, UP) +#define GPIO167_KP_O1 PIN_CFG(167, ALT_A) #define GPIO167_LCD_D38 PIN_CFG(167, ALT_B) #define GPIO167_STMMOD_DAT0 PIN_CFG(167, ALT_C) #define GPIO168_GPIO PIN_CFG(168, GPIO) -#define GPIO168_KP_O0 PIN_CFG_PULL(168, ALT_A, UP) +#define GPIO168_KP_O0 PIN_CFG(168, ALT_A) #define GPIO168_LCD_D39 PIN_CFG(168, ALT_B) #define GPIO168_NONE PIN_CFG(168, ALT_C) diff --git a/trunk/arch/arm/plat-nomadik/include/plat/ske.h b/trunk/arch/arm/plat-nomadik/include/plat/ske.h deleted file mode 100644 index 31382fbc07dc..000000000000 --- a/trunk/arch/arm/plat-nomadik/include/plat/ske.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2010 - * - * License Terms: GNU General Public License v2 - * Author: Naveen Kumar Gaddipati - * - * ux500 Scroll key and Keypad Encoder (SKE) header - */ - -#ifndef __SKE_H -#define __SKE_H - -#include - -/* register definitions for SKE peripheral */ -#define SKE_CR 0x00 -#define SKE_VAL0 0x04 -#define SKE_VAL1 0x08 -#define SKE_DBCR 0x0C -#define SKE_IMSC 0x10 -#define SKE_RIS 0x14 -#define SKE_MIS 0x18 -#define SKE_ICR 0x1C - -/* - * Keypad module - */ - -/** - * struct keypad_platform_data - structure for platform specific data - * @init: pointer to keypad init function - * @exit: pointer to keypad deinitialisation function - * @keymap_data: matrix scan code table for keycodes - * @krow: maximum number of rows - * @kcol: maximum number of columns - * @debounce_ms: platform specific debounce time - * @no_autorepeat: flag for auto repetition - * @wakeup_enable: allow waking up the system - */ -struct ske_keypad_platform_data { - int (*init)(void); - int (*exit)(void); - const struct matrix_keymap_data *keymap_data; - u8 krow; - u8 kcol; - u8 debounce_ms; - bool no_autorepeat; - bool wakeup_enable; -}; -#endif /*__SKE_KPD_H*/ diff --git a/trunk/arch/arm/plat-omap/include/plat/mcbsp.h b/trunk/arch/arm/plat-omap/include/plat/mcbsp.h index 5b20103e68eb..b4ff6a11a8f2 100644 --- a/trunk/arch/arm/plat-omap/include/plat/mcbsp.h +++ b/trunk/arch/arm/plat-omap/include/plat/mcbsp.h @@ -30,13 +30,6 @@ #include #include -/* macro for building platform_device for McBSP ports */ -#define OMAP_MCBSP_PLATFORM_DEVICE(port_nr) \ -static struct platform_device omap_mcbsp##port_nr = { \ - .name = "omap-mcbsp-dai", \ - .id = OMAP_MCBSP##port_nr, \ -} - #define OMAP7XX_MCBSP1_BASE 0xfffb1000 #define OMAP7XX_MCBSP2_BASE 0xfffb1800 diff --git a/trunk/arch/arm/plat-omap/include/plat/omap4-keypad.h b/trunk/arch/arm/plat-omap/include/plat/omap4-keypad.h deleted file mode 100644 index 2b1d9bc1eebb..000000000000 --- a/trunk/arch/arm/plat-omap/include/plat/omap4-keypad.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef ARCH_ARM_PLAT_OMAP4_KEYPAD_H -#define ARCH_ARM_PLAT_OMAP4_KEYPAD_H - -#include - -struct omap4_keypad_platform_data { - const struct matrix_keymap_data *keymap_data; - - u8 rows; - u8 cols; -}; - -extern int omap4_keyboard_init(struct omap4_keypad_platform_data *); -#endif diff --git a/trunk/arch/arm/plat-s3c24xx/devs.c b/trunk/arch/arm/plat-s3c24xx/devs.c index 2f91057a0c02..452e18438b41 100644 --- a/trunk/arch/arm/plat-s3c24xx/devs.c +++ b/trunk/arch/arm/plat-s3c24xx/devs.c @@ -247,7 +247,7 @@ static struct resource s3c_iis_resource[] = { static u64 s3c_device_iis_dmamask = 0xffffffffUL; struct platform_device s3c_device_iis = { - .name = "s3c24xx-iis", + .name = "s3c2410-iis", .id = -1, .num_resources = ARRAY_SIZE(s3c_iis_resource), .resource = s3c_iis_resource, @@ -259,21 +259,6 @@ struct platform_device s3c_device_iis = { EXPORT_SYMBOL(s3c_device_iis); -/* ASoC PCM DMA */ - -static u64 s3c_device_audio_dmamask = 0xffffffffUL; - -struct platform_device s3c_device_pcm = { - .name = "s3c24xx-pcm-audio", - .id = -1, - .dev = { - .dma_mask = &s3c_device_audio_dmamask, - .coherent_dma_mask = 0xffffffffUL - } -}; - -EXPORT_SYMBOL(s3c_device_pcm); - /* RTC */ static struct resource s3c_rtc_resource[] = { @@ -496,30 +481,19 @@ static struct resource s3c_ac97_resource[] = { }, }; +static u64 s3c_device_ac97_dmamask = 0xffffffffUL; + struct platform_device s3c_device_ac97 = { .name = "s3c-ac97", .id = -1, .num_resources = ARRAY_SIZE(s3c_ac97_resource), .resource = s3c_ac97_resource, .dev = { - .dma_mask = &s3c_device_audio_dmamask, + .dma_mask = &s3c_device_ac97_dmamask, .coherent_dma_mask = 0xffffffffUL } }; EXPORT_SYMBOL(s3c_device_ac97); -/* ASoC I2S */ - -struct platform_device s3c2412_device_iis = { - .name = "s3c2412-iis", - .id = -1, - .dev = { - .dma_mask = &s3c_device_audio_dmamask, - .coherent_dma_mask = 0xffffffffUL - } -}; - -EXPORT_SYMBOL(s3c2412_device_iis); - #endif // CONFIG_CPU_S32440 diff --git a/trunk/arch/arm/plat-samsung/include/plat/devs.h b/trunk/arch/arm/plat-samsung/include/plat/devs.h index c8b94279bad1..7d448e138792 100644 --- a/trunk/arch/arm/plat-samsung/include/plat/devs.h +++ b/trunk/arch/arm/plat-samsung/include/plat/devs.h @@ -32,8 +32,6 @@ extern struct platform_device s3c64xx_device_iisv4; extern struct platform_device s3c64xx_device_spi0; extern struct platform_device s3c64xx_device_spi1; -extern struct platform_device s3c_device_pcm; - extern struct platform_device s3c64xx_device_pcm0; extern struct platform_device s3c64xx_device_pcm1; diff --git a/trunk/arch/m68k/include/asm/cacheflush_no.h b/trunk/arch/m68k/include/asm/cacheflush_no.h index 7085bd51668b..89f195656be7 100644 --- a/trunk/arch/m68k/include/asm/cacheflush_no.h +++ b/trunk/arch/m68k/include/asm/cacheflush_no.h @@ -29,7 +29,7 @@ static inline void __flush_cache_all(void) { -#if defined(CONFIG_M5407) || defined(CONFIG_M548x) +#ifdef CONFIG_M5407 /* * Use cpushl to push and invalidate all cache lines. * Gas doesn't seem to know how to generate the ColdFire diff --git a/trunk/arch/m68k/include/asm/coldfire.h b/trunk/arch/m68k/include/asm/coldfire.h index 3b0a34d0fe33..83a9fa4e618a 100644 --- a/trunk/arch/m68k/include/asm/coldfire.h +++ b/trunk/arch/m68k/include/asm/coldfire.h @@ -32,9 +32,7 @@ */ #define MCF_MBAR 0x10000000 #define MCF_MBAR2 0x80000000 -#if defined(CONFIG_M548x) -#define MCF_IPSBAR MCF_MBAR -#elif defined(CONFIG_M520x) +#if defined(CONFIG_M520x) #define MCF_IPSBAR 0xFC000000 #else #define MCF_IPSBAR 0x40000000 diff --git a/trunk/arch/m68k/include/asm/gpio.h b/trunk/arch/m68k/include/asm/gpio.h index 1b57adbafad5..283214dc65a7 100644 --- a/trunk/arch/m68k/include/asm/gpio.h +++ b/trunk/arch/m68k/include/asm/gpio.h @@ -36,8 +36,7 @@ */ #if defined(CONFIG_M5206) || defined(CONFIG_M5206e) || \ defined(CONFIG_M520x) || defined(CONFIG_M523x) || \ - defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ - defined(CONFIG_M532x) || defined(CONFIG_M548x) + defined(CONFIG_M527x) || defined(CONFIG_M528x) || defined(CONFIG_M532x) /* These parts have GPIO organized by 8 bit ports */ @@ -137,8 +136,6 @@ static inline u32 __mcf_gpio_ppdr(unsigned gpio) #endif else return MCFGPIO_PPDR + mcfgpio_port(gpio - MCFGPIO_SCR_START); -#else - return 0; #endif } @@ -176,8 +173,6 @@ static inline u32 __mcf_gpio_podr(unsigned gpio) #endif else return MCFGPIO_PODR + mcfgpio_port(gpio - MCFGPIO_SCR_START); -#else - return 0; #endif } diff --git a/trunk/arch/m68k/include/asm/m548xgpt.h b/trunk/arch/m68k/include/asm/m548xgpt.h deleted file mode 100644 index c8ef158a1c4e..000000000000 --- a/trunk/arch/m68k/include/asm/m548xgpt.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * File: m548xgpt.h - * Purpose: Register and bit definitions for the MCF548X - * - * Notes: - * - */ - -#ifndef m548xgpt_h -#define m548xgpt_h - -/********************************************************************* -* -* General Purpose Timers (GPT) -* -*********************************************************************/ - -/* Register read/write macros */ -#define MCF_GPT_GMS0 0x000800 -#define MCF_GPT_GCIR0 0x000804 -#define MCF_GPT_GPWM0 0x000808 -#define MCF_GPT_GSR0 0x00080C -#define MCF_GPT_GMS1 0x000810 -#define MCF_GPT_GCIR1 0x000814 -#define MCF_GPT_GPWM1 0x000818 -#define MCF_GPT_GSR1 0x00081C -#define MCF_GPT_GMS2 0x000820 -#define MCF_GPT_GCIR2 0x000824 -#define MCF_GPT_GPWM2 0x000828 -#define MCF_GPT_GSR2 0x00082C -#define MCF_GPT_GMS3 0x000830 -#define MCF_GPT_GCIR3 0x000834 -#define MCF_GPT_GPWM3 0x000838 -#define MCF_GPT_GSR3 0x00083C -#define MCF_GPT_GMS(x) (0x000800+((x)*0x010)) -#define MCF_GPT_GCIR(x) (0x000804+((x)*0x010)) -#define MCF_GPT_GPWM(x) (0x000808+((x)*0x010)) -#define MCF_GPT_GSR(x) (0x00080C+((x)*0x010)) - -/* Bit definitions and macros for MCF_GPT_GMS */ -#define MCF_GPT_GMS_TMS(x) (((x)&0x00000007)<<0) -#define MCF_GPT_GMS_GPIO(x) (((x)&0x00000003)<<4) -#define MCF_GPT_GMS_IEN (0x00000100) -#define MCF_GPT_GMS_OD (0x00000200) -#define MCF_GPT_GMS_SC (0x00000400) -#define MCF_GPT_GMS_CE (0x00001000) -#define MCF_GPT_GMS_WDEN (0x00008000) -#define MCF_GPT_GMS_ICT(x) (((x)&0x00000003)<<16) -#define MCF_GPT_GMS_OCT(x) (((x)&0x00000003)<<20) -#define MCF_GPT_GMS_OCPW(x) (((x)&0x000000FF)<<24) -#define MCF_GPT_GMS_OCT_FRCLOW (0x00000000) -#define MCF_GPT_GMS_OCT_PULSEHI (0x00100000) -#define MCF_GPT_GMS_OCT_PULSELO (0x00200000) -#define MCF_GPT_GMS_OCT_TOGGLE (0x00300000) -#define MCF_GPT_GMS_ICT_ANY (0x00000000) -#define MCF_GPT_GMS_ICT_RISE (0x00010000) -#define MCF_GPT_GMS_ICT_FALL (0x00020000) -#define MCF_GPT_GMS_ICT_PULSE (0x00030000) -#define MCF_GPT_GMS_GPIO_INPUT (0x00000000) -#define MCF_GPT_GMS_GPIO_OUTLO (0x00000020) -#define MCF_GPT_GMS_GPIO_OUTHI (0x00000030) -#define MCF_GPT_GMS_TMS_DISABLE (0x00000000) -#define MCF_GPT_GMS_TMS_INCAPT (0x00000001) -#define MCF_GPT_GMS_TMS_OUTCAPT (0x00000002) -#define MCF_GPT_GMS_TMS_PWM (0x00000003) -#define MCF_GPT_GMS_TMS_GPIO (0x00000004) - -/* Bit definitions and macros for MCF_GPT_GCIR */ -#define MCF_GPT_GCIR_CNT(x) (((x)&0x0000FFFF)<<0) -#define MCF_GPT_GCIR_PRE(x) (((x)&0x0000FFFF)<<16) - -/* Bit definitions and macros for MCF_GPT_GPWM */ -#define MCF_GPT_GPWM_LOAD (0x00000001) -#define MCF_GPT_GPWM_PWMOP (0x00000100) -#define MCF_GPT_GPWM_WIDTH(x) (((x)&0x0000FFFF)<<16) - -/* Bit definitions and macros for MCF_GPT_GSR */ -#define MCF_GPT_GSR_CAPT (0x00000001) -#define MCF_GPT_GSR_COMP (0x00000002) -#define MCF_GPT_GSR_PWMP (0x00000004) -#define MCF_GPT_GSR_TEXP (0x00000008) -#define MCF_GPT_GSR_PIN (0x00000100) -#define MCF_GPT_GSR_OVF(x) (((x)&0x00000007)<<12) -#define MCF_GPT_GSR_CAPTURE(x) (((x)&0x0000FFFF)<<16) - -/********************************************************************/ - -#endif /* m548xgpt_h */ diff --git a/trunk/arch/m68k/include/asm/m548xsim.h b/trunk/arch/m68k/include/asm/m548xsim.h deleted file mode 100644 index 149135ef30d2..000000000000 --- a/trunk/arch/m68k/include/asm/m548xsim.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * m548xsim.h -- ColdFire 547x/548x System Integration Unit support. - */ - -#ifndef m548xsim_h -#define m548xsim_h - -#define MCFINT_VECBASE 64 - -/* - * Interrupt Controller Registers - */ -#define MCFICM_INTC0 0x0700 /* Base for Interrupt Ctrl 0 */ -#define MCFINTC_IPRH 0x00 /* Interrupt pending 32-63 */ -#define MCFINTC_IPRL 0x04 /* Interrupt pending 1-31 */ -#define MCFINTC_IMRH 0x08 /* Interrupt mask 32-63 */ -#define MCFINTC_IMRL 0x0c /* Interrupt mask 1-31 */ -#define MCFINTC_INTFRCH 0x10 /* Interrupt force 32-63 */ -#define MCFINTC_INTFRCL 0x14 /* Interrupt force 1-31 */ -#define MCFINTC_IRLR 0x18 /* */ -#define MCFINTC_IACKL 0x19 /* */ -#define MCFINTC_ICR0 0x40 /* Base ICR register */ - -/* - * Define system peripheral IRQ usage. - */ -#define MCF_IRQ_TIMER (64 + 54) /* Slice Timer 0 */ -#define MCF_IRQ_PROFILER (64 + 53) /* Slice Timer 1 */ - -/* - * Generic GPIO support - */ -#define MCFGPIO_PIN_MAX 0 /* I am too lazy to count */ -#define MCFGPIO_IRQ_MAX -1 -#define MCFGPIO_IRQ_VECBASE -1 - -/* - * Some PSC related definitions - */ -#define MCF_PAR_PSC(x) (0x000A4F-((x)&0x3)) -#define MCF_PAR_SDA (0x0008) -#define MCF_PAR_SCL (0x0004) -#define MCF_PAR_PSC_TXD (0x04) -#define MCF_PAR_PSC_RXD (0x08) -#define MCF_PAR_PSC_RTS(x) (((x)&0x03)<<4) -#define MCF_PAR_PSC_CTS(x) (((x)&0x03)<<6) -#define MCF_PAR_PSC_CTS_GPIO (0x00) -#define MCF_PAR_PSC_CTS_BCLK (0x80) -#define MCF_PAR_PSC_CTS_CTS (0xC0) -#define MCF_PAR_PSC_RTS_GPIO (0x00) -#define MCF_PAR_PSC_RTS_FSYNC (0x20) -#define MCF_PAR_PSC_RTS_RTS (0x30) -#define MCF_PAR_PSC_CANRX (0x40) - -#endif /* m548xsim_h */ diff --git a/trunk/arch/m68k/include/asm/mcfcache.h b/trunk/arch/m68k/include/asm/mcfcache.h index f49dfc09f70a..c042634fadaa 100644 --- a/trunk/arch/m68k/include/asm/mcfcache.h +++ b/trunk/arch/m68k/include/asm/mcfcache.h @@ -107,7 +107,7 @@ .endm #endif /* CONFIG_M532x */ -#if defined(CONFIG_M5407) || defined(CONFIG_M548x) +#if defined(CONFIG_M5407) /* * Version 4 cores have a true harvard style separate instruction * and data cache. Invalidate and enable cache, also enable write diff --git a/trunk/arch/m68k/include/asm/mcfsim.h b/trunk/arch/m68k/include/asm/mcfsim.h index 6901fd68165b..9c70a67bf85f 100644 --- a/trunk/arch/m68k/include/asm/mcfsim.h +++ b/trunk/arch/m68k/include/asm/mcfsim.h @@ -41,8 +41,6 @@ #elif defined(CONFIG_M5407) #include #include -#elif defined(CONFIG_M548x) -#include #endif /****************************************************************************/ diff --git a/trunk/arch/m68k/include/asm/mcfslt.h b/trunk/arch/m68k/include/asm/mcfslt.h deleted file mode 100644 index d0d0ecba5333..000000000000 --- a/trunk/arch/m68k/include/asm/mcfslt.h +++ /dev/null @@ -1,44 +0,0 @@ -/****************************************************************************/ - -/* - * mcfslt.h -- ColdFire internal Slice (SLT) timer support defines. - * - * (C) Copyright 2004, Greg Ungerer (gerg@snapgear.com) - * (C) Copyright 2009, Philippe De Muyter (phdm@macqel.be) - */ - -/****************************************************************************/ -#ifndef mcfslt_h -#define mcfslt_h -/****************************************************************************/ - -/* - * Get address specific defines for the 547x. - */ -#define MCFSLT_TIMER0 0x900 /* Base address of TIMER0 */ -#define MCFSLT_TIMER1 0x910 /* Base address of TIMER1 */ - - -/* - * Define the SLT timer register set addresses. - */ -#define MCFSLT_STCNT 0x00 /* Terminal count */ -#define MCFSLT_SCR 0x04 /* Control */ -#define MCFSLT_SCNT 0x08 /* Current count */ -#define MCFSLT_SSR 0x0C /* Status */ - -/* - * Bit definitions for the SCR control register. - */ -#define MCFSLT_SCR_RUN 0x04000000 /* Run mode (continuous) */ -#define MCFSLT_SCR_IEN 0x02000000 /* Interrupt enable */ -#define MCFSLT_SCR_TEN 0x01000000 /* Timer enable */ - -/* - * Bit definitions for the SSR status register. - */ -#define MCFSLT_SSR_BE 0x02000000 /* Bus error condition */ -#define MCFSLT_SSR_TE 0x01000000 /* Timeout condition */ - -/****************************************************************************/ -#endif /* mcfslt_h */ diff --git a/trunk/arch/m68k/include/asm/mcfuart.h b/trunk/arch/m68k/include/asm/mcfuart.h index db72e2b889ca..01a8716c5fc5 100644 --- a/trunk/arch/m68k/include/asm/mcfuart.h +++ b/trunk/arch/m68k/include/asm/mcfuart.h @@ -47,11 +47,6 @@ #define MCFUART_BASE1 0xfc060000 /* Base address of UART1 */ #define MCFUART_BASE2 0xfc064000 /* Base address of UART2 */ #define MCFUART_BASE3 0xfc068000 /* Base address of UART3 */ -#elif defined(CONFIG_M548x) -#define MCFUART_BASE1 0x8600 /* on M548x */ -#define MCFUART_BASE2 0x8700 /* on M548x */ -#define MCFUART_BASE3 0x8800 /* on M548x */ -#define MCFUART_BASE4 0x8900 /* on M548x */ #endif @@ -217,9 +212,7 @@ struct mcf_platform_uart { #define MCFUART_URF_RXS 0xc0 /* Receiver status */ #endif -#if defined(CONFIG_M548x) -#define MCFUART_TXFIFOSIZE 512 -#elif defined(CONFIG_M5272) +#if defined(CONFIG_M5272) #define MCFUART_TXFIFOSIZE 25 #else #define MCFUART_TXFIFOSIZE 1 diff --git a/trunk/arch/m68k/kernel/asm-offsets.c b/trunk/arch/m68k/kernel/asm-offsets.c index 78e59b82ebc3..73e5e581245b 100644 --- a/trunk/arch/m68k/kernel/asm-offsets.c +++ b/trunk/arch/m68k/kernel/asm-offsets.c @@ -22,9 +22,13 @@ int main(void) { /* offsets into the task struct */ + DEFINE(TASK_STATE, offsetof(struct task_struct, state)); + DEFINE(TASK_FLAGS, offsetof(struct task_struct, flags)); + DEFINE(TASK_PTRACE, offsetof(struct task_struct, ptrace)); DEFINE(TASK_THREAD, offsetof(struct task_struct, thread)); DEFINE(TASK_INFO, offsetof(struct task_struct, thread.info)); DEFINE(TASK_MM, offsetof(struct task_struct, mm)); + DEFINE(TASK_ACTIVE_MM, offsetof(struct task_struct, active_mm)); #ifdef CONFIG_MMU DEFINE(TASK_TINFO, offsetof(struct task_struct, thread.info)); #endif @@ -60,6 +64,14 @@ int main(void) /* bitfields are a bit difficult */ DEFINE(PT_OFF_FORMATVEC, offsetof(struct pt_regs, pc) + 4); + /* offsets into the irq_handler struct */ + DEFINE(IRQ_HANDLER, offsetof(struct irq_node, handler)); + DEFINE(IRQ_DEVID, offsetof(struct irq_node, dev_id)); + DEFINE(IRQ_NEXT, offsetof(struct irq_node, next)); + + /* offsets into the kernel_stat struct */ + DEFINE(STAT_IRQ, offsetof(struct kernel_stat, irqs)); + /* offsets into the irq_cpustat_t struct */ DEFINE(CPUSTAT_SOFTIRQ_PENDING, offsetof(irq_cpustat_t, __softirq_pending)); diff --git a/trunk/arch/m68knommu/Kconfig b/trunk/arch/m68knommu/Kconfig index 9287150e5fb0..2609c394e1df 100644 --- a/trunk/arch/m68knommu/Kconfig +++ b/trunk/arch/m68knommu/Kconfig @@ -59,10 +59,6 @@ config GENERIC_HARDIRQS bool default y -config GENERIC_HARDIRQS_NO__DO_IRQ - bool - default y - config GENERIC_CALIBRATE_DELAY bool default y @@ -175,11 +171,6 @@ config M5407 help Motorola ColdFire 5407 processor support. -config M548x - bool "MCF548x" - help - Freescale ColdFire 5480/5481/5482/5483/5484/5485 processor support. - endchoice config M527x @@ -190,7 +181,7 @@ config M527x config COLDFIRE bool - depends on (M5206 || M5206e || M520x || M523x || M5249 || M527x || M5272 || M528x || M5307 || M532x || M5407 || M548x) + depends on (M5206 || M5206e || M520x || M523x || M5249 || M527x || M5272 || M528x || M5307 || M532x || M5407) select GENERIC_GPIO select ARCH_REQUIRE_GPIOLIB default y diff --git a/trunk/arch/m68knommu/Makefile b/trunk/arch/m68knommu/Makefile index 026ef16fa68e..14042574ac21 100644 --- a/trunk/arch/m68knommu/Makefile +++ b/trunk/arch/m68knommu/Makefile @@ -25,7 +25,6 @@ platform-$(CONFIG_M528x) := 528x platform-$(CONFIG_M5307) := 5307 platform-$(CONFIG_M532x) := 532x platform-$(CONFIG_M5407) := 5407 -platform-$(CONFIG_M548x) := 548x PLATFORM := $(platform-y) board-$(CONFIG_PILOT) := pilot @@ -74,7 +73,6 @@ cpuclass-$(CONFIG_M528x) := coldfire cpuclass-$(CONFIG_M5307) := coldfire cpuclass-$(CONFIG_M532x) := coldfire cpuclass-$(CONFIG_M5407) := coldfire -cpuclass-$(CONFIG_M548x) := coldfire cpuclass-$(CONFIG_M68328) := 68328 cpuclass-$(CONFIG_M68EZ328) := 68328 cpuclass-$(CONFIG_M68VZ328) := 68328 @@ -102,7 +100,6 @@ cflags-$(CONFIG_M528x) := $(call cc-option,-m528x,-m5307) cflags-$(CONFIG_M5307) := $(call cc-option,-m5307,-m5200) cflags-$(CONFIG_M532x) := $(call cc-option,-mcpu=532x,-m5307) cflags-$(CONFIG_M5407) := $(call cc-option,-m5407,-m5200) -cflags-$(CONFIG_M548x) := $(call cc-option,-m5407,-m5200) cflags-$(CONFIG_M68328) := -m68000 cflags-$(CONFIG_M68EZ328) := -m68000 cflags-$(CONFIG_M68VZ328) := -m68000 diff --git a/trunk/arch/m68knommu/kernel/.gitignore b/trunk/arch/m68knommu/kernel/.gitignore deleted file mode 100644 index c5f676c3c224..000000000000 --- a/trunk/arch/m68knommu/kernel/.gitignore +++ /dev/null @@ -1 +0,0 @@ -vmlinux.lds diff --git a/trunk/arch/m68knommu/kernel/asm-offsets.c b/trunk/arch/m68knommu/kernel/asm-offsets.c index ffe02f41ad46..24335022fa2c 100644 --- a/trunk/arch/m68knommu/kernel/asm-offsets.c +++ b/trunk/arch/m68knommu/kernel/asm-offsets.c @@ -21,8 +21,14 @@ int main(void) { /* offsets into the task struct */ + DEFINE(TASK_STATE, offsetof(struct task_struct, state)); + DEFINE(TASK_FLAGS, offsetof(struct task_struct, flags)); + DEFINE(TASK_PTRACE, offsetof(struct task_struct, ptrace)); + DEFINE(TASK_BLOCKED, offsetof(struct task_struct, blocked)); DEFINE(TASK_THREAD, offsetof(struct task_struct, thread)); + DEFINE(TASK_THREAD_INFO, offsetof(struct task_struct, stack)); DEFINE(TASK_MM, offsetof(struct task_struct, mm)); + DEFINE(TASK_ACTIVE_MM, offsetof(struct task_struct, active_mm)); /* offsets into the irq_cpustat_t struct */ DEFINE(CPUSTAT_SOFTIRQ_PENDING, offsetof(irq_cpustat_t, __softirq_pending)); @@ -57,7 +63,7 @@ int main(void) DEFINE(PT_OFF_FORMATVEC, offsetof(struct pt_regs, sr) - 2); #else /* bitfields are a bit difficult */ - DEFINE(PT_OFF_FORMATVEC, offsetof(struct pt_regs, pc) + 4); + DEFINE(PT_OFF_VECTOR, offsetof(struct pt_regs, pc) + 4); #endif /* signal defines */ @@ -69,8 +75,11 @@ int main(void) DEFINE(PT_PTRACED, PT_PTRACED); /* Offsets in thread_info structure */ + DEFINE(TI_TASK, offsetof(struct thread_info, task)); + DEFINE(TI_EXECDOMAIN, offsetof(struct thread_info, exec_domain)); DEFINE(TI_FLAGS, offsetof(struct thread_info, flags)); DEFINE(TI_PREEMPTCOUNT, offsetof(struct thread_info, preempt_count)); + DEFINE(TI_CPU, offsetof(struct thread_info, cpu)); return 0; } diff --git a/trunk/arch/m68knommu/kernel/ptrace.c b/trunk/arch/m68knommu/kernel/ptrace.c index 6fe7c38cd556..f6be1248d216 100644 --- a/trunk/arch/m68knommu/kernel/ptrace.c +++ b/trunk/arch/m68knommu/kernel/ptrace.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include @@ -135,6 +134,14 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) tmp >>= 16; } else if (addr >= 21 && addr < 49) { tmp = child->thread.fp[addr - 21]; +#ifdef CONFIG_M68KFPU_EMU + /* Convert internal fpu reg representation + * into long double format + */ + if (FPU_IS_EMU && (addr < 45) && !(addr % 3)) + tmp = ((tmp & 0xffff0000) << 15) | + ((tmp & 0x0000ffff) << 16); +#endif } else if (addr == 49) { tmp = child->mm->start_code; } else if (addr == 50) { @@ -168,6 +175,16 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) } if (addr >= 21 && addr < 48) { +#ifdef CONFIG_M68KFPU_EMU + /* Convert long double format + * into internal fpu reg representation + */ + if (FPU_IS_EMU && (addr < 45) && !(addr % 3)) { + data = (unsigned long)data << 15; + data = (data & 0xffff0000) | + ((data & 0x0000ffff) >> 1); + } +#endif child->thread.fp[addr - 21] = data; ret = 0; } @@ -242,17 +259,21 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) return ret; } -asmlinkage int syscall_trace_enter(void) -{ - int ret = 0; - - if (test_thread_flag(TIF_SYSCALL_TRACE)) - ret = tracehook_report_syscall_entry(task_pt_regs(current)); - return ret; -} - -asmlinkage void syscall_trace_leave(void) +asmlinkage void syscall_trace(void) { - if (test_thread_flag(TIF_SYSCALL_TRACE)) - tracehook_report_syscall_exit(task_pt_regs(current), 0); + if (!test_thread_flag(TIF_SYSCALL_TRACE)) + return; + if (!(current->ptrace & PT_PTRACED)) + return; + ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 0x80 : 0)); + /* + * this isn't the same as continuing with a signal, but it will do + * for normal use. strace only continues with a signal if the + * stopping signal is not SIGTRAP. -brl + */ + if (current->exit_code) { + send_sig(current->exit_code, current, 1); + current->exit_code = 0; + } } diff --git a/trunk/arch/m68knommu/kernel/setup.c b/trunk/arch/m68knommu/kernel/setup.c index c684adf5dc40..ba92b90d5fbc 100644 --- a/trunk/arch/m68knommu/kernel/setup.c +++ b/trunk/arch/m68knommu/kernel/setup.c @@ -54,6 +54,9 @@ void (*mach_reset)(void); void (*mach_halt)(void); void (*mach_power_off)(void); +#ifdef CONFIG_M68000 + #define CPU "MC68000" +#endif #ifdef CONFIG_M68328 #define CPU "MC68328" #endif diff --git a/trunk/arch/m68knommu/kernel/time.c b/trunk/arch/m68knommu/kernel/time.c index d6ac2a43453c..7089dd9d843b 100644 --- a/trunk/arch/m68knommu/kernel/time.c +++ b/trunk/arch/m68knommu/kernel/time.c @@ -60,16 +60,13 @@ static unsigned long read_rtc_mmss(void) { unsigned int year, mon, day, hour, min, sec; - if (mach_gettod) { + if (mach_gettod) mach_gettod(&year, &mon, &day, &hour, &min, &sec); - if ((year += 1900) < 1970) - year += 100; - } else { - year = 1970; - mon = day = 1; - hour = min = sec = 0; - } + else + year = mon = day = hour = min = sec = 0; + if ((year += 1900) < 1970) + year += 100; return mktime(year, mon, day, hour, min, sec); } diff --git a/trunk/arch/m68knommu/kernel/traps.c b/trunk/arch/m68knommu/kernel/traps.c index a768008dfd06..3739c8f657d7 100644 --- a/trunk/arch/m68knommu/kernel/traps.c +++ b/trunk/arch/m68knommu/kernel/traps.c @@ -179,16 +179,14 @@ static void __show_stack(struct task_struct *task, unsigned long *stack) void bad_super_trap(struct frame *fp) { - int vector = (fp->ptregs.vector >> 2) & 0xff; - console_verbose(); - if (vector < ARRAY_SIZE(vec_names)) + if (fp->ptregs.vector < 4 * ARRAY_SIZE(vec_names)) printk (KERN_WARNING "*** %s *** FORMAT=%X\n", - vec_names[vector], + vec_names[(fp->ptregs.vector) >> 2], fp->ptregs.format); else printk (KERN_WARNING "*** Exception %d *** FORMAT=%X\n", - vector, + (fp->ptregs.vector) >> 2, fp->ptregs.format); printk (KERN_WARNING "Current process id is %d\n", current->pid); die_if_kernel("BAD KERNEL TRAP", &fp->ptregs, 0); @@ -197,11 +195,10 @@ void bad_super_trap(struct frame *fp) asmlinkage void trap_c(struct frame *fp) { int sig; - int vector = (fp->ptregs.vector >> 2) & 0xff; siginfo_t info; if (fp->ptregs.sr & PS_S) { - if (vector == VEC_TRACE) { + if ((fp->ptregs.vector >> 2) == VEC_TRACE) { /* traced a trapping instruction */ } else bad_super_trap(fp); @@ -209,7 +206,7 @@ asmlinkage void trap_c(struct frame *fp) } /* send the appropriate signal to the user program */ - switch (vector) { + switch ((fp->ptregs.vector) >> 2) { case VEC_ADDRERR: info.si_code = BUS_ADRALN; sig = SIGBUS; @@ -363,3 +360,16 @@ void show_stack(struct task_struct *task, unsigned long *stack) else __show_stack(task, stack); } + +#ifdef CONFIG_M68KFPU_EMU +asmlinkage void fpemu_signal(int signal, int code, void *addr) +{ + siginfo_t info; + + info.si_signo = signal; + info.si_errno = 0; + info.si_code = code; + info.si_addr = addr; + force_sig_info(signal, &info, current); +} +#endif diff --git a/trunk/arch/m68knommu/platform/5206/Makefile b/trunk/arch/m68knommu/platform/5206/Makefile index b5db05625cfa..113c33390064 100644 --- a/trunk/arch/m68knommu/platform/5206/Makefile +++ b/trunk/arch/m68knommu/platform/5206/Makefile @@ -8,8 +8,8 @@ # on the console port whenever a DBG interrupt occurs. You have to # set up you HW breakpoints to trigger a DBG interrupt: # -# ccflags-y := -DTRAP_DBG_INTERRUPT -# asflags-y := -DTRAP_DBG_INTERRUPT +# EXTRA_CFLAGS += -DTRAP_DBG_INTERRUPT +# EXTRA_AFLAGS += -DTRAP_DBG_INTERRUPT # asflags-$(CONFIG_FULLDEBUG) := -DDEBUGGER_COMPATIBLE_CACHE=1 diff --git a/trunk/arch/m68knommu/platform/5206e/Makefile b/trunk/arch/m68knommu/platform/5206e/Makefile index b5db05625cfa..113c33390064 100644 --- a/trunk/arch/m68knommu/platform/5206e/Makefile +++ b/trunk/arch/m68knommu/platform/5206e/Makefile @@ -8,8 +8,8 @@ # on the console port whenever a DBG interrupt occurs. You have to # set up you HW breakpoints to trigger a DBG interrupt: # -# ccflags-y := -DTRAP_DBG_INTERRUPT -# asflags-y := -DTRAP_DBG_INTERRUPT +# EXTRA_CFLAGS += -DTRAP_DBG_INTERRUPT +# EXTRA_AFLAGS += -DTRAP_DBG_INTERRUPT # asflags-$(CONFIG_FULLDEBUG) := -DDEBUGGER_COMPATIBLE_CACHE=1 diff --git a/trunk/arch/m68knommu/platform/520x/Makefile b/trunk/arch/m68knommu/platform/520x/Makefile index ad3f4e5a57ce..435ab3483dc1 100644 --- a/trunk/arch/m68knommu/platform/520x/Makefile +++ b/trunk/arch/m68knommu/platform/520x/Makefile @@ -8,8 +8,8 @@ # on the console port whenever a DBG interrupt occurs. You have to # set up you HW breakpoints to trigger a DBG interrupt: # -# ccflags-y := -DTRAP_DBG_INTERRUPT -# asflags-y := -DTRAP_DBG_INTERRUPT +# EXTRA_CFLAGS += -DTRAP_DBG_INTERRUPT +# EXTRA_AFLAGS += -DTRAP_DBG_INTERRUPT # asflags-$(CONFIG_FULLDEBUG) := -DDEBUGGER_COMPATIBLE_CACHE=1 diff --git a/trunk/arch/m68knommu/platform/523x/Makefile b/trunk/arch/m68knommu/platform/523x/Makefile index c04b8f71c88c..b8f9b45440c2 100644 --- a/trunk/arch/m68knommu/platform/523x/Makefile +++ b/trunk/arch/m68knommu/platform/523x/Makefile @@ -8,8 +8,8 @@ # on the console port whenever a DBG interrupt occurs. You have to # set up you HW breakpoints to trigger a DBG interrupt: # -# ccflags-y := -DTRAP_DBG_INTERRUPT -# asflags-y := -DTRAP_DBG_INTERRUPT +# EXTRA_CFLAGS += -DTRAP_DBG_INTERRUPT +# EXTRA_AFLAGS += -DTRAP_DBG_INTERRUPT # asflags-$(CONFIG_FULLDEBUG) := -DDEBUGGER_COMPATIBLE_CACHE=1 diff --git a/trunk/arch/m68knommu/platform/5249/Makefile b/trunk/arch/m68knommu/platform/5249/Makefile index 4bed30fd0073..f56225d1582f 100644 --- a/trunk/arch/m68knommu/platform/5249/Makefile +++ b/trunk/arch/m68knommu/platform/5249/Makefile @@ -8,8 +8,8 @@ # on the console port whenever a DBG interrupt occurs. You have to # set up you HW breakpoints to trigger a DBG interrupt: # -# ccflags-y := -DTRAP_DBG_INTERRUPT -# asflags-y := -DTRAP_DBG_INTERRUPT +# EXTRA_CFLAGS += -DTRAP_DBG_INTERRUPT +# EXTRA_AFLAGS += -DTRAP_DBG_INTERRUPT # asflags-$(CONFIG_FULLDEBUG) := -DDEBUGGER_COMPATIBLE_CACHE=1 diff --git a/trunk/arch/m68knommu/platform/5272/Makefile b/trunk/arch/m68knommu/platform/5272/Makefile index 34110fc14301..93673ef8e2c1 100644 --- a/trunk/arch/m68knommu/platform/5272/Makefile +++ b/trunk/arch/m68knommu/platform/5272/Makefile @@ -8,8 +8,8 @@ # on the console port whenever a DBG interrupt occurs. You have to # set up you HW breakpoints to trigger a DBG interrupt: # -# ccflags-y := -DTRAP_DBG_INTERRUPT -# asflags-y := -DTRAP_DBG_INTERRUPT +# EXTRA_CFLAGS += -DTRAP_DBG_INTERRUPT +# EXTRA_AFLAGS += -DTRAP_DBG_INTERRUPT # asflags-$(CONFIG_FULLDEBUG) := -DDEBUGGER_COMPATIBLE_CACHE=1 diff --git a/trunk/arch/m68knommu/platform/5272/config.c b/trunk/arch/m68knommu/platform/5272/config.c index 65bb582734e1..59278c0887d0 100644 --- a/trunk/arch/m68knommu/platform/5272/config.c +++ b/trunk/arch/m68knommu/platform/5272/config.c @@ -13,8 +13,6 @@ #include #include #include -#include -#include #include #include #include @@ -150,23 +148,9 @@ void __init config_BSP(char *commandp, int size) /***************************************************************************/ -/* - * Some 5272 based boards have the FEC ethernet diectly connected to - * an ethernet switch. In this case we need to use the fixed phy type, - * and we need to declare it early in boot. - */ -static struct fixed_phy_status nettel_fixed_phy_status __initdata = { - .link = 1, - .speed = 100, - .duplex = 0, -}; - -/***************************************************************************/ - static int __init init_BSP(void) { m5272_uarts_init(); - fixed_phy_add(PHY_POLL, 0, &nettel_fixed_phy_status); platform_add_devices(m5272_devices, ARRAY_SIZE(m5272_devices)); return 0; } diff --git a/trunk/arch/m68knommu/platform/5272/intc.c b/trunk/arch/m68knommu/platform/5272/intc.c index 3cf681c177aa..7081e0a9720e 100644 --- a/trunk/arch/m68knommu/platform/5272/intc.c +++ b/trunk/arch/m68knommu/platform/5272/intc.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -30,10 +29,6 @@ * via a set of 4 "Interrupt Controller Registers" (ICR). There is a * loose mapping of vector number to register and internal bits, but * a table is the easiest and quickest way to map them. - * - * Note that the external interrupts are edge triggered (unlike the - * internal interrupt sources which are level triggered). Which means - * they also need acknowledgeing via acknowledge bits. */ struct irqmap { unsigned char icr; @@ -73,11 +68,6 @@ static struct irqmap intc_irqmap[MCFINT_VECMAX - MCFINT_VECBASE] = { /*MCF_IRQ_SWTO*/ { .icr = MCFSIM_ICR4, .index = 16, .ack = 0, }, }; -/* - * The act of masking the interrupt also has a side effect of 'ack'ing - * an interrupt on this irq (for the external irqs). So this mask function - * is also an ack_mask function. - */ static void intc_irq_mask(unsigned int irq) { if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX)) { @@ -105,9 +95,7 @@ static void intc_irq_ack(unsigned int irq) irq -= MCFINT_VECBASE; if (intc_irqmap[irq].ack) { u32 v; - v = readl(MCF_MBAR + intc_irqmap[irq].icr); - v &= (0x7 << intc_irqmap[irq].index); - v |= (0x8 << intc_irqmap[irq].index); + v = 0xd << intc_irqmap[irq].index; writel(v, MCF_MBAR + intc_irqmap[irq].icr); } } @@ -115,47 +103,21 @@ static void intc_irq_ack(unsigned int irq) static int intc_irq_set_type(unsigned int irq, unsigned int type) { - if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX)) { - irq -= MCFINT_VECBASE; - if (intc_irqmap[irq].ack) { - u32 v; - v = readl(MCF_MBAR + MCFSIM_PITR); - if (type == IRQ_TYPE_EDGE_FALLING) - v &= ~(0x1 << (32 - irq)); - else - v |= (0x1 << (32 - irq)); - writel(v, MCF_MBAR + MCFSIM_PITR); - } - } + /* We can set the edge type here for external interrupts */ return 0; } -/* - * Simple flow handler to deal with the external edge triggered interrupts. - * We need to be careful with the masking/acking due to the side effects - * of masking an interrupt. - */ -static void intc_external_irq(unsigned int irq, struct irq_desc *desc) -{ - kstat_incr_irqs_this_cpu(irq, desc); - desc->status |= IRQ_INPROGRESS; - desc->chip->ack(irq); - handle_IRQ_event(irq, desc->action); - desc->status &= ~IRQ_INPROGRESS; -} - static struct irq_chip intc_irq_chip = { .name = "CF-INTC", .mask = intc_irq_mask, .unmask = intc_irq_unmask, - .mask_ack = intc_irq_mask, .ack = intc_irq_ack, .set_type = intc_irq_set_type, }; void __init init_IRQ(void) { - int irq, edge; + int irq; init_vectors(); @@ -166,17 +128,11 @@ void __init init_IRQ(void) writel(0x88888888, MCF_MBAR + MCFSIM_ICR4); for (irq = 0; (irq < NR_IRQS); irq++) { - set_irq_chip(irq, &intc_irq_chip); - edge = 0; - if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX)) - edge = intc_irqmap[irq - MCFINT_VECBASE].ack; - if (edge) { - set_irq_type(irq, IRQ_TYPE_EDGE_RISING); - set_irq_handler(irq, intc_external_irq); - } else { - set_irq_type(irq, IRQ_TYPE_LEVEL_HIGH); - set_irq_handler(irq, handle_level_irq); - } + irq_desc[irq].status = IRQ_DISABLED; + irq_desc[irq].action = NULL; + irq_desc[irq].depth = 1; + irq_desc[irq].chip = &intc_irq_chip; + intc_irq_set_type(irq, 0); } } diff --git a/trunk/arch/m68knommu/platform/527x/Makefile b/trunk/arch/m68knommu/platform/527x/Makefile index 6ac4b57370ea..3d90e6d92459 100644 --- a/trunk/arch/m68knommu/platform/527x/Makefile +++ b/trunk/arch/m68knommu/platform/527x/Makefile @@ -8,8 +8,8 @@ # on the console port whenever a DBG interrupt occurs. You have to # set up you HW breakpoints to trigger a DBG interrupt: # -# ccflags-y := -DTRAP_DBG_INTERRUPT -# asflags-y := -DTRAP_DBG_INTERRUPT +# EXTRA_CFLAGS += -DTRAP_DBG_INTERRUPT +# EXTRA_AFLAGS += -DTRAP_DBG_INTERRUPT # asflags-$(CONFIG_FULLDEBUG) := -DDEBUGGER_COMPATIBLE_CACHE=1 diff --git a/trunk/arch/m68knommu/platform/528x/Makefile b/trunk/arch/m68knommu/platform/528x/Makefile index 6ac4b57370ea..3d90e6d92459 100644 --- a/trunk/arch/m68knommu/platform/528x/Makefile +++ b/trunk/arch/m68knommu/platform/528x/Makefile @@ -8,8 +8,8 @@ # on the console port whenever a DBG interrupt occurs. You have to # set up you HW breakpoints to trigger a DBG interrupt: # -# ccflags-y := -DTRAP_DBG_INTERRUPT -# asflags-y := -DTRAP_DBG_INTERRUPT +# EXTRA_CFLAGS += -DTRAP_DBG_INTERRUPT +# EXTRA_AFLAGS += -DTRAP_DBG_INTERRUPT # asflags-$(CONFIG_FULLDEBUG) := -DDEBUGGER_COMPATIBLE_CACHE=1 diff --git a/trunk/arch/m68knommu/platform/5307/Makefile b/trunk/arch/m68knommu/platform/5307/Makefile index d4293b791f2e..6de526976828 100644 --- a/trunk/arch/m68knommu/platform/5307/Makefile +++ b/trunk/arch/m68knommu/platform/5307/Makefile @@ -8,8 +8,8 @@ # on the console port whenever a DBG interrupt occurs. You have to # set up you HW breakpoints to trigger a DBG interrupt: # -# ccflags-y := -DTRAP_DBG_INTERRUPT -# asflags-y := -DTRAP_DBG_INTERRUPT +# EXTRA_CFLAGS += -DTRAP_DBG_INTERRUPT +# EXTRA_AFLAGS += -DTRAP_DBG_INTERRUPT # asflags-$(CONFIG_FULLDEBUG) := -DDEBUGGER_COMPATIBLE_CACHE=1 diff --git a/trunk/arch/m68knommu/platform/532x/Makefile b/trunk/arch/m68knommu/platform/532x/Makefile index ce01669399c6..4cc23245bcd1 100644 --- a/trunk/arch/m68knommu/platform/532x/Makefile +++ b/trunk/arch/m68knommu/platform/532x/Makefile @@ -8,8 +8,8 @@ # on the console port whenever a DBG interrupt occurs. You have to # set up you HW breakpoints to trigger a DBG interrupt: # -# ccflags-y := -DTRAP_DBG_INTERRUPT -# asflags-y := -DTRAP_DBG_INTERRUPT +# EXTRA_CFLAGS += -DTRAP_DBG_INTERRUPT +# EXTRA_AFLAGS += -DTRAP_DBG_INTERRUPT # asflags-$(CONFIG_FULLDEBUG) := -DDEBUGGER_COMPATIBLE_CACHE=1 diff --git a/trunk/arch/m68knommu/platform/5407/Makefile b/trunk/arch/m68knommu/platform/5407/Makefile index e83fe148eddc..dee62c5dbaa6 100644 --- a/trunk/arch/m68knommu/platform/5407/Makefile +++ b/trunk/arch/m68knommu/platform/5407/Makefile @@ -8,8 +8,8 @@ # on the console port whenever a DBG interrupt occurs. You have to # set up you HW breakpoints to trigger a DBG interrupt: # -# ccflags-y := -DTRAP_DBG_INTERRUPT -# asflags-y := -DTRAP_DBG_INTERRUPT +# EXTRA_CFLAGS += -DTRAP_DBG_INTERRUPT +# EXTRA_AFLAGS += -DTRAP_DBG_INTERRUPT # asflags-$(CONFIG_FULLDEBUG) := -DDEBUGGER_COMPATIBLE_CACHE=1 diff --git a/trunk/arch/m68knommu/platform/548x/Makefile b/trunk/arch/m68knommu/platform/548x/Makefile deleted file mode 100644 index e6035e7a2d3f..000000000000 --- a/trunk/arch/m68knommu/platform/548x/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -# -# Makefile for the m68knommu linux kernel. -# - -# -# If you want to play with the HW breakpoints then you will -# need to add define this, which will give you a stack backtrace -# on the console port whenever a DBG interrupt occurs. You have to -# set up you HW breakpoints to trigger a DBG interrupt: -# -# EXTRA_CFLAGS += -DTRAP_DBG_INTERRUPT -# EXTRA_AFLAGS += -DTRAP_DBG_INTERRUPT -# - -asflags-$(CONFIG_FULLDEBUG) := -DDEBUGGER_COMPATIBLE_CACHE=1 - -obj-y := config.o - diff --git a/trunk/arch/m68knommu/platform/548x/config.c b/trunk/arch/m68knommu/platform/548x/config.c deleted file mode 100644 index 9888846bd1cf..000000000000 --- a/trunk/arch/m68knommu/platform/548x/config.c +++ /dev/null @@ -1,115 +0,0 @@ -/***************************************************************************/ - -/* - * linux/arch/m68knommu/platform/548x/config.c - * - * Copyright (C) 2010, Philippe De Muyter - */ - -/***************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/***************************************************************************/ - -static struct mcf_platform_uart m548x_uart_platform[] = { - { - .mapbase = MCF_MBAR + MCFUART_BASE1, - .irq = 64 + 35, - }, - { - .mapbase = MCF_MBAR + MCFUART_BASE2, - .irq = 64 + 34, - }, - { - .mapbase = MCF_MBAR + MCFUART_BASE3, - .irq = 64 + 33, - }, - { - .mapbase = MCF_MBAR + MCFUART_BASE4, - .irq = 64 + 32, - }, -}; - -static struct platform_device m548x_uart = { - .name = "mcfuart", - .id = 0, - .dev.platform_data = m548x_uart_platform, -}; - -static struct platform_device *m548x_devices[] __initdata = { - &m548x_uart, -}; - - -/***************************************************************************/ - -static void __init m548x_uart_init_line(int line, int irq) -{ - int rts_cts; - - /* enable io pins */ - switch (line) { - case 0: - rts_cts = 0; break; - case 1: - rts_cts = MCF_PAR_PSC_RTS_RTS; break; - case 2: - rts_cts = MCF_PAR_PSC_RTS_RTS | MCF_PAR_PSC_CTS_CTS; break; - case 3: - rts_cts = 0; break; - } - __raw_writeb(MCF_PAR_PSC_TXD | rts_cts | MCF_PAR_PSC_RXD, - MCF_MBAR + MCF_PAR_PSC(line)); -} - -static void __init m548x_uarts_init(void) -{ - const int nrlines = ARRAY_SIZE(m548x_uart_platform); - int line; - - for (line = 0; (line < nrlines); line++) - m548x_uart_init_line(line, m548x_uart_platform[line].irq); -} - -/***************************************************************************/ - -static void mcf548x_reset(void) -{ - /* disable interrupts and enable the watchdog */ - asm("movew #0x2700, %sr\n"); - __raw_writel(0, MCF_MBAR + MCF_GPT_GMS0); - __raw_writel(MCF_GPT_GCIR_CNT(1), MCF_MBAR + MCF_GPT_GCIR0); - __raw_writel(MCF_GPT_GMS_WDEN | MCF_GPT_GMS_CE | MCF_GPT_GMS_TMS(4), - MCF_MBAR + MCF_GPT_GMS0); -} - -/***************************************************************************/ - -void __init config_BSP(char *commandp, int size) -{ - mach_reset = mcf548x_reset; - m548x_uarts_init(); -} - -/***************************************************************************/ - -static int __init init_BSP(void) -{ - - platform_add_devices(m548x_devices, ARRAY_SIZE(m548x_devices)); - return 0; -} - -arch_initcall(init_BSP); - -/***************************************************************************/ diff --git a/trunk/arch/m68knommu/platform/68328/entry.S b/trunk/arch/m68knommu/platform/68328/entry.S index 27241e16a526..9d80d2c42866 100644 --- a/trunk/arch/m68knommu/platform/68328/entry.S +++ b/trunk/arch/m68knommu/platform/68328/entry.S @@ -43,10 +43,10 @@ badsys: jra ret_from_exception do_trace: - movel #-ENOSYS,%sp@(PT_OFF_D0) /* needed for strace*/ + movel #-ENOSYS,%sp@(PT_OFF_D0) /* needed for strace*/ subql #4,%sp SAVE_SWITCH_STACK - jbsr syscall_trace_enter + jbsr syscall_trace RESTORE_SWITCH_STACK addql #4,%sp movel %sp@(PT_OFF_ORIG_D0),%d1 @@ -57,10 +57,10 @@ do_trace: lea sys_call_table, %a0 jbsr %a0@(%d1) -1: movel %d0,%sp@(PT_OFF_D0) /* save the return value */ +1: movel %d0,%sp@(PT_OFF_D0) /* save the return value */ subql #4,%sp /* dummy return address */ SAVE_SWITCH_STACK - jbsr syscall_trace_leave + jbsr syscall_trace ret_from_signal: RESTORE_SWITCH_STACK @@ -71,16 +71,16 @@ ENTRY(system_call) SAVE_ALL /* save top of frame*/ - pea %sp@ - jbsr set_esp0 - addql #4,%sp + pea %sp@ + jbsr set_esp0 + addql #4,%sp movel %sp@(PT_OFF_ORIG_D0),%d0 movel %sp,%d1 /* get thread_info pointer */ andl #-THREAD_SIZE,%d1 movel %d1,%a2 - btst #(TIF_SYSCALL_TRACE%8),%a2@(TI_FLAGS+(31-TIF_SYSCALL_TRACE)/8) + btst #TIF_SYSCALL_TRACE,%a2@(TI_FLAGS) jne do_trace cmpl #NR_syscalls,%d0 jcc badsys @@ -88,10 +88,10 @@ ENTRY(system_call) lea sys_call_table,%a0 movel %a0@(%d0), %a0 jbsr %a0@ - movel %d0,%sp@(PT_OFF_D0) /* save the return value*/ + movel %d0,%sp@(PT_OFF_D0) /* save the return value*/ ret_from_exception: - btst #5,%sp@(PT_OFF_SR) /* check if returning to kernel*/ + btst #5,%sp@(PT_OFF_SR) /* check if returning to kernel*/ jeq Luser_return /* if so, skip resched, signals*/ Lkernel_return: @@ -133,7 +133,7 @@ Lreturn: */ inthandler1: SAVE_ALL - movew %sp@(PT_OFF_FORMATVEC), %d0 + movew %sp@(PT_OFF_VECTOR), %d0 and #0x3ff, %d0 movel %sp,%sp@- @@ -144,7 +144,7 @@ inthandler1: inthandler2: SAVE_ALL - movew %sp@(PT_OFF_FORMATVEC), %d0 + movew %sp@(PT_OFF_VECTOR), %d0 and #0x3ff, %d0 movel %sp,%sp@- @@ -155,7 +155,7 @@ inthandler2: inthandler3: SAVE_ALL - movew %sp@(PT_OFF_FORMATVEC), %d0 + movew %sp@(PT_OFF_VECTOR), %d0 and #0x3ff, %d0 movel %sp,%sp@- @@ -166,7 +166,7 @@ inthandler3: inthandler4: SAVE_ALL - movew %sp@(PT_OFF_FORMATVEC), %d0 + movew %sp@(PT_OFF_VECTOR), %d0 and #0x3ff, %d0 movel %sp,%sp@- @@ -177,7 +177,7 @@ inthandler4: inthandler5: SAVE_ALL - movew %sp@(PT_OFF_FORMATVEC), %d0 + movew %sp@(PT_OFF_VECTOR), %d0 and #0x3ff, %d0 movel %sp,%sp@- @@ -188,7 +188,7 @@ inthandler5: inthandler6: SAVE_ALL - movew %sp@(PT_OFF_FORMATVEC), %d0 + movew %sp@(PT_OFF_VECTOR), %d0 and #0x3ff, %d0 movel %sp,%sp@- @@ -199,7 +199,7 @@ inthandler6: inthandler7: SAVE_ALL - movew %sp@(PT_OFF_FORMATVEC), %d0 + movew %sp@(PT_OFF_VECTOR), %d0 and #0x3ff, %d0 movel %sp,%sp@- @@ -210,7 +210,7 @@ inthandler7: inthandler: SAVE_ALL - movew %sp@(PT_OFF_FORMATVEC), %d0 + movew %sp@(PT_OFF_VECTOR), %d0 and #0x3ff, %d0 movel %sp,%sp@- diff --git a/trunk/arch/m68knommu/platform/68328/head-de2.S b/trunk/arch/m68knommu/platform/68328/head-de2.S index f632fdcb93e9..92d96456d363 100644 --- a/trunk/arch/m68knommu/platform/68328/head-de2.S +++ b/trunk/arch/m68knommu/platform/68328/head-de2.S @@ -1,5 +1,11 @@ +#if defined(CONFIG_RAM32MB) +#define MEM_END 0x02000000 /* Memory size 32Mb */ +#elif defined(CONFIG_RAM16MB) +#define MEM_END 0x01000000 /* Memory size 16Mb */ +#else #define MEM_END 0x00800000 /* Memory size 8Mb */ +#endif #undef CRT_DEBUG diff --git a/trunk/arch/m68knommu/platform/68328/head-ram.S b/trunk/arch/m68knommu/platform/68328/head-ram.S index 7f1aeeacb219..252b80b02038 100644 --- a/trunk/arch/m68knommu/platform/68328/head-ram.S +++ b/trunk/arch/m68knommu/platform/68328/head-ram.S @@ -67,6 +67,33 @@ pclp1: beq pclp1 #endif /* DEBUG */ +#ifdef CONFIG_RELOCATE + /* Copy me to RAM */ + moveal #__rom_start, %a0 + moveal #_stext, %a1 + moveal #_edata, %a2 + + /* Copy %a0 to %a1 until %a1 == %a2 */ +LD1: + movel %a0@+, %d0 + movel %d0, %a1@+ + cmpal %a1, %a2 + bhi LD1 + +#ifdef DEBUG + moveq #74, %d7 /* 'J' */ + moveb %d7,0xfffff907 /* No absolute addresses */ +pclp2: + movew 0xfffff906, %d7 + andw #0x2000, %d7 + beq pclp2 +#endif /* DEBUG */ + /* jump into the RAM copy */ + jmp ram_jump +ram_jump: + +#endif /* CONFIG_RELOCATE */ + #ifdef DEBUG moveq #82, %d7 /* 'R' */ moveb %d7,0xfffff907 /* No absolute addresses */ diff --git a/trunk/arch/m68knommu/platform/68328/ints.c b/trunk/arch/m68knommu/platform/68328/ints.c index 865852806a17..b91ee85d4b5d 100644 --- a/trunk/arch/m68knommu/platform/68328/ints.c +++ b/trunk/arch/m68knommu/platform/68328/ints.c @@ -179,8 +179,10 @@ void __init init_IRQ(void) IMR = ~0; for (i = 0; (i < NR_IRQS); i++) { - set_irq_chip(irq, &intc_irq_chip); - set_irq_handler(irq, handle_level_irq); + irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].action = NULL; + irq_desc[i].depth = 1; + irq_desc[i].chip = &intc_irq_chip; } } diff --git a/trunk/arch/m68knommu/platform/68360/entry.S b/trunk/arch/m68knommu/platform/68360/entry.S index c131c6e1d92d..6d3460a39cac 100644 --- a/trunk/arch/m68knommu/platform/68360/entry.S +++ b/trunk/arch/m68knommu/platform/68360/entry.S @@ -42,7 +42,7 @@ do_trace: movel #-ENOSYS,%sp@(PT_OFF_D0) /* needed for strace*/ subql #4,%sp SAVE_SWITCH_STACK - jbsr syscall_trace_enter + jbsr syscall_trace RESTORE_SWITCH_STACK addql #4,%sp movel %sp@(PT_OFF_ORIG_D0),%d1 @@ -56,7 +56,7 @@ do_trace: 1: movel %d0,%sp@(PT_OFF_D0) /* save the return value */ subql #4,%sp /* dummy return address */ SAVE_SWITCH_STACK - jbsr syscall_trace_leave + jbsr syscall_trace ret_from_signal: RESTORE_SWITCH_STACK @@ -71,12 +71,7 @@ ENTRY(system_call) jbsr set_esp0 addql #4,%sp - movel %sp@(PT_OFF_ORIG_D0),%d0 - - movel %sp,%d1 /* get thread_info pointer */ - andl #-THREAD_SIZE,%d1 - movel %d1,%a2 - btst #(TIF_SYSCALL_TRACE%8),%a2@(TI_FLAGS+(31-TIF_SYSCALL_TRACE)/8) + btst #PF_TRACESYS_BIT,%a2@(TASK_FLAGS+PF_TRACESYS_OFF) jne do_trace cmpl #NR_syscalls,%d0 jcc badsys @@ -129,7 +124,7 @@ Lreturn: */ inthandler: SAVE_ALL - movew %sp@(PT_OFF_FORMATVEC), %d0 + movew %sp@(PT_OFF_VECTOR), %d0 and.l #0x3ff, %d0 lsr.l #0x02, %d0 diff --git a/trunk/arch/m68knommu/platform/68360/ints.c b/trunk/arch/m68knommu/platform/68360/ints.c index ad96ab1051f0..6f22970d8c20 100644 --- a/trunk/arch/m68knommu/platform/68360/ints.c +++ b/trunk/arch/m68knommu/platform/68360/ints.c @@ -132,8 +132,10 @@ void init_IRQ(void) pquicc->intr_cimr = 0x00000000; for (i = 0; (i < NR_IRQS); i++) { - set_irq_chip(irq, &intc_irq_chip); - set_irq_handler(irq, handle_level_irq); + irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].action = NULL; + irq_desc[i].depth = 1; + irq_desc[i].chip = &intc_irq_chip; } } diff --git a/trunk/arch/m68knommu/platform/68VZ328/config.c b/trunk/arch/m68knommu/platform/68VZ328/config.c index eabaabe8af36..fc5c63054e98 100644 --- a/trunk/arch/m68knommu/platform/68VZ328/config.c +++ b/trunk/arch/m68knommu/platform/68VZ328/config.c @@ -90,6 +90,11 @@ static void init_hardware(char *command, int size) PDIQEG &= ~PD(1); PDIRQEN |= PD(1); /* IRQ enabled */ +#ifdef CONFIG_68328_SERIAL_UART2 + /* Enable RXD TXD port bits to enable UART2 */ + PJSEL &= ~(PJ(5) | PJ(4)); +#endif + #ifdef CONFIG_INIT_LCD /* initialize LCD controller */ LSSA = (long) screen_bits; diff --git a/trunk/arch/m68knommu/platform/coldfire/Makefile b/trunk/arch/m68knommu/platform/coldfire/Makefile index 45f501fa4525..f72a0e5d9996 100644 --- a/trunk/arch/m68knommu/platform/coldfire/Makefile +++ b/trunk/arch/m68knommu/platform/coldfire/Makefile @@ -8,8 +8,8 @@ # on the console port whenever a DBG interrupt occurs. You have to # set up you HW breakpoints to trigger a DBG interrupt: # -# ccflags-y := -DTRAP_DBG_INTERRUPT -# asflags-y := -DTRAP_DBG_INTERRUPT +# EXTRA_CFLAGS += -DTRAP_DBG_INTERRUPT +# EXTRA_AFLAGS += -DTRAP_DBG_INTERRUPT # asflags-$(CONFIG_FULLDEBUG) := -DDEBUGGER_COMPATIBLE_CACHE=1 @@ -26,7 +26,6 @@ obj-$(CONFIG_M528x) += pit.o intc-2.o obj-$(CONFIG_M5307) += timers.o intc.o obj-$(CONFIG_M532x) += timers.o intc-simr.o obj-$(CONFIG_M5407) += timers.o intc.o -obj-$(CONFIG_M548x) += sltimers.o intc-2.o obj-y += pinmux.o gpio.o extra-y := head.o diff --git a/trunk/arch/m68knommu/platform/coldfire/entry.S b/trunk/arch/m68knommu/platform/coldfire/entry.S index 5e92bed94b7e..cd79d7e92ce6 100644 --- a/trunk/arch/m68knommu/platform/coldfire/entry.S +++ b/trunk/arch/m68knommu/platform/coldfire/entry.S @@ -88,7 +88,7 @@ ENTRY(system_call) movel %d2,PT_OFF_D0(%sp) /* on syscall entry */ subql #4,%sp SAVE_SWITCH_STACK - jbsr syscall_trace_enter + jbsr syscall_trace RESTORE_SWITCH_STACK addql #4,%sp movel %d3,%a0 @@ -96,7 +96,7 @@ ENTRY(system_call) movel %d0,%sp@(PT_OFF_D0) /* save the return value */ subql #4,%sp /* dummy return address */ SAVE_SWITCH_STACK - jbsr syscall_trace_leave + jbsr syscall_trace ret_from_signal: RESTORE_SWITCH_STACK diff --git a/trunk/arch/m68knommu/platform/coldfire/intc-2.c b/trunk/arch/m68knommu/platform/coldfire/intc-2.c index 85daa2b3001a..5598c8b8661f 100644 --- a/trunk/arch/m68knommu/platform/coldfire/intc-2.c +++ b/trunk/arch/m68knommu/platform/coldfire/intc-2.c @@ -1,11 +1,5 @@ /* - * intc-2.c - * - * General interrupt controller code for the many ColdFire cores that use - * interrupt controllers with 63 interrupt sources, organized as 56 fully- - * programmable + 7 fixed-level interrupt sources. This includes the 523x - * family, the 5270, 5271, 5274, 5275, and the 528x family which have two such - * controllers, and the 547x and 548x families which have only one of them. + * intc-1.c * * (C) Copyright 2009, Greg Ungerer * @@ -25,37 +19,21 @@ #include /* - * Bit definitions for the ICR family of registers. - */ -#define MCFSIM_ICR_LEVEL(l) ((l)<<3) /* Level l intr */ -#define MCFSIM_ICR_PRI(p) (p) /* Priority p intr */ - -/* - * Each vector needs a unique priority and level associated with it. + * Each vector needs a unique priority and level asscoiated with it. * We don't really care so much what they are, we don't rely on the - * traditional priority interrupt scheme of the m68k/ColdFire. + * tranditional priority interrupt scheme of the m68k/ColdFire. */ -static u8 intc_intpri = MCFSIM_ICR_LEVEL(6) | MCFSIM_ICR_PRI(6); - -#ifdef MCFICM_INTC1 -#define NR_VECS 128 -#else -#define NR_VECS 64 -#endif +static u8 intc_intpri = 0x36; static void intc_irq_mask(unsigned int irq) { - if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECBASE + NR_VECS)) { + if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECBASE + 128)) { unsigned long imraddr; u32 val, imrbit; irq -= MCFINT_VECBASE; imraddr = MCF_IPSBAR; -#ifdef MCFICM_INTC1 imraddr += (irq & 0x40) ? MCFICM_INTC1 : MCFICM_INTC0; -#else - imraddr += MCFICM_INTC0; -#endif imraddr += (irq & 0x20) ? MCFINTC_IMRH : MCFINTC_IMRL; imrbit = 0x1 << (irq & 0x1f); @@ -66,17 +44,13 @@ static void intc_irq_mask(unsigned int irq) static void intc_irq_unmask(unsigned int irq) { - if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECBASE + NR_VECS)) { + if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECBASE + 128)) { unsigned long intaddr, imraddr, icraddr; u32 val, imrbit; irq -= MCFINT_VECBASE; intaddr = MCF_IPSBAR; -#ifdef MCFICM_INTC1 intaddr += (irq & 0x40) ? MCFICM_INTC1 : MCFICM_INTC0; -#else - intaddr += MCFICM_INTC0; -#endif imraddr = intaddr + ((irq & 0x20) ? MCFINTC_IMRH : MCFINTC_IMRL); icraddr = intaddr + MCFINTC_ICR0 + (irq & 0x3f); imrbit = 0x1 << (irq & 0x1f); @@ -93,16 +67,10 @@ static void intc_irq_unmask(unsigned int irq) } } -static int intc_irq_set_type(unsigned int irq, unsigned int type) -{ - return 0; -} - static struct irq_chip intc_irq_chip = { .name = "CF-INTC", .mask = intc_irq_mask, .unmask = intc_irq_unmask, - .set_type = intc_irq_set_type, }; void __init init_IRQ(void) @@ -113,14 +81,13 @@ void __init init_IRQ(void) /* Mask all interrupt sources */ __raw_writel(0x1, MCF_IPSBAR + MCFICM_INTC0 + MCFINTC_IMRL); -#ifdef MCFICM_INTC1 __raw_writel(0x1, MCF_IPSBAR + MCFICM_INTC1 + MCFINTC_IMRL); -#endif for (irq = 0; (irq < NR_IRQS); irq++) { - set_irq_chip(irq, &intc_irq_chip); - set_irq_type(irq, IRQ_TYPE_LEVEL_HIGH); - set_irq_handler(irq, handle_level_irq); + irq_desc[irq].status = IRQ_DISABLED; + irq_desc[irq].action = NULL; + irq_desc[irq].depth = 1; + irq_desc[irq].chip = &intc_irq_chip; } } diff --git a/trunk/arch/m68knommu/platform/coldfire/intc-simr.c b/trunk/arch/m68knommu/platform/coldfire/intc-simr.c index bb7048636140..1b01e79c2f63 100644 --- a/trunk/arch/m68knommu/platform/coldfire/intc-simr.c +++ b/trunk/arch/m68knommu/platform/coldfire/intc-simr.c @@ -1,8 +1,6 @@ /* * intc-simr.c * - * Interrupt controller code for the ColdFire 5208, 5207 & 532x parts. - * * (C) Copyright 2009, Greg Ungerer * * This file is subject to the terms and conditions of the GNU General Public @@ -70,9 +68,11 @@ void __init init_IRQ(void) __raw_writeb(0xff, MCFINTC1_SIMR); for (irq = 0; (irq < NR_IRQS); irq++) { - set_irq_chip(irq, &intc_irq_chip); - set_irq_type(irq, IRQ_TYPE_LEVEL_HIGH); - set_irq_handler(irq, handle_level_irq); + irq_desc[irq].status = IRQ_DISABLED; + irq_desc[irq].action = NULL; + irq_desc[irq].depth = 1; + irq_desc[irq].chip = &intc_irq_chip; + intc_irq_set_type(irq, 0); } } diff --git a/trunk/arch/m68knommu/platform/coldfire/intc.c b/trunk/arch/m68knommu/platform/coldfire/intc.c index 60d2fcbe182b..a4560c86db71 100644 --- a/trunk/arch/m68knommu/platform/coldfire/intc.c +++ b/trunk/arch/m68knommu/platform/coldfire/intc.c @@ -143,9 +143,11 @@ void __init init_IRQ(void) mcf_maskimr(0xffffffff); for (irq = 0; (irq < NR_IRQS); irq++) { - set_irq_chip(irq, &intc_irq_chip); - set_irq_type(irq, IRQ_TYPE_LEVEL_HIGH); - set_irq_handler(irq, handle_level_irq); + irq_desc[irq].status = IRQ_DISABLED; + irq_desc[irq].action = NULL; + irq_desc[irq].depth = 1; + irq_desc[irq].chip = &intc_irq_chip; + intc_irq_set_type(irq, 0); } } diff --git a/trunk/arch/m68knommu/platform/coldfire/sltimers.c b/trunk/arch/m68knommu/platform/coldfire/sltimers.c deleted file mode 100644 index 0a1b937c3e18..000000000000 --- a/trunk/arch/m68knommu/platform/coldfire/sltimers.c +++ /dev/null @@ -1,145 +0,0 @@ -/***************************************************************************/ - -/* - * sltimers.c -- generic ColdFire slice timer support. - * - * Copyright (C) 2009-2010, Philippe De Muyter - * based on - * timers.c -- generic ColdFire hardware timer support. - * Copyright (C) 1999-2008, Greg Ungerer - */ - -/***************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/***************************************************************************/ - -#ifdef CONFIG_HIGHPROFILE - -/* - * By default use Slice Timer 1 as the profiler clock timer. - */ -#define PA(a) (MCF_MBAR + MCFSLT_TIMER1 + (a)) - -/* - * Choose a reasonably fast profile timer. Make it an odd value to - * try and get good coverage of kernel operations. - */ -#define PROFILEHZ 1013 - -irqreturn_t mcfslt_profile_tick(int irq, void *dummy) -{ - /* Reset Slice Timer 1 */ - __raw_writel(MCFSLT_SSR_BE | MCFSLT_SSR_TE, PA(MCFSLT_SSR)); - if (current->pid) - profile_tick(CPU_PROFILING); - return IRQ_HANDLED; -} - -static struct irqaction mcfslt_profile_irq = { - .name = "profile timer", - .flags = IRQF_DISABLED | IRQF_TIMER, - .handler = mcfslt_profile_tick, -}; - -void mcfslt_profile_init(void) -{ - printk(KERN_INFO "PROFILE: lodging TIMER 1 @ %dHz as profile timer\n", - PROFILEHZ); - - setup_irq(MCF_IRQ_PROFILER, &mcfslt_profile_irq); - - /* Set up TIMER 2 as high speed profile clock */ - __raw_writel(MCF_BUSCLK / PROFILEHZ - 1, PA(MCFSLT_STCNT)); - __raw_writel(MCFSLT_SCR_RUN | MCFSLT_SCR_IEN | MCFSLT_SCR_TEN, - PA(MCFSLT_SCR)); - -} - -#endif /* CONFIG_HIGHPROFILE */ - -/***************************************************************************/ - -/* - * By default use Slice Timer 0 as the system clock timer. - */ -#define TA(a) (MCF_MBAR + MCFSLT_TIMER0 + (a)) - -static u32 mcfslt_cycles_per_jiffy; -static u32 mcfslt_cnt; - -static irqreturn_t mcfslt_tick(int irq, void *dummy) -{ - /* Reset Slice Timer 0 */ - __raw_writel(MCFSLT_SSR_BE | MCFSLT_SSR_TE, TA(MCFSLT_SSR)); - mcfslt_cnt += mcfslt_cycles_per_jiffy; - return arch_timer_interrupt(irq, dummy); -} - -static struct irqaction mcfslt_timer_irq = { - .name = "timer", - .flags = IRQF_DISABLED | IRQF_TIMER, - .handler = mcfslt_tick, -}; - -static cycle_t mcfslt_read_clk(struct clocksource *cs) -{ - unsigned long flags; - u32 cycles; - u16 scnt; - - local_irq_save(flags); - scnt = __raw_readl(TA(MCFSLT_SCNT)); - cycles = mcfslt_cnt; - local_irq_restore(flags); - - /* substract because slice timers count down */ - return cycles - scnt; -} - -static struct clocksource mcfslt_clk = { - .name = "slt", - .rating = 250, - .read = mcfslt_read_clk, - .shift = 20, - .mask = CLOCKSOURCE_MASK(32), - .flags = CLOCK_SOURCE_IS_CONTINUOUS, -}; - -void hw_timer_init(void) -{ - mcfslt_cycles_per_jiffy = MCF_BUSCLK / HZ; - /* - * The coldfire slice timer (SLT) runs from STCNT to 0 included, - * then STCNT again and so on. It counts thus actually - * STCNT + 1 steps for 1 tick, not STCNT. So if you want - * n cycles, initialize STCNT with n - 1. - */ - __raw_writel(mcfslt_cycles_per_jiffy - 1, TA(MCFSLT_STCNT)); - __raw_writel(MCFSLT_SCR_RUN | MCFSLT_SCR_IEN | MCFSLT_SCR_TEN, - TA(MCFSLT_SCR)); - /* initialize mcfslt_cnt knowing that slice timers count down */ - mcfslt_cnt = mcfslt_cycles_per_jiffy; - - setup_irq(MCF_IRQ_TIMER, &mcfslt_timer_irq); - - mcfslt_clk.mult = clocksource_hz2mult(MCF_BUSCLK, mcfslt_clk.shift); - clocksource_register(&mcfslt_clk); - -#ifdef CONFIG_HIGHPROFILE - mcfslt_profile_init(); -#endif -} diff --git a/trunk/arch/microblaze/kernel/prom.c b/trunk/arch/microblaze/kernel/prom.c index bacbd3d41ec7..427b13b4740f 100644 --- a/trunk/arch/microblaze/kernel/prom.c +++ b/trunk/arch/microblaze/kernel/prom.c @@ -42,6 +42,11 @@ #include #include +void __init early_init_dt_scan_chosen_arch(unsigned long node) +{ + /* No Microblaze specific code here */ +} + void __init early_init_dt_add_memory_arch(u64 base, u64 size) { memblock_add(base, size); diff --git a/trunk/arch/mips/Kconfig b/trunk/arch/mips/Kconfig index 46cae2b163e4..784cf822963a 100644 --- a/trunk/arch/mips/Kconfig +++ b/trunk/arch/mips/Kconfig @@ -2128,13 +2128,6 @@ config SECCOMP If unsure, say Y. Only embedded should say N here. -config USE_OF - bool "Flattened Device Tree support" - select OF - select OF_FLATTREE - help - Include support for flattened device tree machine descriptions. - endmenu config LOCKDEP_SUPPORT diff --git a/trunk/arch/mips/alchemy/devboards/db1200/platform.c b/trunk/arch/mips/alchemy/devboards/db1200/platform.c index fbb55935b99e..3fa34c3abc04 100644 --- a/trunk/arch/mips/alchemy/devboards/db1200/platform.c +++ b/trunk/arch/mips/alchemy/devboards/db1200/platform.c @@ -429,11 +429,6 @@ static struct platform_device db1200_audio_dev = { .resource = au1200_psc1_res, }; -static struct platform_device db1200_stac_dev = { - .name = "ac97-codec", - .id = 1, /* on PSC1 */ -}; - static struct platform_device *db1200_devs[] __initdata = { NULL, /* PSC0, selected by S6.8 */ &db1200_ide_dev, @@ -441,7 +436,6 @@ static struct platform_device *db1200_devs[] __initdata = { &db1200_rtc_dev, &db1200_nand_dev, &db1200_audio_dev, - &db1200_stac_dev, }; static int __init db1200_dev_init(void) diff --git a/trunk/arch/mips/include/asm/irq.h b/trunk/arch/mips/include/asm/irq.h index b003ed52ed17..dea4aed6478f 100644 --- a/trunk/arch/mips/include/asm/irq.h +++ b/trunk/arch/mips/include/asm/irq.h @@ -16,11 +16,6 @@ #include -static inline void irq_dispose_mapping(unsigned int virq) -{ - return; -} - #ifdef CONFIG_I8259 static inline int irq_canonicalize(int irq) { diff --git a/trunk/arch/mips/include/asm/prom.h b/trunk/arch/mips/include/asm/prom.h deleted file mode 100644 index f29b862d9db3..000000000000 --- a/trunk/arch/mips/include/asm/prom.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * arch/mips/include/asm/prom.h - * - * Copyright (C) 2010 Cisco Systems Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ -#ifndef __ASM_MIPS_PROM_H -#define __ASM_MIPS_PROM_H - -#ifdef CONFIG_OF -#include - -/* which is compatible with the flattened device tree (FDT) */ -#define cmd_line arcs_cmdline - -extern int early_init_dt_scan_memory_arch(unsigned long node, - const char *uname, int depth, void *data); - -extern int reserve_mem_mach(unsigned long addr, unsigned long size); -extern void free_mem_mach(unsigned long addr, unsigned long size); - -extern void device_tree_init(void); -#else /* CONFIG_OF */ -static inline void device_tree_init(void) { } -#endif /* CONFIG_OF */ - -#endif /* _ASM_MIPS_PROM_H */ diff --git a/trunk/arch/mips/kernel/Makefile b/trunk/arch/mips/kernel/Makefile index 80884983270d..06f848299785 100644 --- a/trunk/arch/mips/kernel/Makefile +++ b/trunk/arch/mips/kernel/Makefile @@ -96,8 +96,6 @@ obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o obj-$(CONFIG_EARLY_PRINTK) += early_printk.o obj-$(CONFIG_SPINLOCK_TEST) += spinlock_test.o -obj-$(CONFIG_OF) += prom.o - CFLAGS_cpu-bugs64.o = $(shell if $(CC) $(KBUILD_CFLAGS) -Wa,-mdaddi -c -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-DHAVE_AS_SET_DADDI"; fi) obj-$(CONFIG_HAVE_STD_PC_SERIAL_PORT) += 8250-platform.o diff --git a/trunk/arch/mips/kernel/mips-mt-fpaff.c b/trunk/arch/mips/kernel/mips-mt-fpaff.c index 802e6160f37e..9a526ba6f257 100644 --- a/trunk/arch/mips/kernel/mips-mt-fpaff.c +++ b/trunk/arch/mips/kernel/mips-mt-fpaff.c @@ -103,7 +103,7 @@ asmlinkage long mipsmt_sys_sched_setaffinity(pid_t pid, unsigned int len, if (!check_same_owner(p) && !capable(CAP_SYS_NICE)) goto out_unlock; - retval = security_task_setscheduler(p); + retval = security_task_setscheduler(p) if (retval) goto out_unlock; diff --git a/trunk/arch/mips/kernel/prom.c b/trunk/arch/mips/kernel/prom.c deleted file mode 100644 index e000b278f024..000000000000 --- a/trunk/arch/mips/kernel/prom.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * MIPS support for CONFIG_OF device tree support - * - * Copyright (C) 2010 Cisco Systems Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -int __init early_init_dt_scan_memory_arch(unsigned long node, - const char *uname, int depth, - void *data) -{ - return early_init_dt_scan_memory(node, uname, depth, data); -} - -void __init early_init_dt_add_memory_arch(u64 base, u64 size) -{ - return add_memory_region(base, size, BOOT_MEM_RAM); -} - -int __init reserve_mem_mach(unsigned long addr, unsigned long size) -{ - return reserve_bootmem(addr, size, BOOTMEM_DEFAULT); -} - -void __init free_mem_mach(unsigned long addr, unsigned long size) -{ - return free_bootmem(addr, size); -} - -u64 __init early_init_dt_alloc_memory_arch(u64 size, u64 align) -{ - return virt_to_phys( - __alloc_bootmem(size, align, __pa(MAX_DMA_ADDRESS)) - ); -} - -#ifdef CONFIG_BLK_DEV_INITRD -void __init early_init_dt_setup_initrd_arch(unsigned long start, - unsigned long end) -{ - initrd_start = (unsigned long)__va(start); - initrd_end = (unsigned long)__va(end); - initrd_below_start_ok = 1; -} -#endif - -/* - * irq_create_of_mapping - Hook to resolve OF irq specifier into a Linux irq# - * - * Currently the mapping mechanism is trivial; simple flat hwirq numbers are - * mapped 1:1 onto Linux irq numbers. Cascaded irq controllers are not - * supported. - */ -unsigned int irq_create_of_mapping(struct device_node *controller, - const u32 *intspec, unsigned int intsize) -{ - return intspec[0]; -} -EXPORT_SYMBOL_GPL(irq_create_of_mapping); - -void __init early_init_devtree(void *params) -{ - /* Setup flat device-tree pointer */ - initial_boot_params = params; - - /* Retrieve various informations from the /chosen node of the - * device-tree, including the platform type, initrd location and - * size, and more ... - */ - of_scan_flat_dt(early_init_dt_scan_chosen, NULL); - - /* Scan memory nodes */ - of_scan_flat_dt(early_init_dt_scan_root, NULL); - of_scan_flat_dt(early_init_dt_scan_memory_arch, NULL); -} - -void __init device_tree_init(void) -{ - unsigned long base, size; - - if (!initial_boot_params) - return; - - base = virt_to_phys((void *)initial_boot_params); - size = initial_boot_params->totalsize; - - /* Before we do anything, lets reserve the dt blob */ - reserve_mem_mach(base, size); - - unflatten_device_tree(); - - /* free the space reserved for the dt blob */ - free_mem_mach(base, size); -} diff --git a/trunk/arch/mips/kernel/setup.c b/trunk/arch/mips/kernel/setup.c index a6b900f2962b..85aef3fc6716 100644 --- a/trunk/arch/mips/kernel/setup.c +++ b/trunk/arch/mips/kernel/setup.c @@ -31,7 +31,6 @@ #include #include #include -#include struct cpuinfo_mips cpu_data[NR_CPUS] __read_mostly; @@ -488,7 +487,6 @@ static void __init arch_mem_init(char **cmdline_p) } bootmem_init(); - device_tree_init(); sparse_init(); paging_init(); } diff --git a/trunk/arch/powerpc/boot/dts/mpc8610_hpcd.dts b/trunk/arch/powerpc/boot/dts/mpc8610_hpcd.dts index 83c3218cb4da..9535ce68caae 100644 --- a/trunk/arch/powerpc/boot/dts/mpc8610_hpcd.dts +++ b/trunk/arch/powerpc/boot/dts/mpc8610_hpcd.dts @@ -286,7 +286,6 @@ ssi@16100 { compatible = "fsl,mpc8610-ssi"; - status = "disabled"; cell-index = <1>; reg = <0x16100 0x100>; interrupt-parent = <&mpic>; diff --git a/trunk/arch/powerpc/configs/mpc85xx_defconfig b/trunk/arch/powerpc/configs/mpc85xx_defconfig index 3aeb5949cfef..c3b113b2ca31 100644 --- a/trunk/arch/powerpc/configs/mpc85xx_defconfig +++ b/trunk/arch/powerpc/configs/mpc85xx_defconfig @@ -124,9 +124,6 @@ CONFIG_I2C_CPM=m CONFIG_I2C_MPC=y # CONFIG_HWMON is not set CONFIG_VIDEO_OUTPUT_CONTROL=y -CONFIG_FB=y -CONFIG_FB_FSL_DIU=y -# CONFIG_VGA_CONSOLE is not set CONFIG_SOUND=y CONFIG_SND=y # CONFIG_SND_SUPPORT_OLD_API is not set diff --git a/trunk/arch/powerpc/configs/mpc85xx_smp_defconfig b/trunk/arch/powerpc/configs/mpc85xx_smp_defconfig index d62c8016f4bc..a075da2ea3fb 100644 --- a/trunk/arch/powerpc/configs/mpc85xx_smp_defconfig +++ b/trunk/arch/powerpc/configs/mpc85xx_smp_defconfig @@ -126,9 +126,6 @@ CONFIG_I2C_CPM=m CONFIG_I2C_MPC=y # CONFIG_HWMON is not set CONFIG_VIDEO_OUTPUT_CONTROL=y -CONFIG_FB=y -CONFIG_FB_FSL_DIU=y -# CONFIG_VGA_CONSOLE is not set CONFIG_SOUND=y CONFIG_SND=y # CONFIG_SND_SUPPORT_OLD_API is not set diff --git a/trunk/arch/powerpc/include/asm/fsl_guts.h b/trunk/arch/powerpc/include/asm/immap_86xx.h similarity index 66% rename from trunk/arch/powerpc/include/asm/fsl_guts.h rename to trunk/arch/powerpc/include/asm/immap_86xx.h index bebd12463ec9..0f165e59c326 100644 --- a/trunk/arch/powerpc/include/asm/fsl_guts.h +++ b/trunk/arch/powerpc/include/asm/immap_86xx.h @@ -1,5 +1,5 @@ /** - * Freecale 85xx and 86xx Global Utilties register set + * MPC86xx Internal Memory Map * * Authors: Jeff Brown * Timur Tabi @@ -10,112 +10,73 @@ * 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 header file defines structures for various 86xx SOC devices that are + * used by multiple source files. */ -#ifndef __ASM_POWERPC_FSL_GUTS_H__ -#define __ASM_POWERPC_FSL_GUTS_H__ +#ifndef __ASM_POWERPC_IMMAP_86XX_H__ +#define __ASM_POWERPC_IMMAP_86XX_H__ #ifdef __KERNEL__ -/* - * These #ifdefs are safe because it's not possible to build a kernel that - * runs on e500 and e600 cores. - */ - -#if !defined(CONFIG_PPC_85xx) && !defined(CONFIG_PPC_86xx) -#error Only 85xx and 86xx SOCs are supported -#endif - -/** - * Global Utility Registers. - * - * Not all registers defined in this structure are available on all chips, so - * you are expected to know whether a given register actually exists on your - * chip before you access it. - * - * Also, some registers are similar on different chips but have slightly - * different names. In these cases, one name is chosen to avoid extraneous - * #ifdefs. - */ -#ifdef CONFIG_PPC_85xx -struct ccsr_guts_85xx { -#else -struct ccsr_guts_86xx { -#endif +/* Global Utility Registers */ +struct ccsr_guts { __be32 porpllsr; /* 0x.0000 - POR PLL Ratio Status Register */ __be32 porbmsr; /* 0x.0004 - POR Boot Mode Status Register */ __be32 porimpscr; /* 0x.0008 - POR I/O Impedance Status and Control Register */ __be32 pordevsr; /* 0x.000c - POR I/O Device Status Register */ __be32 pordbgmsr; /* 0x.0010 - POR Debug Mode Status Register */ - __be32 pordevsr2; /* 0x.0014 - POR device status register 2 */ - u8 res018[0x20 - 0x18]; + u8 res1[0x20 - 0x14]; __be32 porcir; /* 0x.0020 - POR Configuration Information Register */ - u8 res024[0x30 - 0x24]; + u8 res2[0x30 - 0x24]; __be32 gpiocr; /* 0x.0030 - GPIO Control Register */ - u8 res034[0x40 - 0x34]; + u8 res3[0x40 - 0x34]; __be32 gpoutdr; /* 0x.0040 - General-Purpose Output Data Register */ - u8 res044[0x50 - 0x44]; + u8 res4[0x50 - 0x44]; __be32 gpindr; /* 0x.0050 - General-Purpose Input Data Register */ - u8 res054[0x60 - 0x54]; + u8 res5[0x60 - 0x54]; __be32 pmuxcr; /* 0x.0060 - Alternate Function Signal Multiplex Control */ - __be32 pmuxcr2; /* 0x.0064 - Alternate function signal multiplex control 2 */ - __be32 dmuxcr; /* 0x.0068 - DMA Mux Control Register */ - u8 res06c[0x70 - 0x6c]; + u8 res6[0x70 - 0x64]; __be32 devdisr; /* 0x.0070 - Device Disable Control */ __be32 devdisr2; /* 0x.0074 - Device Disable Control 2 */ - u8 res078[0x7c - 0x78]; - __be32 pmjcr; /* 0x.007c - 4 Power Management Jog Control Register */ + u8 res7[0x80 - 0x78]; __be32 powmgtcsr; /* 0x.0080 - Power Management Status and Control Register */ - __be32 pmrccr; /* 0x.0084 - Power Management Reset Counter Configuration Register */ - __be32 pmpdccr; /* 0x.0088 - Power Management Power Down Counter Configuration Register */ - __be32 pmcdr; /* 0x.008c - 4Power management clock disable register */ + u8 res8[0x90 - 0x84]; __be32 mcpsumr; /* 0x.0090 - Machine Check Summary Register */ __be32 rstrscr; /* 0x.0094 - Reset Request Status and Control Register */ - __be32 ectrstcr; /* 0x.0098 - Exception reset control register */ - __be32 autorstsr; /* 0x.009c - Automatic reset status register */ + u8 res9[0xA0 - 0x98]; __be32 pvr; /* 0x.00a0 - Processor Version Register */ __be32 svr; /* 0x.00a4 - System Version Register */ - u8 res0a8[0xb0 - 0xa8]; + u8 res10[0xB0 - 0xA8]; __be32 rstcr; /* 0x.00b0 - Reset Control Register */ - u8 res0b4[0xc0 - 0xb4]; -#ifdef CONFIG_PPC_85xx - __be32 iovselsr; /* 0x.00c0 - I/O voltage select status register */ -#else + u8 res11[0xC0 - 0xB4]; __be32 elbcvselcr; /* 0x.00c0 - eLBC Voltage Select Ctrl Reg */ -#endif - u8 res0c4[0x224 - 0xc4]; - __be32 iodelay1; /* 0x.0224 - IO delay control register 1 */ - __be32 iodelay2; /* 0x.0228 - IO delay control register 2 */ - u8 res22c[0x800 - 0x22c]; + u8 res12[0x800 - 0xC4]; __be32 clkdvdr; /* 0x.0800 - Clock Divide Register */ - u8 res804[0x900 - 0x804]; + u8 res13[0x900 - 0x804]; __be32 ircr; /* 0x.0900 - Infrared Control Register */ - u8 res904[0x908 - 0x904]; + u8 res14[0x908 - 0x904]; __be32 dmacr; /* 0x.0908 - DMA Control Register */ - u8 res90c[0x914 - 0x90c]; + u8 res15[0x914 - 0x90C]; __be32 elbccr; /* 0x.0914 - eLBC Control Register */ - u8 res918[0xb20 - 0x918]; + u8 res16[0xB20 - 0x918]; __be32 ddr1clkdr; /* 0x.0b20 - DDR1 Clock Disable Register */ __be32 ddr2clkdr; /* 0x.0b24 - DDR2 Clock Disable Register */ __be32 ddrclkdr; /* 0x.0b28 - DDR Clock Disable Register */ - u8 resb2c[0xe00 - 0xb2c]; + u8 res17[0xE00 - 0xB2C]; __be32 clkocr; /* 0x.0e00 - Clock Out Select Register */ - u8 rese04[0xe10 - 0xe04]; + u8 res18[0xE10 - 0xE04]; __be32 ddrdllcr; /* 0x.0e10 - DDR DLL Control Register */ - u8 rese14[0xe20 - 0xe14]; + u8 res19[0xE20 - 0xE14]; __be32 lbcdllcr; /* 0x.0e20 - LBC DLL Control Register */ - __be32 cpfor; /* 0x.0e24 - L2 charge pump fuse override register */ - u8 rese28[0xf04 - 0xe28]; + u8 res20[0xF04 - 0xE24]; __be32 srds1cr0; /* 0x.0f04 - SerDes1 Control Register 0 */ __be32 srds1cr1; /* 0x.0f08 - SerDes1 Control Register 0 */ - u8 resf0c[0xf2c - 0xf0c]; - __be32 itcr; /* 0x.0f2c - Internal transaction control register */ - u8 resf30[0xf40 - 0xf30]; - __be32 srds2cr0; /* 0x.0f40 - SerDes2 Control Register 0 */ - __be32 srds2cr1; /* 0x.0f44 - SerDes2 Control Register 0 */ + u8 res21[0xF40 - 0xF0C]; + __be32 srds2cr0; /* 0x.0f40 - SerDes1 Control Register 0 */ + __be32 srds2cr1; /* 0x.0f44 - SerDes1 Control Register 0 */ } __attribute__ ((packed)); -#ifdef CONFIG_PPC_86xx - #define CCSR_GUTS_DMACR_DEV_SSI 0 /* DMA controller/channel set to SSI */ #define CCSR_GUTS_DMACR_DEV_IR 1 /* DMA controller/channel set to IR */ @@ -132,7 +93,7 @@ struct ccsr_guts_86xx { * ch: The channel on the DMA controller (0, 1, 2, or 3) * device: The device to set as the source (CCSR_GUTS_DMACR_DEV_xx) */ -static inline void guts_set_dmacr(struct ccsr_guts_86xx __iomem *guts, +static inline void guts_set_dmacr(struct ccsr_guts __iomem *guts, unsigned int co, unsigned int ch, unsigned int device) { unsigned int shift = 16 + (8 * (1 - co) + 2 * (3 - ch)); @@ -168,7 +129,7 @@ static inline void guts_set_dmacr(struct ccsr_guts_86xx __iomem *guts, * ch: The channel on the DMA controller (0, 1, 2, or 3) * value: the new value for the bit (0 or 1) */ -static inline void guts_set_pmuxcr_dma(struct ccsr_guts_86xx __iomem *guts, +static inline void guts_set_pmuxcr_dma(struct ccsr_guts __iomem *guts, unsigned int co, unsigned int ch, unsigned int value) { if ((ch == 0) || (ch == 3)) { @@ -191,7 +152,5 @@ static inline void guts_set_pmuxcr_dma(struct ccsr_guts_86xx __iomem *guts, #define CCSR_GUTS_CLKDVDR_SSICLK_MASK 0x000000FF #define CCSR_GUTS_CLKDVDR_SSICLK(x) ((x) & CCSR_GUTS_CLKDVDR_SSICLK_MASK) -#endif - -#endif -#endif +#endif /* __ASM_POWERPC_IMMAP_86XX_H__ */ +#endif /* __KERNEL__ */ diff --git a/trunk/arch/powerpc/kernel/ibmebus.c b/trunk/arch/powerpc/kernel/ibmebus.c index f62efdfd1769..9b626cfffce1 100644 --- a/trunk/arch/powerpc/kernel/ibmebus.c +++ b/trunk/arch/powerpc/kernel/ibmebus.c @@ -162,10 +162,13 @@ static int ibmebus_create_device(struct device_node *dn) dev->dev.bus = &ibmebus_bus_type; dev->dev.archdata.dma_ops = &ibmebus_dma_ops; - ret = of_device_add(dev); - if (ret) - platform_device_put(dev); - return ret; + ret = of_device_register(dev); + if (ret) { + of_device_free(dev); + return ret; + } + + return 0; } static int ibmebus_create_devices(const struct of_device_id *matches) diff --git a/trunk/arch/powerpc/kernel/legacy_serial.c b/trunk/arch/powerpc/kernel/legacy_serial.c index c834757bebc0..c1fd0f9658fd 100644 --- a/trunk/arch/powerpc/kernel/legacy_serial.c +++ b/trunk/arch/powerpc/kernel/legacy_serial.c @@ -52,14 +52,14 @@ static int __init add_legacy_port(struct device_node *np, int want_index, phys_addr_t taddr, unsigned long irq, upf_t flags, int irq_check_parent) { - const __be32 *clk, *spd; + const u32 *clk, *spd; u32 clock = BASE_BAUD * 16; int index; /* get clock freq. if present */ clk = of_get_property(np, "clock-frequency", NULL); if (clk && *clk) - clock = be32_to_cpup(clk); + clock = *clk; /* get default speed if present */ spd = of_get_property(np, "current-speed", NULL); @@ -109,7 +109,7 @@ static int __init add_legacy_port(struct device_node *np, int want_index, legacy_serial_infos[index].taddr = taddr; legacy_serial_infos[index].np = of_node_get(np); legacy_serial_infos[index].clock = clock; - legacy_serial_infos[index].speed = spd ? be32_to_cpup(spd) : 0; + legacy_serial_infos[index].speed = spd ? *spd : 0; legacy_serial_infos[index].irq_check_parent = irq_check_parent; printk(KERN_DEBUG "Found legacy serial port %d for %s\n", @@ -168,7 +168,7 @@ static int __init add_legacy_soc_port(struct device_node *np, static int __init add_legacy_isa_port(struct device_node *np, struct device_node *isa_brg) { - const __be32 *reg; + const u32 *reg; const char *typep; int index = -1; u64 taddr; @@ -181,7 +181,7 @@ static int __init add_legacy_isa_port(struct device_node *np, return -1; /* Verify it's an IO port, we don't support anything else */ - if (!(be32_to_cpu(reg[0]) & 0x00000001)) + if (!(reg[0] & 0x00000001)) return -1; /* Now look for an "ibm,aix-loc" property that gives us ordering @@ -202,7 +202,7 @@ static int __init add_legacy_isa_port(struct device_node *np, taddr = 0; /* Add port, irq will be dealt with later */ - return add_legacy_port(np, index, UPIO_PORT, be32_to_cpu(reg[1]), taddr, + return add_legacy_port(np, index, UPIO_PORT, reg[1], taddr, NO_IRQ, UPF_BOOT_AUTOCONF, 0); } @@ -251,9 +251,9 @@ static int __init add_legacy_pci_port(struct device_node *np, * we get to their "reg" property */ if (np != pci_dev) { - const __be32 *reg = of_get_property(np, "reg", NULL); - if (reg && (be32_to_cpup(reg) < 4)) - index = lindex = be32_to_cpup(reg); + const u32 *reg = of_get_property(np, "reg", NULL); + if (reg && (*reg < 4)) + index = lindex = *reg; } /* Local index means it's the Nth port in the PCI chip. Unfortunately @@ -507,7 +507,7 @@ static int __init check_legacy_serial_console(void) struct device_node *prom_stdout = NULL; int i, speed = 0, offset = 0; const char *name; - const __be32 *spd; + const u32 *spd; DBG(" -> check_legacy_serial_console()\n"); @@ -547,7 +547,7 @@ static int __init check_legacy_serial_console(void) } spd = of_get_property(prom_stdout, "current-speed", NULL); if (spd) - speed = be32_to_cpup(spd); + speed = *spd; if (strcmp(name, "serial") != 0) goto not_found; diff --git a/trunk/arch/powerpc/kernel/prom.c b/trunk/arch/powerpc/kernel/prom.c index 9e3132db718b..c3c6a8857544 100644 --- a/trunk/arch/powerpc/kernel/prom.c +++ b/trunk/arch/powerpc/kernel/prom.c @@ -364,15 +364,10 @@ static int __init early_init_dt_scan_cpus(unsigned long node, return 0; } -int __init early_init_dt_scan_chosen_ppc(unsigned long node, const char *uname, - int depth, void *data) +void __init early_init_dt_scan_chosen_arch(unsigned long node) { unsigned long *lprop; - /* Use common scan routine to determine if this is the chosen node */ - if (early_init_dt_scan_chosen(node, uname, depth, data) == 0) - return 0; - #ifdef CONFIG_PPC64 /* check if iommu is forced on or off */ if (of_get_flat_dt_prop(node, "linux,iommu-off", NULL) != NULL) @@ -404,9 +399,6 @@ int __init early_init_dt_scan_chosen_ppc(unsigned long node, const char *uname, if (lprop) crashk_res.end = crashk_res.start + *lprop - 1; #endif - - /* break now */ - return 1; } #ifdef CONFIG_PPC_PSERIES @@ -691,7 +683,7 @@ void __init early_init_devtree(void *params) * device-tree, including the platform type, initrd location and * size, TCE reserve, and more ... */ - of_scan_flat_dt(early_init_dt_scan_chosen_ppc, NULL); + of_scan_flat_dt(early_init_dt_scan_chosen, NULL); /* Scan memory nodes and rebuild MEMBLOCKs */ memblock_init(); diff --git a/trunk/arch/powerpc/platforms/85xx/p1022_ds.c b/trunk/arch/powerpc/platforms/85xx/p1022_ds.c index 7eb5c40c069f..2b390d19a1d1 100644 --- a/trunk/arch/powerpc/platforms/85xx/p1022_ds.c +++ b/trunk/arch/powerpc/platforms/85xx/p1022_ds.c @@ -8,6 +8,7 @@ * Copyright 2010 Freescale Semiconductor, Inc. * * This file is taken from the Freescale P1022DS BSP, with modifications: + * 1) No DIU support (pending rewrite of DIU code) * 2) No AMP support * 3) No PCI endpoint support * @@ -19,211 +20,12 @@ #include #include #include -#include + #include #include #include #include -#include - -#if defined(CONFIG_FB_FSL_DIU) || defined(CONFIG_FB_FSL_DIU_MODULE) - -/* - * Board-specific initialization of the DIU. This code should probably be - * executed when the DIU is opened, rather than in arch code, but the DIU - * driver does not have a mechanism for this (yet). - * - * This is especially problematic on the P1022DS because the local bus (eLBC) - * and the DIU video signals share the same pins, which means that enabling the - * DIU will disable access to NOR flash. - */ - -/* DIU Pixel Clock bits of the CLKDVDR Global Utilities register */ -#define CLKDVDR_PXCKEN 0x80000000 -#define CLKDVDR_PXCKINV 0x10000000 -#define CLKDVDR_PXCKDLY 0x06000000 -#define CLKDVDR_PXCLK_MASK 0x00FF0000 - -/* Some ngPIXIS register definitions */ -#define PX_BRDCFG1_DVIEN 0x80 -#define PX_BRDCFG1_DFPEN 0x40 -#define PX_BRDCFG1_BACKLIGHT 0x20 -#define PX_BRDCFG1_DDCEN 0x10 - -/* - * DIU Area Descriptor - * - * Note that we need to byte-swap the value before it's written to the AD - * register. So even though the registers don't look like they're in the same - * bit positions as they are on the MPC8610, the same value is written to the - * AD register on the MPC8610 and on the P1022. - */ -#define AD_BYTE_F 0x10000000 -#define AD_ALPHA_C_MASK 0x0E000000 -#define AD_ALPHA_C_SHIFT 25 -#define AD_BLUE_C_MASK 0x01800000 -#define AD_BLUE_C_SHIFT 23 -#define AD_GREEN_C_MASK 0x00600000 -#define AD_GREEN_C_SHIFT 21 -#define AD_RED_C_MASK 0x00180000 -#define AD_RED_C_SHIFT 19 -#define AD_PALETTE 0x00040000 -#define AD_PIXEL_S_MASK 0x00030000 -#define AD_PIXEL_S_SHIFT 16 -#define AD_COMP_3_MASK 0x0000F000 -#define AD_COMP_3_SHIFT 12 -#define AD_COMP_2_MASK 0x00000F00 -#define AD_COMP_2_SHIFT 8 -#define AD_COMP_1_MASK 0x000000F0 -#define AD_COMP_1_SHIFT 4 -#define AD_COMP_0_MASK 0x0000000F -#define AD_COMP_0_SHIFT 0 - -#define MAKE_AD(alpha, red, blue, green, size, c0, c1, c2, c3) \ - cpu_to_le32(AD_BYTE_F | (alpha << AD_ALPHA_C_SHIFT) | \ - (blue << AD_BLUE_C_SHIFT) | (green << AD_GREEN_C_SHIFT) | \ - (red << AD_RED_C_SHIFT) | (c3 << AD_COMP_3_SHIFT) | \ - (c2 << AD_COMP_2_SHIFT) | (c1 << AD_COMP_1_SHIFT) | \ - (c0 << AD_COMP_0_SHIFT) | (size << AD_PIXEL_S_SHIFT)) - -/** - * p1022ds_get_pixel_format: return the Area Descriptor for a given pixel depth - * - * The Area Descriptor is a 32-bit value that determine which bits in each - * pixel are to be used for each color. - */ -static unsigned int p1022ds_get_pixel_format(unsigned int bits_per_pixel, - int monitor_port) -{ - switch (bits_per_pixel) { - case 32: - /* 0x88883316 */ - return MAKE_AD(3, 2, 0, 1, 3, 8, 8, 8, 8); - case 24: - /* 0x88082219 */ - return MAKE_AD(4, 0, 1, 2, 2, 0, 8, 8, 8); - case 16: - /* 0x65053118 */ - return MAKE_AD(4, 2, 1, 0, 1, 5, 6, 5, 0); - default: - pr_err("fsl-diu: unsupported pixel depth %u\n", bits_per_pixel); - return 0; - } -} - -/** - * p1022ds_set_gamma_table: update the gamma table, if necessary - * - * On some boards, the gamma table for some ports may need to be modified. - * This is not the case on the P1022DS, so we do nothing. -*/ -static void p1022ds_set_gamma_table(int monitor_port, char *gamma_table_base) -{ -} - -/** - * p1022ds_set_monitor_port: switch the output to a different monitor port - * - */ -static void p1022ds_set_monitor_port(int monitor_port) -{ - struct device_node *pixis_node; - u8 __iomem *brdcfg1; - - pixis_node = of_find_compatible_node(NULL, NULL, "fsl,p1022ds-pixis"); - if (!pixis_node) { - pr_err("p1022ds: missing ngPIXIS node\n"); - return; - } - - brdcfg1 = of_iomap(pixis_node, 0); - if (!brdcfg1) { - pr_err("p1022ds: could not map ngPIXIS registers\n"); - return; - } - brdcfg1 += 9; /* BRDCFG1 is at offset 9 in the ngPIXIS */ - - switch (monitor_port) { - case 0: /* DVI */ - /* Enable the DVI port, disable the DFP and the backlight */ - clrsetbits_8(brdcfg1, PX_BRDCFG1_DFPEN | PX_BRDCFG1_BACKLIGHT, - PX_BRDCFG1_DVIEN); - break; - case 1: /* Single link LVDS */ - /* Enable the DFP port, disable the DVI and the backlight */ - clrsetbits_8(brdcfg1, PX_BRDCFG1_DVIEN | PX_BRDCFG1_BACKLIGHT, - PX_BRDCFG1_DFPEN); - break; - default: - pr_err("p1022ds: unsupported monitor port %i\n", monitor_port); - } -} - -/** - * p1022ds_set_pixel_clock: program the DIU's clock - * - * @pixclock: the wavelength, in picoseconds, of the clock - */ -void p1022ds_set_pixel_clock(unsigned int pixclock) -{ - struct device_node *guts_np = NULL; - struct ccsr_guts_85xx __iomem *guts; - unsigned long freq; - u64 temp; - u32 pxclk; - - /* Map the global utilities registers. */ - guts_np = of_find_compatible_node(NULL, NULL, "fsl,p1022-guts"); - if (!guts_np) { - pr_err("p1022ds: missing global utilties device node\n"); - return; - } - - guts = of_iomap(guts_np, 0); - of_node_put(guts_np); - if (!guts) { - pr_err("p1022ds: could not map global utilties device\n"); - return; - } - - /* Convert pixclock from a wavelength to a frequency */ - temp = 1000000000000ULL; - do_div(temp, pixclock); - freq = temp; - - /* pixclk is the ratio of the platform clock to the pixel clock */ - pxclk = DIV_ROUND_CLOSEST(fsl_get_sys_freq(), freq); - - /* Disable the pixel clock, and set it to non-inverted and no delay */ - clrbits32(&guts->clkdvdr, - CLKDVDR_PXCKEN | CLKDVDR_PXCKDLY | CLKDVDR_PXCLK_MASK); - - /* Enable the clock and set the pxclk */ - setbits32(&guts->clkdvdr, CLKDVDR_PXCKEN | (pxclk << 16)); -} - -/** - * p1022ds_show_monitor_port: show the current monitor - * - * This function returns a string indicating whether the current monitor is - * set to DVI or LVDS. - */ -ssize_t p1022ds_show_monitor_port(int monitor_port, char *buf) -{ - return sprintf(buf, "%c0 - DVI\n%c1 - Single link LVDS\n", - monitor_port == 0 ? '*' : ' ', monitor_port == 1 ? '*' : ' '); -} - -/** - * p1022ds_set_sysfs_monitor_port: set the monitor port for sysfs - */ -int p1022ds_set_sysfs_monitor_port(int val) -{ - return val < 2 ? val : 0; -} - -#endif void __init p1022_ds_pic_init(void) { @@ -290,15 +92,6 @@ static void __init p1022_ds_setup_arch(void) } #endif -#if defined(CONFIG_FB_FSL_DIU) || defined(CONFIG_FB_FSL_DIU_MODULE) - diu_ops.get_pixel_format = p1022ds_get_pixel_format; - diu_ops.set_gamma_table = p1022ds_set_gamma_table; - diu_ops.set_monitor_port = p1022ds_set_monitor_port; - diu_ops.set_pixel_clock = p1022ds_set_pixel_clock; - diu_ops.show_monitor_port = p1022ds_show_monitor_port; - diu_ops.set_sysfs_monitor_port = p1022ds_set_sysfs_monitor_port; -#endif - #ifdef CONFIG_SMP mpc85xx_smp_init(); #endif diff --git a/trunk/arch/s390/include/asm/pgtable.h b/trunk/arch/s390/include/asm/pgtable.h index 3157441ee1da..22a294571000 100644 --- a/trunk/arch/s390/include/asm/pgtable.h +++ b/trunk/arch/s390/include/asm/pgtable.h @@ -46,11 +46,27 @@ extern void vmem_map_init(void); #define update_mmu_cache(vma, address, ptep) do { } while (0) /* - * ZERO_PAGE is a global shared page that is always zero: used + * ZERO_PAGE is a global shared page that is always zero; used * for zero-mapped memory areas etc.. */ -extern char empty_zero_page[PAGE_SIZE]; -#define ZERO_PAGE(vaddr) (virt_to_page(empty_zero_page)) + +extern unsigned long empty_zero_page; +extern unsigned long zero_page_mask; + +#define ZERO_PAGE(vaddr) \ + (virt_to_page((void *)(empty_zero_page + \ + (((unsigned long)(vaddr)) &zero_page_mask)))) + +#define is_zero_pfn is_zero_pfn +static inline int is_zero_pfn(unsigned long pfn) +{ + extern unsigned long zero_pfn; + unsigned long offset_from_zero_pfn = pfn - zero_pfn; + return offset_from_zero_pfn <= (zero_page_mask >> PAGE_SHIFT); +} + +#define my_zero_pfn(addr) page_to_pfn(ZERO_PAGE(addr)) + #endif /* !__ASSEMBLY__ */ /* diff --git a/trunk/arch/s390/mm/init.c b/trunk/arch/s390/mm/init.c index 94b8ba2ec857..0744fb3536b1 100644 --- a/trunk/arch/s390/mm/init.c +++ b/trunk/arch/s390/mm/init.c @@ -42,9 +42,52 @@ DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); pgd_t swapper_pg_dir[PTRS_PER_PGD] __attribute__((__aligned__(PAGE_SIZE))); -char empty_zero_page[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); +unsigned long empty_zero_page, zero_page_mask; EXPORT_SYMBOL(empty_zero_page); +static unsigned long setup_zero_pages(void) +{ + struct cpuid cpu_id; + unsigned int order; + unsigned long size; + struct page *page; + int i; + + get_cpu_id(&cpu_id); + switch (cpu_id.machine) { + case 0x9672: /* g5 */ + case 0x2064: /* z900 */ + case 0x2066: /* z900 */ + case 0x2084: /* z990 */ + case 0x2086: /* z990 */ + case 0x2094: /* z9-109 */ + case 0x2096: /* z9-109 */ + order = 0; + break; + case 0x2097: /* z10 */ + case 0x2098: /* z10 */ + default: + order = 2; + break; + } + + empty_zero_page = __get_free_pages(GFP_KERNEL | __GFP_ZERO, order); + if (!empty_zero_page) + panic("Out of memory in setup_zero_pages"); + + page = virt_to_page((void *) empty_zero_page); + split_page(page, order); + for (i = 1 << order; i > 0; i--) { + SetPageReserved(page); + page++; + } + + size = PAGE_SIZE << order; + zero_page_mask = (size - 1) & PAGE_MASK; + + return 1UL << order; +} + /* * paging_init() sets up the page tables */ @@ -92,14 +135,12 @@ void __init mem_init(void) max_mapnr = num_physpages = max_low_pfn; high_memory = (void *) __va(max_low_pfn * PAGE_SIZE); - /* clear the zero-page */ - memset(empty_zero_page, 0, PAGE_SIZE); - /* Setup guest page hinting */ cmma_init(); /* this will put all low memory onto the freelists */ totalram_pages += free_all_bootmem(); + totalram_pages -= setup_zero_pages(); /* Setup zeroed pages. */ reservedpages = 0; diff --git a/trunk/arch/sh/Kconfig b/trunk/arch/sh/Kconfig index 0f40fc35d0a2..35b6879628a0 100644 --- a/trunk/arch/sh/Kconfig +++ b/trunk/arch/sh/Kconfig @@ -24,7 +24,6 @@ config SUPERH select HAVE_KERNEL_LZMA select HAVE_KERNEL_LZO select HAVE_SYSCALL_TRACEPOINTS - select HAVE_REGS_AND_STACK_ACCESS_API select RTC_LIB select GENERIC_ATOMIC64 help @@ -47,7 +46,7 @@ config SUPERH32 select HAVE_ARCH_KGDB select HAVE_HW_BREAKPOINT select HAVE_MIXED_BREAKPOINTS_REGS - select PERF_EVENTS + select PERF_EVENTS if HAVE_HW_BREAKPOINT select ARCH_HIBERNATION_POSSIBLE if MMU config SUPERH64 @@ -472,7 +471,6 @@ config CPU_SUBTYPE_SHX3 select CPU_SH4A select CPU_SHX3 select GENERIC_CLOCKEVENTS_BROADCAST if SMP - select ARCH_REQUIRE_GPIOLIB # SH4AL-DSP Processor Support @@ -577,7 +575,7 @@ config SH_CLK_CPG config SH_CLK_CPG_LEGACY depends on SH_CLK_CPG def_bool y if !CPU_SUBTYPE_SH7785 && !ARCH_SHMOBILE && \ - !CPU_SHX3 && !CPU_SUBTYPE_SH7757 + !CPU_SUBTYPE_SH7786 config SH_CLK_MD int "CPU Mode Pin Setting" diff --git a/trunk/arch/sh/boards/Kconfig b/trunk/arch/sh/boards/Kconfig index 9c94711aa6ca..07b35ca2f644 100644 --- a/trunk/arch/sh/boards/Kconfig +++ b/trunk/arch/sh/boards/Kconfig @@ -155,8 +155,6 @@ config SH_SDK7786 depends on CPU_SUBTYPE_SH7786 select SYS_SUPPORTS_PCI select NO_IOPORT if !PCI - select ARCH_WANT_OPTIONAL_GPIOLIB - select HAVE_SRAM_POOL help Select SDK7786 if configuring for a Renesas Technology Europe SH7786-65nm board. @@ -167,11 +165,6 @@ config SH_HIGHLANDER select SYS_SUPPORTS_PCI select IO_TRAPPED if MMU -config SH_SH7757LCR - bool "SH7757LCR" - depends on CPU_SUBTYPE_SH7757 - select ARCH_REQUIRE_GPIOLIB - config SH_SH7785LCR bool "SH7785LCR" depends on CPU_SUBTYPE_SH7785 @@ -316,17 +309,6 @@ config SH_POLARIS help Select if configuring for an SMSC Polaris development board -config SH_SH2007 - bool "SH-2007 board" - select NO_IOPORT - depends on CPU_SUBTYPE_SH7780 - help - SH-2007 is a single-board computer based around SH7780 chip - intended for embedded applications. - It has an Ethernet interface (SMC9118), direct connected - Compact Flash socket, two serial ports and PC-104 bus. - More information at . - endmenu source "arch/sh/boards/mach-r2d/Kconfig" diff --git a/trunk/arch/sh/boards/Makefile b/trunk/arch/sh/boards/Makefile index 38ef655cc0f0..4f90f9b7a922 100644 --- a/trunk/arch/sh/boards/Makefile +++ b/trunk/arch/sh/boards/Makefile @@ -2,7 +2,6 @@ # Specific board support, not covered by a mach group. # obj-$(CONFIG_SH_MAGIC_PANEL_R2) += board-magicpanelr2.o -obj-$(CONFIG_SH_SH2007) += board-sh2007.o obj-$(CONFIG_SH_SH7785LCR) += board-sh7785lcr.o obj-$(CONFIG_SH_URQUELL) += board-urquell.o obj-$(CONFIG_SH_SHMIN) += board-shmin.o @@ -10,4 +9,3 @@ obj-$(CONFIG_SH_EDOSK7760) += board-edosk7760.o obj-$(CONFIG_SH_ESPT) += board-espt.o obj-$(CONFIG_SH_POLARIS) += board-polaris.o obj-$(CONFIG_SH_TITAN) += board-titan.o -obj-$(CONFIG_SH_SH7757LCR) += board-sh7757lcr.o diff --git a/trunk/arch/sh/boards/board-sh2007.c b/trunk/arch/sh/boards/board-sh2007.c deleted file mode 100644 index b90b78f6a829..000000000000 --- a/trunk/arch/sh/boards/board-sh2007.c +++ /dev/null @@ -1,133 +0,0 @@ -/* - * SH-2007 board support. - * - * Copyright (C) 2003, 2004 SUGIOKA Toshinobu - * Copyright (C) 2010 Hitoshi Mitake - */ -#include -#include -#include -#include -#include -#include -#include -#include - -struct smsc911x_platform_config smc911x_info = { - .flags = SMSC911X_USE_32BIT, - .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW, - .irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL, -}; - -static struct resource smsc9118_0_resources[] = { - [0] = { - .start = SMC0_BASE, - .end = SMC0_BASE + 0xff, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = evt2irq(0x240), - .end = evt2irq(0x240), - .flags = IORESOURCE_IRQ, - } -}; - -static struct resource smsc9118_1_resources[] = { - [0] = { - .start = SMC1_BASE, - .end = SMC1_BASE + 0xff, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = evt2irq(0x280), - .end = evt2irq(0x280), - .flags = IORESOURCE_IRQ, - } -}; - -static struct platform_device smsc9118_0_device = { - .name = "smsc911x", - .id = 0, - .num_resources = ARRAY_SIZE(smsc9118_0_resources), - .resource = smsc9118_0_resources, - .dev = { - .platform_data = &smc911x_info, - }, -}; - -static struct platform_device smsc9118_1_device = { - .name = "smsc911x", - .id = 1, - .num_resources = ARRAY_SIZE(smsc9118_1_resources), - .resource = smsc9118_1_resources, - .dev = { - .platform_data = &smc911x_info, - }, -}; - -static struct resource cf_resources[] = { - [0] = { - .start = CF_BASE + CF_OFFSET, - .end = CF_BASE + CF_OFFSET + 0x0f, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = CF_BASE + CF_OFFSET + 0x206, - .end = CF_BASE + CF_OFFSET + 0x20f, - .flags = IORESOURCE_MEM, - }, - [2] = { - .start = evt2irq(0x2c0), - .end = evt2irq(0x2c0), - .flags = IORESOURCE_IRQ, - }, -}; - -static struct platform_device cf_device = { - .name = "pata_platform", - .id = 0, - .num_resources = ARRAY_SIZE(cf_resources), - .resource = cf_resources, -}; - -static struct platform_device *sh2007_devices[] __initdata = { - &smsc9118_0_device, - &smsc9118_1_device, - &cf_device, -}; - -static int __init sh2007_io_init(void) -{ - platform_add_devices(sh2007_devices, ARRAY_SIZE(sh2007_devices)); - return 0; -} -subsys_initcall(sh2007_io_init); - -static void __init sh2007_init_irq(void) -{ - plat_irq_setup_pins(IRQ_MODE_IRQ); -} - -/* - * Initialize the board - */ -static void __init sh2007_setup(char **cmdline_p) -{ - printk(KERN_INFO "SH-2007 Setup..."); - - /* setup wait control registers for area 5 */ - __raw_writel(CS5BCR_D, CS5BCR); - __raw_writel(CS5WCR_D, CS5WCR); - __raw_writel(CS5PCR_D, CS5PCR); - - printk(KERN_INFO " done.\n"); -} - -/* - * The Machine Vector - */ -struct sh_machine_vector mv_sh2007 __initmv = { - .mv_setup = sh2007_setup, - .mv_name = "sh2007", - .mv_init_irq = sh2007_init_irq, -}; diff --git a/trunk/arch/sh/boards/board-sh7757lcr.c b/trunk/arch/sh/boards/board-sh7757lcr.c deleted file mode 100644 index c475f1056ab4..000000000000 --- a/trunk/arch/sh/boards/board-sh7757lcr.c +++ /dev/null @@ -1,374 +0,0 @@ -/* - * Renesas R0P7757LC0012RL Support. - * - * Copyright (C) 2009 - 2010 Renesas Solutions Corp. - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static struct resource heartbeat_resource = { - .start = 0xffec005c, /* PUDR */ - .end = 0xffec005c, - .flags = IORESOURCE_MEM | IORESOURCE_MEM_8BIT, -}; - -static unsigned char heartbeat_bit_pos[] = { 0, 1, 2, 3 }; - -static struct heartbeat_data heartbeat_data = { - .bit_pos = heartbeat_bit_pos, - .nr_bits = ARRAY_SIZE(heartbeat_bit_pos), - .flags = HEARTBEAT_INVERTED, -}; - -static struct platform_device heartbeat_device = { - .name = "heartbeat", - .id = -1, - .dev = { - .platform_data = &heartbeat_data, - }, - .num_resources = 1, - .resource = &heartbeat_resource, -}; - -/* Fast Ethernet */ -static struct resource sh_eth0_resources[] = { - { - .start = 0xfef00000, - .end = 0xfef001ff, - .flags = IORESOURCE_MEM, - }, { - .start = 84, - .end = 84, - .flags = IORESOURCE_IRQ, - }, -}; - -static struct sh_eth_plat_data sh7757_eth0_pdata = { - .phy = 1, - .edmac_endian = EDMAC_LITTLE_ENDIAN, -}; - -static struct platform_device sh7757_eth0_device = { - .name = "sh-eth", - .resource = sh_eth0_resources, - .id = 0, - .num_resources = ARRAY_SIZE(sh_eth0_resources), - .dev = { - .platform_data = &sh7757_eth0_pdata, - }, -}; - -static struct resource sh_eth1_resources[] = { - { - .start = 0xfef00800, - .end = 0xfef009ff, - .flags = IORESOURCE_MEM, - }, { - .start = 84, - .end = 84, - .flags = IORESOURCE_IRQ, - }, -}; - -static struct sh_eth_plat_data sh7757_eth1_pdata = { - .phy = 1, - .edmac_endian = EDMAC_LITTLE_ENDIAN, -}; - -static struct platform_device sh7757_eth1_device = { - .name = "sh-eth", - .resource = sh_eth1_resources, - .id = 1, - .num_resources = ARRAY_SIZE(sh_eth1_resources), - .dev = { - .platform_data = &sh7757_eth1_pdata, - }, -}; - -static struct platform_device *sh7757lcr_devices[] __initdata = { - &heartbeat_device, - &sh7757_eth0_device, - &sh7757_eth1_device, -}; - -static int __init sh7757lcr_devices_setup(void) -{ - /* RGMII (PTA) */ - gpio_request(GPIO_FN_ET0_MDC, NULL); - gpio_request(GPIO_FN_ET0_MDIO, NULL); - gpio_request(GPIO_FN_ET1_MDC, NULL); - gpio_request(GPIO_FN_ET1_MDIO, NULL); - - /* ONFI (PTB, PTZ) */ - gpio_request(GPIO_FN_ON_NRE, NULL); - gpio_request(GPIO_FN_ON_NWE, NULL); - gpio_request(GPIO_FN_ON_NWP, NULL); - gpio_request(GPIO_FN_ON_NCE0, NULL); - gpio_request(GPIO_FN_ON_R_B0, NULL); - gpio_request(GPIO_FN_ON_ALE, NULL); - gpio_request(GPIO_FN_ON_CLE, NULL); - - gpio_request(GPIO_FN_ON_DQ7, NULL); - gpio_request(GPIO_FN_ON_DQ6, NULL); - gpio_request(GPIO_FN_ON_DQ5, NULL); - gpio_request(GPIO_FN_ON_DQ4, NULL); - gpio_request(GPIO_FN_ON_DQ3, NULL); - gpio_request(GPIO_FN_ON_DQ2, NULL); - gpio_request(GPIO_FN_ON_DQ1, NULL); - gpio_request(GPIO_FN_ON_DQ0, NULL); - - /* IRQ8 to 0 (PTB, PTC) */ - gpio_request(GPIO_FN_IRQ8, NULL); - gpio_request(GPIO_FN_IRQ7, NULL); - gpio_request(GPIO_FN_IRQ6, NULL); - gpio_request(GPIO_FN_IRQ5, NULL); - gpio_request(GPIO_FN_IRQ4, NULL); - gpio_request(GPIO_FN_IRQ3, NULL); - gpio_request(GPIO_FN_IRQ2, NULL); - gpio_request(GPIO_FN_IRQ1, NULL); - gpio_request(GPIO_FN_IRQ0, NULL); - - /* SPI0 (PTD) */ - gpio_request(GPIO_FN_SP0_MOSI, NULL); - gpio_request(GPIO_FN_SP0_MISO, NULL); - gpio_request(GPIO_FN_SP0_SCK, NULL); - gpio_request(GPIO_FN_SP0_SCK_FB, NULL); - gpio_request(GPIO_FN_SP0_SS0, NULL); - gpio_request(GPIO_FN_SP0_SS1, NULL); - gpio_request(GPIO_FN_SP0_SS2, NULL); - gpio_request(GPIO_FN_SP0_SS3, NULL); - - /* RMII 0/1 (PTE, PTF) */ - gpio_request(GPIO_FN_RMII0_CRS_DV, NULL); - gpio_request(GPIO_FN_RMII0_TXD1, NULL); - gpio_request(GPIO_FN_RMII0_TXD0, NULL); - gpio_request(GPIO_FN_RMII0_TXEN, NULL); - gpio_request(GPIO_FN_RMII0_REFCLK, NULL); - gpio_request(GPIO_FN_RMII0_RXD1, NULL); - gpio_request(GPIO_FN_RMII0_RXD0, NULL); - gpio_request(GPIO_FN_RMII0_RX_ER, NULL); - gpio_request(GPIO_FN_RMII1_CRS_DV, NULL); - gpio_request(GPIO_FN_RMII1_TXD1, NULL); - gpio_request(GPIO_FN_RMII1_TXD0, NULL); - gpio_request(GPIO_FN_RMII1_TXEN, NULL); - gpio_request(GPIO_FN_RMII1_REFCLK, NULL); - gpio_request(GPIO_FN_RMII1_RXD1, NULL); - gpio_request(GPIO_FN_RMII1_RXD0, NULL); - gpio_request(GPIO_FN_RMII1_RX_ER, NULL); - - /* eMMC (PTG) */ - gpio_request(GPIO_FN_MMCCLK, NULL); - gpio_request(GPIO_FN_MMCCMD, NULL); - gpio_request(GPIO_FN_MMCDAT7, NULL); - gpio_request(GPIO_FN_MMCDAT6, NULL); - gpio_request(GPIO_FN_MMCDAT5, NULL); - gpio_request(GPIO_FN_MMCDAT4, NULL); - gpio_request(GPIO_FN_MMCDAT3, NULL); - gpio_request(GPIO_FN_MMCDAT2, NULL); - gpio_request(GPIO_FN_MMCDAT1, NULL); - gpio_request(GPIO_FN_MMCDAT0, NULL); - - /* LPC (PTG, PTH, PTQ, PTU) */ - gpio_request(GPIO_FN_SERIRQ, NULL); - gpio_request(GPIO_FN_LPCPD, NULL); - gpio_request(GPIO_FN_LDRQ, NULL); - gpio_request(GPIO_FN_WP, NULL); - gpio_request(GPIO_FN_FMS0, NULL); - gpio_request(GPIO_FN_LAD3, NULL); - gpio_request(GPIO_FN_LAD2, NULL); - gpio_request(GPIO_FN_LAD1, NULL); - gpio_request(GPIO_FN_LAD0, NULL); - gpio_request(GPIO_FN_LFRAME, NULL); - gpio_request(GPIO_FN_LRESET, NULL); - gpio_request(GPIO_FN_LCLK, NULL); - gpio_request(GPIO_FN_LGPIO7, NULL); - gpio_request(GPIO_FN_LGPIO6, NULL); - gpio_request(GPIO_FN_LGPIO5, NULL); - gpio_request(GPIO_FN_LGPIO4, NULL); - - /* SPI1 (PTH) */ - gpio_request(GPIO_FN_SP1_MOSI, NULL); - gpio_request(GPIO_FN_SP1_MISO, NULL); - gpio_request(GPIO_FN_SP1_SCK, NULL); - gpio_request(GPIO_FN_SP1_SCK_FB, NULL); - gpio_request(GPIO_FN_SP1_SS0, NULL); - gpio_request(GPIO_FN_SP1_SS1, NULL); - - /* SDHI (PTI) */ - gpio_request(GPIO_FN_SD_WP, NULL); - gpio_request(GPIO_FN_SD_CD, NULL); - gpio_request(GPIO_FN_SD_CLK, NULL); - gpio_request(GPIO_FN_SD_CMD, NULL); - gpio_request(GPIO_FN_SD_D3, NULL); - gpio_request(GPIO_FN_SD_D2, NULL); - gpio_request(GPIO_FN_SD_D1, NULL); - gpio_request(GPIO_FN_SD_D0, NULL); - - /* SCIF3/4 (PTJ, PTW) */ - gpio_request(GPIO_FN_RTS3, NULL); - gpio_request(GPIO_FN_CTS3, NULL); - gpio_request(GPIO_FN_TXD3, NULL); - gpio_request(GPIO_FN_RXD3, NULL); - gpio_request(GPIO_FN_RTS4, NULL); - gpio_request(GPIO_FN_RXD4, NULL); - gpio_request(GPIO_FN_TXD4, NULL); - gpio_request(GPIO_FN_CTS4, NULL); - - /* SERMUX (PTK, PTL, PTO, PTV) */ - gpio_request(GPIO_FN_COM2_TXD, NULL); - gpio_request(GPIO_FN_COM2_RXD, NULL); - gpio_request(GPIO_FN_COM2_RTS, NULL); - gpio_request(GPIO_FN_COM2_CTS, NULL); - gpio_request(GPIO_FN_COM2_DTR, NULL); - gpio_request(GPIO_FN_COM2_DSR, NULL); - gpio_request(GPIO_FN_COM2_DCD, NULL); - gpio_request(GPIO_FN_COM2_RI, NULL); - gpio_request(GPIO_FN_RAC_RXD, NULL); - gpio_request(GPIO_FN_RAC_RTS, NULL); - gpio_request(GPIO_FN_RAC_CTS, NULL); - gpio_request(GPIO_FN_RAC_DTR, NULL); - gpio_request(GPIO_FN_RAC_DSR, NULL); - gpio_request(GPIO_FN_RAC_DCD, NULL); - gpio_request(GPIO_FN_RAC_TXD, NULL); - gpio_request(GPIO_FN_COM1_TXD, NULL); - gpio_request(GPIO_FN_COM1_RXD, NULL); - gpio_request(GPIO_FN_COM1_RTS, NULL); - gpio_request(GPIO_FN_COM1_CTS, NULL); - - writeb(0x10, 0xfe470000); /* SMR0: SerMux mode 0 */ - - /* IIC (PTM, PTR, PTS) */ - gpio_request(GPIO_FN_SDA7, NULL); - gpio_request(GPIO_FN_SCL7, NULL); - gpio_request(GPIO_FN_SDA6, NULL); - gpio_request(GPIO_FN_SCL6, NULL); - gpio_request(GPIO_FN_SDA5, NULL); - gpio_request(GPIO_FN_SCL5, NULL); - gpio_request(GPIO_FN_SDA4, NULL); - gpio_request(GPIO_FN_SCL4, NULL); - gpio_request(GPIO_FN_SDA3, NULL); - gpio_request(GPIO_FN_SCL3, NULL); - gpio_request(GPIO_FN_SDA2, NULL); - gpio_request(GPIO_FN_SCL2, NULL); - gpio_request(GPIO_FN_SDA1, NULL); - gpio_request(GPIO_FN_SCL1, NULL); - gpio_request(GPIO_FN_SDA0, NULL); - gpio_request(GPIO_FN_SCL0, NULL); - - /* USB (PTN) */ - gpio_request(GPIO_FN_VBUS_EN, NULL); - gpio_request(GPIO_FN_VBUS_OC, NULL); - - /* SGPIO1/0 (PTN, PTO) */ - gpio_request(GPIO_FN_SGPIO1_CLK, NULL); - gpio_request(GPIO_FN_SGPIO1_LOAD, NULL); - gpio_request(GPIO_FN_SGPIO1_DI, NULL); - gpio_request(GPIO_FN_SGPIO1_DO, NULL); - gpio_request(GPIO_FN_SGPIO0_CLK, NULL); - gpio_request(GPIO_FN_SGPIO0_LOAD, NULL); - gpio_request(GPIO_FN_SGPIO0_DI, NULL); - gpio_request(GPIO_FN_SGPIO0_DO, NULL); - - /* WDT (PTN) */ - gpio_request(GPIO_FN_SUB_CLKIN, NULL); - - /* System (PTT) */ - gpio_request(GPIO_FN_STATUS1, NULL); - gpio_request(GPIO_FN_STATUS0, NULL); - - /* PWMX (PTT) */ - gpio_request(GPIO_FN_PWMX1, NULL); - gpio_request(GPIO_FN_PWMX0, NULL); - - /* R-SPI (PTV) */ - gpio_request(GPIO_FN_R_SPI_MOSI, NULL); - gpio_request(GPIO_FN_R_SPI_MISO, NULL); - gpio_request(GPIO_FN_R_SPI_RSPCK, NULL); - gpio_request(GPIO_FN_R_SPI_SSL0, NULL); - gpio_request(GPIO_FN_R_SPI_SSL1, NULL); - - /* EVC (PTV, PTW) */ - gpio_request(GPIO_FN_EVENT7, NULL); - gpio_request(GPIO_FN_EVENT6, NULL); - gpio_request(GPIO_FN_EVENT5, NULL); - gpio_request(GPIO_FN_EVENT4, NULL); - gpio_request(GPIO_FN_EVENT3, NULL); - gpio_request(GPIO_FN_EVENT2, NULL); - gpio_request(GPIO_FN_EVENT1, NULL); - gpio_request(GPIO_FN_EVENT0, NULL); - - /* LED for heartbeat */ - gpio_request(GPIO_PTU3, NULL); - gpio_direction_output(GPIO_PTU3, 1); - gpio_request(GPIO_PTU2, NULL); - gpio_direction_output(GPIO_PTU2, 1); - gpio_request(GPIO_PTU1, NULL); - gpio_direction_output(GPIO_PTU1, 1); - gpio_request(GPIO_PTU0, NULL); - gpio_direction_output(GPIO_PTU0, 1); - - /* control for MDIO of Gigabit Ethernet */ - gpio_request(GPIO_PTT4, NULL); - gpio_direction_output(GPIO_PTT4, 1); - - /* control for eMMC */ - gpio_request(GPIO_PTT7, NULL); /* eMMC_RST# */ - gpio_direction_output(GPIO_PTT7, 0); - gpio_request(GPIO_PTT6, NULL); /* eMMC_INDEX# */ - gpio_direction_output(GPIO_PTT6, 0); - gpio_request(GPIO_PTT5, NULL); /* eMMC_PRST# */ - gpio_direction_output(GPIO_PTT5, 1); - - /* General platform */ - return platform_add_devices(sh7757lcr_devices, - ARRAY_SIZE(sh7757lcr_devices)); -} -arch_initcall(sh7757lcr_devices_setup); - -/* Initialize IRQ setting */ -void __init init_sh7757lcr_IRQ(void) -{ - plat_irq_setup_pins(IRQ_MODE_IRQ7654); - plat_irq_setup_pins(IRQ_MODE_IRQ3210); -} - -/* Initialize the board */ -static void __init sh7757lcr_setup(char **cmdline_p) -{ - printk(KERN_INFO "Renesas R0P7757LC0012RL support.\n"); -} - -static int sh7757lcr_mode_pins(void) -{ - int value = 0; - - /* These are the factory default settings of S3 (Low active). - * If you change these dip switches then you will need to - * adjust the values below as well. - */ - value |= MODE_PIN0; /* Clock Mode: 1 */ - - return value; -} - -/* The Machine Vector */ -static struct sh_machine_vector mv_sh7757lcr __initmv = { - .mv_name = "SH7757LCR", - .mv_setup = sh7757lcr_setup, - .mv_init_irq = init_sh7757lcr_IRQ, - .mv_mode_pins = sh7757lcr_mode_pins, -}; - diff --git a/trunk/arch/sh/boards/mach-ecovec24/setup.c b/trunk/arch/sh/boards/mach-ecovec24/setup.c index 71a3368ab1fc..1d7b495a7db4 100644 --- a/trunk/arch/sh/boards/mach-ecovec24/setup.c +++ b/trunk/arch/sh/boards/mach-ecovec24/setup.c @@ -1248,14 +1248,14 @@ static int __init arch_setup(void) /* set SPU2 clock to 83.4 MHz */ clk = clk_get(NULL, "spu_clk"); - if (!IS_ERR(clk)) { + if (clk) { clk_set_rate(clk, clk_round_rate(clk, 83333333)); clk_put(clk); } /* change parent of FSI B */ clk = clk_get(NULL, "fsib_clk"); - if (!IS_ERR(clk)) { + if (clk) { clk_register(&fsimckb_clk); clk_set_parent(clk, &fsimckb_clk); clk_set_rate(clk, 11000); @@ -1273,7 +1273,7 @@ static int __init arch_setup(void) /* set VPU clock to 166 MHz */ clk = clk_get(NULL, "vpu_clk"); - if (!IS_ERR(clk)) { + if (clk) { clk_set_rate(clk, clk_round_rate(clk, 166000000)); clk_put(clk); } diff --git a/trunk/arch/sh/boards/mach-sdk7786/Makefile b/trunk/arch/sh/boards/mach-sdk7786/Makefile index 23ff7d4ac491..a29f19e85b63 100644 --- a/trunk/arch/sh/boards/mach-sdk7786/Makefile +++ b/trunk/arch/sh/boards/mach-sdk7786/Makefile @@ -1,4 +1 @@ -obj-y := fpga.o irq.o setup.o - -obj-$(CONFIG_GENERIC_GPIO) += gpio.o -obj-$(CONFIG_HAVE_SRAM_POOL) += sram.o +obj-y := setup.o fpga.o irq.o diff --git a/trunk/arch/sh/boards/mach-sdk7786/gpio.c b/trunk/arch/sh/boards/mach-sdk7786/gpio.c deleted file mode 100644 index f71ce09d4e15..000000000000 --- a/trunk/arch/sh/boards/mach-sdk7786/gpio.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * SDK7786 FPGA USRGPIR Support. - * - * Copyright (C) 2010 Paul Mundt - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#define NR_FPGA_GPIOS 8 - -static const char *usrgpir_gpio_names[NR_FPGA_GPIOS] = { - "in0", "in1", "in2", "in3", "in4", "in5", "in6", "in7", -}; - -static int usrgpir_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) -{ - /* always in */ - return 0; -} - -static int usrgpir_gpio_get(struct gpio_chip *chip, unsigned gpio) -{ - return !!(fpga_read_reg(USRGPIR) & (1 << gpio)); -} - -static struct gpio_chip usrgpir_gpio_chip = { - .label = "sdk7786-fpga", - .names = usrgpir_gpio_names, - .direction_input = usrgpir_gpio_direction_input, - .get = usrgpir_gpio_get, - .base = -1, /* don't care */ - .ngpio = NR_FPGA_GPIOS, -}; - -static int __init usrgpir_gpio_setup(void) -{ - return gpiochip_add(&usrgpir_gpio_chip); -} -device_initcall(usrgpir_gpio_setup); diff --git a/trunk/arch/sh/boards/mach-sdk7786/setup.c b/trunk/arch/sh/boards/mach-sdk7786/setup.c index 7e0c4e3878e0..2ec1ea5cf8ef 100644 --- a/trunk/arch/sh/boards/mach-sdk7786/setup.c +++ b/trunk/arch/sh/boards/mach-sdk7786/setup.c @@ -20,8 +20,6 @@ #include #include #include -#include -#include #include #include @@ -142,45 +140,6 @@ static int sdk7786_mode_pins(void) return fpga_read_reg(MODSWR); } -/* - * FPGA-driven PCIe clocks - * - * Historically these include the oscillator, clock B (slots 2/3/4) and - * clock A (slot 1 and the CPU clock). Newer revs of the PCB shove - * everything under a single PCIe clocks enable bit that happens to map - * to the same bit position as the oscillator bit for earlier FPGA - * versions. - * - * Given that the legacy clocks have the side-effect of shutting the CPU - * off through the FPGA along with the PCI slots, we simply leave them in - * their initial state and don't bother registering them with the clock - * framework. - */ -static int sdk7786_pcie_clk_enable(struct clk *clk) -{ - fpga_write_reg(fpga_read_reg(PCIECR) | PCIECR_CLKEN, PCIECR); - return 0; -} - -static void sdk7786_pcie_clk_disable(struct clk *clk) -{ - fpga_write_reg(fpga_read_reg(PCIECR) & ~PCIECR_CLKEN, PCIECR); -} - -static struct clk_ops sdk7786_pcie_clk_ops = { - .enable = sdk7786_pcie_clk_enable, - .disable = sdk7786_pcie_clk_disable, -}; - -static struct clk sdk7786_pcie_clk = { - .ops = &sdk7786_pcie_clk_ops, -}; - -static struct clk_lookup sdk7786_pcie_cl = { - .con_id = "pcie_plat_clk", - .clk = &sdk7786_pcie_clk, -}; - static int sdk7786_clk_init(void) { struct clk *clk; @@ -199,18 +158,7 @@ static int sdk7786_clk_init(void) ret = clk_set_rate(clk, 33333333); clk_put(clk); - /* - * Setup the FPGA clocks. - */ - ret = clk_register(&sdk7786_pcie_clk); - if (unlikely(ret)) { - pr_err("FPGA clock registration failed\n"); - return ret; - } - - clkdev_add(&sdk7786_pcie_cl); - - return 0; + return ret; } static void sdk7786_restart(char *cmd) diff --git a/trunk/arch/sh/boards/mach-sdk7786/sram.c b/trunk/arch/sh/boards/mach-sdk7786/sram.c deleted file mode 100644 index c81c3abbe01c..000000000000 --- a/trunk/arch/sh/boards/mach-sdk7786/sram.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * SDK7786 FPGA SRAM Support. - * - * Copyright (C) 2010 Paul Mundt - * - * 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. - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include - -static int __init fpga_sram_init(void) -{ - unsigned long phys; - unsigned int area; - void __iomem *vaddr; - int ret; - u16 data; - - /* Enable FPGA SRAM */ - data = fpga_read_reg(LCLASR); - data |= LCLASR_FRAMEN; - fpga_write_reg(data, LCLASR); - - /* - * FPGA_SEL determines the area mapping - */ - area = (data & LCLASR_FPGA_SEL_MASK) >> LCLASR_FPGA_SEL_SHIFT; - if (unlikely(area == LCLASR_AREA_MASK)) { - pr_err("FPGA memory unmapped.\n"); - return -ENXIO; - } - - /* - * The memory itself occupies a 2KiB range at the top of the area - * immediately below the system registers. - */ - phys = (area << 26) + SZ_64M - SZ_4K; - - /* - * The FPGA SRAM resides in translatable physical space, so set - * up a mapping prior to inserting it in to the pool. - */ - vaddr = ioremap(phys, SZ_2K); - if (unlikely(!vaddr)) { - pr_err("Failed remapping FPGA memory.\n"); - return -ENXIO; - } - - pr_info("Adding %dKiB of FPGA memory at 0x%08lx-0x%08lx " - "(area %d) to pool.\n", - SZ_2K >> 10, phys, phys + SZ_2K - 1, area); - - ret = gen_pool_add(sram_pool, (unsigned long)vaddr, SZ_2K, -1); - if (unlikely(ret < 0)) { - pr_err("Failed adding memory\n"); - iounmap(vaddr); - return ret; - } - - return 0; -} -postcore_initcall(fpga_sram_init); diff --git a/trunk/arch/sh/boards/mach-x3proto/Makefile b/trunk/arch/sh/boards/mach-x3proto/Makefile index 708c21c919ff..983e4551fecf 100644 --- a/trunk/arch/sh/boards/mach-x3proto/Makefile +++ b/trunk/arch/sh/boards/mach-x3proto/Makefile @@ -1,3 +1 @@ obj-y += setup.o ilsel.o - -obj-$(CONFIG_GENERIC_GPIO) += gpio.o diff --git a/trunk/arch/sh/boards/mach-x3proto/gpio.c b/trunk/arch/sh/boards/mach-x3proto/gpio.c deleted file mode 100644 index 594adf76e46a..000000000000 --- a/trunk/arch/sh/boards/mach-x3proto/gpio.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - * arch/sh/boards/mach-x3proto/gpio.c - * - * Renesas SH-X3 Prototype Baseboard GPIO Support. - * - * Copyright (C) 2010 Paul Mundt - * - * 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. - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define KEYCTLR 0xb81c0000 -#define KEYOUTR 0xb81c0002 -#define KEYDETR 0xb81c0004 - -static DEFINE_SPINLOCK(x3proto_gpio_lock); -static unsigned int x3proto_gpio_irq_map[NR_BASEBOARD_GPIOS] = { 0, }; - -static int x3proto_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) -{ - unsigned long flags; - unsigned int data; - - spin_lock_irqsave(&x3proto_gpio_lock, flags); - data = __raw_readw(KEYCTLR); - data |= (1 << gpio); - __raw_writew(data, KEYCTLR); - spin_unlock_irqrestore(&x3proto_gpio_lock, flags); - - return 0; -} - -static int x3proto_gpio_get(struct gpio_chip *chip, unsigned gpio) -{ - return !!(__raw_readw(KEYDETR) & (1 << gpio)); -} - -static int x3proto_gpio_to_irq(struct gpio_chip *chip, unsigned gpio) -{ - return x3proto_gpio_irq_map[gpio]; -} - -static void x3proto_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) -{ - struct irq_chip *chip = get_irq_desc_chip(desc); - unsigned long mask; - int pin; - - chip->mask_ack(irq); - - mask = __raw_readw(KEYDETR); - - for_each_set_bit(pin, &mask, NR_BASEBOARD_GPIOS) - generic_handle_irq(x3proto_gpio_to_irq(NULL, pin)); - - chip->unmask(irq); -} - -struct gpio_chip x3proto_gpio_chip = { - .label = "x3proto-gpio", - .direction_input = x3proto_gpio_direction_input, - .get = x3proto_gpio_get, - .to_irq = x3proto_gpio_to_irq, - .base = -1, - .ngpio = NR_BASEBOARD_GPIOS, -}; - -int __init x3proto_gpio_setup(void) -{ - int ilsel; - int ret, i; - - ilsel = ilsel_enable(ILSEL_KEY); - if (unlikely(ilsel < 0)) - return ilsel; - - ret = gpiochip_add(&x3proto_gpio_chip); - if (unlikely(ret)) - goto err_gpio; - - for (i = 0; i < NR_BASEBOARD_GPIOS; i++) { - unsigned long flags; - int irq = create_irq(); - - if (unlikely(irq < 0)) { - ret = -EINVAL; - goto err_irq; - } - - spin_lock_irqsave(&x3proto_gpio_lock, flags); - x3proto_gpio_irq_map[i] = irq; - set_irq_chip_and_handler_name(irq, &dummy_irq_chip, - handle_simple_irq, "gpio"); - spin_unlock_irqrestore(&x3proto_gpio_lock, flags); - } - - pr_info("registering '%s' support, handling GPIOs %u -> %u, " - "bound to IRQ %u\n", - x3proto_gpio_chip.label, x3proto_gpio_chip.base, - x3proto_gpio_chip.base + x3proto_gpio_chip.ngpio, - ilsel); - - set_irq_chained_handler(ilsel, x3proto_gpio_irq_handler); - set_irq_wake(ilsel, 1); - - return 0; - -err_irq: - for (; i >= 0; --i) - if (x3proto_gpio_irq_map[i]) - destroy_irq(x3proto_gpio_irq_map[i]); - - ret = gpiochip_remove(&x3proto_gpio_chip); - if (unlikely(ret)) - pr_err("Failed deregistering GPIO\n"); - -err_gpio: - synchronize_irq(ilsel); - - ilsel_disable(ILSEL_KEY); - - return ret; -} diff --git a/trunk/arch/sh/boards/mach-x3proto/ilsel.c b/trunk/arch/sh/boards/mach-x3proto/ilsel.c index 95e346139515..5c9842704c60 100644 --- a/trunk/arch/sh/boards/mach-x3proto/ilsel.c +++ b/trunk/arch/sh/boards/mach-x3proto/ilsel.c @@ -1,22 +1,20 @@ /* - * arch/sh/boards/mach-x3proto/ilsel.c + * arch/sh/boards/renesas/x3proto/ilsel.c * * Helper routines for SH-X3 proto board ILSEL. * - * Copyright (C) 2007 - 2010 Paul Mundt + * Copyright (C) 2007 Paul Mundt * * 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. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include #include #include #include #include -#include +#include /* * ILSEL is split across: @@ -66,8 +64,6 @@ static void __ilsel_enable(ilsel_source_t set, unsigned int bit) unsigned int tmp, shift; unsigned long addr; - pr_notice("enabling ILSEL set %d\n", set); - addr = mk_ilsel_addr(bit); shift = mk_ilsel_shift(bit); @@ -96,10 +92,8 @@ int ilsel_enable(ilsel_source_t set) { unsigned int bit; - if (unlikely(set > ILSEL_KEY)) { - pr_err("Aliased sources must use ilsel_enable_fixed()\n"); - return -EINVAL; - } + /* Aliased sources must use ilsel_enable_fixed() */ + BUG_ON(set > ILSEL_KEY); do { bit = find_first_zero_bit(&ilsel_level_map, ILSEL_LEVELS); @@ -146,8 +140,6 @@ void ilsel_disable(unsigned int irq) unsigned long addr; unsigned int tmp; - pr_notice("disabling ILSEL set %d\n", irq); - addr = mk_ilsel_addr(irq); tmp = __raw_readw(addr); diff --git a/trunk/arch/sh/boards/mach-x3proto/setup.c b/trunk/arch/sh/boards/mach-x3proto/setup.c index d682e2b6a856..102bf56befb4 100644 --- a/trunk/arch/sh/boards/mach-x3proto/setup.c +++ b/trunk/arch/sh/boards/mach-x3proto/setup.c @@ -1,9 +1,9 @@ /* - * arch/sh/boards/mach-x3proto/setup.c + * arch/sh/boards/renesas/x3proto/setup.c * * Renesas SH-X3 Prototype Board Support. * - * Copyright (C) 2007 - 2010 Paul Mundt + * Copyright (C) 2007 - 2008 Paul Mundt * * 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 @@ -16,13 +16,9 @@ #include #include #include -#include #include #include -#include -#include -#include -#include +#include #include static struct resource heartbeat_resources[] = { @@ -126,128 +122,15 @@ static struct platform_device m66592_usb_peripheral_device = { .resource = m66592_usb_peripheral_resources, }; -static struct gpio_keys_button baseboard_buttons[NR_BASEBOARD_GPIOS] = { - { - .desc = "key44", - .code = KEY_POWER, - .active_low = 1, - .wakeup = 1, - }, { - .desc = "key43", - .code = KEY_SUSPEND, - .active_low = 1, - .wakeup = 1, - }, { - .desc = "key42", - .code = KEY_KATAKANAHIRAGANA, - .active_low = 1, - }, { - .desc = "key41", - .code = KEY_SWITCHVIDEOMODE, - .active_low = 1, - }, { - .desc = "key34", - .code = KEY_F12, - .active_low = 1, - }, { - .desc = "key33", - .code = KEY_F11, - .active_low = 1, - }, { - .desc = "key32", - .code = KEY_F10, - .active_low = 1, - }, { - .desc = "key31", - .code = KEY_F9, - .active_low = 1, - }, { - .desc = "key24", - .code = KEY_F8, - .active_low = 1, - }, { - .desc = "key23", - .code = KEY_F7, - .active_low = 1, - }, { - .desc = "key22", - .code = KEY_F6, - .active_low = 1, - }, { - .desc = "key21", - .code = KEY_F5, - .active_low = 1, - }, { - .desc = "key14", - .code = KEY_F4, - .active_low = 1, - }, { - .desc = "key13", - .code = KEY_F3, - .active_low = 1, - }, { - .desc = "key12", - .code = KEY_F2, - .active_low = 1, - }, { - .desc = "key11", - .code = KEY_F1, - .active_low = 1, - }, -}; - -static struct gpio_keys_platform_data baseboard_buttons_data = { - .buttons = baseboard_buttons, - .nbuttons = ARRAY_SIZE(baseboard_buttons), -}; - -static struct platform_device baseboard_buttons_device = { - .name = "gpio-keys", - .id = -1, - .dev = { - .platform_data = &baseboard_buttons_data, - }, -}; - static struct platform_device *x3proto_devices[] __initdata = { &heartbeat_device, &smc91x_device, &r8a66597_usb_host_device, &m66592_usb_peripheral_device, - &baseboard_buttons_device, }; -static void __init x3proto_init_irq(void) -{ - plat_irq_setup_pins(IRQ_MODE_IRL3210); - - /* Set ICR0.LVLMODE */ - __raw_writel(__raw_readl(0xfe410000) | (1 << 21), 0xfe410000); -} - static int __init x3proto_devices_setup(void) { - int ret, i; - - /* - * IRLs are only needed for ILSEL mappings, so flip over the INTC - * pins at a later point to enable the GPIOs to settle. - */ - x3proto_init_irq(); - - /* - * Now that ILSELs are available, set up the baseboard GPIOs. - */ - ret = x3proto_gpio_setup(); - if (unlikely(ret)) - return ret; - - /* - * Propagate dynamic GPIOs for the baseboard button device. - */ - for (i = 0; i < ARRAY_SIZE(baseboard_buttons); i++) - baseboard_buttons[i].gpio = x3proto_gpio_chip.base + i; - r8a66597_usb_host_resources[1].start = r8a66597_usb_host_resources[1].end = ilsel_enable(ILSEL_USBH_I); @@ -262,6 +145,14 @@ static int __init x3proto_devices_setup(void) } device_initcall(x3proto_devices_setup); +static void __init x3proto_init_irq(void) +{ + plat_irq_setup_pins(IRQ_MODE_IRL3210); + + /* Set ICR0.LVLMODE */ + __raw_writel(__raw_readl(0xfe410000) | (1 << 21), 0xfe410000); +} + static void __init x3proto_setup(char **cmdline_p) { register_smp_ops(&shx3_smp_ops); @@ -270,4 +161,5 @@ static void __init x3proto_setup(char **cmdline_p) static struct sh_machine_vector mv_x3proto __initmv = { .mv_name = "x3proto", .mv_setup = x3proto_setup, + .mv_init_irq = x3proto_init_irq, }; diff --git a/trunk/arch/sh/boot/compressed/head_32.S b/trunk/arch/sh/boot/compressed/head_32.S index 3e150326f1fd..200c1d4f1efe 100644 --- a/trunk/arch/sh/boot/compressed/head_32.S +++ b/trunk/arch/sh/boot/compressed/head_32.S @@ -91,9 +91,7 @@ bss_start_addr: end_addr: .long _end init_sr: - .long 0x500000F0 /* Privileged mode, Bank=0, Block=1, IMASK=0xF */ -kexec_magic: - .long 0x400000F0 /* magic used by kexec to parse zImage format */ + .long 0x400000F0 /* Privileged mode, Bank=0, Block=0, IMASK=0xF */ init_stack_addr: .long stack_start decompress_kernel_addr: diff --git a/trunk/arch/sh/cchips/hd6446x/Makefile b/trunk/arch/sh/cchips/hd6446x/Makefile index 59c348337bb8..9682e3ab668f 100644 --- a/trunk/arch/sh/cchips/hd6446x/Makefile +++ b/trunk/arch/sh/cchips/hd6446x/Makefile @@ -1,3 +1,3 @@ obj-$(CONFIG_HD64461) += hd64461.o -ccflags-y := -Werror +EXTRA_CFLAGS += -Werror diff --git a/trunk/arch/sh/configs/ap325rxa_defconfig b/trunk/arch/sh/configs/ap325rxa_defconfig index e5335123b5e9..238d6833ac70 100644 --- a/trunk/arch/sh/configs/ap325rxa_defconfig +++ b/trunk/arch/sh/configs/ap325rxa_defconfig @@ -3,6 +3,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_BSD_PROCESS_ACCT=y CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_KALLSYMS is not set CONFIG_SLAB=y CONFIG_MODULES=y diff --git a/trunk/arch/sh/configs/cayman_defconfig b/trunk/arch/sh/configs/cayman_defconfig index 67e150631ea5..b3bf11bcf025 100644 --- a/trunk/arch/sh/configs/cayman_defconfig +++ b/trunk/arch/sh/configs/cayman_defconfig @@ -1,6 +1,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_POSIX_MQUEUE=y CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set CONFIG_SLAB=y CONFIG_MODULES=y diff --git a/trunk/arch/sh/configs/dreamcast_defconfig b/trunk/arch/sh/configs/dreamcast_defconfig index ec243ca29529..3cdee4f0c184 100644 --- a/trunk/arch/sh/configs/dreamcast_defconfig +++ b/trunk/arch/sh/configs/dreamcast_defconfig @@ -2,6 +2,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_BSD_PROCESS_ACCT=y CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_SYSCTL_SYSCALL is not set CONFIG_SLAB=y CONFIG_PROFILING=y diff --git a/trunk/arch/sh/configs/ecovec24-romimage_defconfig b/trunk/arch/sh/configs/ecovec24-romimage_defconfig index 5fcb17bff24a..021633b02835 100644 --- a/trunk/arch/sh/configs/ecovec24-romimage_defconfig +++ b/trunk/arch/sh/configs/ecovec24-romimage_defconfig @@ -5,6 +5,7 @@ CONFIG_BSD_PROCESS_ACCT=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y # CONFIG_KALLSYMS is not set CONFIG_SLAB=y diff --git a/trunk/arch/sh/configs/edosk7760_defconfig b/trunk/arch/sh/configs/edosk7760_defconfig index e1077a041ac3..365f2318e9b5 100644 --- a/trunk/arch/sh/configs/edosk7760_defconfig +++ b/trunk/arch/sh/configs/edosk7760_defconfig @@ -5,6 +5,7 @@ CONFIG_POSIX_MQUEUE=y CONFIG_BSD_PROCESS_ACCT=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y +CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y CONFIG_KALLSYMS_ALL=y CONFIG_MODULES=y diff --git a/trunk/arch/sh/configs/espt_defconfig b/trunk/arch/sh/configs/espt_defconfig index 67cb1094a033..ca7fc1b3d567 100644 --- a/trunk/arch/sh/configs/espt_defconfig +++ b/trunk/arch/sh/configs/espt_defconfig @@ -3,6 +3,7 @@ CONFIG_SYSVIPC=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_NAMESPACES=y CONFIG_UTS_NS=y CONFIG_IPC_NS=y diff --git a/trunk/arch/sh/configs/hp6xx_defconfig b/trunk/arch/sh/configs/hp6xx_defconfig index 496edcdf95a3..45c18a3830d2 100644 --- a/trunk/arch/sh/configs/hp6xx_defconfig +++ b/trunk/arch/sh/configs/hp6xx_defconfig @@ -3,6 +3,7 @@ CONFIG_BSD_PROCESS_ACCT=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set # CONFIG_SYSCTL_SYSCALL is not set CONFIG_SLAB=y diff --git a/trunk/arch/sh/configs/kfr2r09-romimage_defconfig b/trunk/arch/sh/configs/kfr2r09-romimage_defconfig index 029a506ca325..d4268b1953bc 100644 --- a/trunk/arch/sh/configs/kfr2r09-romimage_defconfig +++ b/trunk/arch/sh/configs/kfr2r09-romimage_defconfig @@ -5,6 +5,7 @@ CONFIG_BSD_PROCESS_ACCT=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y # CONFIG_KALLSYMS is not set CONFIG_SLAB=y diff --git a/trunk/arch/sh/configs/kfr2r09_defconfig b/trunk/arch/sh/configs/kfr2r09_defconfig index fac13ded07b2..ad5d296b375f 100644 --- a/trunk/arch/sh/configs/kfr2r09_defconfig +++ b/trunk/arch/sh/configs/kfr2r09_defconfig @@ -5,6 +5,7 @@ CONFIG_BSD_PROCESS_ACCT=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y # CONFIG_KALLSYMS is not set CONFIG_SLAB=y diff --git a/trunk/arch/sh/configs/landisk_defconfig b/trunk/arch/sh/configs/landisk_defconfig index 3670e937f2b7..14e658e9318f 100644 --- a/trunk/arch/sh/configs/landisk_defconfig +++ b/trunk/arch/sh/configs/landisk_defconfig @@ -1,6 +1,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_SYSCTL_SYSCALL is not set CONFIG_KALLSYMS_EXTRA_PASS=y CONFIG_SLAB=y diff --git a/trunk/arch/sh/configs/lboxre2_defconfig b/trunk/arch/sh/configs/lboxre2_defconfig index e3c0894b1bb4..6be7eaaa8bb6 100644 --- a/trunk/arch/sh/configs/lboxre2_defconfig +++ b/trunk/arch/sh/configs/lboxre2_defconfig @@ -1,6 +1,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_SYSCTL_SYSCALL is not set CONFIG_KALLSYMS_EXTRA_PASS=y CONFIG_SLAB=y diff --git a/trunk/arch/sh/configs/magicpanelr2_defconfig b/trunk/arch/sh/configs/magicpanelr2_defconfig index 9479872b1ae6..4d61b7711b40 100644 --- a/trunk/arch/sh/configs/magicpanelr2_defconfig +++ b/trunk/arch/sh/configs/magicpanelr2_defconfig @@ -5,6 +5,7 @@ CONFIG_POSIX_MQUEUE=y CONFIG_BSD_PROCESS_ACCT=y CONFIG_BSD_PROCESS_ACCT_V3=y CONFIG_AUDIT=y +CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_RELAY=y CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set diff --git a/trunk/arch/sh/configs/microdev_defconfig b/trunk/arch/sh/configs/microdev_defconfig index f1d2e1b5ee41..0e32a24fed53 100644 --- a/trunk/arch/sh/configs/microdev_defconfig +++ b/trunk/arch/sh/configs/microdev_defconfig @@ -1,6 +1,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_BSD_PROCESS_ACCT=y CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set # CONFIG_SYSCTL_SYSCALL is not set diff --git a/trunk/arch/sh/configs/migor_defconfig b/trunk/arch/sh/configs/migor_defconfig index 9ad904a110de..c19fcdfdee37 100644 --- a/trunk/arch/sh/configs/migor_defconfig +++ b/trunk/arch/sh/configs/migor_defconfig @@ -3,6 +3,7 @@ CONFIG_SYSVIPC=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set # CONFIG_SYSCTL_SYSCALL is not set diff --git a/trunk/arch/sh/configs/polaris_defconfig b/trunk/arch/sh/configs/polaris_defconfig index f3d5d9f76310..984e3fe1ce5d 100644 --- a/trunk/arch/sh/configs/polaris_defconfig +++ b/trunk/arch/sh/configs/polaris_defconfig @@ -7,6 +7,7 @@ CONFIG_BSD_PROCESS_ACCT=y CONFIG_BSD_PROCESS_ACCT_V3=y CONFIG_AUDIT=y CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set CONFIG_SLAB=y CONFIG_MODULES=y diff --git a/trunk/arch/sh/configs/r7780mp_defconfig b/trunk/arch/sh/configs/r7780mp_defconfig index 920b8471ceb7..e8b5472e6d84 100644 --- a/trunk/arch/sh/configs/r7780mp_defconfig +++ b/trunk/arch/sh/configs/r7780mp_defconfig @@ -4,6 +4,7 @@ CONFIG_BSD_PROCESS_ACCT=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_FUTEX is not set # CONFIG_EPOLL is not set diff --git a/trunk/arch/sh/configs/r7785rp_defconfig b/trunk/arch/sh/configs/r7785rp_defconfig index c77da6be06b8..fd8848060982 100644 --- a/trunk/arch/sh/configs/r7785rp_defconfig +++ b/trunk/arch/sh/configs/r7785rp_defconfig @@ -8,6 +8,7 @@ CONFIG_RCU_TRACE=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_SYSCTL_SYSCALL is not set CONFIG_SLAB=y CONFIG_PROFILING=y diff --git a/trunk/arch/sh/configs/rts7751r2d1_defconfig b/trunk/arch/sh/configs/rts7751r2d1_defconfig index a3d081095ce2..a42f7c22ca1a 100644 --- a/trunk/arch/sh/configs/rts7751r2d1_defconfig +++ b/trunk/arch/sh/configs/rts7751r2d1_defconfig @@ -1,6 +1,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set # CONFIG_SYSCTL_SYSCALL is not set CONFIG_SLAB=y diff --git a/trunk/arch/sh/configs/rts7751r2dplus_defconfig b/trunk/arch/sh/configs/rts7751r2dplus_defconfig index b1a04f3c598b..742aa61f2427 100644 --- a/trunk/arch/sh/configs/rts7751r2dplus_defconfig +++ b/trunk/arch/sh/configs/rts7751r2dplus_defconfig @@ -1,6 +1,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set # CONFIG_SYSCTL_SYSCALL is not set CONFIG_SLAB=y diff --git a/trunk/arch/sh/configs/sdk7780_defconfig b/trunk/arch/sh/configs/sdk7780_defconfig index ae1115849dda..aed394d89346 100644 --- a/trunk/arch/sh/configs/sdk7780_defconfig +++ b/trunk/arch/sh/configs/sdk7780_defconfig @@ -6,6 +6,7 @@ CONFIG_BSD_PROCESS_ACCT=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=18 +CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_RELAY=y CONFIG_KALLSYMS_ALL=y CONFIG_MODULES=y diff --git a/trunk/arch/sh/configs/se7343_defconfig b/trunk/arch/sh/configs/se7343_defconfig index be9c474197b3..7a7e13853cfd 100644 --- a/trunk/arch/sh/configs/se7343_defconfig +++ b/trunk/arch/sh/configs/se7343_defconfig @@ -3,6 +3,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_FUTEX is not set # CONFIG_EPOLL is not set diff --git a/trunk/arch/sh/configs/se7712_defconfig b/trunk/arch/sh/configs/se7712_defconfig index 1248635e4f88..3620a7f4c821 100644 --- a/trunk/arch/sh/configs/se7712_defconfig +++ b/trunk/arch/sh/configs/se7712_defconfig @@ -5,6 +5,7 @@ CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y CONFIG_BSD_PROCESS_ACCT=y CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set CONFIG_KALLSYMS_ALL=y # CONFIG_BUG is not set diff --git a/trunk/arch/sh/configs/se7721_defconfig b/trunk/arch/sh/configs/se7721_defconfig index c3ba6e8a9818..fe22f599c0cb 100644 --- a/trunk/arch/sh/configs/se7721_defconfig +++ b/trunk/arch/sh/configs/se7721_defconfig @@ -5,6 +5,7 @@ CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y CONFIG_BSD_PROCESS_ACCT=y CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set CONFIG_KALLSYMS_ALL=y # CONFIG_BUG is not set diff --git a/trunk/arch/sh/configs/se7722_defconfig b/trunk/arch/sh/configs/se7722_defconfig index ae998c7e2ee0..b9b64c38810e 100644 --- a/trunk/arch/sh/configs/se7722_defconfig +++ b/trunk/arch/sh/configs/se7722_defconfig @@ -4,6 +4,7 @@ CONFIG_BSD_PROCESS_ACCT=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y CONFIG_PROFILING=y CONFIG_MODULES=y diff --git a/trunk/arch/sh/configs/se7724_defconfig b/trunk/arch/sh/configs/se7724_defconfig index ed35093e3758..03e736781c2e 100644 --- a/trunk/arch/sh/configs/se7724_defconfig +++ b/trunk/arch/sh/configs/se7724_defconfig @@ -3,6 +3,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_BSD_PROCESS_ACCT=y CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_KALLSYMS is not set CONFIG_SLAB=y CONFIG_MODULES=y diff --git a/trunk/arch/sh/configs/se7750_defconfig b/trunk/arch/sh/configs/se7750_defconfig index 912c98590e22..1a686b6d5cd4 100644 --- a/trunk/arch/sh/configs/se7750_defconfig +++ b/trunk/arch/sh/configs/se7750_defconfig @@ -5,6 +5,7 @@ CONFIG_BSD_PROCESS_ACCT=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_HOTPLUG is not set diff --git a/trunk/arch/sh/configs/se7751_defconfig b/trunk/arch/sh/configs/se7751_defconfig index 75c92fc1876b..7e03451a9fad 100644 --- a/trunk/arch/sh/configs/se7751_defconfig +++ b/trunk/arch/sh/configs/se7751_defconfig @@ -2,6 +2,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_BSD_PROCESS_ACCT=y CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set # CONFIG_SYSCTL_SYSCALL is not set diff --git a/trunk/arch/sh/configs/se7780_defconfig b/trunk/arch/sh/configs/se7780_defconfig index c8c5e7f7a68d..4cfc4deff135 100644 --- a/trunk/arch/sh/configs/se7780_defconfig +++ b/trunk/arch/sh/configs/se7780_defconfig @@ -3,6 +3,7 @@ CONFIG_SYSVIPC=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_KALLSYMS is not set # CONFIG_HOTPLUG is not set # CONFIG_EPOLL is not set diff --git a/trunk/arch/sh/configs/sh03_defconfig b/trunk/arch/sh/configs/sh03_defconfig index 2051821724c6..b95dc76b04c1 100644 --- a/trunk/arch/sh/configs/sh03_defconfig +++ b/trunk/arch/sh/configs/sh03_defconfig @@ -3,6 +3,7 @@ CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y CONFIG_BSD_PROCESS_ACCT=y CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y # CONFIG_SYSCTL_SYSCALL is not set CONFIG_SLAB=y diff --git a/trunk/arch/sh/configs/sh2007_defconfig b/trunk/arch/sh/configs/sh2007_defconfig deleted file mode 100644 index 0d2f41472a19..000000000000 --- a/trunk/arch/sh/configs/sh2007_defconfig +++ /dev/null @@ -1,212 +0,0 @@ -CONFIG_EXPERIMENTAL=y -# CONFIG_LOCALVERSION_AUTO is not set -CONFIG_SYSVIPC=y -CONFIG_POSIX_MQUEUE=y -CONFIG_BSD_PROCESS_ACCT=y -CONFIG_AUDIT=y -CONFIG_AUDITSYSCALL=y -CONFIG_IKCONFIG=y -CONFIG_LOG_BUF_SHIFT=14 -# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_KALLSYMS_ALL=y -CONFIG_SLAB=y -# CONFIG_BLK_DEV_BSG is not set -CONFIG_CPU_SUBTYPE_SH7780=y -CONFIG_MEMORY_SIZE=0x08000000 -# CONFIG_VSYSCALL is not set -CONFIG_FLATMEM_MANUAL=y -CONFIG_SH_SH2007=y -CONFIG_HIGH_RES_TIMERS=y -CONFIG_SH_DMA=y -CONFIG_SH_DMA_API=y -CONFIG_NR_DMA_CHANNELS_BOOL=y -CONFIG_HZ_100=y -CONFIG_CMDLINE_OVERWRITE=y -CONFIG_CMDLINE="console=ttySC1,115200 ip=dhcp root=/dev/nfs rw nfsroot=/nfs/rootfs,rsize=1024,wsize=1024 earlyprintk=sh-sci.1" -CONFIG_PCCARD=y -CONFIG_BINFMT_MISC=y -CONFIG_PACKET=y -CONFIG_UNIX=y -CONFIG_XFRM_USER=y -CONFIG_NET_KEY=y -CONFIG_NET_KEY_MIGRATE=y -CONFIG_INET=y -CONFIG_IP_ADVANCED_ROUTER=y -CONFIG_IP_MULTIPLE_TABLES=y -CONFIG_IP_ROUTE_MULTIPATH=y -CONFIG_IP_ROUTE_VERBOSE=y -CONFIG_IP_PNP=y -CONFIG_IP_PNP_DHCP=y -CONFIG_NET_IPIP=y -# CONFIG_INET_XFRM_MODE_TRANSPORT is not set -# CONFIG_INET_XFRM_MODE_TUNNEL is not set -# CONFIG_INET_XFRM_MODE_BEET is not set -# CONFIG_INET_LRO is not set -# CONFIG_IPV6 is not set -CONFIG_NETWORK_SECMARK=y -CONFIG_NET_PKTGEN=y -CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" -CONFIG_BLK_DEV_LOOP=y -CONFIG_BLK_DEV_RAM=y -CONFIG_CDROM_PKTCDVD=y -# CONFIG_MISC_DEVICES is not set -CONFIG_RAID_ATTRS=y -CONFIG_SCSI=y -CONFIG_SCSI_TGT=y -CONFIG_BLK_DEV_SD=y -CONFIG_BLK_DEV_SR=y -CONFIG_CHR_DEV_SG=y -CONFIG_SCSI_MULTI_LUN=y -CONFIG_SCSI_CONSTANTS=y -CONFIG_SCSI_LOGGING=y -CONFIG_SCSI_SCAN_ASYNC=y -CONFIG_SCSI_SPI_ATTRS=y -CONFIG_SCSI_FC_ATTRS=y -CONFIG_SCSI_ISCSI_ATTRS=y -CONFIG_SCSI_SRP_ATTRS=y -# CONFIG_SCSI_LOWLEVEL is not set -CONFIG_NETDEVICES=y -CONFIG_DUMMY=y -CONFIG_EQUALIZER=y -CONFIG_TUN=y -CONFIG_VETH=y -CONFIG_NET_ETHERNET=y -CONFIG_SMSC911X=y -# CONFIG_NETDEV_1000 is not set -# CONFIG_NETDEV_10000 is not set -# CONFIG_WLAN is not set -CONFIG_INPUT_FF_MEMLESS=y -# CONFIG_INPUT_MOUSEDEV is not set -# CONFIG_INPUT_KEYBOARD is not set -# CONFIG_INPUT_MOUSE is not set -# CONFIG_SERIO is not set -CONFIG_VT_HW_CONSOLE_BINDING=y -# CONFIG_DEVKMEM is not set -CONFIG_SERIAL_SH_SCI=y -CONFIG_SERIAL_SH_SCI_CONSOLE=y -# CONFIG_LEGACY_PTYS is not set -# CONFIG_HWMON is not set -CONFIG_WATCHDOG=y -CONFIG_SH_WDT=y -CONFIG_SSB=y -CONFIG_FB=y -CONFIG_BACKLIGHT_LCD_SUPPORT=y -# CONFIG_LCD_CLASS_DEVICE is not set -CONFIG_FRAMEBUFFER_CONSOLE=y -CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y -CONFIG_LOGO=y -# CONFIG_HID_SUPPORT is not set -CONFIG_USB=y -CONFIG_USB_DEVICEFS=y -# CONFIG_USB_DEVICE_CLASS is not set -CONFIG_USB_MON=y -CONFIG_NEW_LEDS=y -CONFIG_LEDS_CLASS=y -CONFIG_LEDS_TRIGGERS=y -CONFIG_RTC_CLASS=y -CONFIG_RTC_INTF_DEV_UIE_EMUL=y -CONFIG_DMADEVICES=y -CONFIG_TIMB_DMA=y -CONFIG_EXT3_FS=y -CONFIG_ISO9660_FS=y -CONFIG_JOLIET=y -CONFIG_ZISOFS=y -CONFIG_UDF_FS=y -CONFIG_MSDOS_FS=y -CONFIG_VFAT_FS=y -CONFIG_FAT_DEFAULT_CODEPAGE=932 -CONFIG_FAT_DEFAULT_IOCHARSET="ascii" -CONFIG_PROC_KCORE=y -CONFIG_TMPFS=y -CONFIG_TMPFS_POSIX_ACL=y -CONFIG_CONFIGFS_FS=y -# CONFIG_MISC_FILESYSTEMS is not set -CONFIG_NFS_FS=y -CONFIG_NFS_V3=y -CONFIG_NFS_V3_ACL=y -CONFIG_NFS_V4=y -CONFIG_ROOT_NFS=y -CONFIG_NLS_DEFAULT="utf8" -CONFIG_NLS_CODEPAGE_437=y -CONFIG_NLS_CODEPAGE_737=y -CONFIG_NLS_CODEPAGE_775=y -CONFIG_NLS_CODEPAGE_850=y -CONFIG_NLS_CODEPAGE_852=y -CONFIG_NLS_CODEPAGE_855=y -CONFIG_NLS_CODEPAGE_857=y -CONFIG_NLS_CODEPAGE_860=y -CONFIG_NLS_CODEPAGE_861=y -CONFIG_NLS_CODEPAGE_862=y -CONFIG_NLS_CODEPAGE_863=y -CONFIG_NLS_CODEPAGE_864=y -CONFIG_NLS_CODEPAGE_865=y -CONFIG_NLS_CODEPAGE_866=y -CONFIG_NLS_CODEPAGE_869=y -CONFIG_NLS_CODEPAGE_936=y -CONFIG_NLS_CODEPAGE_950=y -CONFIG_NLS_CODEPAGE_932=y -CONFIG_NLS_CODEPAGE_949=y -CONFIG_NLS_CODEPAGE_874=y -CONFIG_NLS_ISO8859_8=y -CONFIG_NLS_CODEPAGE_1250=y -CONFIG_NLS_CODEPAGE_1251=y -CONFIG_NLS_ASCII=y -CONFIG_NLS_ISO8859_1=y -CONFIG_NLS_ISO8859_2=y -CONFIG_NLS_ISO8859_3=y -CONFIG_NLS_ISO8859_4=y -CONFIG_NLS_ISO8859_5=y -CONFIG_NLS_ISO8859_6=y -CONFIG_NLS_ISO8859_7=y -CONFIG_NLS_ISO8859_9=y -CONFIG_NLS_ISO8859_13=y -CONFIG_NLS_ISO8859_14=y -CONFIG_NLS_ISO8859_15=y -CONFIG_NLS_KOI8_R=y -CONFIG_NLS_KOI8_U=y -CONFIG_NLS_UTF8=y -# CONFIG_ENABLE_WARN_DEPRECATED is not set -# CONFIG_ENABLE_MUST_CHECK is not set -CONFIG_DEBUG_FS=y -CONFIG_DEBUG_KERNEL=y -# CONFIG_DETECT_SOFTLOCKUP is not set -# CONFIG_SCHED_DEBUG is not set -CONFIG_DEBUG_INFO=y -CONFIG_FRAME_POINTER=y -# CONFIG_RCU_CPU_STALL_DETECTOR is not set -CONFIG_SH_STANDARD_BIOS=y -CONFIG_CRYPTO_NULL=y -CONFIG_CRYPTO_AUTHENC=y -CONFIG_CRYPTO_ECB=y -CONFIG_CRYPTO_LRW=y -CONFIG_CRYPTO_PCBC=y -CONFIG_CRYPTO_XTS=y -CONFIG_CRYPTO_HMAC=y -CONFIG_CRYPTO_XCBC=y -CONFIG_CRYPTO_MD4=y -CONFIG_CRYPTO_MICHAEL_MIC=y -CONFIG_CRYPTO_SHA1=y -CONFIG_CRYPTO_SHA256=y -CONFIG_CRYPTO_SHA512=y -CONFIG_CRYPTO_TGR192=y -CONFIG_CRYPTO_WP512=y -CONFIG_CRYPTO_AES=y -CONFIG_CRYPTO_ANUBIS=y -CONFIG_CRYPTO_ARC4=y -CONFIG_CRYPTO_BLOWFISH=y -CONFIG_CRYPTO_CAMELLIA=y -CONFIG_CRYPTO_CAST5=y -CONFIG_CRYPTO_CAST6=y -CONFIG_CRYPTO_FCRYPT=y -CONFIG_CRYPTO_KHAZAD=y -CONFIG_CRYPTO_SEED=y -CONFIG_CRYPTO_SERPENT=y -CONFIG_CRYPTO_TEA=y -CONFIG_CRYPTO_TWOFISH=y -CONFIG_CRYPTO_DEFLATE=y -CONFIG_CRYPTO_LZO=y -# CONFIG_CRYPTO_ANSI_CPRNG is not set -# CONFIG_CRYPTO_HW is not set -CONFIG_CRC_CCITT=y -CONFIG_CRC16=y -CONFIG_LIBCRC32C=y diff --git a/trunk/arch/sh/configs/sh7710voipgw_defconfig b/trunk/arch/sh/configs/sh7710voipgw_defconfig index f92ad17cd629..b804641c8dd2 100644 --- a/trunk/arch/sh/configs/sh7710voipgw_defconfig +++ b/trunk/arch/sh/configs/sh7710voipgw_defconfig @@ -3,6 +3,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_FUTEX is not set # CONFIG_EPOLL is not set diff --git a/trunk/arch/sh/configs/sh7757lcr_defconfig b/trunk/arch/sh/configs/sh7757lcr_defconfig deleted file mode 100644 index 273f3fa198f7..000000000000 --- a/trunk/arch/sh/configs/sh7757lcr_defconfig +++ /dev/null @@ -1,85 +0,0 @@ -CONFIG_EXPERIMENTAL=y -# CONFIG_SWAP is not set -CONFIG_SYSVIPC=y -CONFIG_POSIX_MQUEUE=y -CONFIG_BSD_PROCESS_ACCT=y -CONFIG_TASKSTATS=y -CONFIG_TASK_DELAY_ACCT=y -CONFIG_TASK_XACCT=y -CONFIG_TASK_IO_ACCOUNTING=y -CONFIG_LOG_BUF_SHIFT=14 -CONFIG_BLK_DEV_INITRD=y -# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -# CONFIG_SYSCTL_SYSCALL is not set -CONFIG_KALLSYMS_ALL=y -CONFIG_SLAB=y -CONFIG_MODULES=y -CONFIG_MODULE_UNLOAD=y -# CONFIG_BLK_DEV_BSG is not set -CONFIG_CPU_SUBTYPE_SH7757=y -CONFIG_MEMORY_START=0x40000000 -CONFIG_MEMORY_SIZE=0x0f000000 -CONFIG_PMB=y -CONFIG_FLATMEM_MANUAL=y -CONFIG_SH_SH7757LCR=y -CONFIG_HEARTBEAT=y -CONFIG_SECCOMP=y -CONFIG_CMDLINE_OVERWRITE=y -CONFIG_CMDLINE="console=ttySC2,115200 root=/dev/nfs ip=dhcp" -CONFIG_NET=y -CONFIG_PACKET=y -CONFIG_UNIX=y -CONFIG_INET=y -CONFIG_IP_MULTICAST=y -CONFIG_IP_PNP=y -CONFIG_IP_PNP_DHCP=y -# CONFIG_INET_LRO is not set -CONFIG_IPV6=y -# CONFIG_WIRELESS is not set -CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" -# CONFIG_FW_LOADER is not set -CONFIG_BLK_DEV_RAM=y -# CONFIG_MISC_DEVICES is not set -CONFIG_NETDEVICES=y -CONFIG_PHYLIB=y -CONFIG_VITESSE_PHY=y -CONFIG_MDIO_BITBANG=y -CONFIG_NET_ETHERNET=y -CONFIG_MII=y -# CONFIG_NETDEV_10000 is not set -# CONFIG_WLAN is not set -# CONFIG_KEYBOARD_ATKBD is not set -# CONFIG_MOUSE_PS2 is not set -# CONFIG_SERIO is not set -CONFIG_SERIAL_8250=y -CONFIG_SERIAL_8250_CONSOLE=y -CONFIG_SERIAL_8250_NR_UARTS=2 -CONFIG_SERIAL_SH_SCI=y -CONFIG_SERIAL_SH_SCI_NR_UARTS=3 -CONFIG_SERIAL_SH_SCI_CONSOLE=y -# CONFIG_LEGACY_PTYS is not set -# CONFIG_HW_RANDOM is not set -# CONFIG_HWMON is not set -# CONFIG_USB_SUPPORT is not set -CONFIG_EXT2_FS=y -CONFIG_EXT3_FS=y -CONFIG_INOTIFY=y -CONFIG_ISO9660_FS=y -CONFIG_VFAT_FS=y -CONFIG_PROC_KCORE=y -CONFIG_TMPFS=y -CONFIG_SQUASHFS=y -CONFIG_MINIX_FS=y -CONFIG_NFS_FS=y -CONFIG_ROOT_NFS=y -CONFIG_NLS_CODEPAGE_437=y -CONFIG_NLS_CODEPAGE_932=y -CONFIG_NLS_ISO8859_1=y -CONFIG_DEBUG_KERNEL=y -# CONFIG_DETECT_SOFTLOCKUP is not set -# CONFIG_SCHED_DEBUG is not set -# CONFIG_DEBUG_BUGVERBOSE is not set -CONFIG_DEBUG_INFO=y -# CONFIG_RCU_CPU_STALL_DETECTOR is not set -# CONFIG_FTRACE is not set -# CONFIG_CRYPTO_ANSI_CPRNG is not set diff --git a/trunk/arch/sh/configs/sh7763rdp_defconfig b/trunk/arch/sh/configs/sh7763rdp_defconfig index 479536440264..361876786932 100644 --- a/trunk/arch/sh/configs/sh7763rdp_defconfig +++ b/trunk/arch/sh/configs/sh7763rdp_defconfig @@ -3,6 +3,7 @@ CONFIG_SYSVIPC=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_NAMESPACES=y CONFIG_UTS_NS=y CONFIG_IPC_NS=y diff --git a/trunk/arch/sh/configs/sh7785lcr_defconfig b/trunk/arch/sh/configs/sh7785lcr_defconfig index 51561f5677d8..ee6b81f7539e 100644 --- a/trunk/arch/sh/configs/sh7785lcr_defconfig +++ b/trunk/arch/sh/configs/sh7785lcr_defconfig @@ -4,6 +4,7 @@ CONFIG_BSD_PROCESS_ACCT=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_SLAB=y CONFIG_PROFILING=y CONFIG_MODULES=y diff --git a/trunk/arch/sh/configs/shx3_defconfig b/trunk/arch/sh/configs/shx3_defconfig index 3f92d37c6374..bb4f60c0f866 100644 --- a/trunk/arch/sh/configs/shx3_defconfig +++ b/trunk/arch/sh/configs/shx3_defconfig @@ -15,6 +15,7 @@ CONFIG_CGROUP_DEVICE=y CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y CONFIG_CGROUP_MEM_RES_CTLR=y +CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_RELAY=y CONFIG_NAMESPACES=y CONFIG_UTS_NS=y diff --git a/trunk/arch/sh/configs/snapgear_defconfig b/trunk/arch/sh/configs/snapgear_defconfig index 7eae4e59d7f0..f38c98341f15 100644 --- a/trunk/arch/sh/configs/snapgear_defconfig +++ b/trunk/arch/sh/configs/snapgear_defconfig @@ -1,6 +1,7 @@ CONFIG_EXPERIMENTAL=y # CONFIG_SWAP is not set CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_HOTPLUG is not set diff --git a/trunk/arch/sh/configs/systemh_defconfig b/trunk/arch/sh/configs/systemh_defconfig index b58dfc505efe..7007d00c67e0 100644 --- a/trunk/arch/sh/configs/systemh_defconfig +++ b/trunk/arch/sh/configs/systemh_defconfig @@ -1,5 +1,6 @@ CONFIG_EXPERIMENTAL=y CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set # CONFIG_SYSCTL_SYSCALL is not set diff --git a/trunk/arch/sh/configs/titan_defconfig b/trunk/arch/sh/configs/titan_defconfig index 0f558914e760..45c309ff447e 100644 --- a/trunk/arch/sh/configs/titan_defconfig +++ b/trunk/arch/sh/configs/titan_defconfig @@ -5,6 +5,7 @@ CONFIG_POSIX_MQUEUE=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=16 +CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set # CONFIG_SYSCTL_SYSCALL is not set diff --git a/trunk/arch/sh/configs/ul2_defconfig b/trunk/arch/sh/configs/ul2_defconfig index 2d288b887fbd..e107d424acf0 100644 --- a/trunk/arch/sh/configs/ul2_defconfig +++ b/trunk/arch/sh/configs/ul2_defconfig @@ -4,6 +4,7 @@ CONFIG_BSD_PROCESS_ACCT=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y CONFIG_PROFILING=y CONFIG_MODULES=y diff --git a/trunk/arch/sh/drivers/dma/dma-api.c b/trunk/arch/sh/drivers/dma/dma-api.c index f46848f088e4..4a277224a871 100644 --- a/trunk/arch/sh/drivers/dma/dma-api.c +++ b/trunk/arch/sh/drivers/dma/dma-api.c @@ -412,8 +412,8 @@ EXPORT_SYMBOL(unregister_dmac); static int __init dma_api_init(void) { printk(KERN_NOTICE "DMA: Registering DMA API.\n"); - return create_proc_read_entry("dma", 0, 0, dma_read_proc, 0) - ? 0 : -ENOMEM; + create_proc_read_entry("dma", 0, 0, dma_read_proc, 0); + return 0; } subsys_initcall(dma_api_init); diff --git a/trunk/arch/sh/drivers/pci/Makefile b/trunk/arch/sh/drivers/pci/Makefile index 82f0a335fd19..4a59e6890876 100644 --- a/trunk/arch/sh/drivers/pci/Makefile +++ b/trunk/arch/sh/drivers/pci/Makefile @@ -19,7 +19,6 @@ obj-$(CONFIG_SH_RTS7751R2D) += fixups-rts7751r2d.o obj-$(CONFIG_SH_SH03) += fixups-sh03.o obj-$(CONFIG_SH_HIGHLANDER) += fixups-r7780rp.o obj-$(CONFIG_SH_SH7785LCR) += fixups-r7780rp.o -obj-$(CONFIG_SH_SDK7786) += fixups-sdk7786.o obj-$(CONFIG_SH_SDK7780) += fixups-sdk7780.o obj-$(CONFIG_SH_7780_SOLUTION_ENGINE) += fixups-sdk7780.o obj-$(CONFIG_SH_TITAN) += fixups-titan.o diff --git a/trunk/arch/sh/drivers/pci/fixups-sdk7786.c b/trunk/arch/sh/drivers/pci/fixups-sdk7786.c deleted file mode 100644 index 0e18ee332553..000000000000 --- a/trunk/arch/sh/drivers/pci/fixups-sdk7786.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * SDK7786 FPGA PCIe mux handling - * - * Copyright (C) 2010 Paul Mundt - * - * 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. - */ -#define pr_fmt(fmt) "PCI: " fmt - -#include -#include -#include -#include - -/* - * The SDK7786 FPGA supports mangling of most of the slots in some way or - * another. Slots 3/4 are special in that only one can be supported at a - * time, and both appear on port 3 to the PCI bus scan. Enabling slot 4 - * (the horizontal edge connector) will disable slot 3 entirely. - * - * Misconfigurations can be detected through the FPGA via the slot - * resistors to determine card presence. Hotplug remains unsupported. - */ -static unsigned int slot4en __devinitdata; - -char *__devinit pcibios_setup(char *str) -{ - if (strcmp(str, "slot4en") == 0) { - slot4en = 1; - return NULL; - } - - return str; -} - -static int __init sdk7786_pci_init(void) -{ - u16 data = fpga_read_reg(PCIECR); - - /* - * Enable slot #4 if it's been specified on the command line. - * - * Optionally reroute if slot #4 has a card present while slot #3 - * does not, regardless of command line value. - * - * Card presence is logically inverted. - */ - slot4en ?: (!(data & PCIECR_PRST4) && (data & PCIECR_PRST3)); - if (slot4en) { - pr_info("Activating PCIe slot#4 (disabling slot#3)\n"); - - data &= ~PCIECR_PCIEMUX1; - fpga_write_reg(data, PCIECR); - - /* Warn about forced rerouting if slot#3 is occupied */ - if ((data & PCIECR_PRST3) == 0) { - pr_warning("Unreachable card detected in slot#3\n"); - return -EBUSY; - } - } else - pr_info("PCIe slot#4 disabled\n"); - - return 0; -} -postcore_initcall(sdk7786_pci_init); diff --git a/trunk/arch/sh/drivers/pci/ops-sh4.c b/trunk/arch/sh/drivers/pci/ops-sh4.c index b6234203e0ac..0b81999fb88b 100644 --- a/trunk/arch/sh/drivers/pci/ops-sh4.c +++ b/trunk/arch/sh/drivers/pci/ops-sh4.c @@ -9,7 +9,6 @@ */ #include #include -#include #include #include "pci-sh4.h" @@ -19,6 +18,8 @@ #define CONFIG_CMD(bus, devfn, where) \ (0x80000000 | (bus->number << 16) | (devfn << 8) | (where & ~3)) +static DEFINE_SPINLOCK(sh4_pci_lock); + /* * Functions for accessing PCI configuration space with type 1 accesses */ @@ -33,10 +34,10 @@ static int sh4_pci_read(struct pci_bus *bus, unsigned int devfn, * PCIPDR may only be accessed as 32 bit words, * so we must do byte alignment by hand */ - raw_spin_lock_irqsave(&pci_config_lock, flags); + spin_lock_irqsave(&sh4_pci_lock, flags); pci_write_reg(chan, CONFIG_CMD(bus, devfn, where), SH4_PCIPAR); data = pci_read_reg(chan, SH4_PCIPDR); - raw_spin_unlock_irqrestore(&pci_config_lock, flags); + spin_unlock_irqrestore(&sh4_pci_lock, flags); switch (size) { case 1: @@ -68,10 +69,10 @@ static int sh4_pci_write(struct pci_bus *bus, unsigned int devfn, int shift; u32 data; - raw_spin_lock_irqsave(&pci_config_lock, flags); + spin_lock_irqsave(&sh4_pci_lock, flags); pci_write_reg(chan, CONFIG_CMD(bus, devfn, where), SH4_PCIPAR); data = pci_read_reg(chan, SH4_PCIPDR); - raw_spin_unlock_irqrestore(&pci_config_lock, flags); + spin_unlock_irqrestore(&sh4_pci_lock, flags); switch (size) { case 1: diff --git a/trunk/arch/sh/drivers/pci/ops-sh7786.c b/trunk/arch/sh/drivers/pci/ops-sh7786.c index 128421009e3f..48f594b9582b 100644 --- a/trunk/arch/sh/drivers/pci/ops-sh7786.c +++ b/trunk/arch/sh/drivers/pci/ops-sh7786.c @@ -1,7 +1,7 @@ /* * Generic SH7786 PCI-Express operations. * - * Copyright (C) 2009 - 2010 Paul Mundt + * Copyright (C) 2009 Paul Mundt * * This file is subject to the terms and conditions of the GNU General Public * License v2. See the file "COPYING" in the main directory of this archive @@ -19,72 +19,37 @@ enum { PCI_ACCESS_WRITE, }; +static DEFINE_SPINLOCK(sh7786_pcie_lock); + static int sh7786_pcie_config_access(unsigned char access_type, struct pci_bus *bus, unsigned int devfn, int where, u32 *data) { struct pci_channel *chan = bus->sysdata; - int dev, func, type, reg; + int dev, func; dev = PCI_SLOT(devfn); func = PCI_FUNC(devfn); - type = !!bus->parent; - reg = where & ~3; if (bus->number > 255 || dev > 31 || func > 7) return PCIBIOS_FUNC_NOT_SUPPORTED; - - /* - * While each channel has its own memory-mapped extended config - * space, it's generally only accessible when in endpoint mode. - * When in root complex mode, the controller is unable to target - * itself with either type 0 or type 1 accesses, and indeed, any - * controller initiated target transfer to its own config space - * result in a completer abort. - * - * Each channel effectively only supports a single device, but as - * the same channel <-> device access works for any PCI_SLOT() - * value, we cheat a bit here and bind the controller's config - * space to devfn 0 in order to enable self-enumeration. In this - * case the regular PAR/PDR path is sidelined and the mangled - * config access itself is initiated as a SuperHyway transaction. - */ - if (pci_is_root_bus(bus)) { - if (dev == 0) { - if (access_type == PCI_ACCESS_READ) - *data = pci_read_reg(chan, PCI_REG(reg)); - else - pci_write_reg(chan, *data, PCI_REG(reg)); - - return PCIBIOS_SUCCESSFUL; - } else if (dev > 1) - return PCIBIOS_DEVICE_NOT_FOUND; - } - - /* Clear errors */ - pci_write_reg(chan, pci_read_reg(chan, SH4A_PCIEERRFR), SH4A_PCIEERRFR); + if (devfn) + return PCIBIOS_DEVICE_NOT_FOUND; /* Set the PIO address */ pci_write_reg(chan, (bus->number << 24) | (dev << 19) | - (func << 16) | reg, SH4A_PCIEPAR); + (func << 16) | (where & ~3), SH4A_PCIEPAR); /* Enable the configuration access */ - pci_write_reg(chan, (1 << 31) | (type << 8), SH4A_PCIEPCTLR); - - /* Check for errors */ - if (pci_read_reg(chan, SH4A_PCIEERRFR) & 0x10) - return PCIBIOS_DEVICE_NOT_FOUND; - - /* Check for master and target aborts */ - if (pci_read_reg(chan, SH4A_PCIEPCICONF1) & ((1 << 29) | (1 << 28))) - return PCIBIOS_DEVICE_NOT_FOUND; + pci_write_reg(chan, (1 << 31), SH4A_PCIEPCTLR); if (access_type == PCI_ACCESS_READ) *data = pci_read_reg(chan, SH4A_PCIEPDR); else pci_write_reg(chan, *data, SH4A_PCIEPDR); - /* Disable the configuration access */ - pci_write_reg(chan, 0, SH4A_PCIEPCTLR); + /* Check for master and target aborts */ + if (pci_read_reg(chan, SH4A_PCIEPCICONF1) & ((1 << 29) | (1 << 28))) + return PCIBIOS_DEVICE_NOT_FOUND; return PCIBIOS_SUCCESSFUL; } @@ -101,13 +66,11 @@ static int sh7786_pcie_read(struct pci_bus *bus, unsigned int devfn, else if ((size == 4) && (where & 3)) return PCIBIOS_BAD_REGISTER_NUMBER; - raw_spin_lock_irqsave(&pci_config_lock, flags); + spin_lock_irqsave(&sh7786_pcie_lock, flags); ret = sh7786_pcie_config_access(PCI_ACCESS_READ, bus, devfn, where, &data); - if (ret != PCIBIOS_SUCCESSFUL) { - *val = 0xffffffff; + if (ret != PCIBIOS_SUCCESSFUL) goto out; - } if (size == 1) *val = (data >> ((where & 3) << 3)) & 0xff; @@ -121,7 +84,7 @@ static int sh7786_pcie_read(struct pci_bus *bus, unsigned int devfn, devfn, where, size, (unsigned long)*val); out: - raw_spin_unlock_irqrestore(&pci_config_lock, flags); + spin_unlock_irqrestore(&sh7786_pcie_lock, flags); return ret; } @@ -137,7 +100,7 @@ static int sh7786_pcie_write(struct pci_bus *bus, unsigned int devfn, else if ((size == 4) && (where & 3)) return PCIBIOS_BAD_REGISTER_NUMBER; - raw_spin_lock_irqsave(&pci_config_lock, flags); + spin_lock_irqsave(&sh7786_pcie_lock, flags); ret = sh7786_pcie_config_access(PCI_ACCESS_READ, bus, devfn, where, &data); if (ret != PCIBIOS_SUCCESSFUL) @@ -161,7 +124,7 @@ static int sh7786_pcie_write(struct pci_bus *bus, unsigned int devfn, ret = sh7786_pcie_config_access(PCI_ACCESS_WRITE, bus, devfn, where, &data); out: - raw_spin_unlock_irqrestore(&pci_config_lock, flags); + spin_unlock_irqrestore(&sh7786_pcie_lock, flags); return ret; } diff --git a/trunk/arch/sh/drivers/pci/pci-sh7751.c b/trunk/arch/sh/drivers/pci/pci-sh7751.c index 86adb1e235cd..f98141b3b7d7 100644 --- a/trunk/arch/sh/drivers/pci/pci-sh7751.c +++ b/trunk/arch/sh/drivers/pci/pci-sh7751.c @@ -81,7 +81,7 @@ static int __init sh7751_pci_init(void) unsigned int id; u32 word, reg; - printk(KERN_NOTICE "PCI: Starting initialization.\n"); + printk(KERN_NOTICE "PCI: Starting intialization.\n"); chan->reg_base = 0xfe200000; diff --git a/trunk/arch/sh/drivers/pci/pci-sh7780.c b/trunk/arch/sh/drivers/pci/pci-sh7780.c index edb7cca14882..ffdcbf10b95e 100644 --- a/trunk/arch/sh/drivers/pci/pci-sh7780.c +++ b/trunk/arch/sh/drivers/pci/pci-sh7780.c @@ -246,7 +246,7 @@ static int __init sh7780_pci_init(void) const char *type; int ret, i; - printk(KERN_NOTICE "PCI: Starting initialization.\n"); + printk(KERN_NOTICE "PCI: Starting intialization.\n"); chan->reg_base = 0xfe040000; diff --git a/trunk/arch/sh/drivers/pci/pci-sh7780.h b/trunk/arch/sh/drivers/pci/pci-sh7780.h index 1742e2c9db7a..205dcbefe275 100644 --- a/trunk/arch/sh/drivers/pci/pci-sh7780.h +++ b/trunk/arch/sh/drivers/pci/pci-sh7780.h @@ -12,6 +12,12 @@ #ifndef _PCI_SH7780_H_ #define _PCI_SH7780_H_ +#define PCI_VENDOR_ID_RENESAS 0x1912 +#define PCI_DEVICE_ID_RENESAS_SH7781 0x0001 +#define PCI_DEVICE_ID_RENESAS_SH7780 0x0002 +#define PCI_DEVICE_ID_RENESAS_SH7763 0x0004 +#define PCI_DEVICE_ID_RENESAS_SH7785 0x0007 + /* SH7780 Control Registers */ #define PCIECR 0xFE000008 #define PCIECR_ENBL 0x01 diff --git a/trunk/arch/sh/drivers/pci/pci.c b/trunk/arch/sh/drivers/pci/pci.c index 60ee09a4e121..1e9598d2bbf4 100644 --- a/trunk/arch/sh/drivers/pci/pci.c +++ b/trunk/arch/sh/drivers/pci/pci.c @@ -19,7 +19,6 @@ #include #include #include -#include unsigned long PCIBIOS_MIN_IO = 0x0000; unsigned long PCIBIOS_MIN_MEM = 0; @@ -57,11 +56,6 @@ static void __devinit pcibios_scanbus(struct pci_channel *hose) } } -/* - * This interrupt-safe spinlock protects all accesses to PCI - * configuration space. - */ -DEFINE_RAW_SPINLOCK(pci_config_lock); static DEFINE_MUTEX(pci_scan_mutex); int __devinit register_pci_controller(struct pci_channel *hose) @@ -239,7 +233,40 @@ void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, int pcibios_enable_device(struct pci_dev *dev, int mask) { - return pci_enable_resources(dev, mask); + u16 cmd, old_cmd; + int idx; + struct resource *r; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + old_cmd = cmd; + for (idx=0; idx < PCI_NUM_RESOURCES; idx++) { + /* Only set up the requested stuff */ + if (!(mask & (1<resource[idx]; + if (!(r->flags & (IORESOURCE_IO | IORESOURCE_MEM))) + continue; + if ((idx == PCI_ROM_RESOURCE) && + (!(r->flags & IORESOURCE_ROM_ENABLE))) + continue; + if (!r->start && r->end) { + printk(KERN_ERR "PCI: Device %s not available " + "because of resource collisions\n", + pci_name(dev)); + return -EINVAL; + } + if (r->flags & IORESOURCE_IO) + cmd |= PCI_COMMAND_IO; + if (r->flags & IORESOURCE_MEM) + cmd |= PCI_COMMAND_MEMORY; + } + if (cmd != old_cmd) { + printk("PCI: Enabling device %s (%04x -> %04x)\n", + pci_name(dev), old_cmd, cmd); + pci_write_config_word(dev, PCI_COMMAND, cmd); + } + return 0; } /* @@ -268,7 +295,7 @@ void __init pcibios_update_irq(struct pci_dev *dev, int irq) pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); } -char * __devinit __weak pcibios_setup(char *str) +char * __devinit pcibios_setup(char *str) { return str; } diff --git a/trunk/arch/sh/drivers/pci/pcie-sh7786.c b/trunk/arch/sh/drivers/pci/pcie-sh7786.c index 96e9b058aa1d..68cb9b0ac9d2 100644 --- a/trunk/arch/sh/drivers/pci/pcie-sh7786.c +++ b/trunk/arch/sh/drivers/pci/pcie-sh7786.c @@ -13,14 +13,11 @@ #include #include #include -#include -#include #include "pcie-sh7786.h" #include struct sh7786_pcie_port { struct pci_channel *hose; - struct clk *fclk, phy_clk; unsigned int index; int endpoint; int link; @@ -54,7 +51,6 @@ static struct resource sh7786_pci0_resources[] = { .name = "PCIe0 MEM 2", .start = 0xfe100000, .end = 0xfe100000 + SZ_1M - 1, - .flags = IORESOURCE_MEM, }, }; @@ -78,7 +74,6 @@ static struct resource sh7786_pci1_resources[] = { .name = "PCIe1 MEM 2", .start = 0xfe300000, .end = 0xfe300000 + SZ_1M - 1, - .flags = IORESOURCE_MEM, }, }; @@ -87,7 +82,6 @@ static struct resource sh7786_pci2_resources[] = { .name = "PCIe2 IO", .start = 0xfc800000, .end = 0xfc800000 + SZ_4M - 1, - .flags = IORESOURCE_IO, }, { .name = "PCIe2 MEM 0", .start = 0x80000000, @@ -102,7 +96,6 @@ static struct resource sh7786_pci2_resources[] = { .name = "PCIe2 MEM 2", .start = 0xfcd00000, .end = 0xfcd00000 + SZ_1M - 1, - .flags = IORESOURCE_MEM, }, }; @@ -124,29 +117,7 @@ static struct pci_channel sh7786_pci_channels[] = { DEFINE_CONTROLLER(0xfcc00000, 2), }; -static struct clk fixed_pciexclkp = { - .rate = 100000000, /* 100 MHz reference clock */ -}; - -static void __devinit sh7786_pci_fixup(struct pci_dev *dev) -{ - /* - * Prevent enumeration of root complex resources. - */ - if (pci_is_root_bus(dev->bus) && dev->devfn == 0) { - int i; - - for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { - dev->resource[i].start = 0; - dev->resource[i].end = 0; - dev->resource[i].flags = 0; - } - } -} -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_SH7786, - sh7786_pci_fixup); - -static int __init phy_wait_for_ack(struct pci_channel *chan) +static int phy_wait_for_ack(struct pci_channel *chan) { unsigned int timeout = 100; @@ -160,7 +131,7 @@ static int __init phy_wait_for_ack(struct pci_channel *chan) return -ETIMEDOUT; } -static int __init pci_wait_for_irq(struct pci_channel *chan, unsigned int mask) +static int pci_wait_for_irq(struct pci_channel *chan, unsigned int mask) { unsigned int timeout = 100; @@ -174,14 +145,19 @@ static int __init pci_wait_for_irq(struct pci_channel *chan, unsigned int mask) return -ETIMEDOUT; } -static void __init phy_write_reg(struct pci_channel *chan, unsigned int addr, - unsigned int lane, unsigned int data) +static void phy_write_reg(struct pci_channel *chan, unsigned int addr, + unsigned int lane, unsigned int data) { - unsigned long phyaddr; + unsigned long phyaddr, ctrl; phyaddr = (1 << BITS_CMD) + ((lane & 0xf) << BITS_LANE) + ((addr & 0xff) << BITS_ADR); + /* Enable clock */ + ctrl = pci_read_reg(chan, SH4A_PCIEPHYCTLR); + ctrl |= (1 << BITS_CKE); + pci_write_reg(chan, ctrl, SH4A_PCIEPHYCTLR); + /* Set write data */ pci_write_reg(chan, data, SH4A_PCIEPHYDOUTR); pci_write_reg(chan, phyaddr, SH4A_PCIEPHYADRR); @@ -189,74 +165,20 @@ static void __init phy_write_reg(struct pci_channel *chan, unsigned int addr, phy_wait_for_ack(chan); /* Clear command */ - pci_write_reg(chan, 0, SH4A_PCIEPHYDOUTR); pci_write_reg(chan, 0, SH4A_PCIEPHYADRR); phy_wait_for_ack(chan); -} - -static int __init pcie_clk_init(struct sh7786_pcie_port *port) -{ - struct pci_channel *chan = port->hose; - struct clk *clk; - char fclk_name[16]; - int ret; - /* - * First register the fixed clock - */ - ret = clk_register(&fixed_pciexclkp); - if (unlikely(ret != 0)) - return ret; - - /* - * Grab the port's function clock, which the PHY clock depends - * on. clock lookups don't help us much at this point, since no - * dev_id is available this early. Lame. - */ - snprintf(fclk_name, sizeof(fclk_name), "pcie%d_fck", port->index); - - port->fclk = clk_get(NULL, fclk_name); - if (IS_ERR(port->fclk)) { - ret = PTR_ERR(port->fclk); - goto err_fclk; - } - - clk_enable(port->fclk); - - /* - * And now, set up the PHY clock - */ - clk = &port->phy_clk; - - memset(clk, 0, sizeof(struct clk)); - - clk->parent = &fixed_pciexclkp; - clk->enable_reg = (void __iomem *)(chan->reg_base + SH4A_PCIEPHYCTLR); - clk->enable_bit = BITS_CKE; - - ret = sh_clk_mstp32_register(clk, 1); - if (unlikely(ret < 0)) - goto err_phy; - - return 0; - -err_phy: - clk_disable(port->fclk); - clk_put(port->fclk); -err_fclk: - clk_unregister(&fixed_pciexclkp); - - return ret; + /* Disable clock */ + ctrl = pci_read_reg(chan, SH4A_PCIEPHYCTLR); + ctrl &= ~(1 << BITS_CKE); + pci_write_reg(chan, ctrl, SH4A_PCIEPHYCTLR); } -static int __init phy_init(struct sh7786_pcie_port *port) +static int phy_init(struct pci_channel *chan) { - struct pci_channel *chan = port->hose; unsigned int timeout = 100; - clk_enable(&port->phy_clk); - /* Initialize the phy */ phy_write_reg(chan, 0x60, 0xf, 0x004b008b); phy_write_reg(chan, 0x61, 0xf, 0x00007b41); @@ -265,13 +187,9 @@ static int __init phy_init(struct sh7786_pcie_port *port) phy_write_reg(chan, 0x66, 0xf, 0x00000010); phy_write_reg(chan, 0x74, 0xf, 0x0007001c); phy_write_reg(chan, 0x79, 0xf, 0x01fc000d); - phy_write_reg(chan, 0xb0, 0xf, 0x00000610); /* Deassert Standby */ - phy_write_reg(chan, 0x67, 0x1, 0x00000400); - - /* Disable clock */ - clk_disable(&port->phy_clk); + phy_write_reg(chan, 0x67, 0xf, 0x00000400); while (timeout--) { if (pci_read_reg(chan, SH4A_PCIEPHYSR)) @@ -283,33 +201,22 @@ static int __init phy_init(struct sh7786_pcie_port *port) return -ETIMEDOUT; } -static void __init pcie_reset(struct sh7786_pcie_port *port) -{ - struct pci_channel *chan = port->hose; - - pci_write_reg(chan, 1, SH4A_PCIESRSTR); - pci_write_reg(chan, 0, SH4A_PCIETCTLR); - pci_write_reg(chan, 0, SH4A_PCIESRSTR); - pci_write_reg(chan, 0, SH4A_PCIETXVC0SR); -} - -static int __init pcie_init(struct sh7786_pcie_port *port) +static int pcie_init(struct sh7786_pcie_port *port) { struct pci_channel *chan = port->hose; unsigned int data; phys_addr_t memphys; size_t memsize; - int ret, i, win; + int ret, i; /* Begin initialization */ - pcie_reset(port); + pci_write_reg(chan, 0, SH4A_PCIETCTLR); - /* - * Initial header for port config space is type 1, set the device - * class to match. Hardware takes care of propagating the IDSETR - * settings, so there is no need to bother with a quirk. - */ - pci_write_reg(chan, PCI_CLASS_BRIDGE_PCI << 16, SH4A_PCIEIDSETR1); + /* Initialize as type1. */ + data = pci_read_reg(chan, SH4A_PCIEPCICONF3); + data &= ~(0x7f << 16); + data |= PCI_HEADER_TYPE_BRIDGE << 16; + pci_write_reg(chan, data, SH4A_PCIEPCICONF3); /* Initialize default capabilities. */ data = pci_read_reg(chan, SH4A_PCIEEXPCAP0); @@ -361,33 +268,30 @@ static int __init pcie_init(struct sh7786_pcie_port *port) * LAR1/LAMR1. */ if (memsize > SZ_512M) { - pci_write_reg(chan, memphys + SZ_512M, SH4A_PCIELAR1); - pci_write_reg(chan, ((memsize - SZ_512M) - SZ_256) | 1, - SH4A_PCIELAMR1); + __raw_writel(memphys + SZ_512M, chan->reg_base + SH4A_PCIELAR1); + __raw_writel(((memsize - SZ_512M) - SZ_256) | 1, + chan->reg_base + SH4A_PCIELAMR1); memsize = SZ_512M; } else { /* * Otherwise just zero it out and disable it. */ - pci_write_reg(chan, 0, SH4A_PCIELAR1); - pci_write_reg(chan, 0, SH4A_PCIELAMR1); + __raw_writel(0, chan->reg_base + SH4A_PCIELAR1); + __raw_writel(0, chan->reg_base + SH4A_PCIELAMR1); } /* * LAR0/LAMR0 covers up to the first 512MB, which is enough to * cover all of lowmem on most platforms. */ - pci_write_reg(chan, memphys, SH4A_PCIELAR0); - pci_write_reg(chan, (memsize - SZ_256) | 1, SH4A_PCIELAMR0); + __raw_writel(memphys, chan->reg_base + SH4A_PCIELAR0); + __raw_writel((memsize - SZ_256) | 1, chan->reg_base + SH4A_PCIELAMR0); /* Finish initialization */ data = pci_read_reg(chan, SH4A_PCIETCTLR); data |= 0x1; pci_write_reg(chan, data, SH4A_PCIETCTLR); - /* Let things settle down a bit.. */ - mdelay(100); - /* Enable DL_Active Interrupt generation */ data = pci_read_reg(chan, SH4A_PCIEDLINTENR); data |= PCIEDLINTENR_DLL_ACT_ENABLE; @@ -398,12 +302,9 @@ static int __init pcie_init(struct sh7786_pcie_port *port) data |= PCIEMACCTLR_SCR_DIS | (0xff << 16); pci_write_reg(chan, data, SH4A_PCIEMACCTLR); - /* - * This will timeout if we don't have a link, but we permit the - * port to register anyways in order to support hotplug on future - * hardware. - */ ret = pci_wait_for_irq(chan, MASK_INT_TX_CTRL); + if (unlikely(ret != 0)) + return -ENODEV; data = pci_read_reg(chan, SH4A_PCIEPCICONF1); data &= ~(PCI_STATUS_DEVSEL_MASK << 16); @@ -416,48 +317,35 @@ static int __init pcie_init(struct sh7786_pcie_port *port) wmb(); - if (ret == 0) { - data = pci_read_reg(chan, SH4A_PCIEMACSR); - printk(KERN_NOTICE "PCI: PCIe#%d x%d link detected\n", - port->index, (data >> 20) & 0x3f); - } else - printk(KERN_NOTICE "PCI: PCIe#%d link down\n", - port->index); + data = pci_read_reg(chan, SH4A_PCIEMACSR); + printk(KERN_NOTICE "PCI: PCIe#%d link width %d\n", + port->index, (data >> 20) & 0x3f); + - for (i = win = 0; i < chan->nr_resources; i++) { + for (i = 0; i < chan->nr_resources; i++) { struct resource *res = chan->resources + i; resource_size_t size; - u32 mask; + u32 enable_mask; - /* - * We can't use the 32-bit mode windows in legacy 29-bit - * mode, so just skip them entirely. - */ - if ((res->flags & IORESOURCE_MEM_32BIT) && __in_29bit_mode()) - continue; + pci_write_reg(chan, 0x00000000, SH4A_PCIEPTCTLR(i)); - pci_write_reg(chan, 0x00000000, SH4A_PCIEPTCTLR(win)); + size = resource_size(res); /* * The PAMR mask is calculated in units of 256kB, which * keeps things pretty simple. */ - size = resource_size(res); - mask = (roundup_pow_of_two(size) / SZ_256K) - 1; - pci_write_reg(chan, mask << 18, SH4A_PCIEPAMR(win)); + __raw_writel(((roundup_pow_of_two(size) / SZ_256K) - 1) << 18, + chan->reg_base + SH4A_PCIEPAMR(i)); - pci_write_reg(chan, upper_32_bits(res->start), - SH4A_PCIEPARH(win)); - pci_write_reg(chan, lower_32_bits(res->start), - SH4A_PCIEPARL(win)); + pci_write_reg(chan, 0x00000000, SH4A_PCIEPARH(i)); + pci_write_reg(chan, 0x00000000, SH4A_PCIEPARL(i)); - mask = MASK_PARE; + enable_mask = MASK_PARE; if (res->flags & IORESOURCE_IO) - mask |= MASK_SPC; - - pci_write_reg(chan, mask, SH4A_PCIEPTCTLR(win)); + enable_mask |= MASK_SPC; - win++; + pci_write_reg(chan, enable_mask, SH4A_PCIEPTCTLR(i)); } return 0; @@ -468,33 +356,26 @@ int __init pcibios_map_platform_irq(struct pci_dev *pdev, u8 slot, u8 pin) return 71; } -static int __init sh7786_pcie_core_init(void) +static int sh7786_pcie_core_init(void) { /* Return the number of ports */ return test_mode_pin(MODE_PIN12) ? 3 : 2; } -static int __init sh7786_pcie_init_hw(struct sh7786_pcie_port *port) +static int __devinit sh7786_pcie_init_hw(struct sh7786_pcie_port *port) { int ret; + ret = phy_init(port->hose); + if (unlikely(ret < 0)) + return ret; + /* * Check if we are configured in endpoint or root complex mode, * this is a fixed pin setting that applies to all PCIe ports. */ port->endpoint = test_mode_pin(MODE_PIN11); - /* - * Setup clocks, needed both for PHY and PCIe registers. - */ - ret = pcie_clk_init(port); - if (unlikely(ret < 0)) - return ret; - - ret = phy_init(port); - if (unlikely(ret < 0)) - return ret; - ret = pcie_init(port); if (unlikely(ret < 0)) return ret; @@ -509,10 +390,9 @@ static struct sh7786_pcie_hwops sh7786_65nm_pcie_hwops __initdata = { static int __init sh7786_pcie_init(void) { - struct clk *platclk; int ret = 0, i; - printk(KERN_NOTICE "PCI: Starting initialization.\n"); + printk(KERN_NOTICE "PCI: Starting intialization.\n"); sh7786_pcie_hwops = &sh7786_65nm_pcie_hwops; @@ -527,22 +407,6 @@ static int __init sh7786_pcie_init(void) if (unlikely(!sh7786_pcie_ports)) return -ENOMEM; - /* - * Fetch any optional platform clock associated with this block. - * - * This is a rather nasty hack for boards with spec-mocking FPGAs - * that have a secondary set of clocks outside of the on-chip - * ones that need to be accounted for before there is any chance - * of touching the existing MSTP bits or CPG clocks. - */ - platclk = clk_get(NULL, "pcie_plat_clk"); - if (IS_ERR(platclk)) { - /* Sane hardware should probably get a WARN_ON.. */ - platclk = NULL; - } - - clk_enable(platclk); - printk(KERN_NOTICE "PCI: probing %d ports.\n", nr_ports); for (i = 0; i < nr_ports; i++) { @@ -555,11 +419,8 @@ static int __init sh7786_pcie_init(void) ret |= sh7786_pcie_hwops->port_init_hw(port); } - if (unlikely(ret)) { - clk_disable(platclk); - clk_put(platclk); + if (unlikely(ret)) return ret; - } return 0; } diff --git a/trunk/arch/sh/drivers/pci/pcie-sh7786.h b/trunk/arch/sh/drivers/pci/pcie-sh7786.h index 1ee054e47eae..90a6992576b0 100644 --- a/trunk/arch/sh/drivers/pci/pcie-sh7786.h +++ b/trunk/arch/sh/drivers/pci/pcie-sh7786.h @@ -55,11 +55,8 @@ #define BITS_ERRRCV (0) /* 0 ERRRCV 0 */ #define MASK_ERRRCV (1<regs[0]) #define flush_insn_slot(p) do { } while (0) #define kretprobe_blacklist_size 0 diff --git a/trunk/arch/sh/include/asm/pci.h b/trunk/arch/sh/include/asm/pci.h index f0efe97f1750..8bd952fcf3ba 100644 --- a/trunk/arch/sh/include/asm/pci.h +++ b/trunk/arch/sh/include/asm/pci.h @@ -37,8 +37,6 @@ struct pci_channel { }; /* arch/sh/drivers/pci/pci.c */ -extern raw_spinlock_t pci_config_lock; - extern int register_pci_controller(struct pci_channel *hose); extern void pcibios_report_status(unsigned int status_mask, int warn); diff --git a/trunk/arch/sh/include/asm/processor_32.h b/trunk/arch/sh/include/asm/processor_32.h index 46d5179c9f49..61a445d2d02a 100644 --- a/trunk/arch/sh/include/asm/processor_32.h +++ b/trunk/arch/sh/include/asm/processor_32.h @@ -13,6 +13,7 @@ #include #include #include +#include #include /* @@ -193,6 +194,8 @@ extern unsigned long get_wchan(struct task_struct *p); #define KSTK_EIP(tsk) (task_pt_regs(tsk)->pc) #define KSTK_ESP(tsk) (task_pt_regs(tsk)->regs[15]) +#define user_stack_pointer(_regs) ((_regs)->regs[15]) + #if defined(CONFIG_CPU_SH2A) || defined(CONFIG_CPU_SH4) #define PREFETCH_STRIDE L1_CACHE_BYTES #define ARCH_HAS_PREFETCH diff --git a/trunk/arch/sh/include/asm/processor_64.h b/trunk/arch/sh/include/asm/processor_64.h index 2a541ddb5a1b..621bc4618c6b 100644 --- a/trunk/arch/sh/include/asm/processor_64.h +++ b/trunk/arch/sh/include/asm/processor_64.h @@ -17,6 +17,7 @@ #include #include #include +#include #include /* @@ -230,5 +231,7 @@ extern unsigned long get_wchan(struct task_struct *p); #define KSTK_EIP(tsk) ((tsk)->thread.pc) #define KSTK_ESP(tsk) ((tsk)->thread.sp) +#define user_stack_pointer(_regs) ((_regs)->regs[15]) + #endif /* __ASSEMBLY__ */ #endif /* __ASM_SH_PROCESSOR_64_H */ diff --git a/trunk/arch/sh/include/asm/ptrace.h b/trunk/arch/sh/include/asm/ptrace.h index f6edc10aa0d3..2168fde25611 100644 --- a/trunk/arch/sh/include/asm/ptrace.h +++ b/trunk/arch/sh/include/asm/ptrace.h @@ -3,7 +3,90 @@ /* * Copyright (C) 1999, 2000 Niibe Yutaka + * + */ +#if defined(__SH5__) +struct pt_regs { + unsigned long long pc; + unsigned long long sr; + long long syscall_nr; + unsigned long long regs[63]; + unsigned long long tregs[8]; + unsigned long long pad[2]; +}; +#else +/* + * GCC defines register number like this: + * ----------------------------- + * 0 - 15 are integer registers + * 17 - 22 are control/special registers + * 24 - 39 fp registers + * 40 - 47 xd registers + * 48 - fpscr register + * ----------------------------- + * + * We follows above, except: + * 16 --- program counter (PC) + * 22 --- syscall # + * 23 --- floating point communication register */ +#define REG_REG0 0 +#define REG_REG15 15 + +#define REG_PC 16 + +#define REG_PR 17 +#define REG_SR 18 +#define REG_GBR 19 +#define REG_MACH 20 +#define REG_MACL 21 + +#define REG_SYSCALL 22 + +#define REG_FPREG0 23 +#define REG_FPREG15 38 +#define REG_XFREG0 39 +#define REG_XFREG15 54 + +#define REG_FPSCR 55 +#define REG_FPUL 56 + +/* + * This struct defines the way the registers are stored on the + * kernel stack during a system call or other kernel entry. + */ +struct pt_regs { + unsigned long regs[16]; + unsigned long pc; + unsigned long pr; + unsigned long sr; + unsigned long gbr; + unsigned long mach; + unsigned long macl; + long tra; +}; + +/* + * This struct defines the way the DSP registers are stored on the + * kernel stack during a system call or other kernel entry. + */ +struct pt_dspregs { + unsigned long a1; + unsigned long a0g; + unsigned long a1g; + unsigned long m0; + unsigned long m1; + unsigned long a0; + unsigned long x0; + unsigned long x1; + unsigned long y0; + unsigned long y1; + unsigned long dsr; + unsigned long rs; + unsigned long re; + unsigned long mod; +}; +#endif #define PTRACE_GETREGS 12 /* General registers */ #define PTRACE_SETREGS 13 @@ -24,102 +107,22 @@ #define PT_DATA_ADDR 248 /* &(struct user)->start_data */ #define PT_TEXT_LEN 252 -#if defined(__SH5__) || defined(CONFIG_CPU_SH5) -#include "ptrace_64.h" -#else -#include "ptrace_32.h" -#endif - #ifdef __KERNEL__ - -#include -#include -#include #include #include #include #define user_mode(regs) (((regs)->sr & 0x40000000)==0) -#define user_stack_pointer(regs) ((unsigned long)(regs)->regs[15]) -#define kernel_stack_pointer(regs) ((unsigned long)(regs)->regs[15]) #define instruction_pointer(regs) ((unsigned long)(regs)->pc) extern void show_regs(struct pt_regs *); -#define arch_has_single_step() (1) - /* - * kprobe-based event tracer support - */ -struct pt_regs_offset { - const char *name; - int offset; -}; - -#define REG_OFFSET_NAME(r) {.name = #r, .offset = offsetof(struct pt_regs, r)} -#define REGS_OFFSET_NAME(num) \ - {.name = __stringify(r##num), .offset = offsetof(struct pt_regs, regs[num])} -#define TREGS_OFFSET_NAME(num) \ - {.name = __stringify(tr##num), .offset = offsetof(struct pt_regs, tregs[num])} -#define REG_OFFSET_END {.name = NULL, .offset = 0} - -/* Query offset/name of register from its name/offset */ -extern int regs_query_register_offset(const char *name); -extern const char *regs_query_register_name(unsigned int offset); - -extern const struct pt_regs_offset regoffset_table[]; - -/** - * regs_get_register() - get register value from its offset - * @regs: pt_regs from which register value is gotten. - * @offset: offset number of the register. - * - * regs_get_register returns the value of a register. The @offset is the - * offset of the register in struct pt_regs address which specified by @regs. - * If @offset is bigger than MAX_REG_OFFSET, this returns 0. - */ -static inline unsigned long regs_get_register(struct pt_regs *regs, - unsigned int offset) -{ - if (unlikely(offset > MAX_REG_OFFSET)) - return 0; - return *(unsigned long *)((unsigned long)regs + offset); -} - -/** - * regs_within_kernel_stack() - check the address in the stack - * @regs: pt_regs which contains kernel stack pointer. - * @addr: address which is checked. - * - * regs_within_kernel_stack() checks @addr is within the kernel stack page(s). - * If @addr is within the kernel stack, it returns true. If not, returns false. + * These are defined as per linux/ptrace.h. */ -static inline int regs_within_kernel_stack(struct pt_regs *regs, - unsigned long addr) -{ - return ((addr & ~(THREAD_SIZE - 1)) == - (kernel_stack_pointer(regs) & ~(THREAD_SIZE - 1))); -} +struct task_struct; -/** - * regs_get_kernel_stack_nth() - get Nth entry of the stack - * @regs: pt_regs which contains kernel stack pointer. - * @n: stack entry number. - * - * regs_get_kernel_stack_nth() returns @n th entry of the kernel stack which - * is specified by @regs. If the @n th entry is NOT in the kernel stack, - * this returns 0. - */ -static inline unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, - unsigned int n) -{ - unsigned long *addr = (unsigned long *)kernel_stack_pointer(regs); - addr += n; - if (regs_within_kernel_stack(regs, (unsigned long)addr)) - return *addr; - else - return 0; -} +#define arch_has_single_step() (1) struct perf_event; struct perf_sample_data; diff --git a/trunk/arch/sh/include/asm/ptrace_32.h b/trunk/arch/sh/include/asm/ptrace_32.h deleted file mode 100644 index 35d9e257558c..000000000000 --- a/trunk/arch/sh/include/asm/ptrace_32.h +++ /dev/null @@ -1,83 +0,0 @@ -#ifndef __ASM_SH_PTRACE_32_H -#define __ASM_SH_PTRACE_32_H - -/* - * GCC defines register number like this: - * ----------------------------- - * 0 - 15 are integer registers - * 17 - 22 are control/special registers - * 24 - 39 fp registers - * 40 - 47 xd registers - * 48 - fpscr register - * ----------------------------- - * - * We follows above, except: - * 16 --- program counter (PC) - * 22 --- syscall # - * 23 --- floating point communication register - */ -#define REG_REG0 0 -#define REG_REG15 15 - -#define REG_PC 16 - -#define REG_PR 17 -#define REG_SR 18 -#define REG_GBR 19 -#define REG_MACH 20 -#define REG_MACL 21 - -#define REG_SYSCALL 22 - -#define REG_FPREG0 23 -#define REG_FPREG15 38 -#define REG_XFREG0 39 -#define REG_XFREG15 54 - -#define REG_FPSCR 55 -#define REG_FPUL 56 - -/* - * This struct defines the way the registers are stored on the - * kernel stack during a system call or other kernel entry. - */ -struct pt_regs { - unsigned long regs[16]; - unsigned long pc; - unsigned long pr; - unsigned long sr; - unsigned long gbr; - unsigned long mach; - unsigned long macl; - long tra; -}; - -/* - * This struct defines the way the DSP registers are stored on the - * kernel stack during a system call or other kernel entry. - */ -struct pt_dspregs { - unsigned long a1; - unsigned long a0g; - unsigned long a1g; - unsigned long m0; - unsigned long m1; - unsigned long a0; - unsigned long x0; - unsigned long x1; - unsigned long y0; - unsigned long y1; - unsigned long dsr; - unsigned long rs; - unsigned long re; - unsigned long mod; -}; - -#ifdef __KERNEL__ - -#define MAX_REG_OFFSET offsetof(struct pt_regs, tra) -#define regs_return_value(regs) ((regs)->regs[0]) - -#endif /* __KERNEL__ */ - -#endif /* __ASM_SH_PTRACE_32_H */ diff --git a/trunk/arch/sh/include/asm/ptrace_64.h b/trunk/arch/sh/include/asm/ptrace_64.h deleted file mode 100644 index d43c1cb0bbe7..000000000000 --- a/trunk/arch/sh/include/asm/ptrace_64.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef __ASM_SH_PTRACE_64_H -#define __ASM_SH_PTRACE_64_H - -struct pt_regs { - unsigned long long pc; - unsigned long long sr; - long long syscall_nr; - unsigned long long regs[63]; - unsigned long long tregs[8]; - unsigned long long pad[2]; -}; - -#ifdef __KERNEL__ - -#define MAX_REG_OFFSET offsetof(struct pt_regs, tregs[7]) -#define regs_return_value(regs) ((regs)->regs[3]) - -#endif /* __KERNEL__ */ - -#endif /* __ASM_SH_PTRACE_64_H */ diff --git a/trunk/arch/sh/include/asm/sizes.h b/trunk/arch/sh/include/asm/sizes.h index 0b9fe2d5c36d..3a1fb97770f1 100644 --- a/trunk/arch/sh/include/asm/sizes.h +++ b/trunk/arch/sh/include/asm/sizes.h @@ -32,7 +32,6 @@ #define SZ_512 0x00000200 #define SZ_1K 0x00000400 -#define SZ_2K 0x00000800 #define SZ_4K 0x00001000 #define SZ_8K 0x00002000 #define SZ_16K 0x00004000 diff --git a/trunk/arch/sh/include/asm/sram.h b/trunk/arch/sh/include/asm/sram.h deleted file mode 100644 index a2808ce4c0aa..000000000000 --- a/trunk/arch/sh/include/asm/sram.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef __ASM_SRAM_H -#define __ASM_SRAM_H - -#ifdef CONFIG_HAVE_SRAM_POOL - -#include -#include - -/* arch/sh/mm/sram.c */ -extern struct gen_pool *sram_pool; - -static inline unsigned long sram_alloc(size_t len) -{ - if (!sram_pool) - return 0UL; - - return gen_pool_alloc(sram_pool, len); -} - -static inline void sram_free(unsigned long addr, size_t len) -{ - return gen_pool_free(sram_pool, addr, len); -} - -#else - -static inline unsigned long sram_alloc(size_t len) -{ - return 0; -} - -static inline void sram_free(unsigned long addr, size_t len) -{ -} - -#endif /* CONFIG_HAVE_SRAM_POOL */ - -#endif /* __ASM_SRAM_H */ diff --git a/trunk/arch/sh/include/asm/system.h b/trunk/arch/sh/include/asm/system.h index 1f1af5afff03..0bd7a17d5e1a 100644 --- a/trunk/arch/sh/include/asm/system.h +++ b/trunk/arch/sh/include/asm/system.h @@ -140,6 +140,8 @@ extern unsigned int instruction_size(unsigned int insn); extern unsigned long cached_to_uncached; extern unsigned long uncached_size; +extern struct dentry *sh_debugfs_root; + void per_cpu_trap_init(void); void default_idle(void); void cpu_idle_wait(void); diff --git a/trunk/arch/sh/include/asm/system_32.h b/trunk/arch/sh/include/asm/system_32.h index c941b2739405..51296b36770e 100644 --- a/trunk/arch/sh/include/asm/system_32.h +++ b/trunk/arch/sh/include/asm/system_32.h @@ -212,16 +212,17 @@ static inline reg_size_t register_align(void *val) } int handle_unaligned_access(insn_size_t instruction, struct pt_regs *regs, - struct mem_access *ma, int, unsigned long address); + struct mem_access *ma, int); static inline void trigger_address_error(void) { - __asm__ __volatile__ ( - "ldc %0, sr\n\t" - "mov.l @%1, %0" - : - : "r" (0x10000000), "r" (0x80000001) - ); + if (__in_29bit_mode()) + __asm__ __volatile__ ( + "ldc %0, sr\n\t" + "mov.l @%1, %0" + : + : "r" (0x10000000), "r" (0x80000001) + ); } asmlinkage void do_address_error(struct pt_regs *regs, diff --git a/trunk/arch/sh/include/asm/tlbflush.h b/trunk/arch/sh/include/asm/tlbflush.h index 0df66f0c7284..e0ac97221ae6 100644 --- a/trunk/arch/sh/include/asm/tlbflush.h +++ b/trunk/arch/sh/include/asm/tlbflush.h @@ -21,8 +21,6 @@ extern void local_flush_tlb_kernel_range(unsigned long start, unsigned long end); extern void local_flush_tlb_one(unsigned long asid, unsigned long page); -extern void __flush_tlb_global(void); - #ifdef CONFIG_SMP extern void flush_tlb_all(void); diff --git a/trunk/arch/sh/include/asm/unistd_32.h b/trunk/arch/sh/include/asm/unistd_32.h index 903cd618eb74..0e7f0fc8f086 100644 --- a/trunk/arch/sh/include/asm/unistd_32.h +++ b/trunk/arch/sh/include/asm/unistd_32.h @@ -345,34 +345,13 @@ #define __NR_pwritev 334 #define __NR_rt_tgsigqueueinfo 335 #define __NR_perf_event_open 336 -#define __NR_fanotify_init 337 -#define __NR_fanotify_mark 338 -#define __NR_prlimit64 339 -/* Non-multiplexed socket family */ -#define __NR_socket 340 -#define __NR_bind 341 -#define __NR_connect 342 -#define __NR_listen 343 -#define __NR_accept 344 -#define __NR_getsockname 345 -#define __NR_getpeername 346 -#define __NR_socketpair 347 -#define __NR_send 348 -#define __NR_sendto 349 -#define __NR_recv 350 -#define __NR_recvfrom 351 -#define __NR_shutdown 352 -#define __NR_setsockopt 353 -#define __NR_getsockopt 354 -#define __NR_sendmsg 355 -#define __NR_recvmsg 356 -#define __NR_recvmmsg 357 - -#define NR_syscalls 358 +#define NR_syscalls 337 #ifdef __KERNEL__ +#define __IGNORE_recvmmsg + #define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_OLD_STAT diff --git a/trunk/arch/sh/include/asm/unistd_64.h b/trunk/arch/sh/include/asm/unistd_64.h index 09aa93f9eb70..0580c33a1e04 100644 --- a/trunk/arch/sh/include/asm/unistd_64.h +++ b/trunk/arch/sh/include/asm/unistd_64.h @@ -387,13 +387,10 @@ #define __NR_perf_event_open 364 #define __NR_recvmmsg 365 #define __NR_accept4 366 -#define __NR_fanotify_init 367 -#define __NR_fanotify_mark 368 -#define __NR_prlimit64 369 #ifdef __KERNEL__ -#define NR_syscalls 370 +#define NR_syscalls 367 #define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR diff --git a/trunk/arch/sh/include/cpu-sh3/cpu/mmu_context.h b/trunk/arch/sh/include/cpu-sh3/cpu/mmu_context.h index 0c7c735ea82a..ab09da73ce77 100644 --- a/trunk/arch/sh/include/cpu-sh3/cpu/mmu_context.h +++ b/trunk/arch/sh/include/cpu-sh3/cpu/mmu_context.h @@ -16,7 +16,6 @@ #define MMU_TEA 0xFFFFFFFC /* TLB Exception Address */ #define MMUCR 0xFFFFFFE0 /* MMU Control Register */ -#define MMUCR_TI (1 << 2) /* TLB flush bit */ #define MMU_TLB_ADDRESS_ARRAY 0xF2000000 #define MMU_PAGE_ASSOC_BIT 0x80 diff --git a/trunk/arch/sh/include/cpu-sh4/cpu/freq.h b/trunk/arch/sh/include/cpu-sh4/cpu/freq.h index cffd25ed0240..e1e90960ee9a 100644 --- a/trunk/arch/sh/include/cpu-sh4/cpu/freq.h +++ b/trunk/arch/sh/include/cpu-sh4/cpu/freq.h @@ -56,9 +56,7 @@ #define FRQCR1 0xffc40004 #define FRQMR1 0xffc40014 #elif defined(CONFIG_CPU_SUBTYPE_SHX3) -#define FRQCR0 0xffc00000 -#define FRQCR1 0xffc00004 -#define FRQMR1 0xffc00014 +#define FRQCR 0xffc00014 #else #define FRQCR 0xffc00000 #define FRQCR_PSTBY 0x0200 diff --git a/trunk/arch/sh/include/cpu-sh4/cpu/sh7757.h b/trunk/arch/sh/include/cpu-sh4/cpu/sh7757.h index 15f3de11c55a..f4d267efad71 100644 --- a/trunk/arch/sh/include/cpu-sh4/cpu/sh7757.h +++ b/trunk/arch/sh/include/cpu-sh4/cpu/sh7757.h @@ -3,252 +3,241 @@ enum { /* PTA */ - GPIO_PTA0, GPIO_PTA1, GPIO_PTA2, GPIO_PTA3, - GPIO_PTA4, GPIO_PTA5, GPIO_PTA6, GPIO_PTA7, + GPIO_PTA7, GPIO_PTA6, GPIO_PTA5, GPIO_PTA4, + GPIO_PTA3, GPIO_PTA2, GPIO_PTA1, GPIO_PTA0, /* PTB */ - GPIO_PTB0, GPIO_PTB1, GPIO_PTB2, GPIO_PTB3, - GPIO_PTB4, GPIO_PTB5, GPIO_PTB6, GPIO_PTB7, + GPIO_PTB7, GPIO_PTB6, GPIO_PTB5, GPIO_PTB4, + GPIO_PTB3, GPIO_PTB2, GPIO_PTB1, GPIO_PTB0, /* PTC */ - GPIO_PTC0, GPIO_PTC1, GPIO_PTC2, GPIO_PTC3, - GPIO_PTC4, GPIO_PTC5, GPIO_PTC6, GPIO_PTC7, + GPIO_PTC7, GPIO_PTC6, GPIO_PTC5, GPIO_PTC4, + GPIO_PTC3, GPIO_PTC2, GPIO_PTC1, GPIO_PTC0, /* PTD */ - GPIO_PTD0, GPIO_PTD1, GPIO_PTD2, GPIO_PTD3, - GPIO_PTD4, GPIO_PTD5, GPIO_PTD6, GPIO_PTD7, + GPIO_PTD7, GPIO_PTD6, GPIO_PTD5, GPIO_PTD4, + GPIO_PTD3, GPIO_PTD2, GPIO_PTD1, GPIO_PTD0, /* PTE */ - GPIO_PTE0, GPIO_PTE1, GPIO_PTE2, GPIO_PTE3, - GPIO_PTE4, GPIO_PTE5, GPIO_PTE6, GPIO_PTE7, + GPIO_PTE7, GPIO_PTE6, GPIO_PTE5, GPIO_PTE4, + GPIO_PTE3, GPIO_PTE2, GPIO_PTE1, GPIO_PTE0, /* PTF */ - GPIO_PTF0, GPIO_PTF1, GPIO_PTF2, GPIO_PTF3, - GPIO_PTF4, GPIO_PTF5, GPIO_PTF6, GPIO_PTF7, + GPIO_PTF7, GPIO_PTF6, GPIO_PTF5, GPIO_PTF4, + GPIO_PTF3, GPIO_PTF2, GPIO_PTF1, GPIO_PTF0, /* PTG */ - GPIO_PTG0, GPIO_PTG1, GPIO_PTG2, GPIO_PTG3, - GPIO_PTG4, GPIO_PTG5, GPIO_PTG6, GPIO_PTG7, + GPIO_PTG7, GPIO_PTG6, GPIO_PTG5, GPIO_PTG4, + GPIO_PTG3, GPIO_PTG2, GPIO_PTG1, GPIO_PTG0, /* PTH */ - GPIO_PTH0, GPIO_PTH1, GPIO_PTH2, GPIO_PTH3, - GPIO_PTH4, GPIO_PTH5, GPIO_PTH6, GPIO_PTH7, + GPIO_PTH7, GPIO_PTH6, GPIO_PTH5, GPIO_PTH4, + GPIO_PTH3, GPIO_PTH2, GPIO_PTH1, GPIO_PTH0, /* PTI */ - GPIO_PTI0, GPIO_PTI1, GPIO_PTI2, GPIO_PTI3, - GPIO_PTI4, GPIO_PTI5, GPIO_PTI6, GPIO_PTI7, + GPIO_PTI7, GPIO_PTI6, GPIO_PTI5, GPIO_PTI4, + GPIO_PTI3, GPIO_PTI2, GPIO_PTI1, GPIO_PTI0, /* PTJ */ - GPIO_PTJ0, GPIO_PTJ1, GPIO_PTJ2, GPIO_PTJ3, - GPIO_PTJ4, GPIO_PTJ5, GPIO_PTJ6, GPIO_PTJ7_RESV, + GPIO_PTJ7, GPIO_PTJ6, GPIO_PTJ5, GPIO_PTJ4, + GPIO_PTJ3, GPIO_PTJ2, GPIO_PTJ1, GPIO_PTJ0, /* PTK */ - GPIO_PTK0, GPIO_PTK1, GPIO_PTK2, GPIO_PTK3, - GPIO_PTK4, GPIO_PTK5, GPIO_PTK6, GPIO_PTK7, + GPIO_PTK7, GPIO_PTK6, GPIO_PTK5, GPIO_PTK4, + GPIO_PTK3, GPIO_PTK2, GPIO_PTK1, GPIO_PTK0, /* PTL */ - GPIO_PTL0, GPIO_PTL1, GPIO_PTL2, GPIO_PTL3, - GPIO_PTL4, GPIO_PTL5, GPIO_PTL6, GPIO_PTL7_RESV, + GPIO_PTL7, GPIO_PTL6, GPIO_PTL5, GPIO_PTL4, + GPIO_PTL3, GPIO_PTL2, GPIO_PTL1, GPIO_PTL0, /* PTM */ - GPIO_PTM0, GPIO_PTM1, GPIO_PTM2, GPIO_PTM3, - GPIO_PTM4, GPIO_PTM5, GPIO_PTM6, GPIO_PTM7, + GPIO_PTM6, GPIO_PTM5, GPIO_PTM4, + GPIO_PTM3, GPIO_PTM2, GPIO_PTM1, GPIO_PTM0, /* PTN */ - GPIO_PTN0, GPIO_PTN1, GPIO_PTN2, GPIO_PTN3, - GPIO_PTN4, GPIO_PTN5, GPIO_PTN6, GPIO_PTN7_RESV, + GPIO_PTN7, GPIO_PTN6, GPIO_PTN5, GPIO_PTN4, + GPIO_PTN3, GPIO_PTN2, GPIO_PTN1, GPIO_PTN0, /* PTO */ - GPIO_PTO0, GPIO_PTO1, GPIO_PTO2, GPIO_PTO3, - GPIO_PTO4, GPIO_PTO5, GPIO_PTO6, GPIO_PTO7, + GPIO_PTO7, GPIO_PTO6, GPIO_PTO5, GPIO_PTO4, + GPIO_PTO3, GPIO_PTO2, GPIO_PTO1, GPIO_PTO0, /* PTP */ - GPIO_PTP0, GPIO_PTP1, GPIO_PTP2, GPIO_PTP3, - GPIO_PTP4, GPIO_PTP5, GPIO_PTP6, GPIO_PTP7, + GPIO_PTP6, GPIO_PTP5, GPIO_PTP4, + GPIO_PTP3, GPIO_PTP2, GPIO_PTP1, GPIO_PTP0, /* PTQ */ - GPIO_PTQ0, GPIO_PTQ1, GPIO_PTQ2, GPIO_PTQ3, - GPIO_PTQ4, GPIO_PTQ5, GPIO_PTQ6, GPIO_PTQ7_RESV, + GPIO_PTQ6, GPIO_PTQ5, GPIO_PTQ4, + GPIO_PTQ3, GPIO_PTQ2, GPIO_PTQ1, GPIO_PTQ0, /* PTR */ - GPIO_PTR0, GPIO_PTR1, GPIO_PTR2, GPIO_PTR3, - GPIO_PTR4, GPIO_PTR5, GPIO_PTR6, GPIO_PTR7, + GPIO_PTR7, GPIO_PTR6, GPIO_PTR5, GPIO_PTR4, + GPIO_PTR3, GPIO_PTR2, GPIO_PTR1, GPIO_PTR0, /* PTS */ - GPIO_PTS0, GPIO_PTS1, GPIO_PTS2, GPIO_PTS3, - GPIO_PTS4, GPIO_PTS5, GPIO_PTS6, GPIO_PTS7, + GPIO_PTS7, GPIO_PTS6, GPIO_PTS5, GPIO_PTS4, + GPIO_PTS3, GPIO_PTS2, GPIO_PTS1, GPIO_PTS0, /* PTT */ - GPIO_PTT0, GPIO_PTT1, GPIO_PTT2, GPIO_PTT3, - GPIO_PTT4, GPIO_PTT5, GPIO_PTT6, GPIO_PTT7, + GPIO_PTT5, GPIO_PTT4, + GPIO_PTT3, GPIO_PTT2, GPIO_PTT1, GPIO_PTT0, /* PTU */ - GPIO_PTU0, GPIO_PTU1, GPIO_PTU2, GPIO_PTU3, - GPIO_PTU4, GPIO_PTU5, GPIO_PTU6, GPIO_PTU7, + GPIO_PTU7, GPIO_PTU6, GPIO_PTU5, GPIO_PTU4, + GPIO_PTU3, GPIO_PTU2, GPIO_PTU1, GPIO_PTU0, /* PTV */ - GPIO_PTV0, GPIO_PTV1, GPIO_PTV2, GPIO_PTV3, - GPIO_PTV4, GPIO_PTV5, GPIO_PTV6, GPIO_PTV7, + GPIO_PTV7, GPIO_PTV6, GPIO_PTV5, GPIO_PTV4, + GPIO_PTV3, GPIO_PTV2, GPIO_PTV1, GPIO_PTV0, /* PTW */ - GPIO_PTW0, GPIO_PTW1, GPIO_PTW2, GPIO_PTW3, - GPIO_PTW4, GPIO_PTW5, GPIO_PTW6, GPIO_PTW7, + GPIO_PTW7, GPIO_PTW6, GPIO_PTW5, GPIO_PTW4, + GPIO_PTW3, GPIO_PTW2, GPIO_PTW1, GPIO_PTW0, /* PTX */ - GPIO_PTX0, GPIO_PTX1, GPIO_PTX2, GPIO_PTX3, - GPIO_PTX4, GPIO_PTX5, GPIO_PTX6, GPIO_PTX7, + GPIO_PTX7, GPIO_PTX6, GPIO_PTX5, GPIO_PTX4, + GPIO_PTX3, GPIO_PTX2, GPIO_PTX1, GPIO_PTX0, /* PTY */ - GPIO_PTY0, GPIO_PTY1, GPIO_PTY2, GPIO_PTY3, - GPIO_PTY4, GPIO_PTY5, GPIO_PTY6, GPIO_PTY7, + GPIO_PTY7, GPIO_PTY6, GPIO_PTY5, GPIO_PTY4, + GPIO_PTY3, GPIO_PTY2, GPIO_PTY1, GPIO_PTY0, /* PTZ */ - GPIO_PTZ0, GPIO_PTZ1, GPIO_PTZ2, GPIO_PTZ3, - GPIO_PTZ4, GPIO_PTZ5, GPIO_PTZ6, GPIO_PTZ7, + GPIO_PTZ7, GPIO_PTZ6, GPIO_PTZ5, GPIO_PTZ4, + GPIO_PTZ3, GPIO_PTZ2, GPIO_PTZ1, GPIO_PTZ0, - /* PTA (mobule: LBSC, RGMII) */ + /* PTA (mobule: LBSC, CPG, LPC) */ GPIO_FN_BS, GPIO_FN_RDWR, GPIO_FN_WE1, GPIO_FN_RDY, - GPIO_FN_ET0_MDC, GPIO_FN_ET0_MDIO, - GPIO_FN_ET1_MDC, GPIO_FN_ET1_MDIO, + GPIO_FN_MD10, GPIO_FN_MD9, GPIO_FN_MD8, + GPIO_FN_LGPIO7, GPIO_FN_LGPIO6, GPIO_FN_LGPIO5, GPIO_FN_LGPIO4, + GPIO_FN_LGPIO3, GPIO_FN_LGPIO2, GPIO_FN_LGPIO1, GPIO_FN_LGPIO0, - /* PTB (mobule: INTC, ONFI, TMU) */ - GPIO_FN_IRQ15, GPIO_FN_IRQ14, GPIO_FN_IRQ13, GPIO_FN_IRQ12, - GPIO_FN_IRQ11, GPIO_FN_IRQ10, GPIO_FN_IRQ9, GPIO_FN_IRQ8, - GPIO_FN_ON_NRE, GPIO_FN_ON_NWE, GPIO_FN_ON_NWP, GPIO_FN_ON_NCE0, - GPIO_FN_ON_R_B0, GPIO_FN_ON_ALE, GPIO_FN_ON_CLE, - GPIO_FN_TCLK, + /* PTB (mobule: LBSC, EtherC, SIM, LPC) */ + GPIO_FN_D15, GPIO_FN_D14, GPIO_FN_D13, GPIO_FN_D12, + GPIO_FN_D11, GPIO_FN_D10, GPIO_FN_D9, GPIO_FN_D8, + GPIO_FN_ET0_MDC, GPIO_FN_ET0_MDIO, + GPIO_FN_ET1_MDC, GPIO_FN_ET1_MDIO, + GPIO_FN_SIM_D, GPIO_FN_SIM_CLK, GPIO_FN_SIM_RST, + GPIO_FN_WPSZ1, GPIO_FN_WPSZ0, GPIO_FN_FWID, GPIO_FN_FLSHSZ, + GPIO_FN_LPC_SPIEN, GPIO_FN_BASEL, - /* PTC (mobule: IRQ, PWMU) */ + /* PTC (mobule: SD) */ + GPIO_FN_SD_WP, GPIO_FN_SD_CD, GPIO_FN_SD_CLK, GPIO_FN_SD_CMD, + GPIO_FN_SD_D3, GPIO_FN_SD_D2, GPIO_FN_SD_D1, GPIO_FN_SD_D0, + + /* PTD (mobule: INTC, SPI0, LBSC, CPG, ADC) */ GPIO_FN_IRQ7, GPIO_FN_IRQ6, GPIO_FN_IRQ5, GPIO_FN_IRQ4, GPIO_FN_IRQ3, GPIO_FN_IRQ2, GPIO_FN_IRQ1, GPIO_FN_IRQ0, - GPIO_FN_PWMU0, GPIO_FN_PWMU1, GPIO_FN_PWMU2, GPIO_FN_PWMU3, - GPIO_FN_PWMU4, GPIO_FN_PWMU5, - - /* PTD (mobule: SPI0, DMAC) */ - GPIO_FN_SP0_MOSI, GPIO_FN_SP0_MISO, GPIO_FN_SP0_SCK, - GPIO_FN_SP0_SCK_FB, GPIO_FN_SP0_SS0, GPIO_FN_SP0_SS1, - GPIO_FN_SP0_SS2, GPIO_FN_SP0_SS3, GPIO_FN_DREQ0, - GPIO_FN_DACK0, GPIO_FN_TEND0, - - /* PTE (mobule: RMII) */ - GPIO_FN_RMII0_CRS_DV, GPIO_FN_RMII0_TXD1, GPIO_FN_RMII0_TXD0, - GPIO_FN_RMII0_TXEN, GPIO_FN_RMII0_REFCLK, GPIO_FN_RMII0_RXD1, - GPIO_FN_RMII0_RXD0, GPIO_FN_RMII0_RX_ER, - - /* PTF (mobule: RMII, SerMux) */ - GPIO_FN_RMII1_CRS_DV, GPIO_FN_RMII1_TXD1, GPIO_FN_RMII1_TXD0, - GPIO_FN_RMII1_TXEN, GPIO_FN_RMII1_REFCLK, GPIO_FN_RMII1_RXD1, - GPIO_FN_RMII1_RXD0, GPIO_FN_RMII1_RX_ER, GPIO_FN_RAC_RI, - - /* PTG (mobule: system, LBSC, LPC, WDT, LPC, eMMC) */ - GPIO_FN_BOOTFMS, GPIO_FN_BOOTWP, - GPIO_FN_A25, GPIO_FN_A24, GPIO_FN_SERIRQ, GPIO_FN_WDTOVF, - GPIO_FN_LPCPD, GPIO_FN_LDRQ, GPIO_FN_MMCCLK, GPIO_FN_MMCCMD, - - /* PTH (mobule: SPI1, LPC, DMAC, ADC) */ + GPIO_FN_MD6, GPIO_FN_MD5, GPIO_FN_MD3, GPIO_FN_MD2, + GPIO_FN_MD1, GPIO_FN_MD0, GPIO_FN_ADTRG1, GPIO_FN_ADTRG0, + + /* PTE (mobule: EtherC) */ + GPIO_FN_ET0_CRS_DV, GPIO_FN_ET0_TXD1, + GPIO_FN_ET0_TXD0, GPIO_FN_ET0_TX_EN, + GPIO_FN_ET0_REF_CLK, GPIO_FN_ET0_RXD1, + GPIO_FN_ET0_RXD0, GPIO_FN_ET0_RX_ER, + + /* PTF (mobule: EtherC) */ + GPIO_FN_ET1_CRS_DV, GPIO_FN_ET1_TXD1, + GPIO_FN_ET1_TXD0, GPIO_FN_ET1_TX_EN, + GPIO_FN_ET1_REF_CLK, GPIO_FN_ET1_RXD1, + GPIO_FN_ET1_RXD0, GPIO_FN_ET1_RX_ER, + + /* PTG (mobule: SYSTEM, PWMX, LPC) */ + GPIO_FN_STATUS0, GPIO_FN_STATUS1, + GPIO_FN_PWX0, GPIO_FN_PWX1, GPIO_FN_PWX2, GPIO_FN_PWX3, + GPIO_FN_SERIRQ, GPIO_FN_CLKRUN, GPIO_FN_LPCPD, GPIO_FN_LDRQ, + + /* PTH (mobule: TMU, SCIF234, SPI1, SPI0) */ + GPIO_FN_TCLK, GPIO_FN_RXD4, GPIO_FN_TXD4, GPIO_FN_SP1_MOSI, GPIO_FN_SP1_MISO, GPIO_FN_SP1_SCK, GPIO_FN_SP1_SCK_FB, GPIO_FN_SP1_SS0, GPIO_FN_SP1_SS1, - GPIO_FN_WP, GPIO_FN_FMS0, GPIO_FN_TEND1, GPIO_FN_DREQ1, - GPIO_FN_DACK1, GPIO_FN_ADTRG1, GPIO_FN_ADTRG0, + GPIO_FN_SP0_SS1, - /* PTI (mobule: LBSC, SDHI) */ - GPIO_FN_D15, GPIO_FN_D14, GPIO_FN_D13, GPIO_FN_D12, - GPIO_FN_D11, GPIO_FN_D10, GPIO_FN_D9, GPIO_FN_D8, - GPIO_FN_SD_WP, GPIO_FN_SD_CD, GPIO_FN_SD_CLK, GPIO_FN_SD_CMD, - GPIO_FN_SD_D3, GPIO_FN_SD_D2, GPIO_FN_SD_D1, GPIO_FN_SD_D0, + /* PTI (mobule: INTC) */ + GPIO_FN_IRQ15, GPIO_FN_IRQ14, GPIO_FN_IRQ13, GPIO_FN_IRQ12, + GPIO_FN_IRQ11, GPIO_FN_IRQ10, GPIO_FN_IRQ9, GPIO_FN_IRQ8, + + /* PTJ (mobule: SCIF234, SERMUX) */ + GPIO_FN_RXD3, GPIO_FN_TXD3, GPIO_FN_RXD2, GPIO_FN_TXD2, + GPIO_FN_COM1_TXD, GPIO_FN_COM1_RXD, + GPIO_FN_COM1_RTS, GPIO_FN_COM1_CTS, + + /* PTK (mobule: SERMUX) */ + GPIO_FN_COM2_TXD, GPIO_FN_COM2_RXD, + GPIO_FN_COM2_RTS, GPIO_FN_COM2_CTS, + GPIO_FN_COM2_DTR, GPIO_FN_COM2_DSR, + GPIO_FN_COM2_DCD, GPIO_FN_COM2_RI, - /* PTJ (mobule: SCIF234) */ - GPIO_FN_RTS3, GPIO_FN_CTS3, GPIO_FN_TXD3, GPIO_FN_RXD3, - GPIO_FN_RTS4, GPIO_FN_RXD4, GPIO_FN_TXD4, - - /* PTK (mobule: SERMUX, LBSC, SCIF) */ - GPIO_FN_COM2_TXD, GPIO_FN_COM2_RXD, GPIO_FN_COM2_RTS, - GPIO_FN_COM2_CTS, GPIO_FN_COM2_DTR, GPIO_FN_COM2_DSR, - GPIO_FN_COM2_DCD, GPIO_FN_CLKOUT, - GPIO_FN_SCK2, GPIO_FN_SCK4, GPIO_FN_SCK3, - - /* PTL (mobule: SERMUX, SCIF, LBSC, AUD) */ - GPIO_FN_RAC_RXD, GPIO_FN_RAC_RTS, GPIO_FN_RAC_CTS, - GPIO_FN_RAC_DTR, GPIO_FN_RAC_DSR, GPIO_FN_RAC_DCD, - GPIO_FN_RAC_TXD, GPIO_FN_RXD2, GPIO_FN_CS5, - GPIO_FN_CS6, GPIO_FN_AUDSYNC, GPIO_FN_AUDCK, - GPIO_FN_TXD2, - - /* PTM (mobule: LBSC, IIC) */ - GPIO_FN_CS4, GPIO_FN_RD, GPIO_FN_WE0, GPIO_FN_CS0, + /* PTL (mobule: SERMUX) */ + GPIO_FN_RAC_TXD, GPIO_FN_RAC_RXD, + GPIO_FN_RAC_RTS, GPIO_FN_RAC_CTS, + GPIO_FN_RAC_DTR, GPIO_FN_RAC_DSR, + GPIO_FN_RAC_DCD, GPIO_FN_RAC_RI, + + /* PTM (mobule: IIC, LPC) */ GPIO_FN_SDA6, GPIO_FN_SCL6, GPIO_FN_SDA7, GPIO_FN_SCL7, + GPIO_FN_WP, GPIO_FN_FMS0, GPIO_FN_FMS1, + + /* PTN (mobule: SCIF234, EVC) */ + GPIO_FN_SCK2, GPIO_FN_RTS4, GPIO_FN_RTS3, GPIO_FN_RTS2, + GPIO_FN_CTS4, GPIO_FN_CTS3, GPIO_FN_CTS2, + GPIO_FN_EVENT7, GPIO_FN_EVENT6, GPIO_FN_EVENT5, GPIO_FN_EVENT4, + GPIO_FN_EVENT3, GPIO_FN_EVENT2, GPIO_FN_EVENT1, GPIO_FN_EVENT0, - /* PTN (mobule: USB, JMC, SGPIO, WDT) */ - GPIO_FN_VBUS_EN, GPIO_FN_VBUS_OC, GPIO_FN_JMCTCK, - GPIO_FN_JMCTMS, GPIO_FN_JMCTDO, GPIO_FN_JMCTDI, - GPIO_FN_JMCTRST, - GPIO_FN_SGPIO1_CLK, GPIO_FN_SGPIO1_LOAD, GPIO_FN_SGPIO1_DI, - GPIO_FN_SGPIO1_DO, GPIO_FN_SUB_CLKIN, + /* PTO (mobule: SGPIO) */ + GPIO_FN_SGPIO0_CLK, GPIO_FN_SGPIO0_LOAD, + GPIO_FN_SGPIO0_DI, GPIO_FN_SGPIO0_DO, + GPIO_FN_SGPIO1_CLK, GPIO_FN_SGPIO1_LOAD, + GPIO_FN_SGPIO1_DI, GPIO_FN_SGPIO1_DO, - /* PTO (mobule: SGPIO, SerMux) */ - GPIO_FN_SGPIO0_CLK, GPIO_FN_SGPIO0_LOAD, GPIO_FN_SGPIO0_DI, - GPIO_FN_SGPIO0_DO, GPIO_FN_SGPIO2_CLK, GPIO_FN_SGPIO2_LOAD, - GPIO_FN_SGPIO2_DI, GPIO_FN_SGPIO2_DO, GPIO_FN_COM1_TXD, - GPIO_FN_COM1_RXD, GPIO_FN_COM1_RTS, GPIO_FN_COM1_CTS, + /* PTP (mobule: JMC, SCIF234) */ + GPIO_FN_JMCTCK, GPIO_FN_JMCTMS, GPIO_FN_JMCTDO, GPIO_FN_JMCTDI, + GPIO_FN_JMCRST, GPIO_FN_SCK4, GPIO_FN_SCK3, /* PTQ (mobule: LPC) */ GPIO_FN_LAD3, GPIO_FN_LAD2, GPIO_FN_LAD1, GPIO_FN_LAD0, GPIO_FN_LFRAME, GPIO_FN_LRESET, GPIO_FN_LCLK, /* PTR (mobule: GRA, IIC) */ - GPIO_FN_DDC3, GPIO_FN_DDC2, GPIO_FN_SDA2, GPIO_FN_SCL2, + GPIO_FN_DDC3, GPIO_FN_DDC2, + GPIO_FN_SDA8, GPIO_FN_SCL8, GPIO_FN_SDA2, GPIO_FN_SCL2, GPIO_FN_SDA1, GPIO_FN_SCL1, GPIO_FN_SDA0, GPIO_FN_SCL0, - GPIO_FN_SDA8, GPIO_FN_SCL8, /* PTS (mobule: GRA, IIC) */ - GPIO_FN_DDC1, GPIO_FN_DDC0, GPIO_FN_SDA5, GPIO_FN_SCL5, + GPIO_FN_DDC1, GPIO_FN_DDC0, + GPIO_FN_SDA9, GPIO_FN_SCL9, GPIO_FN_SDA5, GPIO_FN_SCL5, GPIO_FN_SDA4, GPIO_FN_SCL4, GPIO_FN_SDA3, GPIO_FN_SCL3, - GPIO_FN_SDA9, GPIO_FN_SCL9, - /* PTT (mobule: PWMX, AUD) */ - GPIO_FN_PWMX7, GPIO_FN_PWMX6, GPIO_FN_PWMX5, GPIO_FN_PWMX4, - GPIO_FN_PWMX3, GPIO_FN_PWMX2, GPIO_FN_PWMX1, GPIO_FN_PWMX0, - GPIO_FN_AUDATA3, GPIO_FN_AUDATA2, GPIO_FN_AUDATA1, - GPIO_FN_AUDATA0, GPIO_FN_STATUS1, GPIO_FN_STATUS0, + /* PTT (mobule: SYSTEM, PWMX) */ + GPIO_FN_AUDSYNC, GPIO_FN_AUDCK, + GPIO_FN_AUDATA3, GPIO_FN_AUDATA2, + GPIO_FN_AUDATA1, GPIO_FN_AUDATA0, + GPIO_FN_PWX7, GPIO_FN_PWX6, GPIO_FN_PWX5, GPIO_FN_PWX4, - /* PTU (mobule: LPC, APM) */ - GPIO_FN_LGPIO7, GPIO_FN_LGPIO6, GPIO_FN_LGPIO5, GPIO_FN_LGPIO4, - GPIO_FN_LGPIO3, GPIO_FN_LGPIO2, GPIO_FN_LGPIO1, GPIO_FN_LGPIO0, - GPIO_FN_APMONCTL_O, GPIO_FN_APMPWBTOUT_O, GPIO_FN_APMSCI_O, - GPIO_FN_APMVDDON, GPIO_FN_APMSLPBTN, GPIO_FN_APMPWRBTN, - GPIO_FN_APMS5N, GPIO_FN_APMS3N, + /* PTU (mobule: LBSC, DMAC) */ + GPIO_FN_CS6, GPIO_FN_CS5, GPIO_FN_CS4, GPIO_FN_CS0, + GPIO_FN_RD, GPIO_FN_WE0, GPIO_FN_A25, GPIO_FN_A24, + GPIO_FN_DREQ0, GPIO_FN_DACK0, - /* PTV (mobule: LBSC, SerMux, R-SPI, EVC, GRA) */ + /* PTV (mobule: LBSC, DMAC) */ GPIO_FN_A23, GPIO_FN_A22, GPIO_FN_A21, GPIO_FN_A20, GPIO_FN_A19, GPIO_FN_A18, GPIO_FN_A17, GPIO_FN_A16, - GPIO_FN_COM2_RI, GPIO_FN_R_SPI_MOSI, GPIO_FN_R_SPI_MISO, - GPIO_FN_R_SPI_RSPCK, GPIO_FN_R_SPI_SSL0, GPIO_FN_R_SPI_SSL1, - GPIO_FN_EVENT7, GPIO_FN_EVENT6, GPIO_FN_VBIOS_DI, - GPIO_FN_VBIOS_DO, GPIO_FN_VBIOS_CLK, GPIO_FN_VBIOS_CS, + GPIO_FN_TEND0, GPIO_FN_DREQ1, GPIO_FN_DACK1, GPIO_FN_TEND1, - /* PTW (mobule: LBSC, EVC, SCIF) */ + /* PTW (mobule: LBSC) */ GPIO_FN_A15, GPIO_FN_A14, GPIO_FN_A13, GPIO_FN_A12, GPIO_FN_A11, GPIO_FN_A10, GPIO_FN_A9, GPIO_FN_A8, - GPIO_FN_EVENT5, GPIO_FN_EVENT4, GPIO_FN_EVENT3, GPIO_FN_EVENT2, - GPIO_FN_EVENT1, GPIO_FN_EVENT0, GPIO_FN_CTS4, GPIO_FN_CTS2, - /* PTX (mobule: LBSC, SCIF, SIM) */ + /* PTX (mobule: LBSC) */ GPIO_FN_A7, GPIO_FN_A6, GPIO_FN_A5, GPIO_FN_A4, GPIO_FN_A3, GPIO_FN_A2, GPIO_FN_A1, GPIO_FN_A0, - GPIO_FN_RTS2, GPIO_FN_SIM_D, GPIO_FN_SIM_CLK, GPIO_FN_SIM_RST, /* PTY (mobule: LBSC) */ GPIO_FN_D7, GPIO_FN_D6, GPIO_FN_D5, GPIO_FN_D4, GPIO_FN_D3, GPIO_FN_D2, GPIO_FN_D1, GPIO_FN_D0, - - /* PTZ (mobule: eMMC, ONFI) */ - GPIO_FN_MMCDAT7, GPIO_FN_MMCDAT6, GPIO_FN_MMCDAT5, - GPIO_FN_MMCDAT4, GPIO_FN_MMCDAT3, GPIO_FN_MMCDAT2, - GPIO_FN_MMCDAT1, GPIO_FN_MMCDAT0, - GPIO_FN_ON_DQ7, GPIO_FN_ON_DQ6, GPIO_FN_ON_DQ5, GPIO_FN_ON_DQ4, - GPIO_FN_ON_DQ3, GPIO_FN_ON_DQ2, GPIO_FN_ON_DQ1, GPIO_FN_ON_DQ0, }; #endif /* __ASM_SH7757_H__ */ diff --git a/trunk/arch/sh/include/cpu-sh4/cpu/shx3.h b/trunk/arch/sh/include/cpu-sh4/cpu/shx3.h deleted file mode 100644 index 68d9080a8da9..000000000000 --- a/trunk/arch/sh/include/cpu-sh4/cpu/shx3.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef __CPU_SHX3_H -#define __CPU_SHX3_H - -enum { - /* PA */ - GPIO_PA7, GPIO_PA6, GPIO_PA5, GPIO_PA4, - GPIO_PA3, GPIO_PA2, GPIO_PA1, GPIO_PA0, - - /* PB */ - GPIO_PB7, GPIO_PB6, GPIO_PB5, GPIO_PB4, - GPIO_PB3, GPIO_PB2, GPIO_PB1, GPIO_PB0, - - /* PC */ - GPIO_PC7, GPIO_PC6, GPIO_PC5, GPIO_PC4, - GPIO_PC3, GPIO_PC2, GPIO_PC1, GPIO_PC0, - - /* PD */ - GPIO_PD7, GPIO_PD6, GPIO_PD5, GPIO_PD4, - GPIO_PD3, GPIO_PD2, GPIO_PD1, GPIO_PD0, - - /* PE */ - GPIO_PE7, GPIO_PE6, GPIO_PE5, GPIO_PE4, - GPIO_PE3, GPIO_PE2, GPIO_PE1, GPIO_PE0, - - /* PF */ - GPIO_PF7, GPIO_PF6, GPIO_PF5, GPIO_PF4, - GPIO_PF3, GPIO_PF2, GPIO_PF1, GPIO_PF0, - - /* PG */ - GPIO_PG7, GPIO_PG6, GPIO_PG5, GPIO_PG4, - GPIO_PG3, GPIO_PG2, GPIO_PG1, GPIO_PG0, - - /* PH */ - GPIO_PH5, GPIO_PH4, - GPIO_PH3, GPIO_PH2, GPIO_PH1, GPIO_PH0, - - /* SCIF */ - GPIO_FN_SCK3, GPIO_FN_TXD3, GPIO_FN_RXD3, - GPIO_FN_SCK2, GPIO_FN_TXD2, GPIO_FN_RXD2, - GPIO_FN_SCK1, GPIO_FN_TXD1, GPIO_FN_RXD1, - GPIO_FN_SCK0, GPIO_FN_TXD0, GPIO_FN_RXD0, - - /* LBSC */ - GPIO_FN_D31, GPIO_FN_D30, GPIO_FN_D29, GPIO_FN_D28, - GPIO_FN_D27, GPIO_FN_D26, GPIO_FN_D25, GPIO_FN_D24, - GPIO_FN_D23, GPIO_FN_D22, GPIO_FN_D21, GPIO_FN_D20, - GPIO_FN_D19, GPIO_FN_D18, GPIO_FN_D17, GPIO_FN_D16, - GPIO_FN_WE3, GPIO_FN_WE2, GPIO_FN_CS6, GPIO_FN_CS5, - GPIO_FN_CS4, GPIO_FN_CLKOUTENB, GPIO_FN_BREQ, - GPIO_FN_IOIS16, GPIO_FN_CE2B, GPIO_FN_CE2A, GPIO_FN_BACK, - - /* DMAC */ - GPIO_FN_DACK0, GPIO_FN_DREQ0, GPIO_FN_DRAK0, - GPIO_FN_DACK1, GPIO_FN_DREQ1, GPIO_FN_DRAK1, - GPIO_FN_DACK2, GPIO_FN_DREQ2, GPIO_FN_DRAK2, - GPIO_FN_DACK3, GPIO_FN_DREQ3, GPIO_FN_DRAK3, - - /* INTC */ - GPIO_FN_IRQ3, GPIO_FN_IRQ2, GPIO_FN_IRQ1, GPIO_FN_IRQ0, - GPIO_FN_IRL3, GPIO_FN_IRL2, GPIO_FN_IRL1, GPIO_FN_IRL0, - GPIO_FN_IRQOUT, GPIO_FN_STATUS1, GPIO_FN_STATUS0, -}; - -#endif /* __CPU_SHX3_H */ diff --git a/trunk/arch/sh/include/mach-common/mach/sh2007.h b/trunk/arch/sh/include/mach-common/mach/sh2007.h deleted file mode 100644 index 48180b9aa03d..000000000000 --- a/trunk/arch/sh/include/mach-common/mach/sh2007.h +++ /dev/null @@ -1,117 +0,0 @@ -#ifndef __MACH_SH2007_H -#define __MACH_SH2007_H - -#define CS5BCR 0xff802050 -#define CS5WCR 0xff802058 -#define CS5PCR 0xff802070 - -#define BUS_SZ8 1 -#define BUS_SZ16 2 -#define BUS_SZ32 3 - -#define PCMCIA_IODYN 1 -#define PCMCIA_ATA 0 -#define PCMCIA_IO8 2 -#define PCMCIA_IO16 3 -#define PCMCIA_COMM8 4 -#define PCMCIA_COMM16 5 -#define PCMCIA_ATTR8 6 -#define PCMCIA_ATTR16 7 - -#define TYPE_SRAM 0 -#define TYPE_PCMCIA 4 - -/* write-read/write-write delay (0-7:0,1,2,3,4,5,6,7) */ -#define IWW5 0 -#define IWW6 3 -/* different area, read-write delay (0-7:0,1,2,3,4,5,6,7) */ -#define IWRWD5 2 -#define IWRWD6 2 -/* same area, read-write delay (0-7:0,1,2,3,4,5,6,7) */ -#define IWRWS5 2 -#define IWRWS6 2 -/* different area, read-read delay (0-7:0,1,2,3,4,5,6,7) */ -#define IWRRD5 2 -#define IWRRD6 2 -/* same area, read-read delay (0-7:0,1,2,3,4,5,6,7) */ -#define IWRRS5 0 -#define IWRRS6 2 -/* burst count (0-3:4,8,16,32) */ -#define BST5 0 -#define BST6 0 -/* bus size */ -#define SZ5 BUS_SZ16 -#define SZ6 BUS_SZ16 -/* RD hold for SRAM (0-1:0,1) */ -#define RDSPL5 0 -#define RDSPL6 0 -/* Burst pitch (0-7:0,1,2,3,4,5,6,7) */ -#define BW5 0 -#define BW6 0 -/* Multiplex (0-1:0,1) */ -#define MPX5 0 -#define MPX6 0 -/* device type */ -#define TYPE5 TYPE_PCMCIA -#define TYPE6 TYPE_PCMCIA -/* address setup before assert CSn for SRAM (0-7:0,1,2,3,4,5,6,7) */ -#define ADS5 0 -#define ADS6 0 -/* address hold after negate CSn for SRAM (0-7:0,1,2,3,4,5,6,7) */ -#define ADH5 0 -#define ADH6 0 -/* CSn assert to RD assert delay for SRAM (0-7:0,1,2,3,4,5,6,7) */ -#define RDS5 0 -#define RDS6 0 -/* RD negate to CSn negate delay for SRAM (0-7:0,1,2,3,4,5,6,7) */ -#define RDH5 0 -#define RDH6 0 -/* CSn assert to WE assert delay for SRAM (0-7:0,1,2,3,4,5,6,7) */ -#define WTS5 0 -#define WTS6 0 -/* WE negate to CSn negate delay for SRAM (0-7:0,1,2,3,4,5,6,7) */ -#define WTH5 0 -#define WTH6 0 -/* BS hold (0-1:1,2) */ -#define BSH5 0 -#define BSH6 0 -/* wait cycle (0-15:0,1,2,3,4,5,6,7,8,9,11,13,15,17,21,25) */ -#define IW5 6 /* 60ns PIO mode 4 */ -#define IW6 15 /* 250ns */ - -#define SAA5 PCMCIA_IODYN /* IDE area b4000000-b5ffffff */ -#define SAB5 PCMCIA_IODYN /* CF area b6000000-b7ffffff */ -#define PCWA5 0 /* additional wait A (0-3:0,15,30,50) */ -#define PCWB5 0 /* additional wait B (0-3:0,15,30,50) */ -/* wait B (0-15:0,1,2,3,4,5,6,7,8,9,11,13,15,17,21,25) */ -#define PCIW5 12 -/* Address->OE/WE assert delay A (0-7:0,1,2,3,6,9,12,15) */ -#define TEDA5 2 -/* Address->OE/WE assert delay B (0-7:0,1,2,3,6,9,12,15) */ -#define TEDB5 4 -/* OE/WE negate->Address delay A (0-7:0,1,2,3,6,9,12,15) */ -#define TEHA5 2 -/* OE/WE negate->Address delay B (0-7:0,1,2,3,6,9,12,15) */ -#define TEHB5 3 - -#define CS5BCR_D ((IWW5<<28)|(IWRWD5<<24)|(IWRWS5<<20)| \ - (IWRRD5<<16)|(IWRRS5<<12)|(BST5<<10)| \ - (SZ5<<8)|(RDSPL5<<7)|(BW5<<4)|(MPX5<<3)|TYPE5) -#define CS5WCR_D ((ADS5<<28)|(ADH5<<24)|(RDS5<<20)| \ - (RDH5<<16)|(WTS5<<12)|(WTH5<<8)|(BSH5<<4)|IW5) -#define CS5PCR_D ((SAA5<<28)|(SAB5<<24)|(PCWA5<<22)| \ - (PCWB5<<20)|(PCIW5<<16)|(TEDA5<<12)| \ - (TEDB5<<8)|(TEHA5<<4)|TEHB5) - -#define SMC0_BASE 0xb0800000 /* eth0 */ -#define SMC1_BASE 0xb0900000 /* eth1 */ -#define CF_BASE 0xb6100000 /* Compact Flash (I/O area) */ -#define IDE_BASE 0xb4000000 /* IDE */ -#define PC104_IO_BASE 0xb8000000 -#define PC104_MEM_BASE 0xba000000 -#define SMC_IO_SIZE 0x100 - -#define CF_OFFSET 0x1f0 -#define IDE_OFFSET 0x170 - -#endif /* __MACH_SH2007_H */ diff --git a/trunk/arch/sh/include/mach-sdk7786/mach/fpga.h b/trunk/arch/sh/include/mach-sdk7786/mach/fpga.h index 40f0c2d3690c..416b621d94d1 100644 --- a/trunk/arch/sh/include/mach-sdk7786/mach/fpga.h +++ b/trunk/arch/sh/include/mach-sdk7786/mach/fpga.h @@ -31,35 +31,11 @@ #define EXTASR 0x110 #define SPCAR 0x120 #define INTMSR 0x130 - #define PCIECR 0x140 -#define PCIECR_PCIEMUX1 BIT(15) -#define PCIECR_PCIEMUX0 BIT(14) -#define PCIECR_PRST4 BIT(12) /* slot 4 card present */ -#define PCIECR_PRST3 BIT(11) /* slot 3 card present */ -#define PCIECR_PRST2 BIT(10) /* slot 2 card present */ -#define PCIECR_PRST1 BIT(9) /* slot 1 card present */ -#define PCIECR_CLKEN BIT(4) /* oscillator enable */ - #define FAER 0x150 #define USRGPIR 0x160 - /* 0x170 reserved */ - -#define LCLASR 0x180 -#define LCLASR_FRAMEN BIT(15) - -#define LCLASR_FPGA_SEL_SHIFT 12 -#define LCLASR_NAND_SEL_SHIFT 8 -#define LCLASR_NORB_SEL_SHIFT 4 -#define LCLASR_NORA_SEL_SHIFT 0 - -#define LCLASR_AREA_MASK 0x7 - -#define LCLASR_FPGA_SEL_MASK (LCLASR_AREA_MASK << LCLASR_FPGA_SEL_SHIFT) -#define LCLASR_NAND_SEL_MASK (LCLASR_AREA_MASK << LCLASR_NAND_SEL_SHIFT) -#define LCLASR_NORB_SEL_MASK (LCLASR_AREA_MASK << LCLASR_NORB_SEL_SHIFT) -#define LCLASR_NORA_SEL_MASK (LCLASR_AREA_MASK << LCLASR_NORA_SEL_SHIFT) +#define LCLASR 0x180 #define SBCR 0x190 #define SCBR_I2CMEN BIT(0) /* FPGA I2C master enable */ diff --git a/trunk/arch/sh/include/mach-x3proto/mach/hardware.h b/trunk/arch/sh/include/mach-x3proto/mach/hardware.h deleted file mode 100644 index 52bca57bfeb6..000000000000 --- a/trunk/arch/sh/include/mach-x3proto/mach/hardware.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef __MACH_X3PROTO_HARDWARE_H -#define __MACH_X3PROTO_HARDWARE_H - -struct gpio_chip; - -/* arch/sh/boards/mach-x3proto/gpio.c */ -int x3proto_gpio_setup(void); -extern struct gpio_chip x3proto_gpio_chip; - -#define NR_BASEBOARD_GPIOS 16 - -#endif /* __MACH_X3PROTO_HARDWARE_H */ diff --git a/trunk/arch/sh/kernel/Makefile b/trunk/arch/sh/kernel/Makefile index 8eed6a485446..e25f3c69525d 100644 --- a/trunk/arch/sh/kernel/Makefile +++ b/trunk/arch/sh/kernel/Makefile @@ -12,9 +12,9 @@ endif CFLAGS_REMOVE_return_address.o = -pg obj-y := clkdev.o debugtraps.o dma-nommu.o dumpstack.o \ - idle.o io.o irq.o irq_$(BITS).o kdebugfs.o \ - machvec.o nmi_debug.o process.o \ - process_$(BITS).o ptrace.o ptrace_$(BITS).o \ + idle.o io.o irq.o \ + irq_$(BITS).o machvec.o nmi_debug.o process.o \ + process_$(BITS).o ptrace_$(BITS).o \ reboot.o return_address.o \ setup.o signal_$(BITS).o sys_sh.o sys_sh$(BITS).o \ syscalls_$(BITS).o time.o topology.o traps.o \ @@ -44,4 +44,4 @@ obj-$(CONFIG_HAS_IOPORT) += io_generic.o obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o obj-$(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) += localtimer.o -ccflags-y := -Werror +EXTRA_CFLAGS += -Werror diff --git a/trunk/arch/sh/kernel/clkdev.c b/trunk/arch/sh/kernel/clkdev.c index 1f800ef4a735..befc255830a4 100644 --- a/trunk/arch/sh/kernel/clkdev.c +++ b/trunk/arch/sh/kernel/clkdev.c @@ -161,11 +161,9 @@ EXPORT_SYMBOL(clk_add_alias); */ void clkdev_drop(struct clk_lookup *cl) { - struct clk_lookup_alloc *cla = container_of(cl, struct clk_lookup_alloc, cl); - mutex_lock(&clocks_mutex); list_del(&cl->node); mutex_unlock(&clocks_mutex); - kfree(cla); + kfree(cl); } EXPORT_SYMBOL(clkdev_drop); diff --git a/trunk/arch/sh/kernel/cpu/sh4/probe.c b/trunk/arch/sh/kernel/cpu/sh4/probe.c index b93458f33b74..d180f16281ed 100644 --- a/trunk/arch/sh/kernel/cpu/sh4/probe.c +++ b/trunk/arch/sh/kernel/cpu/sh4/probe.c @@ -150,7 +150,7 @@ void __cpuinit cpu_probe(void) boot_cpu_data.type = CPU_SH7724; boot_cpu_data.flags |= CPU_HAS_L2_CACHE; break; - case 0x10: + case 0x50: boot_cpu_data.type = CPU_SH7757; break; } diff --git a/trunk/arch/sh/kernel/cpu/sh4a/Makefile b/trunk/arch/sh/kernel/cpu/sh4a/Makefile index cc122b1d3035..b144e8af89dc 100644 --- a/trunk/arch/sh/kernel/cpu/sh4a/Makefile +++ b/trunk/arch/sh/kernel/cpu/sh4a/Makefile @@ -8,13 +8,13 @@ obj-$(CONFIG_CPU_SUBTYPE_SH7763) += setup-sh7763.o obj-$(CONFIG_CPU_SUBTYPE_SH7770) += setup-sh7770.o obj-$(CONFIG_CPU_SUBTYPE_SH7780) += setup-sh7780.o obj-$(CONFIG_CPU_SUBTYPE_SH7785) += setup-sh7785.o -obj-$(CONFIG_CPU_SUBTYPE_SH7786) += setup-sh7786.o intc-shx3.o +obj-$(CONFIG_CPU_SUBTYPE_SH7786) += setup-sh7786.o obj-$(CONFIG_CPU_SUBTYPE_SH7343) += setup-sh7343.o obj-$(CONFIG_CPU_SUBTYPE_SH7722) += setup-sh7722.o obj-$(CONFIG_CPU_SUBTYPE_SH7723) += setup-sh7723.o obj-$(CONFIG_CPU_SUBTYPE_SH7724) += setup-sh7724.o obj-$(CONFIG_CPU_SUBTYPE_SH7366) += setup-sh7366.o -obj-$(CONFIG_CPU_SUBTYPE_SHX3) += setup-shx3.o intc-shx3.o +obj-$(CONFIG_CPU_SUBTYPE_SHX3) += setup-shx3.o # SMP setup smp-$(CONFIG_CPU_SHX3) := smp-shx3.o @@ -40,7 +40,6 @@ pinmux-$(CONFIG_CPU_SUBTYPE_SH7724) := pinmux-sh7724.o pinmux-$(CONFIG_CPU_SUBTYPE_SH7757) := pinmux-sh7757.o pinmux-$(CONFIG_CPU_SUBTYPE_SH7785) := pinmux-sh7785.o pinmux-$(CONFIG_CPU_SUBTYPE_SH7786) := pinmux-sh7786.o -pinmux-$(CONFIG_CPU_SUBTYPE_SHX3) := pinmux-shx3.o obj-y += $(clock-y) obj-$(CONFIG_SMP) += $(smp-y) diff --git a/trunk/arch/sh/kernel/cpu/sh4a/clock-sh7757.c b/trunk/arch/sh/kernel/cpu/sh4a/clock-sh7757.c index ce39a2ae8c6c..0a752bd324ac 100644 --- a/trunk/arch/sh/kernel/cpu/sh4a/clock-sh7757.c +++ b/trunk/arch/sh/kernel/cpu/sh4a/clock-sh7757.c @@ -3,7 +3,7 @@ * * SH7757 support for the clock framework * - * Copyright (C) 2009-2010 Renesas Solutions Corp. + * Copyright (C) 2009 Renesas Solutions Corp. * * 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 @@ -16,147 +16,124 @@ #include #include -/* - * Default rate for the root input clock, reset this with clk_set_rate() - * from the platform code. - */ -static struct clk extal_clk = { - .rate = 48000000, -}; +static int ifc_divisors[] = { 2, 1, 4, 1, 1, 8, 1, 1, + 16, 1, 1, 32, 1, 1, 1, 1 }; +static int sfc_divisors[] = { 2, 1, 4, 1, 1, 8, 1, 1, + 16, 1, 1, 32, 1, 1, 1, 1 }; +static int bfc_divisors[] = { 2, 1, 4, 1, 1, 8, 1, 1, + 16, 1, 1, 32, 1, 1, 1, 1 }; +static int p1fc_divisors[] = { 2, 1, 4, 1, 1, 8, 1, 1, + 16, 1, 1, 32, 1, 1, 1, 1 }; -static unsigned long pll_recalc(struct clk *clk) +static void master_clk_init(struct clk *clk) { - int multiplier; + clk->rate = CONFIG_SH_PCLK_FREQ * 16; +} - multiplier = test_mode_pin(MODE_PIN0) ? 24 : 16; +static struct clk_ops sh7757_master_clk_ops = { + .init = master_clk_init, +}; - return clk->parent->rate * multiplier; +static void module_clk_recalc(struct clk *clk) +{ + int idx = __raw_readl(FRQCR) & 0x0000000f; + clk->rate = clk->parent->rate / p1fc_divisors[idx]; } -static struct clk_ops pll_clk_ops = { - .recalc = pll_recalc, +static struct clk_ops sh7757_module_clk_ops = { + .recalc = module_clk_recalc, }; -static struct clk pll_clk = { - .ops = &pll_clk_ops, - .parent = &extal_clk, - .flags = CLK_ENABLE_ON_INIT, -}; +static void bus_clk_recalc(struct clk *clk) +{ + int idx = (__raw_readl(FRQCR) >> 8) & 0x0000000f; + clk->rate = clk->parent->rate / bfc_divisors[idx]; +} -static struct clk *clks[] = { - &extal_clk, - &pll_clk, +static struct clk_ops sh7757_bus_clk_ops = { + .recalc = bus_clk_recalc, }; -static unsigned int div2[] = { 1, 1, 2, 1, 1, 4, 1, 6, - 1, 1, 1, 16, 1, 24, 1, 1 }; +static void cpu_clk_recalc(struct clk *clk) +{ + int idx = (__raw_readl(FRQCR) >> 20) & 0x0000000f; + clk->rate = clk->parent->rate / ifc_divisors[idx]; +} -static struct clk_div_mult_table div4_div_mult_table = { - .divisors = div2, - .nr_divisors = ARRAY_SIZE(div2), +static struct clk_ops sh7757_cpu_clk_ops = { + .recalc = cpu_clk_recalc, }; -static struct clk_div4_table div4_table = { - .div_mult_table = &div4_div_mult_table, +static struct clk_ops *sh7757_clk_ops[] = { + &sh7757_master_clk_ops, + &sh7757_module_clk_ops, + &sh7757_bus_clk_ops, + &sh7757_cpu_clk_ops, }; -enum { DIV4_I, DIV4_SH, DIV4_P, DIV4_NR }; +void __init arch_init_clk_ops(struct clk_ops **ops, int idx) +{ + if (idx < ARRAY_SIZE(sh7757_clk_ops)) + *ops = sh7757_clk_ops[idx]; +} -#define DIV4(_bit, _mask, _flags) \ - SH_CLK_DIV4(&pll_clk, FRQCR, _bit, _mask, _flags) +static void shyway_clk_recalc(struct clk *clk) +{ + int idx = (__raw_readl(FRQCR) >> 12) & 0x0000000f; + clk->rate = clk->parent->rate / sfc_divisors[idx]; +} -struct clk div4_clks[DIV4_NR] = { - /* - * P clock is always enable, because some P clock modules is used - * by Host PC. - */ - [DIV4_P] = DIV4(0, 0x2800, CLK_ENABLE_ON_INIT), - [DIV4_SH] = DIV4(12, 0x00a0, CLK_ENABLE_ON_INIT), - [DIV4_I] = DIV4(20, 0x0004, CLK_ENABLE_ON_INIT), +static struct clk_ops sh7757_shyway_clk_ops = { + .recalc = shyway_clk_recalc, }; -#define MSTPCR0 0xffc80030 -#define MSTPCR1 0xffc80034 - -enum { MSTP004, MSTP000, MSTP114, MSTP113, MSTP112, - MSTP111, MSTP110, MSTP103, MSTP102, - MSTP_NR }; - -static struct clk mstp_clks[MSTP_NR] = { - /* MSTPCR0 */ - [MSTP004] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 4, 0), - [MSTP000] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 0, 0), - - /* MSTPCR1 */ - [MSTP114] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 14, 0), - [MSTP113] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 13, 0), - [MSTP112] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 12, 0), - [MSTP111] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 11, 0), - [MSTP110] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 10, 0), - [MSTP103] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 3, 0), - [MSTP102] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 2, 0), +static struct clk sh7757_shyway_clk = { + .flags = CLK_ENABLE_ON_INIT, + .ops = &sh7757_shyway_clk_ops, +}; + +/* + * Additional sh7757-specific on-chip clocks that aren't already part of the + * clock framework + */ +static struct clk *sh7757_onchip_clocks[] = { + &sh7757_shyway_clk, }; #define CLKDEV_CON_ID(_id, _clk) { .con_id = _id, .clk = _clk } static struct clk_lookup lookups[] = { /* main clocks */ - CLKDEV_CON_ID("extal", &extal_clk), - CLKDEV_CON_ID("pll_clk", &pll_clk), - - /* DIV4 clocks */ - CLKDEV_CON_ID("peripheral_clk", &div4_clks[DIV4_P]), - CLKDEV_CON_ID("shyway_clk", &div4_clks[DIV4_SH]), - CLKDEV_CON_ID("cpu_clk", &div4_clks[DIV4_I]), - - /* MSTP32 clocks */ - CLKDEV_CON_ID("sdhi0", &mstp_clks[MSTP004]), - CLKDEV_CON_ID("riic", &mstp_clks[MSTP000]), - { - /* TMU0 */ - .dev_id = "sh_tmu.0", - .con_id = "tmu_fck", - .clk = &mstp_clks[MSTP113], - }, { - /* TMU1 */ - .dev_id = "sh_tmu.1", - .con_id = "tmu_fck", - .clk = &mstp_clks[MSTP114], - }, - { - /* SCIF4 (But, ID is 2) */ - .dev_id = "sh-sci.2", - .con_id = "sci_fck", - .clk = &mstp_clks[MSTP112], - }, { - /* SCIF3 */ - .dev_id = "sh-sci.1", - .con_id = "sci_fck", - .clk = &mstp_clks[MSTP111], - }, { - /* SCIF2 */ - .dev_id = "sh-sci.0", - .con_id = "sci_fck", - .clk = &mstp_clks[MSTP110], - }, - CLKDEV_CON_ID("usb0", &mstp_clks[MSTP102]), + CLKDEV_CON_ID("shyway_clk", &sh7757_shyway_clk), }; -int __init arch_clk_init(void) +static int __init sh7757_clk_init(void) { - int i, ret = 0; + struct clk *clk = clk_get(NULL, "master_clk"); + int i; + + for (i = 0; i < ARRAY_SIZE(sh7757_onchip_clocks); i++) { + struct clk *clkp = sh7757_onchip_clocks[i]; - for (i = 0; i < ARRAY_SIZE(clks); i++) - ret |= clk_register(clks[i]); - for (i = 0; i < ARRAY_SIZE(lookups); i++) - clkdev_add(&lookups[i]); + clkp->parent = clk; + clk_register(clkp); + clk_enable(clkp); + } - if (!ret) - ret = sh_clk_div4_register(div4_clks, ARRAY_SIZE(div4_clks), - &div4_table); - if (!ret) - ret = sh_clk_mstp32_register(mstp_clks, MSTP_NR); + /* + * Now that we have the rest of the clocks registered, we need to + * force the parent clock to propagate so that these clocks will + * automatically figure out their rate. We cheat by handing the + * parent clock its current rate and forcing child propagation. + */ + clk_set_rate(clk, clk_get_rate(clk)); - return ret; + clk_put(clk); + + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); + + return 0; } +arch_initcall(sh7757_clk_init); + diff --git a/trunk/arch/sh/kernel/cpu/sh4a/clock-shx3.c b/trunk/arch/sh/kernel/cpu/sh4a/clock-shx3.c index 4f70df6b6169..236a6282d778 100644 --- a/trunk/arch/sh/kernel/cpu/sh4a/clock-shx3.c +++ b/trunk/arch/sh/kernel/cpu/sh4a/clock-shx3.c @@ -5,7 +5,7 @@ * * Copyright (C) 2006-2007 Renesas Technology Corp. * Copyright (C) 2006-2007 Renesas Solutions Corp. - * Copyright (C) 2006-2010 Paul Mundt + * Copyright (C) 2006-2007 Paul Mundt * * 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 @@ -18,179 +18,120 @@ #include #include -/* - * Default rate for the root input clock, reset this with clk_set_rate() - * from the platform code. - */ -static struct clk extal_clk = { - .rate = 16666666, +static int ifc_divisors[] = { 1, 2, 4 ,6 }; +static int bfc_divisors[] = { 1, 1, 1, 1, 1, 12, 16, 18, 24, 32, 36, 48 }; +static int pfc_divisors[] = { 1, 1, 1, 1, 1, 1, 1, 18, 24, 32, 36, 48 }; +static int cfc_divisors[] = { 1, 1, 4, 6 }; + +#define IFC_POS 28 +#define IFC_MSK 0x0003 +#define BFC_MSK 0x000f +#define PFC_MSK 0x000f +#define CFC_MSK 0x0003 +#define BFC_POS 16 +#define PFC_POS 0 +#define CFC_POS 20 + +static void master_clk_init(struct clk *clk) +{ + clk->rate *= pfc_divisors[(__raw_readl(FRQCR) >> PFC_POS) & PFC_MSK]; +} + +static struct clk_ops shx3_master_clk_ops = { + .init = master_clk_init, }; -static unsigned long pll_recalc(struct clk *clk) +static unsigned long module_clk_recalc(struct clk *clk) { - /* PLL1 has a fixed x72 multiplier. */ - return clk->parent->rate * 72; + int idx = ((__raw_readl(FRQCR) >> PFC_POS) & PFC_MSK); + return clk->parent->rate / pfc_divisors[idx]; } -static struct clk_ops pll_clk_ops = { - .recalc = pll_recalc, +static struct clk_ops shx3_module_clk_ops = { + .recalc = module_clk_recalc, }; -static struct clk pll_clk = { - .ops = &pll_clk_ops, - .parent = &extal_clk, - .flags = CLK_ENABLE_ON_INIT, -}; +static unsigned long bus_clk_recalc(struct clk *clk) +{ + int idx = ((__raw_readl(FRQCR) >> BFC_POS) & BFC_MSK); + return clk->parent->rate / bfc_divisors[idx]; +} -static struct clk *clks[] = { - &extal_clk, - &pll_clk, +static struct clk_ops shx3_bus_clk_ops = { + .recalc = bus_clk_recalc, }; -static unsigned int div2[] = { 1, 2, 4, 6, 8, 12, 16, 18, - 24, 32, 36, 48 }; +static unsigned long cpu_clk_recalc(struct clk *clk) +{ + int idx = ((__raw_readl(FRQCR) >> IFC_POS) & IFC_MSK); + return clk->parent->rate / ifc_divisors[idx]; +} -static struct clk_div_mult_table div4_div_mult_table = { - .divisors = div2, - .nr_divisors = ARRAY_SIZE(div2), +static struct clk_ops shx3_cpu_clk_ops = { + .recalc = cpu_clk_recalc, }; -static struct clk_div4_table div4_table = { - .div_mult_table = &div4_div_mult_table, +static struct clk_ops *shx3_clk_ops[] = { + &shx3_master_clk_ops, + &shx3_module_clk_ops, + &shx3_bus_clk_ops, + &shx3_cpu_clk_ops, }; -enum { DIV4_I, DIV4_SH, DIV4_B, DIV4_DDR, DIV4_SHA, DIV4_P, DIV4_NR }; +void __init arch_init_clk_ops(struct clk_ops **ops, int idx) +{ + if (idx < ARRAY_SIZE(shx3_clk_ops)) + *ops = shx3_clk_ops[idx]; +} -#define DIV4(_bit, _mask, _flags) \ - SH_CLK_DIV4(&pll_clk, FRQMR1, _bit, _mask, _flags) +static unsigned long shyway_clk_recalc(struct clk *clk) +{ + int idx = ((__raw_readl(FRQCR) >> CFC_POS) & CFC_MSK); + return clk->parent->rate / cfc_divisors[idx]; +} -struct clk div4_clks[DIV4_NR] = { - [DIV4_P] = DIV4(0, 0x0f80, 0), - [DIV4_SHA] = DIV4(4, 0x0ff0, 0), - [DIV4_DDR] = DIV4(12, 0x000c, CLK_ENABLE_ON_INIT), - [DIV4_B] = DIV4(16, 0x0fe0, CLK_ENABLE_ON_INIT), - [DIV4_SH] = DIV4(20, 0x000c, CLK_ENABLE_ON_INIT), - [DIV4_I] = DIV4(28, 0x000e, CLK_ENABLE_ON_INIT), +static struct clk_ops shx3_shyway_clk_ops = { + .recalc = shyway_clk_recalc, }; -#define MSTPCR0 0xffc00030 -#define MSTPCR1 0xffc00034 - -enum { MSTP027, MSTP026, MSTP025, MSTP024, - MSTP009, MSTP008, MSTP003, MSTP002, - MSTP001, MSTP000, MSTP119, MSTP105, - MSTP104, MSTP_NR }; - -static struct clk mstp_clks[MSTP_NR] = { - /* MSTPCR0 */ - [MSTP027] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 27, 0), - [MSTP026] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 26, 0), - [MSTP025] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 25, 0), - [MSTP024] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 24, 0), - [MSTP009] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 9, 0), - [MSTP008] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 8, 0), - [MSTP003] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 3, 0), - [MSTP002] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 2, 0), - [MSTP001] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 1, 0), - [MSTP000] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 0, 0), - - /* MSTPCR1 */ - [MSTP119] = SH_CLK_MSTP32(NULL, MSTPCR1, 19, 0), - [MSTP105] = SH_CLK_MSTP32(NULL, MSTPCR1, 5, 0), - [MSTP104] = SH_CLK_MSTP32(NULL, MSTPCR1, 4, 0), +static struct clk shx3_shyway_clk = { + .flags = CLK_ENABLE_ON_INIT, + .ops = &shx3_shyway_clk_ops, +}; + +/* + * Additional SHx3-specific on-chip clocks that aren't already part of the + * clock framework + */ +static struct clk *shx3_onchip_clocks[] = { + &shx3_shyway_clk, }; #define CLKDEV_CON_ID(_id, _clk) { .con_id = _id, .clk = _clk } static struct clk_lookup lookups[] = { /* main clocks */ - CLKDEV_CON_ID("extal", &extal_clk), - CLKDEV_CON_ID("pll_clk", &pll_clk), - - /* DIV4 clocks */ - CLKDEV_CON_ID("peripheral_clk", &div4_clks[DIV4_P]), - CLKDEV_CON_ID("shywaya_clk", &div4_clks[DIV4_SHA]), - CLKDEV_CON_ID("ddr_clk", &div4_clks[DIV4_DDR]), - CLKDEV_CON_ID("bus_clk", &div4_clks[DIV4_B]), - CLKDEV_CON_ID("shyway_clk", &div4_clks[DIV4_SH]), - CLKDEV_CON_ID("cpu_clk", &div4_clks[DIV4_I]), - - /* MSTP32 clocks */ - { - /* SCIF3 */ - .dev_id = "sh-sci.3", - .con_id = "sci_fck", - .clk = &mstp_clks[MSTP027], - }, { - /* SCIF2 */ - .dev_id = "sh-sci.2", - .con_id = "sci_fck", - .clk = &mstp_clks[MSTP026], - }, { - /* SCIF1 */ - .dev_id = "sh-sci.1", - .con_id = "sci_fck", - .clk = &mstp_clks[MSTP025], - }, { - /* SCIF0 */ - .dev_id = "sh-sci.0", - .con_id = "sci_fck", - .clk = &mstp_clks[MSTP024], - }, - CLKDEV_CON_ID("h8ex_fck", &mstp_clks[MSTP003]), - CLKDEV_CON_ID("csm_fck", &mstp_clks[MSTP002]), - CLKDEV_CON_ID("fe1_fck", &mstp_clks[MSTP001]), - CLKDEV_CON_ID("fe0_fck", &mstp_clks[MSTP000]), - { - /* TMU0 */ - .dev_id = "sh_tmu.0", - .con_id = "tmu_fck", - .clk = &mstp_clks[MSTP008], - }, { - /* TMU1 */ - .dev_id = "sh_tmu.1", - .con_id = "tmu_fck", - .clk = &mstp_clks[MSTP008], - }, { - /* TMU2 */ - .dev_id = "sh_tmu.2", - .con_id = "tmu_fck", - .clk = &mstp_clks[MSTP008], - }, { - /* TMU3 */ - .dev_id = "sh_tmu.3", - .con_id = "tmu_fck", - .clk = &mstp_clks[MSTP009], - }, { - /* TMU4 */ - .dev_id = "sh_tmu.4", - .con_id = "tmu_fck", - .clk = &mstp_clks[MSTP009], - }, { - /* TMU5 */ - .dev_id = "sh_tmu.5", - .con_id = "tmu_fck", - .clk = &mstp_clks[MSTP009], - }, - CLKDEV_CON_ID("hudi_fck", &mstp_clks[MSTP119]), - CLKDEV_CON_ID("dmac_11_6_fck", &mstp_clks[MSTP105]), - CLKDEV_CON_ID("dmac_5_0_fck", &mstp_clks[MSTP104]), + CLKDEV_CON_ID("shyway_clk", &shx3_shyway_clk), }; int __init arch_clk_init(void) { + struct clk *clk; int i, ret = 0; - for (i = 0; i < ARRAY_SIZE(clks); i++) - ret |= clk_register(clks[i]); - for (i = 0; i < ARRAY_SIZE(lookups); i++) - clkdev_add(&lookups[i]); + cpg_clk_init(); + + clk = clk_get(NULL, "master_clk"); + for (i = 0; i < ARRAY_SIZE(shx3_onchip_clocks); i++) { + struct clk *clkp = shx3_onchip_clocks[i]; + + clkp->parent = clk; + ret |= clk_register(clkp); + } + + clk_put(clk); - if (!ret) - ret = sh_clk_div4_register(div4_clks, ARRAY_SIZE(div4_clks), - &div4_table); - if (!ret) - ret = sh_clk_mstp32_register(mstp_clks, MSTP_NR); + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); return ret; } diff --git a/trunk/arch/sh/kernel/cpu/sh4a/intc-shx3.c b/trunk/arch/sh/kernel/cpu/sh4a/intc-shx3.c deleted file mode 100644 index 78c971486b4e..000000000000 --- a/trunk/arch/sh/kernel/cpu/sh4a/intc-shx3.c +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Shared support for SH-X3 interrupt controllers. - * - * Copyright (C) 2009 - 2010 Paul Mundt - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - */ -#include -#include -#include - -#define INTACK 0xfe4100b8 -#define INTACKCLR 0xfe4100bc -#define INTC_USERIMASK 0xfe411000 - -#ifdef CONFIG_INTC_BALANCING -unsigned int irq_lookup(unsigned int irq) -{ - return __raw_readl(INTACK) & 1 ? irq : NO_IRQ_IGNORE; -} - -void irq_finish(unsigned int irq) -{ - __raw_writel(irq2evt(irq), INTACKCLR); -} -#endif - -static int __init shx3_irq_setup(void) -{ - return register_intc_userimask(INTC_USERIMASK); -} -arch_initcall(shx3_irq_setup); diff --git a/trunk/arch/sh/kernel/cpu/sh4a/perf_event.c b/trunk/arch/sh/kernel/cpu/sh4a/perf_event.c index b8b873d8d6b5..eddc21973fa1 100644 --- a/trunk/arch/sh/kernel/cpu/sh4a/perf_event.c +++ b/trunk/arch/sh/kernel/cpu/sh4a/perf_event.c @@ -1,7 +1,7 @@ /* * Performance events support for SH-4A performance counters * - * Copyright (C) 2009, 2010 Paul Mundt + * Copyright (C) 2009 Paul Mundt * * 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 @@ -22,25 +22,7 @@ #define CCBR_CMDS (1 << 1) #define CCBR_PPCE (1 << 0) -#ifdef CONFIG_CPU_SHX3 -/* - * The PMCAT location for SH-X3 CPUs was quietly moved, while the CCBR - * and PMCTR locations remains tentatively constant. This change remains - * wholly undocumented, and was simply found through trial and error. - * - * Early cuts of SH-X3 still appear to use the SH-X/SH-X2 locations, and - * it's unclear when this ceased to be the case. For now we always use - * the new location (if future parts keep up with this trend then - * scanning for them at runtime also remains a viable option.) - * - * The gap in the register space also suggests that there are other - * undocumented counters, so this will need to be revisited at a later - * point in time. - */ -#define PPC_PMCAT 0xfc100240 -#else #define PPC_PMCAT 0xfc100080 -#endif #define PMCAT_OVF3 (1 << 27) #define PMCAT_CNN3 (1 << 26) diff --git a/trunk/arch/sh/kernel/cpu/sh4a/pinmux-sh7757.c b/trunk/arch/sh/kernel/cpu/sh4a/pinmux-sh7757.c index 4c74bd04bba4..ed23b155c097 100644 --- a/trunk/arch/sh/kernel/cpu/sh4a/pinmux-sh7757.c +++ b/trunk/arch/sh/kernel/cpu/sh4a/pinmux-sh7757.c @@ -1,11 +1,11 @@ /* - * SH7757 (B0 step) Pinmux + * SH7757 (A0 step) Pinmux * - * Copyright (C) 2009-2010 Renesas Solutions Corp. + * Copyright (C) 2009 Renesas Solutions Corp. * * Author : Yoshihiro Shimoda * - * Based on SH7723 Pinmux + * Based on SH7757 Pinmux * Copyright (C) 2008 Magnus Damm * * This file is subject to the terms and conditions of the GNU General Public @@ -40,27 +40,27 @@ enum { PTH3_DATA, PTH2_DATA, PTH1_DATA, PTH0_DATA, PTI7_DATA, PTI6_DATA, PTI5_DATA, PTI4_DATA, PTI3_DATA, PTI2_DATA, PTI1_DATA, PTI0_DATA, - PTJ6_DATA, PTJ5_DATA, PTJ4_DATA, + PTJ7_DATA, PTJ6_DATA, PTJ5_DATA, PTJ4_DATA, PTJ3_DATA, PTJ2_DATA, PTJ1_DATA, PTJ0_DATA, PTK7_DATA, PTK6_DATA, PTK5_DATA, PTK4_DATA, PTK3_DATA, PTK2_DATA, PTK1_DATA, PTK0_DATA, - PTL6_DATA, PTL5_DATA, PTL4_DATA, + PTL7_DATA, PTL6_DATA, PTL5_DATA, PTL4_DATA, PTL3_DATA, PTL2_DATA, PTL1_DATA, PTL0_DATA, - PTM7_DATA, PTM6_DATA, PTM5_DATA, PTM4_DATA, + PTM6_DATA, PTM5_DATA, PTM4_DATA, PTM3_DATA, PTM2_DATA, PTM1_DATA, PTM0_DATA, - PTN6_DATA, PTN5_DATA, PTN4_DATA, + PTN7_DATA, PTN6_DATA, PTN5_DATA, PTN4_DATA, PTN3_DATA, PTN2_DATA, PTN1_DATA, PTN0_DATA, PTO7_DATA, PTO6_DATA, PTO5_DATA, PTO4_DATA, PTO3_DATA, PTO2_DATA, PTO1_DATA, PTO0_DATA, - PTP7_DATA, PTP6_DATA, PTP5_DATA, PTP4_DATA, + PTP6_DATA, PTP5_DATA, PTP4_DATA, PTP3_DATA, PTP2_DATA, PTP1_DATA, PTP0_DATA, - PTQ6_DATA, PTQ5_DATA, PTQ4_DATA, + PTQ6_DATA, PTQ5_DATA, PTQ4_DATA, PTQ3_DATA, PTQ2_DATA, PTQ1_DATA, PTQ0_DATA, PTR7_DATA, PTR6_DATA, PTR5_DATA, PTR4_DATA, PTR3_DATA, PTR2_DATA, PTR1_DATA, PTR0_DATA, PTS7_DATA, PTS6_DATA, PTS5_DATA, PTS4_DATA, PTS3_DATA, PTS2_DATA, PTS1_DATA, PTS0_DATA, - PTT7_DATA, PTT6_DATA, PTT5_DATA, PTT4_DATA, + PTT5_DATA, PTT4_DATA, PTT3_DATA, PTT2_DATA, PTT1_DATA, PTT0_DATA, PTU7_DATA, PTU6_DATA, PTU5_DATA, PTU4_DATA, PTU3_DATA, PTU2_DATA, PTU1_DATA, PTU0_DATA, @@ -95,27 +95,27 @@ enum { PTH3_IN, PTH2_IN, PTH1_IN, PTH0_IN, PTI7_IN, PTI6_IN, PTI5_IN, PTI4_IN, PTI3_IN, PTI2_IN, PTI1_IN, PTI0_IN, - PTJ6_IN, PTJ5_IN, PTJ4_IN, + PTJ7_IN, PTJ6_IN, PTJ5_IN, PTJ4_IN, PTJ3_IN, PTJ2_IN, PTJ1_IN, PTJ0_IN, PTK7_IN, PTK6_IN, PTK5_IN, PTK4_IN, PTK3_IN, PTK2_IN, PTK1_IN, PTK0_IN, - PTL6_IN, PTL5_IN, PTL4_IN, + PTL7_IN, PTL6_IN, PTL5_IN, PTL4_IN, PTL3_IN, PTL2_IN, PTL1_IN, PTL0_IN, - PTM7_IN, PTM6_IN, PTM5_IN, PTM4_IN, + PTM6_IN, PTM5_IN, PTM4_IN, PTM3_IN, PTM2_IN, PTM1_IN, PTM0_IN, - PTN6_IN, PTN5_IN, PTN4_IN, + PTN7_IN, PTN6_IN, PTN5_IN, PTN4_IN, PTN3_IN, PTN2_IN, PTN1_IN, PTN0_IN, PTO7_IN, PTO6_IN, PTO5_IN, PTO4_IN, PTO3_IN, PTO2_IN, PTO1_IN, PTO0_IN, - PTP7_IN, PTP6_IN, PTP5_IN, PTP4_IN, + PTP6_IN, PTP5_IN, PTP4_IN, PTP3_IN, PTP2_IN, PTP1_IN, PTP0_IN, - PTQ6_IN, PTQ5_IN, PTQ4_IN, + PTQ6_IN, PTQ5_IN, PTQ4_IN, PTQ3_IN, PTQ2_IN, PTQ1_IN, PTQ0_IN, PTR7_IN, PTR6_IN, PTR5_IN, PTR4_IN, PTR3_IN, PTR2_IN, PTR1_IN, PTR0_IN, PTS7_IN, PTS6_IN, PTS5_IN, PTS4_IN, PTS3_IN, PTS2_IN, PTS1_IN, PTS0_IN, - PTT7_IN, PTT6_IN, PTT5_IN, PTT4_IN, + PTT5_IN, PTT4_IN, PTT3_IN, PTT2_IN, PTT1_IN, PTT0_IN, PTU7_IN, PTU6_IN, PTU5_IN, PTU4_IN, PTU3_IN, PTU2_IN, PTU1_IN, PTU0_IN, @@ -132,43 +132,16 @@ enum { PINMUX_INPUT_END, PINMUX_INPUT_PULLUP_BEGIN, - PTA7_IN_PU, PTA6_IN_PU, PTA5_IN_PU, PTA4_IN_PU, - PTA3_IN_PU, PTA2_IN_PU, PTA1_IN_PU, PTA0_IN_PU, - PTD7_IN_PU, PTD6_IN_PU, PTD5_IN_PU, PTD4_IN_PU, - PTD3_IN_PU, PTD2_IN_PU, PTD1_IN_PU, PTD0_IN_PU, - PTE7_IN_PU, PTE6_IN_PU, PTE5_IN_PU, PTE4_IN_PU, - PTE3_IN_PU, PTE2_IN_PU, PTE1_IN_PU, PTE0_IN_PU, - PTF7_IN_PU, PTF6_IN_PU, PTF5_IN_PU, PTF4_IN_PU, - PTF3_IN_PU, PTF2_IN_PU, PTF1_IN_PU, PTF0_IN_PU, - PTG7_IN_PU, PTG6_IN_PU, PTG4_IN_PU, - PTH7_IN_PU, PTH6_IN_PU, PTH5_IN_PU, PTH4_IN_PU, - PTH3_IN_PU, PTH2_IN_PU, PTH1_IN_PU, PTH0_IN_PU, - PTI7_IN_PU, PTI6_IN_PU, PTI4_IN_PU, - PTI3_IN_PU, PTI2_IN_PU, PTI1_IN_PU, PTI0_IN_PU, - PTJ6_IN_PU, PTJ5_IN_PU, PTJ4_IN_PU, - PTJ3_IN_PU, PTJ2_IN_PU, PTJ1_IN_PU, PTJ0_IN_PU, - PTK7_IN_PU, PTK6_IN_PU, PTK5_IN_PU, PTK4_IN_PU, - PTK3_IN_PU, PTK2_IN_PU, PTK1_IN_PU, PTK0_IN_PU, - PTL6_IN_PU, PTL5_IN_PU, PTL4_IN_PU, - PTL3_IN_PU, PTL2_IN_PU, PTL1_IN_PU, PTL0_IN_PU, - PTM7_IN_PU, PTM6_IN_PU, PTM5_IN_PU, PTM4_IN_PU, - PTN4_IN_PU, - PTN3_IN_PU, PTN2_IN_PU, PTN1_IN_PU, PTN0_IN_PU, - PTO7_IN_PU, PTO6_IN_PU, PTO5_IN_PU, PTO4_IN_PU, - PTO3_IN_PU, PTO2_IN_PU, PTO1_IN_PU, PTO0_IN_PU, - PTT7_IN_PU, PTT6_IN_PU, PTT5_IN_PU, PTT4_IN_PU, - PTT3_IN_PU, PTT2_IN_PU, PTT1_IN_PU, PTT0_IN_PU, PTU7_IN_PU, PTU6_IN_PU, PTU5_IN_PU, PTU4_IN_PU, PTU3_IN_PU, PTU2_IN_PU, PTU1_IN_PU, PTU0_IN_PU, PTV7_IN_PU, PTV6_IN_PU, PTV5_IN_PU, PTV4_IN_PU, - PTV3_IN_PU, PTV2_IN_PU, - PTW1_IN_PU, PTW0_IN_PU, + PTV3_IN_PU, PTV2_IN_PU, PTV1_IN_PU, PTV0_IN_PU, + PTW7_IN_PU, PTW6_IN_PU, PTW5_IN_PU, PTW4_IN_PU, + PTW3_IN_PU, PTW2_IN_PU, PTW1_IN_PU, PTW0_IN_PU, PTX7_IN_PU, PTX6_IN_PU, PTX5_IN_PU, PTX4_IN_PU, PTX3_IN_PU, PTX2_IN_PU, PTX1_IN_PU, PTX0_IN_PU, PTY7_IN_PU, PTY6_IN_PU, PTY5_IN_PU, PTY4_IN_PU, PTY3_IN_PU, PTY2_IN_PU, PTY1_IN_PU, PTY0_IN_PU, - PTZ7_IN_PU, PTZ6_IN_PU, PTZ5_IN_PU, PTZ4_IN_PU, - PTZ3_IN_PU, PTZ2_IN_PU, PTZ1_IN_PU, PTZ0_IN_PU, PINMUX_INPUT_PULLUP_END, PINMUX_OUTPUT_BEGIN, @@ -190,27 +163,27 @@ enum { PTH3_OUT, PTH2_OUT, PTH1_OUT, PTH0_OUT, PTI7_OUT, PTI6_OUT, PTI5_OUT, PTI4_OUT, PTI3_OUT, PTI2_OUT, PTI1_OUT, PTI0_OUT, - PTJ6_OUT, PTJ5_OUT, PTJ4_OUT, + PTJ7_OUT, PTJ6_OUT, PTJ5_OUT, PTJ4_OUT, PTJ3_OUT, PTJ2_OUT, PTJ1_OUT, PTJ0_OUT, PTK7_OUT, PTK6_OUT, PTK5_OUT, PTK4_OUT, PTK3_OUT, PTK2_OUT, PTK1_OUT, PTK0_OUT, - PTL6_OUT, PTL5_OUT, PTL4_OUT, + PTL7_OUT, PTL6_OUT, PTL5_OUT, PTL4_OUT, PTL3_OUT, PTL2_OUT, PTL1_OUT, PTL0_OUT, - PTM7_OUT, PTM6_OUT, PTM5_OUT, PTM4_OUT, + PTM6_OUT, PTM5_OUT, PTM4_OUT, PTM3_OUT, PTM2_OUT, PTM1_OUT, PTM0_OUT, - PTN6_OUT, PTN5_OUT, PTN4_OUT, + PTN7_OUT, PTN6_OUT, PTN5_OUT, PTN4_OUT, PTN3_OUT, PTN2_OUT, PTN1_OUT, PTN0_OUT, PTO7_OUT, PTO6_OUT, PTO5_OUT, PTO4_OUT, PTO3_OUT, PTO2_OUT, PTO1_OUT, PTO0_OUT, - PTP7_OUT, PTP6_OUT, PTP5_OUT, PTP4_OUT, + PTP6_OUT, PTP5_OUT, PTP4_OUT, PTP3_OUT, PTP2_OUT, PTP1_OUT, PTP0_OUT, - PTQ6_OUT, PTQ5_OUT, PTQ4_OUT, + PTQ6_OUT, PTQ5_OUT, PTQ4_OUT, PTQ3_OUT, PTQ2_OUT, PTQ1_OUT, PTQ0_OUT, PTR7_OUT, PTR6_OUT, PTR5_OUT, PTR4_OUT, PTR3_OUT, PTR2_OUT, PTR1_OUT, PTR0_OUT, PTS7_OUT, PTS6_OUT, PTS5_OUT, PTS4_OUT, PTS3_OUT, PTS2_OUT, PTS1_OUT, PTS0_OUT, - PTT7_OUT, PTT6_OUT, PTT5_OUT, PTT4_OUT, + PTT5_OUT, PTT4_OUT, PTT3_OUT, PTT2_OUT, PTT1_OUT, PTT0_OUT, PTU7_OUT, PTU6_OUT, PTU5_OUT, PTU4_OUT, PTU3_OUT, PTU2_OUT, PTU1_OUT, PTU0_OUT, @@ -245,27 +218,27 @@ enum { PTH3_FN, PTH2_FN, PTH1_FN, PTH0_FN, PTI7_FN, PTI6_FN, PTI5_FN, PTI4_FN, PTI3_FN, PTI2_FN, PTI1_FN, PTI0_FN, - PTJ6_FN, PTJ5_FN, PTJ4_FN, + PTJ7_FN, PTJ6_FN, PTJ5_FN, PTJ4_FN, PTJ3_FN, PTJ2_FN, PTJ1_FN, PTJ0_FN, PTK7_FN, PTK6_FN, PTK5_FN, PTK4_FN, PTK3_FN, PTK2_FN, PTK1_FN, PTK0_FN, - PTL6_FN, PTL5_FN, PTL4_FN, + PTL7_FN, PTL6_FN, PTL5_FN, PTL4_FN, PTL3_FN, PTL2_FN, PTL1_FN, PTL0_FN, - PTM7_FN, PTM6_FN, PTM5_FN, PTM4_FN, + PTM6_FN, PTM5_FN, PTM4_FN, PTM3_FN, PTM2_FN, PTM1_FN, PTM0_FN, - PTN6_FN, PTN5_FN, PTN4_FN, + PTN7_FN, PTN6_FN, PTN5_FN, PTN4_FN, PTN3_FN, PTN2_FN, PTN1_FN, PTN0_FN, PTO7_FN, PTO6_FN, PTO5_FN, PTO4_FN, PTO3_FN, PTO2_FN, PTO1_FN, PTO0_FN, - PTP7_FN, PTP6_FN, PTP5_FN, PTP4_FN, + PTP6_FN, PTP5_FN, PTP4_FN, PTP3_FN, PTP2_FN, PTP1_FN, PTP0_FN, - PTQ6_FN, PTQ5_FN, PTQ4_FN, + PTQ6_FN, PTQ5_FN, PTQ4_FN, PTQ3_FN, PTQ2_FN, PTQ1_FN, PTQ0_FN, PTR7_FN, PTR6_FN, PTR5_FN, PTR4_FN, PTR3_FN, PTR2_FN, PTR1_FN, PTR0_FN, PTS7_FN, PTS6_FN, PTS5_FN, PTS4_FN, PTS3_FN, PTS2_FN, PTS1_FN, PTS0_FN, - PTT7_FN, PTT6_FN, PTT5_FN, PTT4_FN, + PTT5_FN, PTT4_FN, PTT3_FN, PTT2_FN, PTT1_FN, PTT0_FN, PTU7_FN, PTU6_FN, PTU5_FN, PTU4_FN, PTU3_FN, PTU2_FN, PTU1_FN, PTU0_FN, @@ -280,248 +253,181 @@ enum { PTZ7_FN, PTZ6_FN, PTZ5_FN, PTZ4_FN, PTZ3_FN, PTZ2_FN, PTZ1_FN, PTZ0_FN, - PS0_15_FN1, PS0_15_FN2, - PS0_14_FN1, PS0_14_FN2, - PS0_13_FN1, PS0_13_FN2, - PS0_12_FN1, PS0_12_FN2, - PS0_11_FN1, PS0_11_FN2, - PS0_10_FN1, PS0_10_FN2, - PS0_9_FN1, PS0_9_FN2, - PS0_8_FN1, PS0_8_FN2, + PS0_15_FN1, PS0_15_FN3, + PS0_14_FN1, PS0_14_FN3, + PS0_13_FN1, PS0_13_FN3, + PS0_12_FN1, PS0_12_FN3, PS0_7_FN1, PS0_7_FN2, PS0_6_FN1, PS0_6_FN2, PS0_5_FN1, PS0_5_FN2, PS0_4_FN1, PS0_4_FN2, PS0_3_FN1, PS0_3_FN2, PS0_2_FN1, PS0_2_FN2, + PS0_1_FN1, PS0_1_FN2, - PS1_10_FN1, PS1_10_FN2, - PS1_9_FN1, PS1_9_FN2, - PS1_8_FN1, PS1_8_FN2, - PS1_2_FN1, PS1_2_FN2, - - PS2_13_FN1, PS2_13_FN2, - PS2_12_FN1, PS2_12_FN2, - PS2_7_FN1, PS2_7_FN2, - PS2_6_FN1, PS2_6_FN2, - PS2_5_FN1, PS2_5_FN2, - PS2_4_FN1, PS2_4_FN2, - PS2_2_FN1, PS2_2_FN2, - - PS3_15_FN1, PS3_15_FN2, - PS3_14_FN1, PS3_14_FN2, - PS3_13_FN1, PS3_13_FN2, - PS3_12_FN1, PS3_12_FN2, - PS3_11_FN1, PS3_11_FN2, - PS3_10_FN1, PS3_10_FN2, - PS3_9_FN1, PS3_9_FN2, - PS3_8_FN1, PS3_8_FN2, - PS3_7_FN1, PS3_7_FN2, - PS3_2_FN1, PS3_2_FN2, - PS3_1_FN1, PS3_1_FN2, + PS1_7_FN1, PS1_7_FN3, + PS1_6_FN1, PS1_6_FN3, + PS2_13_FN1, PS2_13_FN3, + PS2_12_FN1, PS2_12_FN3, + PS2_1_FN1, PS2_1_FN2, + PS2_0_FN1, PS2_0_FN2, + + PS4_15_FN1, PS4_15_FN2, PS4_14_FN1, PS4_14_FN2, PS4_13_FN1, PS4_13_FN2, PS4_12_FN1, PS4_12_FN2, + PS4_11_FN1, PS4_11_FN2, PS4_10_FN1, PS4_10_FN2, PS4_9_FN1, PS4_9_FN2, - PS4_8_FN1, PS4_8_FN2, - PS4_4_FN1, PS4_4_FN2, PS4_3_FN1, PS4_3_FN2, PS4_2_FN1, PS4_2_FN2, PS4_1_FN1, PS4_1_FN2, PS4_0_FN1, PS4_0_FN2, - PS5_11_FN1, PS5_11_FN2, - PS5_10_FN1, PS5_10_FN2, PS5_9_FN1, PS5_9_FN2, PS5_8_FN1, PS5_8_FN2, PS5_7_FN1, PS5_7_FN2, PS5_6_FN1, PS5_6_FN2, PS5_5_FN1, PS5_5_FN2, PS5_4_FN1, PS5_4_FN2, - PS5_3_FN1, PS5_3_FN2, - PS5_2_FN1, PS5_2_FN2, - - PS6_15_FN1, PS6_15_FN2, - PS6_14_FN1, PS6_14_FN2, - PS6_13_FN1, PS6_13_FN2, - PS6_12_FN1, PS6_12_FN2, - PS6_11_FN1, PS6_11_FN2, - PS6_10_FN1, PS6_10_FN2, - PS6_9_FN1, PS6_9_FN2, - PS6_8_FN1, PS6_8_FN2, - PS6_7_FN1, PS6_7_FN2, - PS6_6_FN1, PS6_6_FN2, - PS6_5_FN1, PS6_5_FN2, - PS6_4_FN1, PS6_4_FN2, - PS6_3_FN1, PS6_3_FN2, - PS6_2_FN1, PS6_2_FN2, - PS6_1_FN1, PS6_1_FN2, - PS6_0_FN1, PS6_0_FN2, - - PS7_15_FN1, PS7_15_FN2, - PS7_14_FN1, PS7_14_FN2, - PS7_13_FN1, PS7_13_FN2, - PS7_12_FN1, PS7_12_FN2, - PS7_11_FN1, PS7_11_FN2, - PS7_10_FN1, PS7_10_FN2, - PS7_9_FN1, PS7_9_FN2, - PS7_8_FN1, PS7_8_FN2, - PS7_7_FN1, PS7_7_FN2, - PS7_6_FN1, PS7_6_FN2, - PS7_5_FN1, PS7_5_FN2, - PS7_4_FN1, PS7_4_FN2, - - PS8_15_FN1, PS8_15_FN2, - PS8_14_FN1, PS8_14_FN2, - PS8_13_FN1, PS8_13_FN2, - PS8_12_FN1, PS8_12_FN2, - PS8_11_FN1, PS8_11_FN2, - PS8_10_FN1, PS8_10_FN2, - PS8_9_FN1, PS8_9_FN2, - PS8_8_FN1, PS8_8_FN2, + + /* AN15 to 8 : EVENT15 to 8 */ + PS6_7_FN_AN, PS6_7_FN_EV, + PS6_6_FN_AN, PS6_6_FN_EV, + PS6_5_FN_AN, PS6_5_FN_EV, + PS6_4_FN_AN, PS6_4_FN_EV, + PS6_3_FN_AN, PS6_3_FN_EV, + PS6_2_FN_AN, PS6_2_FN_EV, + PS6_1_FN_AN, PS6_1_FN_EV, + PS6_0_FN_AN, PS6_0_FN_EV, + PINMUX_FUNCTION_END, PINMUX_MARK_BEGIN, - /* PTA (mobule: LBSC, RGMII) */ + /* PTA (mobule: LBSC, CPG, LPC) */ BS_MARK, RDWR_MARK, WE1_MARK, RDY_MARK, + MD10_MARK, MD9_MARK, MD8_MARK, + LGPIO7_MARK, LGPIO6_MARK, LGPIO5_MARK, LGPIO4_MARK, + LGPIO3_MARK, LGPIO2_MARK, LGPIO1_MARK, LGPIO0_MARK, + + /* PTB (mobule: LBSC, EtherC, SIM, LPC) */ + D15_MARK, D14_MARK, D13_MARK, D12_MARK, + D11_MARK, D10_MARK, D9_MARK, D8_MARK, ET0_MDC_MARK, ET0_MDIO_MARK, ET1_MDC_MARK, ET1_MDIO_MARK, + SIM_D_MARK, SIM_CLK_MARK, SIM_RST_MARK, + WPSZ1_MARK, WPSZ0_MARK, FWID_MARK, FLSHSZ_MARK, + LPC_SPIEN_MARK, BASEL_MARK, - /* PTB (mobule: INTC, ONFI, TMU) */ - IRQ15_MARK, IRQ14_MARK, IRQ13_MARK, IRQ12_MARK, - IRQ11_MARK, IRQ10_MARK, IRQ9_MARK, IRQ8_MARK, - ON_NRE_MARK, ON_NWE_MARK, ON_NWP_MARK, ON_NCE0_MARK, - ON_R_B0_MARK, ON_ALE_MARK, ON_CLE_MARK, TCLK_MARK, + /* PTC (mobule: SD) */ + SD_WP_MARK, SD_CD_MARK, SD_CLK_MARK, SD_CMD_MARK, + SD_D3_MARK, SD_D2_MARK, SD_D1_MARK, SD_D0_MARK, - /* PTC (mobule: IRQ, PWMU) */ + /* PTD (mobule: INTC, SPI0, LBSC, CPG, ADC) */ IRQ7_MARK, IRQ6_MARK, IRQ5_MARK, IRQ4_MARK, IRQ3_MARK, IRQ2_MARK, IRQ1_MARK, IRQ0_MARK, - PWMU0_MARK, PWMU1_MARK, PWMU2_MARK, PWMU3_MARK, - PWMU4_MARK, PWMU5_MARK, - - /* PTD (mobule: SPI0, DMAC) */ - SP0_MOSI_MARK, SP0_MISO_MARK, SP0_SCK_MARK, SP0_SCK_FB_MARK, - SP0_SS0_MARK, SP0_SS1_MARK, SP0_SS2_MARK, SP0_SS3_MARK, - DREQ0_MARK, DACK0_MARK, TEND0_MARK, - - /* PTE (mobule: RMII) */ - RMII0_CRS_DV_MARK, RMII0_TXD1_MARK, - RMII0_TXD0_MARK, RMII0_TXEN_MARK, - RMII0_REFCLK_MARK, RMII0_RXD1_MARK, - RMII0_RXD0_MARK, RMII0_RX_ER_MARK, - - /* PTF (mobule: RMII, SerMux) */ - RMII1_CRS_DV_MARK, RMII1_TXD1_MARK, - RMII1_TXD0_MARK, RMII1_TXEN_MARK, - RMII1_REFCLK_MARK, RMII1_RXD1_MARK, - RMII1_RXD0_MARK, RMII1_RX_ER_MARK, - RAC_RI_MARK, - - /* PTG (mobule: system, LBSC, LPC, WDT, LPC, eMMC) */ - BOOTFMS_MARK, BOOTWP_MARK, A25_MARK, A24_MARK, - SERIRQ_MARK, WDTOVF_MARK, LPCPD_MARK, LDRQ_MARK, - MMCCLK_MARK, MMCCMD_MARK, - - /* PTH (mobule: SPI1, LPC, DMAC, ADC) */ + MD6_MARK, MD5_MARK, MD3_MARK, MD2_MARK, + MD1_MARK, MD0_MARK, ADTRG1_MARK, ADTRG0_MARK, + + /* PTE (mobule: EtherC) */ + ET0_CRS_DV_MARK, ET0_TXD1_MARK, + ET0_TXD0_MARK, ET0_TX_EN_MARK, + ET0_REF_CLK_MARK, ET0_RXD1_MARK, + ET0_RXD0_MARK, ET0_RX_ER_MARK, + + /* PTF (mobule: EtherC) */ + ET1_CRS_DV_MARK, ET1_TXD1_MARK, + ET1_TXD0_MARK, ET1_TX_EN_MARK, + ET1_REF_CLK_MARK, ET1_RXD1_MARK, + ET1_RXD0_MARK, ET1_RX_ER_MARK, + + /* PTG (mobule: SYSTEM, PWMX, LPC) */ + STATUS0_MARK, STATUS1_MARK, + PWX0_MARK, PWX1_MARK, PWX2_MARK, PWX3_MARK, + SERIRQ_MARK, CLKRUN_MARK, LPCPD_MARK, LDRQ_MARK, + + /* PTH (mobule: TMU, SCIF234, SPI1, SPI0) */ + TCLK_MARK, RXD4_MARK, TXD4_MARK, SP1_MOSI_MARK, SP1_MISO_MARK, SP1_SCK_MARK, SP1_SCK_FB_MARK, - SP1_SS0_MARK, SP1_SS1_MARK, WP_MARK, FMS0_MARK, - TEND1_MARK, DREQ1_MARK, DACK1_MARK, ADTRG1_MARK, - ADTRG0_MARK, + SP1_SS0_MARK, SP1_SS1_MARK, SP0_SS1_MARK, - /* PTI (mobule: LBSC, SDHI) */ - D15_MARK, D14_MARK, D13_MARK, D12_MARK, - D11_MARK, D10_MARK, D9_MARK, D8_MARK, - SD_WP_MARK, SD_CD_MARK, SD_CLK_MARK, SD_CMD_MARK, - SD_D3_MARK, SD_D2_MARK, SD_D1_MARK, SD_D0_MARK, + /* PTI (mobule: INTC) */ + IRQ15_MARK, IRQ14_MARK, IRQ13_MARK, IRQ12_MARK, + IRQ11_MARK, IRQ10_MARK, IRQ9_MARK, IRQ8_MARK, - /* PTJ (mobule: SCIF234) */ - RTS3_MARK, CTS3_MARK, TXD3_MARK, RXD3_MARK, - RTS4_MARK, RXD4_MARK, TXD4_MARK, + /* PTJ (mobule: SCIF234, SERMUX) */ + RXD3_MARK, TXD3_MARK, RXD2_MARK, TXD2_MARK, + COM1_TXD_MARK, COM1_RXD_MARK, COM1_RTS_MARK, COM1_CTS_MARK, - /* PTK (mobule: SERMUX, LBSC, SCIF) */ + /* PTK (mobule: SERMUX) */ COM2_TXD_MARK, COM2_RXD_MARK, COM2_RTS_MARK, COM2_CTS_MARK, - COM2_DTR_MARK, COM2_DSR_MARK, COM2_DCD_MARK, CLKOUT_MARK, - SCK2_MARK, SCK4_MARK, SCK3_MARK, + COM2_DTR_MARK, COM2_DSR_MARK, COM2_DCD_MARK, COM2_RI_MARK, - /* PTL (mobule: SERMUX, SCIF, LBSC, AUD) */ - RAC_RXD_MARK, RAC_RTS_MARK, RAC_CTS_MARK, RAC_DTR_MARK, - RAC_DSR_MARK, RAC_DCD_MARK, RAC_TXD_MARK, RXD2_MARK, - CS5_MARK, CS6_MARK, AUDSYNC_MARK, AUDCK_MARK, - TXD2_MARK, + /* PTL (mobule: SERMUX) */ + RAC_TXD_MARK, RAC_RXD_MARK, RAC_RTS_MARK, RAC_CTS_MARK, + RAC_DTR_MARK, RAC_DSR_MARK, RAC_DCD_MARK, RAC_RI_MARK, - /* PTM (mobule: LBSC, IIC) */ - CS4_MARK, RD_MARK, WE0_MARK, CS0_MARK, + /* PTM (mobule: IIC, LPC) */ SDA6_MARK, SCL6_MARK, SDA7_MARK, SCL7_MARK, + WP_MARK, FMS0_MARK, FMS1_MARK, - /* PTN (mobule: USB, JMC, SGPIO, WDT) */ - VBUS_EN_MARK, VBUS_OC_MARK, JMCTCK_MARK, JMCTMS_MARK, - JMCTDO_MARK, JMCTDI_MARK, JMCTRST_MARK, - SGPIO1_CLK_MARK, SGPIO1_LOAD_MARK, SGPIO1_DI_MARK, - SGPIO1_DO_MARK, SUB_CLKIN_MARK, + /* PTN (mobule: SCIF234, EVC) */ + SCK2_MARK, RTS4_MARK, RTS3_MARK, RTS2_MARK, + CTS4_MARK, CTS3_MARK, CTS2_MARK, + EVENT7_MARK, EVENT6_MARK, EVENT5_MARK, EVENT4_MARK, + EVENT3_MARK, EVENT2_MARK, EVENT1_MARK, EVENT0_MARK, - /* PTO (mobule: SGPIO, SerMux) */ - SGPIO0_CLK_MARK, SGPIO0_LOAD_MARK, SGPIO0_DI_MARK, - SGPIO0_DO_MARK, SGPIO2_CLK_MARK, SGPIO2_LOAD_MARK, - SGPIO2_DI_MARK, SGPIO2_DO_MARK, - COM1_TXD_MARK, COM1_RXD_MARK, COM1_RTS_MARK, COM1_CTS_MARK, + /* PTO (mobule: SGPIO) */ + SGPIO0_CLK_MARK, SGPIO0_LOAD_MARK, + SGPIO0_DI_MARK, SGPIO0_DO_MARK, + SGPIO1_CLK_MARK, SGPIO1_LOAD_MARK, + SGPIO1_DI_MARK, SGPIO1_DO_MARK, + + /* PTP (mobule: JMC, SCIF234) */ + JMCTCK_MARK, JMCTMS_MARK, JMCTDO_MARK, JMCTDI_MARK, + JMCRST_MARK, SCK4_MARK, SCK3_MARK, /* PTQ (mobule: LPC) */ LAD3_MARK, LAD2_MARK, LAD1_MARK, LAD0_MARK, LFRAME_MARK, LRESET_MARK, LCLK_MARK, /* PTR (mobule: GRA, IIC) */ - DDC3_MARK, DDC2_MARK, SDA2_MARK, SCL2_MARK, + DDC3_MARK, DDC2_MARK, + SDA8_MARK, SCL8_MARK, SDA2_MARK, SCL2_MARK, SDA1_MARK, SCL1_MARK, SDA0_MARK, SCL0_MARK, - SDA8_MARK, SCL8_MARK, /* PTS (mobule: GRA, IIC) */ - DDC1_MARK, DDC0_MARK, SDA5_MARK, SCL5_MARK, + DDC1_MARK, DDC0_MARK, + SDA9_MARK, SCL9_MARK, SDA5_MARK, SCL5_MARK, SDA4_MARK, SCL4_MARK, SDA3_MARK, SCL3_MARK, - SDA9_MARK, SCL9_MARK, - /* PTT (mobule: PWMX, AUD) */ - PWMX7_MARK, PWMX6_MARK, PWMX5_MARK, PWMX4_MARK, - PWMX3_MARK, PWMX2_MARK, PWMX1_MARK, PWMX0_MARK, - AUDATA3_MARK, AUDATA2_MARK, AUDATA1_MARK, AUDATA0_MARK, - STATUS1_MARK, STATUS0_MARK, + /* PTT (mobule: SYSTEM, PWMX) */ + AUDSYNC_MARK, AUDCK_MARK, + AUDATA3_MARK, AUDATA2_MARK, + AUDATA1_MARK, AUDATA0_MARK, + PWX7_MARK, PWX6_MARK, PWX5_MARK, PWX4_MARK, - /* PTU (mobule: LPC, APM) */ - LGPIO7_MARK, LGPIO6_MARK, LGPIO5_MARK, LGPIO4_MARK, - LGPIO3_MARK, LGPIO2_MARK, LGPIO1_MARK, LGPIO0_MARK, - APMONCTL_O_MARK, APMPWBTOUT_O_MARK, APMSCI_O_MARK, - APMVDDON_MARK, APMSLPBTN_MARK, APMPWRBTN_MARK, APMS5N_MARK, - APMS3N_MARK, + /* PTU (mobule: LBSC, DMAC) */ + CS6_MARK, CS5_MARK, CS4_MARK, CS0_MARK, + RD_MARK, WE0_MARK, A25_MARK, A24_MARK, + DREQ0_MARK, DACK0_MARK, - /* PTV (mobule: LBSC, SerMux, R-SPI, EVC, GRA) */ + /* PTV (mobule: LBSC, DMAC) */ A23_MARK, A22_MARK, A21_MARK, A20_MARK, A19_MARK, A18_MARK, A17_MARK, A16_MARK, - COM2_RI_MARK, R_SPI_MOSI_MARK, R_SPI_MISO_MARK, - R_SPI_RSPCK_MARK, R_SPI_SSL0_MARK, R_SPI_SSL1_MARK, - EVENT7_MARK, EVENT6_MARK, VBIOS_DI_MARK, VBIOS_DO_MARK, - VBIOS_CLK_MARK, VBIOS_CS_MARK, + TEND0_MARK, DREQ1_MARK, DACK1_MARK, TEND1_MARK, - /* PTW (mobule: LBSC, EVC, SCIF) */ + /* PTW (mobule: LBSC) */ A15_MARK, A14_MARK, A13_MARK, A12_MARK, A11_MARK, A10_MARK, A9_MARK, A8_MARK, - EVENT5_MARK, EVENT4_MARK, EVENT3_MARK, EVENT2_MARK, - EVENT1_MARK, EVENT0_MARK, CTS4_MARK, CTS2_MARK, - /* PTX (mobule: LBSC, SCIF, SIM) */ + /* PTX (mobule: LBSC) */ A7_MARK, A6_MARK, A5_MARK, A4_MARK, A3_MARK, A2_MARK, A1_MARK, A0_MARK, - RTS2_MARK, SIM_D_MARK, SIM_CLK_MARK, SIM_RST_MARK, /* PTY (mobule: LBSC) */ D7_MARK, D6_MARK, D5_MARK, D4_MARK, D3_MARK, D2_MARK, D1_MARK, D0_MARK, - - /* PTZ (mobule: eMMC, ONFI) */ - MMCDAT7_MARK, MMCDAT6_MARK, MMCDAT5_MARK, MMCDAT4_MARK, - MMCDAT3_MARK, MMCDAT2_MARK, MMCDAT1_MARK, MMCDAT0_MARK, - ON_DQ7_MARK, ON_DQ6_MARK, ON_DQ5_MARK, ON_DQ4_MARK, - ON_DQ3_MARK, ON_DQ2_MARK, ON_DQ1_MARK, ON_DQ0_MARK, - PINMUX_MARK_END, }; @@ -567,8 +473,6 @@ static pinmux_enum_t pinmux_data[] = { PINMUX_DATA(PTD0_DATA, PTD0_IN, PTD0_OUT), /* PTE GPIO */ - PINMUX_DATA(PTE7_DATA, PTE7_IN, PTE7_OUT), - PINMUX_DATA(PTE6_DATA, PTE6_IN, PTE6_OUT), PINMUX_DATA(PTE5_DATA, PTE5_IN, PTE5_OUT), PINMUX_DATA(PTE4_DATA, PTE4_IN, PTE4_OUT), PINMUX_DATA(PTE3_DATA, PTE3_IN, PTE3_OUT), @@ -617,6 +521,7 @@ static pinmux_enum_t pinmux_data[] = { PINMUX_DATA(PTI0_DATA, PTI0_IN, PTI0_OUT), /* PTJ GPIO */ + PINMUX_DATA(PTJ7_DATA, PTJ7_IN, PTJ7_OUT), PINMUX_DATA(PTJ6_DATA, PTJ6_IN, PTJ6_OUT), PINMUX_DATA(PTJ5_DATA, PTJ5_IN, PTJ5_OUT), PINMUX_DATA(PTJ4_DATA, PTJ4_IN, PTJ4_OUT), @@ -636,6 +541,7 @@ static pinmux_enum_t pinmux_data[] = { PINMUX_DATA(PTK0_DATA, PTK0_IN, PTK0_OUT), /* PTL GPIO */ + PINMUX_DATA(PTL7_DATA, PTL7_IN, PTL7_OUT), PINMUX_DATA(PTL6_DATA, PTL6_IN, PTL6_OUT), PINMUX_DATA(PTL5_DATA, PTL5_IN, PTL5_OUT), PINMUX_DATA(PTL4_DATA, PTL4_IN, PTL4_OUT), @@ -654,6 +560,7 @@ static pinmux_enum_t pinmux_data[] = { PINMUX_DATA(PTM0_DATA, PTM0_IN, PTM0_OUT), /* PTN GPIO */ + PINMUX_DATA(PTN7_DATA, PTN7_IN, PTN7_OUT), PINMUX_DATA(PTN6_DATA, PTN6_IN, PTN6_OUT), PINMUX_DATA(PTN5_DATA, PTN5_IN, PTN5_OUT), PINMUX_DATA(PTN4_DATA, PTN4_IN, PTN4_OUT), @@ -702,8 +609,6 @@ static pinmux_enum_t pinmux_data[] = { PINMUX_DATA(PTS0_DATA, PTS0_IN, PTS0_OUT), /* PTT GPIO */ - PINMUX_DATA(PTT7_DATA, PTT7_IN, PTT7_OUT), - PINMUX_DATA(PTT6_DATA, PTT6_IN, PTT6_OUT), PINMUX_DATA(PTT5_DATA, PTT5_IN, PTT5_OUT), PINMUX_DATA(PTT4_DATA, PTT4_IN, PTT4_OUT), PINMUX_DATA(PTT3_DATA, PTT3_IN, PTT3_OUT), @@ -772,204 +677,186 @@ static pinmux_enum_t pinmux_data[] = { PINMUX_DATA(PTZ0_DATA, PTZ0_IN, PTZ0_OUT), /* PTA FN */ - PINMUX_DATA(BS_MARK, PTA7_FN), - PINMUX_DATA(RDWR_MARK, PTA6_FN), - PINMUX_DATA(WE1_MARK, PTA5_FN), - PINMUX_DATA(RDY_MARK, PTA4_FN), - PINMUX_DATA(ET0_MDC_MARK, PTA3_FN), - PINMUX_DATA(ET0_MDIO_MARK, PTA2_FN), - PINMUX_DATA(ET1_MDC_MARK, PTA1_FN), - PINMUX_DATA(ET1_MDIO_MARK, PTA0_FN), + PINMUX_DATA(BS_MARK, PS0_15_FN1, PTA7_FN), + PINMUX_DATA(LGPIO7_MARK, PS0_15_FN3, PTA7_FN), + PINMUX_DATA(RDWR_MARK, PS0_14_FN1, PTA6_FN), + PINMUX_DATA(LGPIO6_MARK, PS0_14_FN3, PTA6_FN), + PINMUX_DATA(WE1_MARK, PS0_13_FN1, PTA5_FN), + PINMUX_DATA(LGPIO5_MARK, PS0_13_FN3, PTA5_FN), + PINMUX_DATA(RDY_MARK, PS0_12_FN1, PTA4_FN), + PINMUX_DATA(LGPIO4_MARK, PS0_12_FN3, PTA4_FN), + PINMUX_DATA(LGPIO3_MARK, PTA3_FN), + PINMUX_DATA(LGPIO2_MARK, PTA2_FN), + PINMUX_DATA(LGPIO1_MARK, PTA1_FN), + PINMUX_DATA(LGPIO0_MARK, PTA0_FN), /* PTB FN */ - PINMUX_DATA(IRQ15_MARK, PS0_15_FN1, PTB7_FN), - PINMUX_DATA(ON_NRE_MARK, PS0_15_FN2, PTB7_FN), - PINMUX_DATA(IRQ14_MARK, PS0_14_FN1, PTB6_FN), - PINMUX_DATA(ON_NWE_MARK, PS0_14_FN2, PTB6_FN), - PINMUX_DATA(IRQ13_MARK, PS0_13_FN1, PTB5_FN), - PINMUX_DATA(ON_NWP_MARK, PS0_13_FN2, PTB5_FN), - PINMUX_DATA(IRQ12_MARK, PS0_12_FN1, PTB4_FN), - PINMUX_DATA(ON_NCE0_MARK, PS0_12_FN2, PTB4_FN), - PINMUX_DATA(IRQ11_MARK, PS0_11_FN1, PTB3_FN), - PINMUX_DATA(ON_R_B0_MARK, PS0_11_FN2, PTB3_FN), - PINMUX_DATA(IRQ10_MARK, PS0_10_FN1, PTB2_FN), - PINMUX_DATA(ON_ALE_MARK, PS0_10_FN2, PTB2_FN), - PINMUX_DATA(IRQ9_MARK, PS0_9_FN1, PTB1_FN), - PINMUX_DATA(ON_CLE_MARK, PS0_9_FN2, PTB1_FN), - PINMUX_DATA(IRQ8_MARK, PS0_8_FN1, PTB0_FN), - PINMUX_DATA(TCLK_MARK, PS0_8_FN2, PTB0_FN), + PINMUX_DATA(D15_MARK, PS0_7_FN1, PTB7_FN), + PINMUX_DATA(ET0_MDC_MARK, PS0_7_FN2, PTB7_FN), + PINMUX_DATA(D14_MARK, PS0_6_FN1, PTB6_FN), + PINMUX_DATA(ET0_MDIO_MARK, PS0_6_FN2, PTB6_FN), + PINMUX_DATA(D13_MARK, PS0_5_FN1, PTB5_FN), + PINMUX_DATA(ET1_MDC_MARK, PS0_5_FN2, PTB5_FN), + PINMUX_DATA(D12_MARK, PS0_4_FN1, PTB4_FN), + PINMUX_DATA(ET1_MDIO_MARK, PS0_4_FN2, PTB4_FN), + PINMUX_DATA(D11_MARK, PS0_3_FN1, PTB3_FN), + PINMUX_DATA(SIM_D_MARK, PS0_3_FN2, PTB3_FN), + PINMUX_DATA(D10_MARK, PS0_2_FN1, PTB2_FN), + PINMUX_DATA(SIM_CLK_MARK, PS0_2_FN2, PTB2_FN), + PINMUX_DATA(D9_MARK, PS0_1_FN1, PTB1_FN), + PINMUX_DATA(SIM_RST_MARK, PS0_1_FN2, PTB1_FN), + PINMUX_DATA(D8_MARK, PTB0_FN), /* PTC FN */ - PINMUX_DATA(IRQ7_MARK, PS0_7_FN1, PTC7_FN), - PINMUX_DATA(PWMU0_MARK, PS0_7_FN2, PTC7_FN), - PINMUX_DATA(IRQ6_MARK, PS0_6_FN1, PTC6_FN), - PINMUX_DATA(PWMU1_MARK, PS0_6_FN2, PTC6_FN), - PINMUX_DATA(IRQ5_MARK, PS0_5_FN1, PTC5_FN), - PINMUX_DATA(PWMU2_MARK, PS0_5_FN2, PTC5_FN), - PINMUX_DATA(IRQ4_MARK, PS0_4_FN1, PTC5_FN), - PINMUX_DATA(PWMU3_MARK, PS0_4_FN2, PTC4_FN), - PINMUX_DATA(IRQ3_MARK, PS0_3_FN1, PTC3_FN), - PINMUX_DATA(PWMU4_MARK, PS0_3_FN2, PTC3_FN), - PINMUX_DATA(IRQ2_MARK, PS0_2_FN1, PTC2_FN), - PINMUX_DATA(PWMU5_MARK, PS0_2_FN2, PTC2_FN), - PINMUX_DATA(IRQ1_MARK, PTC1_FN), - PINMUX_DATA(IRQ0_MARK, PTC0_FN), + PINMUX_DATA(SD_WP_MARK, PTC7_FN), + PINMUX_DATA(SD_CD_MARK, PTC6_FN), + PINMUX_DATA(SD_CLK_MARK, PTC5_FN), + PINMUX_DATA(SD_CMD_MARK, PTC4_FN), + PINMUX_DATA(SD_D3_MARK, PTC3_FN), + PINMUX_DATA(SD_D2_MARK, PTC2_FN), + PINMUX_DATA(SD_D1_MARK, PTC1_FN), + PINMUX_DATA(SD_D0_MARK, PTC0_FN), /* PTD FN */ - PINMUX_DATA(SP0_MOSI_MARK, PTD7_FN), - PINMUX_DATA(SP0_MISO_MARK, PTD6_FN), - PINMUX_DATA(SP0_SCK_MARK, PTD5_FN), - PINMUX_DATA(SP0_SCK_FB_MARK, PTD4_FN), - PINMUX_DATA(SP0_SS0_MARK, PTD3_FN), - PINMUX_DATA(SP0_SS1_MARK, PS1_10_FN1, PTD2_FN), - PINMUX_DATA(DREQ0_MARK, PS1_10_FN2, PTD2_FN), - PINMUX_DATA(SP0_SS2_MARK, PS1_9_FN1, PTD1_FN), - PINMUX_DATA(DACK0_MARK, PS1_9_FN2, PTD1_FN), - PINMUX_DATA(SP0_SS3_MARK, PS1_8_FN1, PTD0_FN), - PINMUX_DATA(TEND0_MARK, PS1_8_FN2, PTD0_FN), + PINMUX_DATA(IRQ7_MARK, PS1_7_FN1, PTD7_FN), + PINMUX_DATA(ADTRG1_MARK, PS1_7_FN3, PTD7_FN), + PINMUX_DATA(IRQ6_MARK, PS1_6_FN1, PTD6_FN), + PINMUX_DATA(ADTRG0_MARK, PS1_6_FN3, PTD6_FN), + PINMUX_DATA(IRQ5_MARK, PTD5_FN), + PINMUX_DATA(IRQ4_MARK, PTD4_FN), + PINMUX_DATA(IRQ3_MARK, PTD3_FN), + PINMUX_DATA(IRQ2_MARK, PTD2_FN), + PINMUX_DATA(IRQ1_MARK, PTD1_FN), + PINMUX_DATA(IRQ0_MARK, PTD0_FN), /* PTE FN */ - PINMUX_DATA(RMII0_CRS_DV_MARK, PTE7_FN), - PINMUX_DATA(RMII0_TXD1_MARK, PTE6_FN), - PINMUX_DATA(RMII0_TXD0_MARK, PTE5_FN), - PINMUX_DATA(RMII0_TXEN_MARK, PTE4_FN), - PINMUX_DATA(RMII0_REFCLK_MARK, PTE3_FN), - PINMUX_DATA(RMII0_RXD1_MARK, PTE2_FN), - PINMUX_DATA(RMII0_RXD0_MARK, PTE1_FN), - PINMUX_DATA(RMII0_RX_ER_MARK, PTE0_FN), + PINMUX_DATA(ET0_CRS_DV_MARK, PTE7_FN), + PINMUX_DATA(ET0_TXD1_MARK, PTE6_FN), + PINMUX_DATA(ET0_TXD0_MARK, PTE5_FN), + PINMUX_DATA(ET0_TX_EN_MARK, PTE4_FN), + PINMUX_DATA(ET0_REF_CLK_MARK, PTE3_FN), + PINMUX_DATA(ET0_RXD1_MARK, PTE2_FN), + PINMUX_DATA(ET0_RXD0_MARK, PTE1_FN), + PINMUX_DATA(ET0_RX_ER_MARK, PTE0_FN), /* PTF FN */ - PINMUX_DATA(RMII1_CRS_DV_MARK, PTF7_FN), - PINMUX_DATA(RMII1_TXD1_MARK, PTF6_FN), - PINMUX_DATA(RMII1_TXD0_MARK, PTF5_FN), - PINMUX_DATA(RMII1_TXEN_MARK, PTF4_FN), - PINMUX_DATA(RMII1_REFCLK_MARK, PTF3_FN), - PINMUX_DATA(RMII1_RXD1_MARK, PS1_2_FN1, PTF2_FN), - PINMUX_DATA(RAC_RI_MARK, PS1_2_FN2, PTF2_FN), - PINMUX_DATA(RMII1_RXD0_MARK, PTF1_FN), - PINMUX_DATA(RMII1_RX_ER_MARK, PTF0_FN), + PINMUX_DATA(ET1_CRS_DV_MARK, PTF7_FN), + PINMUX_DATA(ET1_TXD1_MARK, PTF6_FN), + PINMUX_DATA(ET1_TXD0_MARK, PTF5_FN), + PINMUX_DATA(ET1_TX_EN_MARK, PTF4_FN), + PINMUX_DATA(ET1_REF_CLK_MARK, PTF3_FN), + PINMUX_DATA(ET1_RXD1_MARK, PTF2_FN), + PINMUX_DATA(ET1_RXD0_MARK, PTF1_FN), + PINMUX_DATA(ET1_RX_ER_MARK, PTF0_FN), /* PTG FN */ - PINMUX_DATA(BOOTFMS_MARK, PTG7_FN), - PINMUX_DATA(BOOTWP_MARK, PTG6_FN), - PINMUX_DATA(A25_MARK, PS2_13_FN1, PTG5_FN), - PINMUX_DATA(MMCCLK_MARK, PS2_13_FN2, PTG5_FN), - PINMUX_DATA(A24_MARK, PS2_12_FN1, PTG4_FN), - PINMUX_DATA(MMCCMD_MARK, PS2_12_FN2, PTG4_FN), + PINMUX_DATA(PWX0_MARK, PTG7_FN), + PINMUX_DATA(PWX1_MARK, PTG6_FN), + PINMUX_DATA(STATUS0_MARK, PS2_13_FN1, PTG5_FN), + PINMUX_DATA(PWX2_MARK, PS2_13_FN3, PTG5_FN), + PINMUX_DATA(STATUS1_MARK, PS2_12_FN1, PTG4_FN), + PINMUX_DATA(PWX3_MARK, PS2_12_FN3, PTG4_FN), PINMUX_DATA(SERIRQ_MARK, PTG3_FN), - PINMUX_DATA(WDTOVF_MARK, PTG2_FN), + PINMUX_DATA(CLKRUN_MARK, PTG2_FN), PINMUX_DATA(LPCPD_MARK, PTG1_FN), PINMUX_DATA(LDRQ_MARK, PTG0_FN), /* PTH FN */ - PINMUX_DATA(SP1_MOSI_MARK, PS2_7_FN1, PTH7_FN), - PINMUX_DATA(TEND1_MARK, PS2_7_FN2, PTH7_FN), - PINMUX_DATA(SP1_MISO_MARK, PS2_6_FN1, PTH6_FN), - PINMUX_DATA(DREQ1_MARK, PS2_6_FN2, PTH6_FN), - PINMUX_DATA(SP1_SCK_MARK, PS2_5_FN1, PTH5_FN), - PINMUX_DATA(DACK1_MARK, PS2_5_FN2, PTH5_FN), - PINMUX_DATA(SP1_SCK_FB_MARK, PS2_4_FN1, PTH4_FN), - PINMUX_DATA(ADTRG1_MARK, PS2_4_FN2, PTH4_FN), + PINMUX_DATA(SP1_MOSI_MARK, PTH7_FN), + PINMUX_DATA(SP1_MISO_MARK, PTH6_FN), + PINMUX_DATA(SP1_SCK_MARK, PTH5_FN), + PINMUX_DATA(SP1_SCK_FB_MARK, PTH4_FN), PINMUX_DATA(SP1_SS0_MARK, PTH3_FN), - PINMUX_DATA(SP1_SS1_MARK, PS2_2_FN1, PTH2_FN), - PINMUX_DATA(ADTRG0_MARK, PS2_2_FN2, PTH2_FN), - PINMUX_DATA(WP_MARK, PTH1_FN), - PINMUX_DATA(FMS0_MARK, PTH0_FN), + PINMUX_DATA(TCLK_MARK, PTH2_FN), + PINMUX_DATA(RXD4_MARK, PS2_1_FN1, PTH1_FN), + PINMUX_DATA(SP1_SS1_MARK, PS2_1_FN2, PTH1_FN), + PINMUX_DATA(TXD4_MARK, PS2_0_FN1, PTH0_FN), + PINMUX_DATA(SP0_SS1_MARK, PS2_0_FN2, PTH0_FN), /* PTI FN */ - PINMUX_DATA(D15_MARK, PS3_15_FN1, PTI7_FN), - PINMUX_DATA(SD_WP_MARK, PS3_15_FN2, PTI7_FN), - PINMUX_DATA(D14_MARK, PS3_14_FN1, PTI6_FN), - PINMUX_DATA(SD_CD_MARK, PS3_14_FN2, PTI6_FN), - PINMUX_DATA(D13_MARK, PS3_13_FN1, PTI5_FN), - PINMUX_DATA(SD_CLK_MARK, PS3_13_FN2, PTI5_FN), - PINMUX_DATA(D12_MARK, PS3_12_FN1, PTI4_FN), - PINMUX_DATA(SD_CMD_MARK, PS3_12_FN2, PTI4_FN), - PINMUX_DATA(D11_MARK, PS3_11_FN1, PTI3_FN), - PINMUX_DATA(SD_D3_MARK, PS3_11_FN2, PTI3_FN), - PINMUX_DATA(D10_MARK, PS3_10_FN1, PTI2_FN), - PINMUX_DATA(SD_D2_MARK, PS3_10_FN2, PTI2_FN), - PINMUX_DATA(D9_MARK, PS3_9_FN1, PTI1_FN), - PINMUX_DATA(SD_D1_MARK, PS3_9_FN2, PTI1_FN), - PINMUX_DATA(D8_MARK, PS3_8_FN1, PTI0_FN), - PINMUX_DATA(SD_D0_MARK, PS3_8_FN2, PTI0_FN), + PINMUX_DATA(IRQ15_MARK, PTI7_FN), + PINMUX_DATA(IRQ14_MARK, PTI6_FN), + PINMUX_DATA(IRQ13_MARK, PTI5_FN), + PINMUX_DATA(IRQ12_MARK, PTI4_FN), + PINMUX_DATA(IRQ11_MARK, PTI3_FN), + PINMUX_DATA(IRQ10_MARK, PTI2_FN), + PINMUX_DATA(IRQ9_MARK, PTI1_FN), + PINMUX_DATA(IRQ8_MARK, PTI0_FN), /* PTJ FN */ - PINMUX_DATA(RTS3_MARK, PTJ6_FN), - PINMUX_DATA(CTS3_MARK, PTJ5_FN), - PINMUX_DATA(TXD3_MARK, PTJ4_FN), - PINMUX_DATA(RXD3_MARK, PTJ3_FN), - PINMUX_DATA(RTS4_MARK, PTJ2_FN), - PINMUX_DATA(RXD4_MARK, PTJ1_FN), - PINMUX_DATA(TXD4_MARK, PTJ0_FN), + PINMUX_DATA(RXD3_MARK, PTJ7_FN), + PINMUX_DATA(TXD3_MARK, PTJ6_FN), + PINMUX_DATA(RXD2_MARK, PTJ5_FN), + PINMUX_DATA(TXD2_MARK, PTJ4_FN), + PINMUX_DATA(COM1_TXD_MARK, PTJ3_FN), + PINMUX_DATA(COM1_RXD_MARK, PTJ2_FN), + PINMUX_DATA(COM1_RTS_MARK, PTJ1_FN), + PINMUX_DATA(COM1_CTS_MARK, PTJ0_FN), /* PTK FN */ - PINMUX_DATA(COM2_TXD_MARK, PS3_7_FN1, PTK7_FN), - PINMUX_DATA(SCK2_MARK, PS3_7_FN2, PTK7_FN), + PINMUX_DATA(COM2_TXD_MARK, PTK7_FN), PINMUX_DATA(COM2_RXD_MARK, PTK6_FN), PINMUX_DATA(COM2_RTS_MARK, PTK5_FN), PINMUX_DATA(COM2_CTS_MARK, PTK4_FN), PINMUX_DATA(COM2_DTR_MARK, PTK3_FN), - PINMUX_DATA(COM2_DSR_MARK, PS3_2_FN1, PTK2_FN), - PINMUX_DATA(SCK4_MARK, PS3_2_FN2, PTK2_FN), - PINMUX_DATA(COM2_DCD_MARK, PS3_1_FN1, PTK1_FN), - PINMUX_DATA(SCK3_MARK, PS3_1_FN2, PTK1_FN), - PINMUX_DATA(CLKOUT_MARK, PTK0_FN), + PINMUX_DATA(COM2_DSR_MARK, PTK2_FN), + PINMUX_DATA(COM2_DCD_MARK, PTK1_FN), + PINMUX_DATA(COM2_RI_MARK, PTK0_FN), /* PTL FN */ - PINMUX_DATA(RAC_RXD_MARK, PS4_14_FN1, PTL6_FN), - PINMUX_DATA(RXD2_MARK, PS4_14_FN2, PTL6_FN), - PINMUX_DATA(RAC_RTS_MARK, PS4_13_FN1, PTL5_FN), - PINMUX_DATA(CS5_MARK, PS4_13_FN2, PTL5_FN), - PINMUX_DATA(RAC_CTS_MARK, PS4_12_FN1, PTL4_FN), - PINMUX_DATA(CS6_MARK, PS4_12_FN2, PTL4_FN), + PINMUX_DATA(RAC_TXD_MARK, PTL7_FN), + PINMUX_DATA(RAC_RXD_MARK, PTL6_FN), + PINMUX_DATA(RAC_RTS_MARK, PTL5_FN), + PINMUX_DATA(RAC_CTS_MARK, PTL4_FN), PINMUX_DATA(RAC_DTR_MARK, PTL3_FN), - PINMUX_DATA(RAC_DSR_MARK, PS4_10_FN1, PTL2_FN), - PINMUX_DATA(AUDSYNC_MARK, PS4_10_FN2, PTL2_FN), - PINMUX_DATA(RAC_DCD_MARK, PS4_9_FN1, PTL1_FN), - PINMUX_DATA(AUDCK_MARK, PS4_9_FN2, PTL1_FN), - PINMUX_DATA(RAC_TXD_MARK, PS4_8_FN1, PTL0_FN), - PINMUX_DATA(TXD2_MARK, PS4_8_FN1, PTL0_FN), + PINMUX_DATA(RAC_DSR_MARK, PTL2_FN), + PINMUX_DATA(RAC_DCD_MARK, PTL1_FN), + PINMUX_DATA(RAC_RI_MARK, PTL0_FN), /* PTM FN */ - PINMUX_DATA(CS4_MARK, PTM7_FN), - PINMUX_DATA(RD_MARK, PTM6_FN), - PINMUX_DATA(WE0_MARK, PTM7_FN), - PINMUX_DATA(CS0_MARK, PTM4_FN), + PINMUX_DATA(WP_MARK, PTM6_FN), + PINMUX_DATA(FMS0_MARK, PTM5_FN), + PINMUX_DATA(FMS1_MARK, PTM4_FN), PINMUX_DATA(SDA6_MARK, PTM3_FN), PINMUX_DATA(SCL6_MARK, PTM2_FN), PINMUX_DATA(SDA7_MARK, PTM1_FN), PINMUX_DATA(SCL7_MARK, PTM0_FN), /* PTN FN */ - PINMUX_DATA(VBUS_EN_MARK, PTN6_FN), - PINMUX_DATA(VBUS_OC_MARK, PTN5_FN), - PINMUX_DATA(JMCTCK_MARK, PS4_4_FN1, PTN4_FN), - PINMUX_DATA(SGPIO1_CLK_MARK, PS4_4_FN2, PTN4_FN), - PINMUX_DATA(JMCTMS_MARK, PS4_3_FN1, PTN5_FN), - PINMUX_DATA(SGPIO1_LOAD_MARK, PS4_3_FN2, PTN5_FN), - PINMUX_DATA(JMCTDO_MARK, PS4_2_FN1, PTN2_FN), - PINMUX_DATA(SGPIO1_DO_MARK, PS4_2_FN2, PTN2_FN), - PINMUX_DATA(JMCTDI_MARK, PS4_1_FN1, PTN1_FN), - PINMUX_DATA(SGPIO1_DI_MARK, PS4_1_FN2, PTN1_FN), - PINMUX_DATA(JMCTRST_MARK, PS4_0_FN1, PTN0_FN), - PINMUX_DATA(SUB_CLKIN_MARK, PS4_0_FN2, PTN0_FN), + PINMUX_DATA(SCK2_MARK, PS4_15_FN1, PTN7_FN), + PINMUX_DATA(EVENT7_MARK, PS4_15_FN2, PTN7_FN), + PINMUX_DATA(RTS4_MARK, PS4_14_FN1, PTN6_FN), + PINMUX_DATA(EVENT6_MARK, PS4_14_FN2, PTN6_FN), + PINMUX_DATA(RTS3_MARK, PS4_13_FN1, PTN5_FN), + PINMUX_DATA(EVENT5_MARK, PS4_13_FN2, PTN5_FN), + PINMUX_DATA(RTS2_MARK, PS4_12_FN1, PTN4_FN), + PINMUX_DATA(EVENT4_MARK, PS4_12_FN2, PTN4_FN), + PINMUX_DATA(CTS4_MARK, PS4_11_FN1, PTN3_FN), + PINMUX_DATA(EVENT3_MARK, PS4_11_FN2, PTN3_FN), + PINMUX_DATA(CTS3_MARK, PS4_10_FN1, PTN2_FN), + PINMUX_DATA(EVENT2_MARK, PS4_10_FN2, PTN2_FN), + PINMUX_DATA(CTS2_MARK, PS4_9_FN1, PTN1_FN), + PINMUX_DATA(EVENT1_MARK, PS4_9_FN2, PTN1_FN), + PINMUX_DATA(EVENT0_MARK, PTN0_FN), /* PTO FN */ PINMUX_DATA(SGPIO0_CLK_MARK, PTO7_FN), PINMUX_DATA(SGPIO0_LOAD_MARK, PTO6_FN), PINMUX_DATA(SGPIO0_DI_MARK, PTO5_FN), PINMUX_DATA(SGPIO0_DO_MARK, PTO4_FN), - PINMUX_DATA(SGPIO2_CLK_MARK, PS5_11_FN1, PTO3_FN), - PINMUX_DATA(COM1_TXD_MARK, PS5_11_FN2, PTO3_FN), - PINMUX_DATA(SGPIO2_LOAD_MARK, PS5_10_FN1, PTO2_FN), - PINMUX_DATA(COM1_RXD_MARK, PS5_10_FN2, PTO2_FN), - PINMUX_DATA(SGPIO2_DI_MARK, PS5_9_FN1, PTO1_FN), - PINMUX_DATA(COM1_RTS_MARK, PS5_9_FN2, PTO1_FN), - PINMUX_DATA(SGPIO2_DO_MARK, PS5_8_FN1, PTO0_FN), - PINMUX_DATA(COM1_CTS_MARK, PS5_8_FN2, PTO0_FN), + PINMUX_DATA(SGPIO1_CLK_MARK, PTO3_FN), + PINMUX_DATA(SGPIO1_LOAD_MARK, PTO2_FN), + PINMUX_DATA(SGPIO1_DI_MARK, PTO1_FN), + PINMUX_DATA(SGPIO1_DO_MARK, PTO0_FN), /* PTP FN */ + PINMUX_DATA(JMCTCK_MARK, PTP6_FN), + PINMUX_DATA(JMCTMS_MARK, PTP5_FN), + PINMUX_DATA(JMCTDO_MARK, PTP4_FN), + PINMUX_DATA(JMCTDI_MARK, PTP3_FN), + PINMUX_DATA(JMCRST_MARK, PTP2_FN), + PINMUX_DATA(SCK4_MARK, PTP1_FN), + PINMUX_DATA(SCK3_MARK, PTP0_FN), /* PTQ FN */ PINMUX_DATA(LAD3_MARK, PTQ6_FN), @@ -977,8 +864,8 @@ static pinmux_enum_t pinmux_data[] = { PINMUX_DATA(LAD1_MARK, PTQ4_FN), PINMUX_DATA(LAD0_MARK, PTQ3_FN), PINMUX_DATA(LFRAME_MARK, PTQ2_FN), - PINMUX_DATA(LRESET_MARK, PTQ1_FN), - PINMUX_DATA(LCLK_MARK, PTQ0_FN), + PINMUX_DATA(SCK4_MARK, PTQ1_FN), + PINMUX_DATA(SCK3_MARK, PTQ0_FN), /* PTR FN */ PINMUX_DATA(SDA8_MARK, PTR7_FN), /* DDC3? */ @@ -1001,84 +888,58 @@ static pinmux_enum_t pinmux_data[] = { PINMUX_DATA(SCL3_MARK, PTS0_FN), /* PTT FN */ - PINMUX_DATA(PWMX7_MARK, PS5_7_FN1, PTT7_FN), - PINMUX_DATA(AUDATA3_MARK, PS5_7_FN2, PTT7_FN), - PINMUX_DATA(PWMX6_MARK, PS5_6_FN1, PTT6_FN), - PINMUX_DATA(AUDATA2_MARK, PS5_6_FN2, PTT6_FN), - PINMUX_DATA(PWMX5_MARK, PS5_5_FN1, PTT5_FN), - PINMUX_DATA(AUDATA1_MARK, PS5_5_FN2, PTT5_FN), - PINMUX_DATA(PWMX4_MARK, PS5_4_FN1, PTT4_FN), - PINMUX_DATA(AUDATA0_MARK, PS5_4_FN2, PTT4_FN), - PINMUX_DATA(PWMX3_MARK, PS5_3_FN1, PTT3_FN), - PINMUX_DATA(STATUS1_MARK, PS5_3_FN2, PTT3_FN), - PINMUX_DATA(PWMX2_MARK, PS5_2_FN1, PTT2_FN), - PINMUX_DATA(STATUS0_MARK, PS5_2_FN2, PTT2_FN), - PINMUX_DATA(PWMX1_MARK, PTT1_FN), - PINMUX_DATA(PWMX0_MARK, PTT0_FN), + PINMUX_DATA(AUDSYNC_MARK, PTS5_FN), + PINMUX_DATA(AUDCK_MARK, PTS4_FN), + PINMUX_DATA(AUDATA3_MARK, PS4_3_FN1, PTS3_FN), + PINMUX_DATA(PWX7_MARK, PS4_3_FN2, PTS3_FN), + PINMUX_DATA(AUDATA2_MARK, PS4_2_FN1, PTS2_FN), + PINMUX_DATA(PWX6_MARK, PS4_2_FN2, PTS2_FN), + PINMUX_DATA(AUDATA1_MARK, PS4_1_FN1, PTS1_FN), + PINMUX_DATA(PWX5_MARK, PS4_1_FN2, PTS1_FN), + PINMUX_DATA(AUDATA0_MARK, PS4_0_FN1, PTS0_FN), + PINMUX_DATA(PWX4_MARK, PS4_0_FN2, PTS0_FN), /* PTU FN */ - PINMUX_DATA(LGPIO7_MARK, PS6_15_FN1, PTU7_FN), - PINMUX_DATA(APMONCTL_O_MARK, PS6_15_FN2, PTU7_FN), - PINMUX_DATA(LGPIO6_MARK, PS6_14_FN1, PTU6_FN), - PINMUX_DATA(APMPWBTOUT_O_MARK, PS6_14_FN2, PTU6_FN), - PINMUX_DATA(LGPIO5_MARK, PS6_13_FN1, PTU5_FN), - PINMUX_DATA(APMSCI_O_MARK, PS6_13_FN2, PTU5_FN), - PINMUX_DATA(LGPIO4_MARK, PS6_12_FN1, PTU4_FN), - PINMUX_DATA(APMVDDON_MARK, PS6_12_FN2, PTU4_FN), - PINMUX_DATA(LGPIO3_MARK, PS6_11_FN1, PTU3_FN), - PINMUX_DATA(APMSLPBTN_MARK, PS6_11_FN2, PTU3_FN), - PINMUX_DATA(LGPIO2_MARK, PS6_10_FN1, PTU2_FN), - PINMUX_DATA(APMPWRBTN_MARK, PS6_10_FN2, PTU2_FN), - PINMUX_DATA(LGPIO1_MARK, PS6_9_FN1, PTU1_FN), - PINMUX_DATA(APMS5N_MARK, PS6_9_FN2, PTU1_FN), - PINMUX_DATA(LGPIO0_MARK, PS6_8_FN1, PTU0_FN), - PINMUX_DATA(APMS3N_MARK, PS6_8_FN2, PTU0_FN), + PINMUX_DATA(CS6_MARK, PTU7_FN), + PINMUX_DATA(CS5_MARK, PTU6_FN), + PINMUX_DATA(CS4_MARK, PTU5_FN), + PINMUX_DATA(CS0_MARK, PTU4_FN), + PINMUX_DATA(RD_MARK, PTU3_FN), + PINMUX_DATA(WE0_MARK, PTU2_FN), + PINMUX_DATA(A25_MARK, PS5_9_FN1, PTU1_FN), + PINMUX_DATA(DREQ0_MARK, PS5_9_FN2, PTU1_FN), + PINMUX_DATA(A24_MARK, PS5_8_FN1, PTU0_FN), + PINMUX_DATA(DACK0_MARK, PS5_8_FN2, PTU0_FN), /* PTV FN */ - PINMUX_DATA(A23_MARK, PS6_7_FN1, PTV7_FN), - PINMUX_DATA(COM2_RI_MARK, PS6_7_FN2, PTV7_FN), - PINMUX_DATA(A22_MARK, PS6_6_FN1, PTV6_FN), - PINMUX_DATA(R_SPI_MOSI_MARK, PS6_6_FN2, PTV6_FN), - PINMUX_DATA(A21_MARK, PS6_5_FN1, PTV5_FN), - PINMUX_DATA(R_SPI_MISO_MARK, PS6_5_FN2, PTV5_FN), - PINMUX_DATA(A20_MARK, PS6_4_FN1, PTV4_FN), - PINMUX_DATA(R_SPI_RSPCK_MARK, PS6_4_FN2, PTV4_FN), - PINMUX_DATA(A19_MARK, PS6_3_FN1, PTV3_FN), - PINMUX_DATA(R_SPI_SSL0_MARK, PS6_3_FN2, PTV3_FN), - PINMUX_DATA(A18_MARK, PS6_2_FN1, PTV2_FN), - PINMUX_DATA(R_SPI_SSL1_MARK, PS6_2_FN2, PTV2_FN), - PINMUX_DATA(A17_MARK, PS6_1_FN1, PTV1_FN), - PINMUX_DATA(EVENT7_MARK, PS6_1_FN2, PTV1_FN), - PINMUX_DATA(A16_MARK, PS6_0_FN1, PTV0_FN), - PINMUX_DATA(EVENT6_MARK, PS6_0_FN1, PTV0_FN), + PINMUX_DATA(A23_MARK, PS5_7_FN1, PTV7_FN), + PINMUX_DATA(TEND0_MARK, PS5_7_FN2, PTV7_FN), + PINMUX_DATA(A22_MARK, PS5_6_FN1, PTV6_FN), + PINMUX_DATA(DREQ1_MARK, PS5_6_FN2, PTV6_FN), + PINMUX_DATA(A21_MARK, PS5_5_FN1, PTV5_FN), + PINMUX_DATA(DACK1_MARK, PS5_5_FN2, PTV5_FN), + PINMUX_DATA(A20_MARK, PS5_4_FN1, PTV4_FN), + PINMUX_DATA(TEND1_MARK, PS5_4_FN2, PTV4_FN), + PINMUX_DATA(A19_MARK, PTV3_FN), + PINMUX_DATA(A18_MARK, PTV2_FN), + PINMUX_DATA(A17_MARK, PTV1_FN), + PINMUX_DATA(A16_MARK, PTV0_FN), /* PTW FN */ - PINMUX_DATA(A15_MARK, PS7_15_FN1, PTW7_FN), - PINMUX_DATA(EVENT5_MARK, PS7_15_FN2, PTW7_FN), - PINMUX_DATA(A14_MARK, PS7_14_FN1, PTW6_FN), - PINMUX_DATA(EVENT4_MARK, PS7_14_FN2, PTW6_FN), - PINMUX_DATA(A13_MARK, PS7_13_FN1, PTW5_FN), - PINMUX_DATA(EVENT3_MARK, PS7_13_FN2, PTW5_FN), - PINMUX_DATA(A12_MARK, PS7_12_FN1, PTW4_FN), - PINMUX_DATA(EVENT2_MARK, PS7_12_FN2, PTW4_FN), - PINMUX_DATA(A11_MARK, PS7_11_FN1, PTW3_FN), - PINMUX_DATA(EVENT1_MARK, PS7_11_FN2, PTW3_FN), - PINMUX_DATA(A10_MARK, PS7_10_FN1, PTW2_FN), - PINMUX_DATA(EVENT0_MARK, PS7_10_FN2, PTW2_FN), - PINMUX_DATA(A9_MARK, PS7_9_FN1, PTW1_FN), - PINMUX_DATA(CTS4_MARK, PS7_9_FN2, PTW1_FN), - PINMUX_DATA(A8_MARK, PS7_8_FN1, PTW0_FN), - PINMUX_DATA(CTS2_MARK, PS7_8_FN2, PTW0_FN), + PINMUX_DATA(A15_MARK, PTW7_FN), + PINMUX_DATA(A14_MARK, PTW6_FN), + PINMUX_DATA(A13_MARK, PTW5_FN), + PINMUX_DATA(A12_MARK, PTW4_FN), + PINMUX_DATA(A11_MARK, PTW3_FN), + PINMUX_DATA(A10_MARK, PTW2_FN), + PINMUX_DATA(A9_MARK, PTW1_FN), + PINMUX_DATA(A8_MARK, PTW0_FN), /* PTX FN */ - PINMUX_DATA(A7_MARK, PS7_7_FN1, PTX7_FN), - PINMUX_DATA(RTS2_MARK, PS7_7_FN2, PTX7_FN), - PINMUX_DATA(A6_MARK, PS7_6_FN1, PTX6_FN), - PINMUX_DATA(SIM_D_MARK, PS7_6_FN2, PTX6_FN), - PINMUX_DATA(A5_MARK, PS7_5_FN1, PTX5_FN), - PINMUX_DATA(SIM_CLK_MARK, PS7_5_FN2, PTX5_FN), - PINMUX_DATA(A4_MARK, PS7_4_FN1, PTX4_FN), - PINMUX_DATA(SIM_RST_MARK, PS7_4_FN2, PTX4_FN), + PINMUX_DATA(A7_MARK, PTX7_FN), + PINMUX_DATA(A6_MARK, PTX6_FN), + PINMUX_DATA(A5_MARK, PTX5_FN), + PINMUX_DATA(A4_MARK, PTX4_FN), PINMUX_DATA(A3_MARK, PTX3_FN), PINMUX_DATA(A2_MARK, PTX2_FN), PINMUX_DATA(A1_MARK, PTX1_FN), @@ -1093,24 +954,6 @@ static pinmux_enum_t pinmux_data[] = { PINMUX_DATA(D2_MARK, PTY2_FN), PINMUX_DATA(D1_MARK, PTY1_FN), PINMUX_DATA(D0_MARK, PTY0_FN), - - /* PTZ FN */ - PINMUX_DATA(MMCDAT7_MARK, PS8_15_FN1, PTZ7_FN), - PINMUX_DATA(ON_DQ7_MARK, PS8_15_FN2, PTZ7_FN), - PINMUX_DATA(MMCDAT6_MARK, PS8_14_FN1, PTZ6_FN), - PINMUX_DATA(ON_DQ6_MARK, PS8_14_FN2, PTZ6_FN), - PINMUX_DATA(MMCDAT5_MARK, PS8_13_FN1, PTZ5_FN), - PINMUX_DATA(ON_DQ5_MARK, PS8_13_FN2, PTZ5_FN), - PINMUX_DATA(MMCDAT4_MARK, PS8_12_FN1, PTZ4_FN), - PINMUX_DATA(ON_DQ4_MARK, PS8_12_FN2, PTZ4_FN), - PINMUX_DATA(MMCDAT3_MARK, PS8_11_FN1, PTZ3_FN), - PINMUX_DATA(ON_DQ3_MARK, PS8_11_FN2, PTZ3_FN), - PINMUX_DATA(MMCDAT2_MARK, PS8_10_FN1, PTZ2_FN), - PINMUX_DATA(ON_DQ2_MARK, PS8_10_FN2, PTZ2_FN), - PINMUX_DATA(MMCDAT1_MARK, PS8_9_FN1, PTZ1_FN), - PINMUX_DATA(ON_DQ1_MARK, PS8_9_FN2, PTZ1_FN), - PINMUX_DATA(MMCDAT0_MARK, PS8_8_FN1, PTZ0_FN), - PINMUX_DATA(ON_DQ0_MARK, PS8_8_FN2, PTZ0_FN), }; static struct pinmux_gpio pinmux_gpios[] = { @@ -1205,6 +1048,7 @@ static struct pinmux_gpio pinmux_gpios[] = { PINMUX_GPIO(GPIO_PTI0, PTI0_DATA), /* PTJ */ + PINMUX_GPIO(GPIO_PTJ7, PTJ7_DATA), PINMUX_GPIO(GPIO_PTJ6, PTJ6_DATA), PINMUX_GPIO(GPIO_PTJ5, PTJ5_DATA), PINMUX_GPIO(GPIO_PTJ4, PTJ4_DATA), @@ -1224,6 +1068,7 @@ static struct pinmux_gpio pinmux_gpios[] = { PINMUX_GPIO(GPIO_PTK0, PTK0_DATA), /* PTL */ + PINMUX_GPIO(GPIO_PTL7, PTL7_DATA), PINMUX_GPIO(GPIO_PTL6, PTL6_DATA), PINMUX_GPIO(GPIO_PTL5, PTL5_DATA), PINMUX_GPIO(GPIO_PTL4, PTL4_DATA), @@ -1233,7 +1078,6 @@ static struct pinmux_gpio pinmux_gpios[] = { PINMUX_GPIO(GPIO_PTL0, PTL0_DATA), /* PTM */ - PINMUX_GPIO(GPIO_PTM7, PTM7_DATA), PINMUX_GPIO(GPIO_PTM6, PTM6_DATA), PINMUX_GPIO(GPIO_PTM5, PTM5_DATA), PINMUX_GPIO(GPIO_PTM4, PTM4_DATA), @@ -1243,6 +1087,7 @@ static struct pinmux_gpio pinmux_gpios[] = { PINMUX_GPIO(GPIO_PTM0, PTM0_DATA), /* PTN */ + PINMUX_GPIO(GPIO_PTN7, PTN7_DATA), PINMUX_GPIO(GPIO_PTN6, PTN6_DATA), PINMUX_GPIO(GPIO_PTN5, PTN5_DATA), PINMUX_GPIO(GPIO_PTN4, PTN4_DATA), @@ -1262,7 +1107,6 @@ static struct pinmux_gpio pinmux_gpios[] = { PINMUX_GPIO(GPIO_PTO0, PTO0_DATA), /* PTP */ - PINMUX_GPIO(GPIO_PTP7, PTP7_DATA), PINMUX_GPIO(GPIO_PTP6, PTP6_DATA), PINMUX_GPIO(GPIO_PTP5, PTP5_DATA), PINMUX_GPIO(GPIO_PTP4, PTP4_DATA), @@ -1301,8 +1145,6 @@ static struct pinmux_gpio pinmux_gpios[] = { PINMUX_GPIO(GPIO_PTS0, PTS0_DATA), /* PTT */ - PINMUX_GPIO(GPIO_PTT7, PTT7_DATA), - PINMUX_GPIO(GPIO_PTT6, PTT6_DATA), PINMUX_GPIO(GPIO_PTT5, PTT5_DATA), PINMUX_GPIO(GPIO_PTT4, PTT4_DATA), PINMUX_GPIO(GPIO_PTT3, PTT3_DATA), @@ -1370,35 +1212,54 @@ static struct pinmux_gpio pinmux_gpios[] = { PINMUX_GPIO(GPIO_PTZ1, PTZ1_DATA), PINMUX_GPIO(GPIO_PTZ0, PTZ0_DATA), - /* PTA (mobule: LBSC, RGMII) */ + /* PTA (mobule: LBSC, CPG, LPC) */ PINMUX_GPIO(GPIO_FN_BS, BS_MARK), PINMUX_GPIO(GPIO_FN_RDWR, RDWR_MARK), PINMUX_GPIO(GPIO_FN_WE1, WE1_MARK), PINMUX_GPIO(GPIO_FN_RDY, RDY_MARK), + PINMUX_GPIO(GPIO_FN_MD10, MD10_MARK), + PINMUX_GPIO(GPIO_FN_MD9, MD9_MARK), + PINMUX_GPIO(GPIO_FN_MD8, MD8_MARK), + PINMUX_GPIO(GPIO_FN_LGPIO7, LGPIO7_MARK), + PINMUX_GPIO(GPIO_FN_LGPIO6, LGPIO6_MARK), + PINMUX_GPIO(GPIO_FN_LGPIO5, LGPIO5_MARK), + PINMUX_GPIO(GPIO_FN_LGPIO4, LGPIO4_MARK), + PINMUX_GPIO(GPIO_FN_LGPIO3, LGPIO3_MARK), + PINMUX_GPIO(GPIO_FN_LGPIO2, LGPIO2_MARK), + PINMUX_GPIO(GPIO_FN_LGPIO1, LGPIO1_MARK), + PINMUX_GPIO(GPIO_FN_LGPIO0, LGPIO0_MARK), + + /* PTB (mobule: LBSC, EtherC, SIM, LPC) */ + PINMUX_GPIO(GPIO_FN_D15, D15_MARK), + PINMUX_GPIO(GPIO_FN_D14, D14_MARK), + PINMUX_GPIO(GPIO_FN_D13, D13_MARK), + PINMUX_GPIO(GPIO_FN_D12, D12_MARK), + PINMUX_GPIO(GPIO_FN_D11, D11_MARK), + PINMUX_GPIO(GPIO_FN_D10, D10_MARK), + PINMUX_GPIO(GPIO_FN_D9, D9_MARK), + PINMUX_GPIO(GPIO_FN_D8, D8_MARK), PINMUX_GPIO(GPIO_FN_ET0_MDC, ET0_MDC_MARK), - PINMUX_GPIO(GPIO_FN_ET0_MDIO, ET0_MDC_MARK), + PINMUX_GPIO(GPIO_FN_ET0_MDIO, ET0_MDIO_MARK), PINMUX_GPIO(GPIO_FN_ET1_MDC, ET1_MDC_MARK), - PINMUX_GPIO(GPIO_FN_ET1_MDIO, ET1_MDC_MARK), - - /* PTB (mobule: INTC, ONFI, TMU) */ - PINMUX_GPIO(GPIO_FN_IRQ15, IRQ15_MARK), - PINMUX_GPIO(GPIO_FN_IRQ14, IRQ14_MARK), - PINMUX_GPIO(GPIO_FN_IRQ13, IRQ13_MARK), - PINMUX_GPIO(GPIO_FN_IRQ12, IRQ12_MARK), - PINMUX_GPIO(GPIO_FN_IRQ11, IRQ11_MARK), - PINMUX_GPIO(GPIO_FN_IRQ10, IRQ10_MARK), - PINMUX_GPIO(GPIO_FN_IRQ9, IRQ9_MARK), - PINMUX_GPIO(GPIO_FN_IRQ8, IRQ8_MARK), - PINMUX_GPIO(GPIO_FN_ON_NRE, ON_NRE_MARK), - PINMUX_GPIO(GPIO_FN_ON_NWE, ON_NWE_MARK), - PINMUX_GPIO(GPIO_FN_ON_NWP, ON_NWP_MARK), - PINMUX_GPIO(GPIO_FN_ON_NCE0, ON_NCE0_MARK), - PINMUX_GPIO(GPIO_FN_ON_R_B0, ON_R_B0_MARK), - PINMUX_GPIO(GPIO_FN_ON_ALE, ON_ALE_MARK), - PINMUX_GPIO(GPIO_FN_ON_CLE, ON_CLE_MARK), - PINMUX_GPIO(GPIO_FN_TCLK, TCLK_MARK), + PINMUX_GPIO(GPIO_FN_ET1_MDIO, ET1_MDIO_MARK), + PINMUX_GPIO(GPIO_FN_WPSZ1, WPSZ1_MARK), + PINMUX_GPIO(GPIO_FN_WPSZ0, WPSZ0_MARK), + PINMUX_GPIO(GPIO_FN_FWID, FWID_MARK), + PINMUX_GPIO(GPIO_FN_FLSHSZ, FLSHSZ_MARK), + PINMUX_GPIO(GPIO_FN_LPC_SPIEN, LPC_SPIEN_MARK), + PINMUX_GPIO(GPIO_FN_BASEL, BASEL_MARK), + + /* PTC (mobule: SD) */ + PINMUX_GPIO(GPIO_FN_SD_WP, SD_WP_MARK), + PINMUX_GPIO(GPIO_FN_SD_CD, SD_CD_MARK), + PINMUX_GPIO(GPIO_FN_SD_CLK, SD_CLK_MARK), + PINMUX_GPIO(GPIO_FN_SD_CMD, SD_CMD_MARK), + PINMUX_GPIO(GPIO_FN_SD_D3, SD_D3_MARK), + PINMUX_GPIO(GPIO_FN_SD_D2, SD_D2_MARK), + PINMUX_GPIO(GPIO_FN_SD_D1, SD_D1_MARK), + PINMUX_GPIO(GPIO_FN_SD_D0, SD_D0_MARK), - /* PTC (mobule: IRQ, PWMU) */ + /* PTD (mobule: INTC, SPI0, LBSC, CPG, ADC) */ PINMUX_GPIO(GPIO_FN_IRQ7, IRQ7_MARK), PINMUX_GPIO(GPIO_FN_IRQ6, IRQ6_MARK), PINMUX_GPIO(GPIO_FN_IRQ5, IRQ5_MARK), @@ -1407,102 +1268,80 @@ static struct pinmux_gpio pinmux_gpios[] = { PINMUX_GPIO(GPIO_FN_IRQ2, IRQ2_MARK), PINMUX_GPIO(GPIO_FN_IRQ1, IRQ1_MARK), PINMUX_GPIO(GPIO_FN_IRQ0, IRQ0_MARK), - PINMUX_GPIO(GPIO_FN_PWMU0, PWMU0_MARK), - PINMUX_GPIO(GPIO_FN_PWMU1, PWMU1_MARK), - PINMUX_GPIO(GPIO_FN_PWMU2, PWMU2_MARK), - PINMUX_GPIO(GPIO_FN_PWMU3, PWMU3_MARK), - PINMUX_GPIO(GPIO_FN_PWMU4, PWMU4_MARK), - PINMUX_GPIO(GPIO_FN_PWMU5, PWMU5_MARK), - - /* PTD (mobule: SPI0, DMAC) */ - PINMUX_GPIO(GPIO_FN_SP0_MOSI, SP0_MOSI_MARK), - PINMUX_GPIO(GPIO_FN_SP0_MISO, SP0_MISO_MARK), - PINMUX_GPIO(GPIO_FN_SP0_SCK, SP0_SCK_MARK), - PINMUX_GPIO(GPIO_FN_SP0_SCK_FB, SP0_SCK_FB_MARK), - PINMUX_GPIO(GPIO_FN_SP0_SS0, SP0_SS0_MARK), - PINMUX_GPIO(GPIO_FN_SP0_SS1, SP0_SS1_MARK), - PINMUX_GPIO(GPIO_FN_SP0_SS2, SP0_SS2_MARK), - PINMUX_GPIO(GPIO_FN_SP0_SS3, SP0_SS3_MARK), - PINMUX_GPIO(GPIO_FN_DREQ0, DREQ0_MARK), - PINMUX_GPIO(GPIO_FN_DACK0, DACK0_MARK), - PINMUX_GPIO(GPIO_FN_TEND0, TEND0_MARK), - - /* PTE (mobule: RMII) */ - PINMUX_GPIO(GPIO_FN_RMII0_CRS_DV, RMII0_CRS_DV_MARK), - PINMUX_GPIO(GPIO_FN_RMII0_TXD1, RMII0_TXD1_MARK), - PINMUX_GPIO(GPIO_FN_RMII0_TXD0, RMII0_TXD0_MARK), - PINMUX_GPIO(GPIO_FN_RMII0_TXEN, RMII0_TXEN_MARK), - PINMUX_GPIO(GPIO_FN_RMII0_REFCLK, RMII0_REFCLK_MARK), - PINMUX_GPIO(GPIO_FN_RMII0_RXD1, RMII0_RXD1_MARK), - PINMUX_GPIO(GPIO_FN_RMII0_RXD0, RMII0_RXD0_MARK), - PINMUX_GPIO(GPIO_FN_RMII0_RX_ER, RMII0_RX_ER_MARK), - - /* PTF (mobule: RMII, SerMux) */ - PINMUX_GPIO(GPIO_FN_RMII1_CRS_DV, RMII1_CRS_DV_MARK), - PINMUX_GPIO(GPIO_FN_RMII1_TXD1, RMII1_TXD1_MARK), - PINMUX_GPIO(GPIO_FN_RMII1_TXD0, RMII1_TXD0_MARK), - PINMUX_GPIO(GPIO_FN_RMII1_TXEN, RMII1_TXEN_MARK), - PINMUX_GPIO(GPIO_FN_RMII1_REFCLK, RMII1_REFCLK_MARK), - PINMUX_GPIO(GPIO_FN_RMII1_RXD1, RMII1_RXD1_MARK), - PINMUX_GPIO(GPIO_FN_RMII1_RXD0, RMII1_RXD0_MARK), - PINMUX_GPIO(GPIO_FN_RMII1_RX_ER, RMII1_RX_ER_MARK), - PINMUX_GPIO(GPIO_FN_RAC_RI, RAC_RI_MARK), + PINMUX_GPIO(GPIO_FN_MD6, MD6_MARK), + PINMUX_GPIO(GPIO_FN_MD5, MD5_MARK), + PINMUX_GPIO(GPIO_FN_MD3, MD3_MARK), + PINMUX_GPIO(GPIO_FN_MD2, MD2_MARK), + PINMUX_GPIO(GPIO_FN_MD1, MD1_MARK), + PINMUX_GPIO(GPIO_FN_MD0, MD0_MARK), + PINMUX_GPIO(GPIO_FN_ADTRG1, ADTRG1_MARK), + PINMUX_GPIO(GPIO_FN_ADTRG0, ADTRG0_MARK), - /* PTG (mobule: system, LBSC, LPC, WDT, LPC, eMMC) */ - PINMUX_GPIO(GPIO_FN_BOOTFMS, BOOTFMS_MARK), - PINMUX_GPIO(GPIO_FN_BOOTWP, BOOTWP_MARK), - PINMUX_GPIO(GPIO_FN_A25, A25_MARK), - PINMUX_GPIO(GPIO_FN_A24, A24_MARK), + /* PTE (mobule: EtherC) */ + PINMUX_GPIO(GPIO_FN_ET0_CRS_DV, ET0_CRS_DV_MARK), + PINMUX_GPIO(GPIO_FN_ET0_TXD1, ET0_TXD1_MARK), + PINMUX_GPIO(GPIO_FN_ET0_TXD0, ET0_TXD0_MARK), + PINMUX_GPIO(GPIO_FN_ET0_TX_EN, ET0_TX_EN_MARK), + PINMUX_GPIO(GPIO_FN_ET0_REF_CLK, ET0_REF_CLK_MARK), + PINMUX_GPIO(GPIO_FN_ET0_RXD1, ET0_RXD1_MARK), + PINMUX_GPIO(GPIO_FN_ET0_RXD0, ET0_RXD0_MARK), + PINMUX_GPIO(GPIO_FN_ET0_RX_ER, ET0_RX_ER_MARK), + + /* PTF (mobule: EtherC) */ + PINMUX_GPIO(GPIO_FN_ET1_CRS_DV, ET1_CRS_DV_MARK), + PINMUX_GPIO(GPIO_FN_ET1_TXD1, ET1_TXD1_MARK), + PINMUX_GPIO(GPIO_FN_ET1_TXD0, ET1_TXD0_MARK), + PINMUX_GPIO(GPIO_FN_ET1_TX_EN, ET1_TX_EN_MARK), + PINMUX_GPIO(GPIO_FN_ET1_REF_CLK, ET1_REF_CLK_MARK), + PINMUX_GPIO(GPIO_FN_ET1_RXD1, ET1_RXD1_MARK), + PINMUX_GPIO(GPIO_FN_ET1_RXD0, ET1_RXD0_MARK), + PINMUX_GPIO(GPIO_FN_ET1_RX_ER, ET1_RX_ER_MARK), + + /* PTG (mobule: SYSTEM, PWMX, LPC) */ + PINMUX_GPIO(GPIO_FN_STATUS0, STATUS0_MARK), + PINMUX_GPIO(GPIO_FN_STATUS1, STATUS1_MARK), + PINMUX_GPIO(GPIO_FN_PWX0, PWX0_MARK), + PINMUX_GPIO(GPIO_FN_PWX1, PWX1_MARK), + PINMUX_GPIO(GPIO_FN_PWX2, PWX2_MARK), + PINMUX_GPIO(GPIO_FN_PWX3, PWX3_MARK), PINMUX_GPIO(GPIO_FN_SERIRQ, SERIRQ_MARK), - PINMUX_GPIO(GPIO_FN_WDTOVF, WDTOVF_MARK), + PINMUX_GPIO(GPIO_FN_CLKRUN, CLKRUN_MARK), PINMUX_GPIO(GPIO_FN_LPCPD, LPCPD_MARK), PINMUX_GPIO(GPIO_FN_LDRQ, LDRQ_MARK), - PINMUX_GPIO(GPIO_FN_MMCCLK, MMCCLK_MARK), - PINMUX_GPIO(GPIO_FN_MMCCMD, MMCCMD_MARK), - /* PTH (mobule: SPI1, LPC, DMAC, ADC) */ + /* PTH (mobule: TMU, SCIF234, SPI1, SPI0) */ + PINMUX_GPIO(GPIO_FN_TCLK, TCLK_MARK), + PINMUX_GPIO(GPIO_FN_RXD4, RXD4_MARK), + PINMUX_GPIO(GPIO_FN_TXD4, TXD4_MARK), PINMUX_GPIO(GPIO_FN_SP1_MOSI, SP1_MOSI_MARK), PINMUX_GPIO(GPIO_FN_SP1_MISO, SP1_MISO_MARK), PINMUX_GPIO(GPIO_FN_SP1_SCK, SP1_SCK_MARK), PINMUX_GPIO(GPIO_FN_SP1_SCK_FB, SP1_SCK_FB_MARK), PINMUX_GPIO(GPIO_FN_SP1_SS0, SP1_SS0_MARK), PINMUX_GPIO(GPIO_FN_SP1_SS1, SP1_SS1_MARK), - PINMUX_GPIO(GPIO_FN_WP, WP_MARK), - PINMUX_GPIO(GPIO_FN_FMS0, FMS0_MARK), - PINMUX_GPIO(GPIO_FN_TEND1, TEND1_MARK), - PINMUX_GPIO(GPIO_FN_DREQ1, DREQ1_MARK), - PINMUX_GPIO(GPIO_FN_DACK1, DACK1_MARK), - PINMUX_GPIO(GPIO_FN_ADTRG1, ADTRG1_MARK), - PINMUX_GPIO(GPIO_FN_ADTRG0, ADTRG0_MARK), + PINMUX_GPIO(GPIO_FN_SP0_SS1, SP0_SS1_MARK), - /* PTI (mobule: LBSC, SDHI) */ - PINMUX_GPIO(GPIO_FN_D15, D15_MARK), - PINMUX_GPIO(GPIO_FN_D14, D14_MARK), - PINMUX_GPIO(GPIO_FN_D13, D13_MARK), - PINMUX_GPIO(GPIO_FN_D12, D12_MARK), - PINMUX_GPIO(GPIO_FN_D11, D11_MARK), - PINMUX_GPIO(GPIO_FN_D10, D10_MARK), - PINMUX_GPIO(GPIO_FN_D9, D9_MARK), - PINMUX_GPIO(GPIO_FN_D8, D8_MARK), - PINMUX_GPIO(GPIO_FN_SD_WP, SD_WP_MARK), - PINMUX_GPIO(GPIO_FN_SD_CD, SD_CD_MARK), - PINMUX_GPIO(GPIO_FN_SD_CLK, SD_CLK_MARK), - PINMUX_GPIO(GPIO_FN_SD_CMD, SD_CMD_MARK), - PINMUX_GPIO(GPIO_FN_SD_D3, SD_D3_MARK), - PINMUX_GPIO(GPIO_FN_SD_D2, SD_D2_MARK), - PINMUX_GPIO(GPIO_FN_SD_D1, SD_D1_MARK), - PINMUX_GPIO(GPIO_FN_SD_D0, SD_D0_MARK), + /* PTI (mobule: INTC) */ + PINMUX_GPIO(GPIO_FN_IRQ15, IRQ15_MARK), + PINMUX_GPIO(GPIO_FN_IRQ14, IRQ14_MARK), + PINMUX_GPIO(GPIO_FN_IRQ13, IRQ13_MARK), + PINMUX_GPIO(GPIO_FN_IRQ12, IRQ12_MARK), + PINMUX_GPIO(GPIO_FN_IRQ11, IRQ11_MARK), + PINMUX_GPIO(GPIO_FN_IRQ10, IRQ10_MARK), + PINMUX_GPIO(GPIO_FN_IRQ9, IRQ9_MARK), + PINMUX_GPIO(GPIO_FN_IRQ8, IRQ8_MARK), /* PTJ (mobule: SCIF234, SERMUX) */ - PINMUX_GPIO(GPIO_FN_RTS3, RTS3_MARK), - PINMUX_GPIO(GPIO_FN_CTS3, CTS3_MARK), - PINMUX_GPIO(GPIO_FN_TXD3, TXD3_MARK), PINMUX_GPIO(GPIO_FN_RXD3, RXD3_MARK), - PINMUX_GPIO(GPIO_FN_RTS4, RTS4_MARK), - PINMUX_GPIO(GPIO_FN_RXD4, RXD4_MARK), - PINMUX_GPIO(GPIO_FN_TXD4, TXD4_MARK), + PINMUX_GPIO(GPIO_FN_TXD3, TXD3_MARK), + PINMUX_GPIO(GPIO_FN_RXD2, RXD2_MARK), + PINMUX_GPIO(GPIO_FN_TXD2, TXD2_MARK), + PINMUX_GPIO(GPIO_FN_COM1_TXD, COM1_TXD_MARK), + PINMUX_GPIO(GPIO_FN_COM1_RXD, COM1_RXD_MARK), + PINMUX_GPIO(GPIO_FN_COM1_RTS, COM1_RTS_MARK), + PINMUX_GPIO(GPIO_FN_COM1_CTS, COM1_CTS_MARK), - /* PTK (mobule: SERMUX, LBSC, SCIF) */ + /* PTK (mobule: SERMUX) */ PINMUX_GPIO(GPIO_FN_COM2_TXD, COM2_TXD_MARK), PINMUX_GPIO(GPIO_FN_COM2_RXD, COM2_RXD_MARK), PINMUX_GPIO(GPIO_FN_COM2_RTS, COM2_RTS_MARK), @@ -1510,65 +1349,62 @@ static struct pinmux_gpio pinmux_gpios[] = { PINMUX_GPIO(GPIO_FN_COM2_DTR, COM2_DTR_MARK), PINMUX_GPIO(GPIO_FN_COM2_DSR, COM2_DSR_MARK), PINMUX_GPIO(GPIO_FN_COM2_DCD, COM2_DCD_MARK), - PINMUX_GPIO(GPIO_FN_CLKOUT, CLKOUT_MARK), - PINMUX_GPIO(GPIO_FN_SCK2, SCK2_MARK), - PINMUX_GPIO(GPIO_FN_SCK4, SCK4_MARK), - PINMUX_GPIO(GPIO_FN_SCK3, SCK3_MARK), + PINMUX_GPIO(GPIO_FN_COM2_RI, COM2_RI_MARK), - /* PTL (mobule: SERMUX, SCIF, LBSC, AUD) */ + /* PTL (mobule: SERMUX) */ + PINMUX_GPIO(GPIO_FN_RAC_TXD, RAC_TXD_MARK), PINMUX_GPIO(GPIO_FN_RAC_RXD, RAC_RXD_MARK), PINMUX_GPIO(GPIO_FN_RAC_RTS, RAC_RTS_MARK), PINMUX_GPIO(GPIO_FN_RAC_CTS, RAC_CTS_MARK), PINMUX_GPIO(GPIO_FN_RAC_DTR, RAC_DTR_MARK), PINMUX_GPIO(GPIO_FN_RAC_DSR, RAC_DSR_MARK), PINMUX_GPIO(GPIO_FN_RAC_DCD, RAC_DCD_MARK), - PINMUX_GPIO(GPIO_FN_RAC_TXD, RAC_TXD_MARK), - PINMUX_GPIO(GPIO_FN_RXD2, RXD2_MARK), - PINMUX_GPIO(GPIO_FN_CS5, CS5_MARK), - PINMUX_GPIO(GPIO_FN_CS6, CS6_MARK), - PINMUX_GPIO(GPIO_FN_AUDSYNC, AUDSYNC_MARK), - PINMUX_GPIO(GPIO_FN_AUDCK, AUDCK_MARK), - PINMUX_GPIO(GPIO_FN_TXD2, TXD2_MARK), + PINMUX_GPIO(GPIO_FN_RAC_RI, RAC_RI_MARK), - /* PTM (mobule: LBSC, IIC) */ - PINMUX_GPIO(GPIO_FN_CS4, CS4_MARK), - PINMUX_GPIO(GPIO_FN_RD, RD_MARK), - PINMUX_GPIO(GPIO_FN_WE0, WE0_MARK), - PINMUX_GPIO(GPIO_FN_CS0, CS0_MARK), + /* PTM (mobule: IIC, LPC) */ PINMUX_GPIO(GPIO_FN_SDA6, SDA6_MARK), PINMUX_GPIO(GPIO_FN_SCL6, SCL6_MARK), PINMUX_GPIO(GPIO_FN_SDA7, SDA7_MARK), PINMUX_GPIO(GPIO_FN_SCL7, SCL7_MARK), + PINMUX_GPIO(GPIO_FN_WP, WP_MARK), + PINMUX_GPIO(GPIO_FN_FMS0, FMS0_MARK), + PINMUX_GPIO(GPIO_FN_FMS1, FMS1_MARK), - /* PTN (mobule: USB, JMC, SGPIO, WDT) */ - PINMUX_GPIO(GPIO_FN_VBUS_EN, VBUS_EN_MARK), - PINMUX_GPIO(GPIO_FN_VBUS_OC, VBUS_OC_MARK), - PINMUX_GPIO(GPIO_FN_JMCTCK, JMCTCK_MARK), - PINMUX_GPIO(GPIO_FN_JMCTMS, JMCTMS_MARK), - PINMUX_GPIO(GPIO_FN_JMCTDO, JMCTDO_MARK), - PINMUX_GPIO(GPIO_FN_JMCTDI, JMCTDI_MARK), - PINMUX_GPIO(GPIO_FN_JMCTRST, JMCTRST_MARK), - PINMUX_GPIO(GPIO_FN_SGPIO1_CLK, SGPIO1_CLK_MARK), - PINMUX_GPIO(GPIO_FN_SGPIO1_LOAD, SGPIO1_LOAD_MARK), - PINMUX_GPIO(GPIO_FN_SGPIO1_DI, SGPIO1_DI_MARK), - PINMUX_GPIO(GPIO_FN_SGPIO1_DO, SGPIO1_DO_MARK), - PINMUX_GPIO(GPIO_FN_SUB_CLKIN, SUB_CLKIN_MARK), + /* PTN (mobule: SCIF234, EVC) */ + PINMUX_GPIO(GPIO_FN_SCK2, SCK2_MARK), + PINMUX_GPIO(GPIO_FN_RTS4, RTS4_MARK), + PINMUX_GPIO(GPIO_FN_RTS3, RTS3_MARK), + PINMUX_GPIO(GPIO_FN_RTS2, RTS2_MARK), + PINMUX_GPIO(GPIO_FN_CTS4, CTS4_MARK), + PINMUX_GPIO(GPIO_FN_CTS3, CTS3_MARK), + PINMUX_GPIO(GPIO_FN_CTS2, CTS2_MARK), + PINMUX_GPIO(GPIO_FN_EVENT7, EVENT7_MARK), + PINMUX_GPIO(GPIO_FN_EVENT6, EVENT6_MARK), + PINMUX_GPIO(GPIO_FN_EVENT5, EVENT5_MARK), + PINMUX_GPIO(GPIO_FN_EVENT4, EVENT4_MARK), + PINMUX_GPIO(GPIO_FN_EVENT3, EVENT3_MARK), + PINMUX_GPIO(GPIO_FN_EVENT2, EVENT2_MARK), + PINMUX_GPIO(GPIO_FN_EVENT1, EVENT1_MARK), + PINMUX_GPIO(GPIO_FN_EVENT0, EVENT0_MARK), - /* PTO (mobule: SGPIO, SerMux) */ + /* PTO (mobule: SGPIO) */ PINMUX_GPIO(GPIO_FN_SGPIO0_CLK, SGPIO0_CLK_MARK), PINMUX_GPIO(GPIO_FN_SGPIO0_LOAD, SGPIO0_LOAD_MARK), PINMUX_GPIO(GPIO_FN_SGPIO0_DI, SGPIO0_DI_MARK), PINMUX_GPIO(GPIO_FN_SGPIO0_DO, SGPIO0_DO_MARK), - PINMUX_GPIO(GPIO_FN_SGPIO2_CLK, SGPIO2_CLK_MARK), - PINMUX_GPIO(GPIO_FN_SGPIO2_LOAD, SGPIO2_LOAD_MARK), - PINMUX_GPIO(GPIO_FN_SGPIO2_DI, SGPIO2_DI_MARK), - PINMUX_GPIO(GPIO_FN_SGPIO2_DO, SGPIO2_DO_MARK), - PINMUX_GPIO(GPIO_FN_COM1_TXD, COM1_TXD_MARK), - PINMUX_GPIO(GPIO_FN_COM1_RXD, COM1_RXD_MARK), - PINMUX_GPIO(GPIO_FN_COM1_RTS, COM1_RTS_MARK), - PINMUX_GPIO(GPIO_FN_COM1_CTS, COM1_CTS_MARK), + PINMUX_GPIO(GPIO_FN_SGPIO1_CLK, SGPIO1_CLK_MARK), + PINMUX_GPIO(GPIO_FN_SGPIO1_LOAD, SGPIO1_LOAD_MARK), + PINMUX_GPIO(GPIO_FN_SGPIO1_DI, SGPIO1_DI_MARK), + PINMUX_GPIO(GPIO_FN_SGPIO1_DO, SGPIO1_DO_MARK), - /* PTP (mobule: EVC, ADC) */ + /* PTP (mobule: JMC, SCIF234) */ + PINMUX_GPIO(GPIO_FN_JMCTCK, JMCTCK_MARK), + PINMUX_GPIO(GPIO_FN_JMCTMS, JMCTMS_MARK), + PINMUX_GPIO(GPIO_FN_JMCTDO, JMCTDO_MARK), + PINMUX_GPIO(GPIO_FN_JMCTDI, JMCTDI_MARK), + PINMUX_GPIO(GPIO_FN_JMCRST, JMCRST_MARK), + PINMUX_GPIO(GPIO_FN_SCK4, SCK4_MARK), + PINMUX_GPIO(GPIO_FN_SCK3, SCK3_MARK), /* PTQ (mobule: LPC) */ PINMUX_GPIO(GPIO_FN_LAD3, LAD3_MARK), @@ -1603,41 +1439,31 @@ static struct pinmux_gpio pinmux_gpios[] = { PINMUX_GPIO(GPIO_FN_SDA3, SDA3_MARK), PINMUX_GPIO(GPIO_FN_SCL3, SCL3_MARK), - /* PTT (mobule: PWMX, AUD) */ - PINMUX_GPIO(GPIO_FN_PWMX7, PWMX7_MARK), - PINMUX_GPIO(GPIO_FN_PWMX6, PWMX6_MARK), - PINMUX_GPIO(GPIO_FN_PWMX5, PWMX5_MARK), - PINMUX_GPIO(GPIO_FN_PWMX4, PWMX4_MARK), - PINMUX_GPIO(GPIO_FN_PWMX3, PWMX3_MARK), - PINMUX_GPIO(GPIO_FN_PWMX2, PWMX2_MARK), - PINMUX_GPIO(GPIO_FN_PWMX1, PWMX1_MARK), - PINMUX_GPIO(GPIO_FN_PWMX0, PWMX0_MARK), + /* PTT (mobule: SYSTEM, PWMX) */ + PINMUX_GPIO(GPIO_FN_AUDSYNC, AUDSYNC_MARK), + PINMUX_GPIO(GPIO_FN_AUDCK, AUDCK_MARK), PINMUX_GPIO(GPIO_FN_AUDATA3, AUDATA3_MARK), PINMUX_GPIO(GPIO_FN_AUDATA2, AUDATA2_MARK), PINMUX_GPIO(GPIO_FN_AUDATA1, AUDATA1_MARK), PINMUX_GPIO(GPIO_FN_AUDATA0, AUDATA0_MARK), - PINMUX_GPIO(GPIO_FN_STATUS1, STATUS1_MARK), - PINMUX_GPIO(GPIO_FN_STATUS0, STATUS0_MARK), + PINMUX_GPIO(GPIO_FN_PWX7, PWX7_MARK), + PINMUX_GPIO(GPIO_FN_PWX6, PWX6_MARK), + PINMUX_GPIO(GPIO_FN_PWX5, PWX5_MARK), + PINMUX_GPIO(GPIO_FN_PWX4, PWX4_MARK), - /* PTU (mobule: LPC, APM) */ - PINMUX_GPIO(GPIO_FN_LGPIO7, LGPIO7_MARK), - PINMUX_GPIO(GPIO_FN_LGPIO6, LGPIO6_MARK), - PINMUX_GPIO(GPIO_FN_LGPIO5, LGPIO5_MARK), - PINMUX_GPIO(GPIO_FN_LGPIO4, LGPIO4_MARK), - PINMUX_GPIO(GPIO_FN_LGPIO3, LGPIO3_MARK), - PINMUX_GPIO(GPIO_FN_LGPIO2, LGPIO2_MARK), - PINMUX_GPIO(GPIO_FN_LGPIO1, LGPIO1_MARK), - PINMUX_GPIO(GPIO_FN_LGPIO0, LGPIO0_MARK), - PINMUX_GPIO(GPIO_FN_APMONCTL_O, APMONCTL_O_MARK), - PINMUX_GPIO(GPIO_FN_APMPWBTOUT_O, APMPWBTOUT_O_MARK), - PINMUX_GPIO(GPIO_FN_APMSCI_O, APMSCI_O_MARK), - PINMUX_GPIO(GPIO_FN_APMVDDON, APMVDDON_MARK), - PINMUX_GPIO(GPIO_FN_APMSLPBTN, APMSLPBTN_MARK), - PINMUX_GPIO(GPIO_FN_APMPWRBTN, APMPWRBTN_MARK), - PINMUX_GPIO(GPIO_FN_APMS5N, APMS5N_MARK), - PINMUX_GPIO(GPIO_FN_APMS3N, APMS3N_MARK), - - /* PTV (mobule: LBSC, SerMux, R-SPI, EVC, GRA) */ + /* PTU (mobule: LBSC, DMAC) */ + PINMUX_GPIO(GPIO_FN_CS6, CS6_MARK), + PINMUX_GPIO(GPIO_FN_CS5, CS5_MARK), + PINMUX_GPIO(GPIO_FN_CS4, CS4_MARK), + PINMUX_GPIO(GPIO_FN_CS0, CS0_MARK), + PINMUX_GPIO(GPIO_FN_RD, RD_MARK), + PINMUX_GPIO(GPIO_FN_WE0, WE0_MARK), + PINMUX_GPIO(GPIO_FN_A25, A25_MARK), + PINMUX_GPIO(GPIO_FN_A24, A24_MARK), + PINMUX_GPIO(GPIO_FN_DREQ0, DREQ0_MARK), + PINMUX_GPIO(GPIO_FN_DACK0, DACK0_MARK), + + /* PTV (mobule: LBSC, DMAC) */ PINMUX_GPIO(GPIO_FN_A23, A23_MARK), PINMUX_GPIO(GPIO_FN_A22, A22_MARK), PINMUX_GPIO(GPIO_FN_A21, A21_MARK), @@ -1646,20 +1472,12 @@ static struct pinmux_gpio pinmux_gpios[] = { PINMUX_GPIO(GPIO_FN_A18, A18_MARK), PINMUX_GPIO(GPIO_FN_A17, A17_MARK), PINMUX_GPIO(GPIO_FN_A16, A16_MARK), - PINMUX_GPIO(GPIO_FN_COM2_RI, COM2_RI_MARK), - PINMUX_GPIO(GPIO_FN_R_SPI_MOSI, R_SPI_MOSI_MARK), - PINMUX_GPIO(GPIO_FN_R_SPI_MISO, R_SPI_MISO_MARK), - PINMUX_GPIO(GPIO_FN_R_SPI_RSPCK, R_SPI_RSPCK_MARK), - PINMUX_GPIO(GPIO_FN_R_SPI_SSL0, R_SPI_SSL0_MARK), - PINMUX_GPIO(GPIO_FN_R_SPI_SSL1, R_SPI_SSL1_MARK), - PINMUX_GPIO(GPIO_FN_EVENT7, EVENT7_MARK), - PINMUX_GPIO(GPIO_FN_EVENT6, EVENT6_MARK), - PINMUX_GPIO(GPIO_FN_VBIOS_DI, VBIOS_DI_MARK), - PINMUX_GPIO(GPIO_FN_VBIOS_DO, VBIOS_DO_MARK), - PINMUX_GPIO(GPIO_FN_VBIOS_CLK, VBIOS_CLK_MARK), - PINMUX_GPIO(GPIO_FN_VBIOS_CS, VBIOS_CS_MARK), + PINMUX_GPIO(GPIO_FN_TEND0, TEND0_MARK), + PINMUX_GPIO(GPIO_FN_DREQ1, DREQ1_MARK), + PINMUX_GPIO(GPIO_FN_DACK1, DACK1_MARK), + PINMUX_GPIO(GPIO_FN_TEND1, TEND1_MARK), - /* PTW (mobule: LBSC, EVC, SCIF) */ + /* PTW (mobule: LBSC) */ PINMUX_GPIO(GPIO_FN_A16, A16_MARK), PINMUX_GPIO(GPIO_FN_A15, A15_MARK), PINMUX_GPIO(GPIO_FN_A14, A14_MARK), @@ -1669,14 +1487,6 @@ static struct pinmux_gpio pinmux_gpios[] = { PINMUX_GPIO(GPIO_FN_A10, A10_MARK), PINMUX_GPIO(GPIO_FN_A9, A9_MARK), PINMUX_GPIO(GPIO_FN_A8, A8_MARK), - PINMUX_GPIO(GPIO_FN_EVENT5, EVENT5_MARK), - PINMUX_GPIO(GPIO_FN_EVENT4, EVENT4_MARK), - PINMUX_GPIO(GPIO_FN_EVENT3, EVENT3_MARK), - PINMUX_GPIO(GPIO_FN_EVENT2, EVENT2_MARK), - PINMUX_GPIO(GPIO_FN_EVENT1, EVENT1_MARK), - PINMUX_GPIO(GPIO_FN_EVENT0, EVENT0_MARK), - PINMUX_GPIO(GPIO_FN_CTS4, CTS4_MARK), - PINMUX_GPIO(GPIO_FN_CTS2, CTS2_MARK), /* PTX (mobule: LBSC) */ PINMUX_GPIO(GPIO_FN_A7, A7_MARK), @@ -1687,10 +1497,6 @@ static struct pinmux_gpio pinmux_gpios[] = { PINMUX_GPIO(GPIO_FN_A2, A2_MARK), PINMUX_GPIO(GPIO_FN_A1, A1_MARK), PINMUX_GPIO(GPIO_FN_A0, A0_MARK), - PINMUX_GPIO(GPIO_FN_RTS2, RTS2_MARK), - PINMUX_GPIO(GPIO_FN_SIM_D, SIM_D_MARK), - PINMUX_GPIO(GPIO_FN_SIM_CLK, SIM_CLK_MARK), - PINMUX_GPIO(GPIO_FN_SIM_RST, SIM_RST_MARK), /* PTY (mobule: LBSC) */ PINMUX_GPIO(GPIO_FN_D7, D7_MARK), @@ -1701,36 +1507,18 @@ static struct pinmux_gpio pinmux_gpios[] = { PINMUX_GPIO(GPIO_FN_D2, D2_MARK), PINMUX_GPIO(GPIO_FN_D1, D1_MARK), PINMUX_GPIO(GPIO_FN_D0, D0_MARK), - - /* PTZ (mobule: eMMC, ONFI) */ - PINMUX_GPIO(GPIO_FN_MMCDAT7, MMCDAT7_MARK), - PINMUX_GPIO(GPIO_FN_MMCDAT6, MMCDAT6_MARK), - PINMUX_GPIO(GPIO_FN_MMCDAT5, MMCDAT5_MARK), - PINMUX_GPIO(GPIO_FN_MMCDAT4, MMCDAT4_MARK), - PINMUX_GPIO(GPIO_FN_MMCDAT3, MMCDAT3_MARK), - PINMUX_GPIO(GPIO_FN_MMCDAT2, MMCDAT2_MARK), - PINMUX_GPIO(GPIO_FN_MMCDAT1, MMCDAT1_MARK), - PINMUX_GPIO(GPIO_FN_MMCDAT0, MMCDAT0_MARK), - PINMUX_GPIO(GPIO_FN_ON_DQ7, ON_DQ7_MARK), - PINMUX_GPIO(GPIO_FN_ON_DQ6, ON_DQ6_MARK), - PINMUX_GPIO(GPIO_FN_ON_DQ5, ON_DQ5_MARK), - PINMUX_GPIO(GPIO_FN_ON_DQ4, ON_DQ4_MARK), - PINMUX_GPIO(GPIO_FN_ON_DQ3, ON_DQ3_MARK), - PINMUX_GPIO(GPIO_FN_ON_DQ2, ON_DQ2_MARK), - PINMUX_GPIO(GPIO_FN_ON_DQ1, ON_DQ1_MARK), - PINMUX_GPIO(GPIO_FN_ON_DQ0, ON_DQ0_MARK), }; static struct pinmux_cfg_reg pinmux_config_regs[] = { { PINMUX_CFG_REG("PACR", 0xffec0000, 16, 2) { - PTA7_FN, PTA7_OUT, PTA7_IN, PTA7_IN_PU, - PTA6_FN, PTA6_OUT, PTA6_IN, PTA6_IN_PU, - PTA5_FN, PTA5_OUT, PTA5_IN, PTA5_IN_PU, - PTA4_FN, PTA4_OUT, PTA4_IN, PTA4_IN_PU, - PTA3_FN, PTA3_OUT, PTA3_IN, PTA3_IN_PU, - PTA2_FN, PTA2_OUT, PTA2_IN, PTA2_IN_PU, - PTA1_FN, PTA1_OUT, PTA1_IN, PTA1_IN_PU, - PTA0_FN, PTA0_OUT, PTA0_IN, PTA0_IN_PU } + PTA7_FN, PTA7_OUT, PTA7_IN, 0, + PTA6_FN, PTA6_OUT, PTA6_IN, 0, + PTA5_FN, PTA5_OUT, PTA5_IN, 0, + PTA4_FN, PTA4_OUT, PTA4_IN, 0, + PTA3_FN, PTA3_OUT, PTA3_IN, 0, + PTA2_FN, PTA2_OUT, PTA2_IN, 0, + PTA1_FN, PTA1_OUT, PTA1_IN, 0, + PTA0_FN, PTA0_OUT, PTA0_IN, 0 } }, { PINMUX_CFG_REG("PBCR", 0xffec0002, 16, 2) { PTB7_FN, PTB7_OUT, PTB7_IN, 0, @@ -1753,126 +1541,125 @@ static struct pinmux_cfg_reg pinmux_config_regs[] = { PTC0_FN, PTC0_OUT, PTC0_IN, 0 } }, { PINMUX_CFG_REG("PDCR", 0xffec0006, 16, 2) { - PTD7_FN, PTD7_OUT, PTD7_IN, PTD7_IN_PU, - PTD6_FN, PTD6_OUT, PTD6_IN, PTD6_IN_PU, - PTD5_FN, PTD5_OUT, PTD5_IN, PTD5_IN_PU, - PTD4_FN, PTD4_OUT, PTD4_IN, PTD4_IN_PU, - PTD3_FN, PTD3_OUT, PTD3_IN, PTD3_IN_PU, - PTD2_FN, PTD2_OUT, PTD2_IN, PTD2_IN_PU, - PTD1_FN, PTD1_OUT, PTD1_IN, PTD1_IN_PU, - PTD0_FN, PTD0_OUT, PTD0_IN, PTD0_IN_PU } + PTD7_FN, PTD7_OUT, PTD7_IN, 0, + PTD6_FN, PTD6_OUT, PTD6_IN, 0, + PTD5_FN, PTD5_OUT, PTD5_IN, 0, + PTD4_FN, PTD4_OUT, PTD4_IN, 0, + PTD3_FN, PTD3_OUT, PTD3_IN, 0, + PTD2_FN, PTD2_OUT, PTD2_IN, 0, + PTD1_FN, PTD1_OUT, PTD1_IN, 0, + PTD0_FN, PTD0_OUT, PTD0_IN, 0 } }, { PINMUX_CFG_REG("PECR", 0xffec0008, 16, 2) { - PTE7_FN, PTE7_OUT, PTE7_IN, PTE7_IN_PU, - PTE6_FN, PTE6_OUT, PTE6_IN, PTE6_IN_PU, - PTE5_FN, PTE5_OUT, PTE5_IN, PTE5_IN_PU, - PTE4_FN, PTE4_OUT, PTE4_IN, PTE4_IN_PU, - PTE3_FN, PTE3_OUT, PTE3_IN, PTE3_IN_PU, - PTE2_FN, PTE2_OUT, PTE2_IN, PTE2_IN_PU, - PTE1_FN, PTE1_OUT, PTE1_IN, PTE1_IN_PU, - PTE0_FN, PTE0_OUT, PTE0_IN, PTE0_IN_PU } + PTE7_FN, PTE7_OUT, PTE7_IN, 0, + PTE6_FN, PTE6_OUT, PTE6_IN, 0, + PTE5_FN, PTE5_OUT, PTE5_IN, 0, + PTE4_FN, PTE4_OUT, PTE4_IN, 0, + PTE3_FN, PTE3_OUT, PTE3_IN, 0, + PTE2_FN, PTE2_OUT, PTE2_IN, 0, + PTE1_FN, PTE1_OUT, PTE1_IN, 0, + PTE0_FN, PTE0_OUT, PTE0_IN, 0 } }, { PINMUX_CFG_REG("PFCR", 0xffec000a, 16, 2) { - PTF7_FN, PTF7_OUT, PTF7_IN, PTF7_IN_PU, - PTF6_FN, PTF6_OUT, PTF6_IN, PTF6_IN_PU, - PTF5_FN, PTF5_OUT, PTF5_IN, PTF5_IN_PU, - PTF4_FN, PTF4_OUT, PTF4_IN, PTF4_IN_PU, - PTF3_FN, PTF3_OUT, PTF3_IN, PTF3_IN_PU, - PTF2_FN, PTF2_OUT, PTF2_IN, PTF2_IN_PU, - PTF1_FN, PTF1_OUT, PTF1_IN, PTF1_IN_PU, - PTF0_FN, PTF0_OUT, PTF0_IN, PTF0_IN_PU } + PTF7_FN, PTF7_OUT, PTF7_IN, 0, + PTF6_FN, PTF6_OUT, PTF6_IN, 0, + PTF5_FN, PTF5_OUT, PTF5_IN, 0, + PTF4_FN, PTF4_OUT, PTF4_IN, 0, + PTF3_FN, PTF3_OUT, PTF3_IN, 0, + PTF2_FN, PTF2_OUT, PTF2_IN, 0, + PTF1_FN, PTF1_OUT, PTF1_IN, 0, + PTF0_FN, PTF0_OUT, PTF0_IN, 0 } }, { PINMUX_CFG_REG("PGCR", 0xffec000c, 16, 2) { - PTG7_FN, PTG7_OUT, PTG7_IN, PTG7_IN_PU , - PTG6_FN, PTG6_OUT, PTG6_IN, PTG6_IN_PU , + PTG7_FN, PTG7_OUT, PTG7_IN, 0, + PTG6_FN, PTG6_OUT, PTG6_IN, 0, PTG5_FN, PTG5_OUT, PTG5_IN, 0, - PTG4_FN, PTG4_OUT, PTG4_IN, PTG4_IN_PU , + PTG4_FN, PTG4_OUT, PTG4_IN, 0, PTG3_FN, PTG3_OUT, PTG3_IN, 0, PTG2_FN, PTG2_OUT, PTG2_IN, 0, PTG1_FN, PTG1_OUT, PTG1_IN, 0, PTG0_FN, PTG0_OUT, PTG0_IN, 0 } }, { PINMUX_CFG_REG("PHCR", 0xffec000e, 16, 2) { - PTH7_FN, PTH7_OUT, PTH7_IN, PTH7_IN_PU, - PTH6_FN, PTH6_OUT, PTH6_IN, PTH6_IN_PU, - PTH5_FN, PTH5_OUT, PTH5_IN, PTH5_IN_PU, - PTH4_FN, PTH4_OUT, PTH4_IN, PTH4_IN_PU, - PTH3_FN, PTH3_OUT, PTH3_IN, PTH3_IN_PU, - PTH2_FN, PTH2_OUT, PTH2_IN, PTH2_IN_PU, - PTH1_FN, PTH1_OUT, PTH1_IN, PTH1_IN_PU, - PTH0_FN, PTH0_OUT, PTH0_IN, PTH0_IN_PU } + PTH7_FN, PTH7_OUT, PTH7_IN, 0, + PTH6_FN, PTH6_OUT, PTH6_IN, 0, + PTH5_FN, PTH5_OUT, PTH5_IN, 0, + PTH4_FN, PTH4_OUT, PTH4_IN, 0, + PTH3_FN, PTH3_OUT, PTH3_IN, 0, + PTH2_FN, PTH2_OUT, PTH2_IN, 0, + PTH1_FN, PTH1_OUT, PTH1_IN, 0, + PTH0_FN, PTH0_OUT, PTH0_IN, 0 } }, { PINMUX_CFG_REG("PICR", 0xffec0010, 16, 2) { - PTI7_FN, PTI7_OUT, PTI7_IN, PTI7_IN_PU, - PTI6_FN, PTI6_OUT, PTI6_IN, PTI6_IN_PU, + PTI7_FN, PTI7_OUT, PTI7_IN, 0, + PTI6_FN, PTI6_OUT, PTI6_IN, 0, PTI5_FN, PTI5_OUT, PTI5_IN, 0, - PTI4_FN, PTI4_OUT, PTI4_IN, PTI4_IN_PU, - PTI3_FN, PTI3_OUT, PTI3_IN, PTI3_IN_PU, - PTI2_FN, PTI2_OUT, PTI2_IN, PTI2_IN_PU, - PTI1_FN, PTI1_OUT, PTI1_IN, PTI1_IN_PU, - PTI0_FN, PTI0_OUT, PTI0_IN, PTI0_IN_PU } + PTI4_FN, PTI4_OUT, PTI4_IN, 0, + PTI3_FN, PTI3_OUT, PTI3_IN, 0, + PTI2_FN, PTI2_OUT, PTI2_IN, 0, + PTI1_FN, PTI1_OUT, PTI1_IN, 0, + PTI0_FN, PTI0_OUT, PTI0_IN, 0 } }, { PINMUX_CFG_REG("PJCR", 0xffec0012, 16, 2) { - 0, 0, 0, 0, /* reserved: always set 1 */ - PTJ6_FN, PTJ6_OUT, PTJ6_IN, PTJ6_IN_PU, - PTJ5_FN, PTJ5_OUT, PTJ5_IN, PTJ5_IN_PU, - PTJ4_FN, PTJ4_OUT, PTJ4_IN, PTJ4_IN_PU, - PTJ3_FN, PTJ3_OUT, PTJ3_IN, PTJ3_IN_PU, - PTJ2_FN, PTJ2_OUT, PTJ2_IN, PTJ2_IN_PU, - PTJ1_FN, PTJ1_OUT, PTJ1_IN, PTJ1_IN_PU, - PTJ0_FN, PTJ0_OUT, PTJ0_IN, PTJ0_IN_PU } + PTJ7_FN, PTJ7_OUT, PTJ7_IN, 0, + PTJ6_FN, PTJ6_OUT, PTJ6_IN, 0, + PTJ5_FN, PTJ5_OUT, PTJ5_IN, 0, + PTJ4_FN, PTJ4_OUT, PTJ4_IN, 0, + PTJ3_FN, PTJ3_OUT, PTJ3_IN, 0, + PTJ2_FN, PTJ2_OUT, PTJ2_IN, 0, + PTJ1_FN, PTJ1_OUT, PTJ1_IN, 0, + PTJ0_FN, PTJ0_OUT, PTJ0_IN, 0 } }, { PINMUX_CFG_REG("PKCR", 0xffec0014, 16, 2) { - PTK7_FN, PTK7_OUT, PTK7_IN, PTK7_IN_PU, - PTK6_FN, PTK6_OUT, PTK6_IN, PTK6_IN_PU, - PTK5_FN, PTK5_OUT, PTK5_IN, PTK5_IN_PU, - PTK4_FN, PTK4_OUT, PTK4_IN, PTK4_IN_PU, - PTK3_FN, PTK3_OUT, PTK3_IN, PTK3_IN_PU, - PTK2_FN, PTK2_OUT, PTK2_IN, PTK2_IN_PU, - PTK1_FN, PTK1_OUT, PTK1_IN, PTK1_IN_PU, - PTK0_FN, PTK0_OUT, PTK0_IN, PTK0_IN_PU } + PTK7_FN, PTK7_OUT, PTK7_IN, 0, + PTK6_FN, PTK6_OUT, PTK6_IN, 0, + PTK5_FN, PTK5_OUT, PTK5_IN, 0, + PTK4_FN, PTK4_OUT, PTK4_IN, 0, + PTK3_FN, PTK3_OUT, PTK3_IN, 0, + PTK2_FN, PTK2_OUT, PTK2_IN, 0, + PTK1_FN, PTK1_OUT, PTK1_IN, 0, + PTK0_FN, PTK0_OUT, PTK0_IN, 0 } }, { PINMUX_CFG_REG("PLCR", 0xffec0016, 16, 2) { - 0, 0, 0, 0, /* reserved: always set 1 */ - PTL6_FN, PTL6_OUT, PTL6_IN, PTL6_IN_PU, - PTL5_FN, PTL5_OUT, PTL5_IN, PTL5_IN_PU, - PTL4_FN, PTL4_OUT, PTL4_IN, PTL4_IN_PU, - PTL3_FN, PTL3_OUT, PTL3_IN, PTL3_IN_PU, - PTL2_FN, PTL2_OUT, PTL2_IN, PTL2_IN_PU, - PTL1_FN, PTL1_OUT, PTL1_IN, PTL1_IN_PU, - PTL0_FN, PTL0_OUT, PTL0_IN, PTL0_IN_PU } + PTL7_FN, PTL7_OUT, PTL7_IN, 0, + PTL6_FN, PTL6_OUT, PTL6_IN, 0, + PTL5_FN, PTL5_OUT, PTL5_IN, 0, + PTL4_FN, PTL4_OUT, PTL4_IN, 0, + PTL3_FN, PTL3_OUT, PTL3_IN, 0, + PTL2_FN, PTL2_OUT, PTL2_IN, 0, + PTL1_FN, PTL1_OUT, PTL1_IN, 0, + PTL0_FN, PTL0_OUT, PTL0_IN, 0 } }, { PINMUX_CFG_REG("PMCR", 0xffec0018, 16, 2) { - PTM7_FN, PTM7_OUT, PTM7_IN, PTM7_IN_PU, - PTM6_FN, PTM6_OUT, PTM6_IN, PTM6_IN_PU, - PTM5_FN, PTM5_OUT, PTM5_IN, PTM5_IN_PU, - PTM4_FN, PTM4_OUT, PTM4_IN, PTM4_IN_PU, + 0, 0, 0, 0, /* reserved: always set 1 */ + PTM6_FN, PTM6_OUT, PTM6_IN, 0, + PTM5_FN, PTM5_OUT, PTM5_IN, 0, + PTM4_FN, PTM4_OUT, PTM4_IN, 0, PTM3_FN, PTM3_OUT, PTM3_IN, 0, PTM2_FN, PTM2_OUT, PTM2_IN, 0, PTM1_FN, PTM1_OUT, PTM1_IN, 0, PTM0_FN, PTM0_OUT, PTM0_IN, 0 } }, { PINMUX_CFG_REG("PNCR", 0xffec001a, 16, 2) { - 0, 0, 0, 0, /* reserved: always set 1 */ + PTN7_FN, PTN7_OUT, PTN7_IN, 0, PTN6_FN, PTN6_OUT, PTN6_IN, 0, PTN5_FN, PTN5_OUT, PTN5_IN, 0, - PTN4_FN, PTN4_OUT, PTN4_IN, PTN4_IN_PU, - PTN3_FN, PTN3_OUT, PTN3_IN, PTN3_IN_PU, - PTN2_FN, PTN2_OUT, PTN2_IN, PTN2_IN_PU, - PTN1_FN, PTN1_OUT, PTN1_IN, PTN1_IN_PU, - PTN0_FN, PTN0_OUT, PTN0_IN, PTN0_IN_PU } + PTN4_FN, PTN4_OUT, PTN4_IN, 0, + PTN3_FN, PTN3_OUT, PTN3_IN, 0, + PTN2_FN, PTN2_OUT, PTN2_IN, 0, + PTN1_FN, PTN1_OUT, PTN1_IN, 0, + PTN0_FN, PTN0_OUT, PTN0_IN, 0 } }, { PINMUX_CFG_REG("POCR", 0xffec001c, 16, 2) { - PTO7_FN, PTO7_OUT, PTO7_IN, PTO7_IN_PU, - PTO6_FN, PTO6_OUT, PTO6_IN, PTO6_IN_PU, - PTO5_FN, PTO5_OUT, PTO5_IN, PTO5_IN_PU, - PTO4_FN, PTO4_OUT, PTO4_IN, PTO4_IN_PU, - PTO3_FN, PTO3_OUT, PTO3_IN, PTO3_IN_PU, - PTO2_FN, PTO2_OUT, PTO2_IN, PTO2_IN_PU, - PTO1_FN, PTO1_OUT, PTO1_IN, PTO1_IN_PU, - PTO0_FN, PTO0_OUT, PTO0_IN, PTO0_IN_PU } + PTO7_FN, PTO7_OUT, PTO7_IN, 0, + PTO6_FN, PTO6_OUT, PTO6_IN, 0, + PTO5_FN, PTO5_OUT, PTO5_IN, 0, + PTO4_FN, PTO4_OUT, PTO4_IN, 0, + PTO3_FN, PTO3_OUT, PTO3_IN, 0, + PTO2_FN, PTO2_OUT, PTO2_IN, 0, + PTO1_FN, PTO1_OUT, PTO1_IN, 0, + PTO0_FN, PTO0_OUT, PTO0_IN, 0 } }, -#if 0 /* FIXME: Remove it? */ { PINMUX_CFG_REG("PPCR", 0xffec001e, 16, 2) { 0, 0, 0, 0, /* reserved: always set 1 */ PTP6_FN, PTP6_OUT, PTP6_IN, 0, @@ -1883,7 +1670,6 @@ static struct pinmux_cfg_reg pinmux_config_regs[] = { PTP1_FN, PTP1_OUT, PTP1_IN, 0, PTP0_FN, PTP0_OUT, PTP0_IN, 0 } }, -#endif { PINMUX_CFG_REG("PQCR", 0xffec0020, 16, 2) { 0, 0, 0, 0, /* reserved: always set 1 */ PTQ6_FN, PTQ6_OUT, PTQ6_IN, 0, @@ -1915,14 +1701,14 @@ static struct pinmux_cfg_reg pinmux_config_regs[] = { PTS0_FN, PTS0_OUT, PTS0_IN, 0 } }, { PINMUX_CFG_REG("PTCR", 0xffec0026, 16, 2) { - PTT7_FN, PTT7_OUT, PTT7_IN, PTO7_IN_PU, - PTT6_FN, PTT6_OUT, PTT6_IN, PTO6_IN_PU, - PTT5_FN, PTT5_OUT, PTT5_IN, PTO5_IN_PU, - PTT4_FN, PTT4_OUT, PTT4_IN, PTO4_IN_PU, - PTT3_FN, PTT3_OUT, PTT3_IN, PTO3_IN_PU, - PTT2_FN, PTT2_OUT, PTT2_IN, PTO2_IN_PU, - PTT1_FN, PTT1_OUT, PTT1_IN, PTO1_IN_PU, - PTT0_FN, PTT0_OUT, PTT0_IN, PTO0_IN_PU } + 0, 0, 0, 0, /* reserved: always set 1 */ + 0, 0, 0, 0, /* reserved: always set 1 */ + PTT5_FN, PTT5_OUT, PTT5_IN, 0, + PTT4_FN, PTT4_OUT, PTT4_IN, 0, + PTT3_FN, PTT3_OUT, PTT3_IN, 0, + PTT2_FN, PTT2_OUT, PTT2_IN, 0, + PTT1_FN, PTT1_OUT, PTT1_IN, 0, + PTT0_FN, PTT0_OUT, PTT0_IN, 0 } }, { PINMUX_CFG_REG("PUCR", 0xffec0028, 16, 2) { PTU7_FN, PTU7_OUT, PTU7_IN, PTU7_IN_PU, @@ -1941,16 +1727,16 @@ static struct pinmux_cfg_reg pinmux_config_regs[] = { PTV4_FN, PTV4_OUT, PTV4_IN, PTV4_IN_PU, PTV3_FN, PTV3_OUT, PTV3_IN, PTV3_IN_PU, PTV2_FN, PTV2_OUT, PTV2_IN, PTV2_IN_PU, - PTV1_FN, PTV1_OUT, PTV1_IN, 0, - PTV0_FN, PTV0_OUT, PTV0_IN, 0 } + PTV1_FN, PTV1_OUT, PTV1_IN, PTV1_IN_PU, + PTV0_FN, PTV0_OUT, PTV0_IN, PTV0_IN_PU } }, { PINMUX_CFG_REG("PWCR", 0xffec002c, 16, 2) { - PTW7_FN, PTW7_OUT, PTW7_IN, 0, - PTW6_FN, PTW6_OUT, PTW6_IN, 0, - PTW5_FN, PTW5_OUT, PTW5_IN, 0, - PTW4_FN, PTW4_OUT, PTW4_IN, 0, - PTW3_FN, PTW3_OUT, PTW3_IN, 0, - PTW2_FN, PTW2_OUT, PTW2_IN, 0, + PTW7_FN, PTW7_OUT, PTW7_IN, PTW7_IN_PU, + PTW6_FN, PTW6_OUT, PTW6_IN, PTW6_IN_PU, + PTW5_FN, PTW5_OUT, PTW5_IN, PTW5_IN_PU, + PTW4_FN, PTW4_OUT, PTW4_IN, PTW4_IN_PU, + PTW3_FN, PTW3_OUT, PTW3_IN, PTW3_IN_PU, + PTW2_FN, PTW2_OUT, PTW2_IN, PTW2_IN_PU, PTW1_FN, PTW1_OUT, PTW1_IN, PTW1_IN_PU, PTW0_FN, PTW0_OUT, PTW0_IN, PTW0_IN_PU } }, @@ -1975,32 +1761,32 @@ static struct pinmux_cfg_reg pinmux_config_regs[] = { PTY0_FN, PTY0_OUT, PTY0_IN, PTY0_IN_PU } }, { PINMUX_CFG_REG("PZCR", 0xffec0032, 16, 2) { - PTZ7_FN, PTZ7_OUT, PTZ7_IN, 0, - PTZ6_FN, PTZ6_OUT, PTZ6_IN, 0, - PTZ5_FN, PTZ5_OUT, PTZ5_IN, 0, - PTZ4_FN, PTZ4_OUT, PTZ4_IN, 0, - PTZ3_FN, PTZ3_OUT, PTZ3_IN, 0, - PTZ2_FN, PTZ2_OUT, PTZ2_IN, 0, - PTZ1_FN, PTZ1_OUT, PTZ1_IN, 0, - PTZ0_FN, PTZ0_OUT, PTZ0_IN, 0 } + 0, PTZ7_OUT, PTZ7_IN, 0, + 0, PTZ6_OUT, PTZ6_IN, 0, + 0, PTZ5_OUT, PTZ5_IN, 0, + 0, PTZ4_OUT, PTZ4_IN, 0, + 0, PTZ3_OUT, PTZ3_IN, 0, + 0, PTZ2_OUT, PTZ2_IN, 0, + 0, PTZ1_OUT, PTZ1_IN, 0, + 0, PTZ0_OUT, PTZ0_IN, 0 } }, { PINMUX_CFG_REG("PSEL0", 0xffec0070, 16, 1) { - PS0_15_FN1, PS0_15_FN2, - PS0_14_FN1, PS0_14_FN2, - PS0_13_FN1, PS0_13_FN2, - PS0_12_FN1, PS0_12_FN2, - PS0_11_FN1, PS0_11_FN2, - PS0_10_FN1, PS0_10_FN2, - PS0_9_FN1, PS0_9_FN2, - PS0_8_FN1, PS0_8_FN2, - PS0_7_FN1, PS0_7_FN2, - PS0_6_FN1, PS0_6_FN2, - PS0_5_FN1, PS0_5_FN2, - PS0_4_FN1, PS0_4_FN2, - PS0_3_FN1, PS0_3_FN2, - PS0_2_FN1, PS0_2_FN2, + PS0_15_FN3, PS0_15_FN1, + PS0_14_FN3, PS0_14_FN1, + PS0_13_FN3, PS0_13_FN1, + PS0_12_FN3, PS0_12_FN1, + 0, 0, + 0, 0, 0, 0, + 0, 0, + PS0_7_FN2, PS0_7_FN1, + PS0_6_FN2, PS0_6_FN1, + PS0_5_FN2, PS0_5_FN1, + PS0_4_FN2, PS0_4_FN1, + PS0_3_FN2, PS0_3_FN1, + PS0_2_FN2, PS0_2_FN1, + PS0_1_FN2, PS0_1_FN1, 0, 0, } }, { PINMUX_CFG_REG("PSEL1", 0xffec0072, 16, 1) { @@ -2009,136 +1795,73 @@ static struct pinmux_cfg_reg pinmux_config_regs[] = { 0, 0, 0, 0, 0, 0, - PS1_10_FN1, PS1_10_FN2, - PS1_9_FN1, PS1_9_FN2, - PS1_8_FN1, PS1_8_FN2, 0, 0, 0, 0, 0, 0, + PS1_7_FN1, PS1_7_FN3, + PS1_6_FN1, PS1_6_FN3, + 0, 0, + 0, 0, 0, 0, 0, 0, - PS1_2_FN1, PS1_2_FN2, 0, 0, 0, 0, } }, { PINMUX_CFG_REG("PSEL2", 0xffec0074, 16, 1) { 0, 0, 0, 0, - PS2_13_FN1, PS2_13_FN2, - PS2_12_FN1, PS2_12_FN2, + PS2_13_FN3, PS2_13_FN1, + PS2_12_FN3, PS2_12_FN1, 0, 0, 0, 0, 0, 0, 0, 0, - PS2_7_FN1, PS2_7_FN2, - PS2_6_FN1, PS2_6_FN2, - PS2_5_FN1, PS2_5_FN2, - PS2_4_FN1, PS2_4_FN2, 0, 0, - PS2_2_FN1, PS2_2_FN2, 0, 0, - 0, 0, } - }, - { PINMUX_CFG_REG("PSEL3", 0xffec0076, 16, 1) { - PS3_15_FN1, PS3_15_FN2, - PS3_14_FN1, PS3_14_FN2, - PS3_13_FN1, PS3_13_FN2, - PS3_12_FN1, PS3_12_FN2, - PS3_11_FN1, PS3_11_FN2, - PS3_10_FN1, PS3_10_FN2, - PS3_9_FN1, PS3_9_FN2, - PS3_8_FN1, PS3_8_FN2, - PS3_7_FN1, PS3_7_FN2, 0, 0, 0, 0, 0, 0, 0, 0, - PS3_2_FN1, PS3_2_FN2, - PS3_1_FN1, PS3_1_FN2, - 0, 0, } + PS2_1_FN1, PS2_1_FN2, + PS2_0_FN1, PS2_0_FN2, } }, - { PINMUX_CFG_REG("PSEL4", 0xffec0078, 16, 1) { + PS4_15_FN2, PS4_15_FN1, + PS4_14_FN2, PS4_14_FN1, + PS4_13_FN2, PS4_13_FN1, + PS4_12_FN2, PS4_12_FN1, + PS4_11_FN2, PS4_11_FN1, + PS4_10_FN2, PS4_10_FN1, + PS4_9_FN2, PS4_9_FN1, 0, 0, - PS4_14_FN1, PS4_14_FN2, - PS4_13_FN1, PS4_13_FN2, - PS4_12_FN1, PS4_12_FN2, 0, 0, - PS4_10_FN1, PS4_10_FN2, - PS4_9_FN1, PS4_9_FN2, - PS4_8_FN1, PS4_8_FN2, 0, 0, 0, 0, 0, 0, - PS4_4_FN1, PS4_4_FN2, - PS4_3_FN1, PS4_3_FN2, - PS4_2_FN1, PS4_2_FN2, - PS4_1_FN1, PS4_1_FN2, - PS4_0_FN1, PS4_0_FN2, } + PS4_3_FN2, PS4_3_FN1, + PS4_2_FN2, PS4_2_FN1, + PS4_1_FN2, PS4_1_FN1, + PS4_0_FN2, PS4_0_FN1, } }, { PINMUX_CFG_REG("PSEL5", 0xffec007a, 16, 1) { 0, 0, 0, 0, 0, 0, 0, 0, - PS5_11_FN1, PS5_11_FN2, - PS5_10_FN1, PS5_10_FN2, + 0, 0, + 0, 0, PS5_9_FN1, PS5_9_FN2, PS5_8_FN1, PS5_8_FN2, PS5_7_FN1, PS5_7_FN2, PS5_6_FN1, PS5_6_FN2, PS5_5_FN1, PS5_5_FN2, - PS5_4_FN1, PS5_4_FN2, - PS5_3_FN1, PS5_3_FN2, - PS5_2_FN1, PS5_2_FN2, - 0, 0, - 0, 0, } - }, - { PINMUX_CFG_REG("PSEL6", 0xffec007c, 16, 1) { - PS6_15_FN1, PS6_15_FN2, - PS6_14_FN1, PS6_14_FN2, - PS6_13_FN1, PS6_13_FN2, - PS6_12_FN1, PS6_12_FN2, - PS6_11_FN1, PS6_11_FN2, - PS6_10_FN1, PS6_10_FN2, - PS6_9_FN1, PS6_9_FN2, - PS6_8_FN1, PS6_8_FN2, - PS6_7_FN1, PS6_7_FN2, - PS6_6_FN1, PS6_6_FN2, - PS6_5_FN1, PS6_5_FN2, - PS6_4_FN1, PS6_4_FN2, - PS6_3_FN1, PS6_3_FN2, - PS6_2_FN1, PS6_2_FN2, - PS6_1_FN1, PS6_1_FN2, - PS6_0_FN1, PS6_0_FN2, } - }, - { PINMUX_CFG_REG("PSEL7", 0xffec0082, 16, 1) { - PS7_15_FN1, PS7_15_FN2, - PS7_14_FN1, PS7_14_FN2, - PS7_13_FN1, PS7_13_FN2, - PS7_12_FN1, PS7_12_FN2, - PS7_11_FN1, PS7_11_FN2, - PS7_10_FN1, PS7_10_FN2, - PS7_9_FN1, PS7_9_FN2, - PS7_8_FN1, PS7_8_FN2, - PS7_7_FN1, PS7_7_FN2, - PS7_6_FN1, PS7_6_FN2, - PS7_5_FN1, PS7_5_FN2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } }, - { PINMUX_CFG_REG("PSEL8", 0xffec0084, 16, 1) { - PS8_15_FN1, PS8_15_FN2, - PS8_14_FN1, PS8_14_FN2, - PS8_13_FN1, PS8_13_FN2, - PS8_12_FN1, PS8_12_FN2, - PS8_11_FN1, PS8_11_FN2, - PS8_10_FN1, PS8_10_FN2, - PS8_9_FN1, PS8_9_FN2, - PS8_8_FN1, PS8_8_FN2, + { PINMUX_CFG_REG("PSEL6", 0xffec007c, 16, 1) { 0, 0, 0, 0, 0, 0, @@ -2146,7 +1869,15 @@ static struct pinmux_cfg_reg pinmux_config_regs[] = { 0, 0, 0, 0, 0, 0, - 0, 0, } + 0, 0, + PS6_7_FN_AN, PS6_7_FN_EV, + PS6_6_FN_AN, PS6_6_FN_EV, + PS6_5_FN_AN, PS6_5_FN_EV, + PS6_4_FN_AN, PS6_4_FN_EV, + PS6_3_FN_AN, PS6_3_FN_EV, + PS6_2_FN_AN, PS6_2_FN_EV, + PS6_1_FN_AN, PS6_1_FN_EV, + PS6_0_FN_AN, PS6_0_FN_EV, } }, {} }; @@ -2189,7 +1920,7 @@ static struct pinmux_data_reg pinmux_data_regs[] = { PTI3_DATA, PTI2_DATA, PTI1_DATA, PTI0_DATA } }, { PINMUX_DATA_REG("PJDR", 0xffec0046, 8) { - 0, PTJ6_DATA, PTJ5_DATA, PTJ4_DATA, + PTJ7_DATA, PTJ6_DATA, PTJ5_DATA, PTJ4_DATA, PTJ3_DATA, PTJ2_DATA, PTJ1_DATA, PTJ0_DATA } }, { PINMUX_DATA_REG("PKDR", 0xffec0048, 8) { @@ -2197,15 +1928,15 @@ static struct pinmux_data_reg pinmux_data_regs[] = { PTK3_DATA, PTK2_DATA, PTK1_DATA, PTK0_DATA } }, { PINMUX_DATA_REG("PLDR", 0xffec004a, 8) { - 0, PTL6_DATA, PTL5_DATA, PTL4_DATA, + PTL7_DATA, PTL6_DATA, PTL5_DATA, PTL4_DATA, PTL3_DATA, PTL2_DATA, PTL1_DATA, PTL0_DATA } }, { PINMUX_DATA_REG("PMDR", 0xffec004c, 8) { - PTM7_DATA, PTM6_DATA, PTM5_DATA, PTM4_DATA, + 0, PTM6_DATA, PTM5_DATA, PTM4_DATA, PTM3_DATA, PTM2_DATA, PTM1_DATA, PTM0_DATA } }, { PINMUX_DATA_REG("PNDR", 0xffec004e, 8) { - 0, PTN6_DATA, PTN5_DATA, PTN4_DATA, + PTN7_DATA, PTN6_DATA, PTN5_DATA, PTN4_DATA, PTN3_DATA, PTN2_DATA, PTN1_DATA, PTN0_DATA } }, { PINMUX_DATA_REG("PODR", 0xffec0050, 8) { @@ -2213,7 +1944,7 @@ static struct pinmux_data_reg pinmux_data_regs[] = { PTO3_DATA, PTO2_DATA, PTO1_DATA, PTO0_DATA } }, { PINMUX_DATA_REG("PPDR", 0xffec0052, 8) { - PTP7_DATA, PTP6_DATA, PTP5_DATA, PTP4_DATA, + 0, PTP6_DATA, PTP5_DATA, PTP4_DATA, PTP3_DATA, PTP2_DATA, PTP1_DATA, PTP0_DATA } }, { PINMUX_DATA_REG("PQDR", 0xffec0054, 8) { @@ -2229,7 +1960,7 @@ static struct pinmux_data_reg pinmux_data_regs[] = { PTS3_DATA, PTS2_DATA, PTS1_DATA, PTS0_DATA } }, { PINMUX_DATA_REG("PTDR", 0xffec005a, 8) { - PTT7_DATA, PTT6_DATA, PTT5_DATA, PTT4_DATA, + 0, 0, PTT5_DATA, PTT4_DATA, PTT3_DATA, PTT2_DATA, PTT1_DATA, PTT0_DATA } }, { PINMUX_DATA_REG("PUDR", 0xffec005c, 8) { @@ -2269,8 +2000,8 @@ static struct pinmux_info sh7757_pinmux_info = { .mark = { PINMUX_MARK_BEGIN, PINMUX_MARK_END }, .function = { PINMUX_FUNCTION_BEGIN, PINMUX_FUNCTION_END }, - .first_gpio = GPIO_PTA0, - .last_gpio = GPIO_FN_ON_DQ0, + .first_gpio = GPIO_PTA7, + .last_gpio = GPIO_FN_D0, .gpios = pinmux_gpios, .cfg_regs = pinmux_config_regs, @@ -2284,4 +2015,5 @@ static int __init plat_pinmux_setup(void) { return register_pinmux(&sh7757_pinmux_info); } + arch_initcall(plat_pinmux_setup); diff --git a/trunk/arch/sh/kernel/cpu/sh4a/pinmux-shx3.c b/trunk/arch/sh/kernel/cpu/sh4a/pinmux-shx3.c deleted file mode 100644 index aaa5338abbff..000000000000 --- a/trunk/arch/sh/kernel/cpu/sh4a/pinmux-shx3.c +++ /dev/null @@ -1,587 +0,0 @@ -/* - * SH-X3 prototype CPU pinmux - * - * Copyright (C) 2010 Paul Mundt - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - */ -#include -#include -#include -#include - -enum { - PINMUX_RESERVED = 0, - - PINMUX_DATA_BEGIN, - PA7_DATA, PA6_DATA, PA5_DATA, PA4_DATA, - PA3_DATA, PA2_DATA, PA1_DATA, PA0_DATA, - PB7_DATA, PB6_DATA, PB5_DATA, PB4_DATA, - PB3_DATA, PB2_DATA, PB1_DATA, PB0_DATA, - PC7_DATA, PC6_DATA, PC5_DATA, PC4_DATA, - PC3_DATA, PC2_DATA, PC1_DATA, PC0_DATA, - PD7_DATA, PD6_DATA, PD5_DATA, PD4_DATA, - PD3_DATA, PD2_DATA, PD1_DATA, PD0_DATA, - PE7_DATA, PE6_DATA, PE5_DATA, PE4_DATA, - PE3_DATA, PE2_DATA, PE1_DATA, PE0_DATA, - PF7_DATA, PF6_DATA, PF5_DATA, PF4_DATA, - PF3_DATA, PF2_DATA, PF1_DATA, PF0_DATA, - PG7_DATA, PG6_DATA, PG5_DATA, PG4_DATA, - PG3_DATA, PG2_DATA, PG1_DATA, PG0_DATA, - - PH5_DATA, PH4_DATA, - PH3_DATA, PH2_DATA, PH1_DATA, PH0_DATA, - PINMUX_DATA_END, - - PINMUX_INPUT_BEGIN, - PA7_IN, PA6_IN, PA5_IN, PA4_IN, - PA3_IN, PA2_IN, PA1_IN, PA0_IN, - PB7_IN, PB6_IN, PB5_IN, PB4_IN, - PB3_IN, PB2_IN, PB1_IN, PB0_IN, - PC7_IN, PC6_IN, PC5_IN, PC4_IN, - PC3_IN, PC2_IN, PC1_IN, PC0_IN, - PD7_IN, PD6_IN, PD5_IN, PD4_IN, - PD3_IN, PD2_IN, PD1_IN, PD0_IN, - PE7_IN, PE6_IN, PE5_IN, PE4_IN, - PE3_IN, PE2_IN, PE1_IN, PE0_IN, - PF7_IN, PF6_IN, PF5_IN, PF4_IN, - PF3_IN, PF2_IN, PF1_IN, PF0_IN, - PG7_IN, PG6_IN, PG5_IN, PG4_IN, - PG3_IN, PG2_IN, PG1_IN, PG0_IN, - - PH5_IN, PH4_IN, - PH3_IN, PH2_IN, PH1_IN, PH0_IN, - PINMUX_INPUT_END, - - PINMUX_INPUT_PULLUP_BEGIN, - PA7_IN_PU, PA6_IN_PU, PA5_IN_PU, PA4_IN_PU, - PA3_IN_PU, PA2_IN_PU, PA1_IN_PU, PA0_IN_PU, - PB7_IN_PU, PB6_IN_PU, PB5_IN_PU, PB4_IN_PU, - PB3_IN_PU, PB2_IN_PU, PB1_IN_PU, PB0_IN_PU, - PC7_IN_PU, PC6_IN_PU, PC5_IN_PU, PC4_IN_PU, - PC3_IN_PU, PC2_IN_PU, PC1_IN_PU, PC0_IN_PU, - PD7_IN_PU, PD6_IN_PU, PD5_IN_PU, PD4_IN_PU, - PD3_IN_PU, PD2_IN_PU, PD1_IN_PU, PD0_IN_PU, - PE7_IN_PU, PE6_IN_PU, PE5_IN_PU, PE4_IN_PU, - PE3_IN_PU, PE2_IN_PU, PE1_IN_PU, PE0_IN_PU, - PF7_IN_PU, PF6_IN_PU, PF5_IN_PU, PF4_IN_PU, - PF3_IN_PU, PF2_IN_PU, PF1_IN_PU, PF0_IN_PU, - PG7_IN_PU, PG6_IN_PU, PG5_IN_PU, PG4_IN_PU, - PG3_IN_PU, PG2_IN_PU, PG1_IN_PU, PG0_IN_PU, - - PH5_IN_PU, PH4_IN_PU, - PH3_IN_PU, PH2_IN_PU, PH1_IN_PU, PH0_IN_PU, - PINMUX_INPUT_PULLUP_END, - - PINMUX_OUTPUT_BEGIN, - PA7_OUT, PA6_OUT, PA5_OUT, PA4_OUT, - PA3_OUT, PA2_OUT, PA1_OUT, PA0_OUT, - PB7_OUT, PB6_OUT, PB5_OUT, PB4_OUT, - PB3_OUT, PB2_OUT, PB1_OUT, PB0_OUT, - PC7_OUT, PC6_OUT, PC5_OUT, PC4_OUT, - PC3_OUT, PC2_OUT, PC1_OUT, PC0_OUT, - PD7_OUT, PD6_OUT, PD5_OUT, PD4_OUT, - PD3_OUT, PD2_OUT, PD1_OUT, PD0_OUT, - PE7_OUT, PE6_OUT, PE5_OUT, PE4_OUT, - PE3_OUT, PE2_OUT, PE1_OUT, PE0_OUT, - PF7_OUT, PF6_OUT, PF5_OUT, PF4_OUT, - PF3_OUT, PF2_OUT, PF1_OUT, PF0_OUT, - PG7_OUT, PG6_OUT, PG5_OUT, PG4_OUT, - PG3_OUT, PG2_OUT, PG1_OUT, PG0_OUT, - - PH5_OUT, PH4_OUT, - PH3_OUT, PH2_OUT, PH1_OUT, PH0_OUT, - PINMUX_OUTPUT_END, - - PINMUX_FUNCTION_BEGIN, - PA7_FN, PA6_FN, PA5_FN, PA4_FN, - PA3_FN, PA2_FN, PA1_FN, PA0_FN, - PB7_FN, PB6_FN, PB5_FN, PB4_FN, - PB3_FN, PB2_FN, PB1_FN, PB0_FN, - PC7_FN, PC6_FN, PC5_FN, PC4_FN, - PC3_FN, PC2_FN, PC1_FN, PC0_FN, - PD7_FN, PD6_FN, PD5_FN, PD4_FN, - PD3_FN, PD2_FN, PD1_FN, PD0_FN, - PE7_FN, PE6_FN, PE5_FN, PE4_FN, - PE3_FN, PE2_FN, PE1_FN, PE0_FN, - PF7_FN, PF6_FN, PF5_FN, PF4_FN, - PF3_FN, PF2_FN, PF1_FN, PF0_FN, - PG7_FN, PG6_FN, PG5_FN, PG4_FN, - PG3_FN, PG2_FN, PG1_FN, PG0_FN, - - PH5_FN, PH4_FN, - PH3_FN, PH2_FN, PH1_FN, PH0_FN, - PINMUX_FUNCTION_END, - - PINMUX_MARK_BEGIN, - - D31_MARK, D30_MARK, D29_MARK, D28_MARK, D27_MARK, D26_MARK, - D25_MARK, D24_MARK, D23_MARK, D22_MARK, D21_MARK, D20_MARK, - D19_MARK, D18_MARK, D17_MARK, D16_MARK, - - BACK_MARK, BREQ_MARK, - WE3_MARK, WE2_MARK, - CS6_MARK, CS5_MARK, CS4_MARK, - CLKOUTENB_MARK, - - DACK3_MARK, DACK2_MARK, DACK1_MARK, DACK0_MARK, - DREQ3_MARK, DREQ2_MARK, DREQ1_MARK, DREQ0_MARK, - - IRQ3_MARK, IRQ2_MARK, IRQ1_MARK, IRQ0_MARK, - - DRAK3_MARK, DRAK2_MARK, DRAK1_MARK, DRAK0_MARK, - - SCK3_MARK, SCK2_MARK, SCK1_MARK, SCK0_MARK, - IRL3_MARK, IRL2_MARK, IRL1_MARK, IRL0_MARK, - TXD3_MARK, TXD2_MARK, TXD1_MARK, TXD0_MARK, - RXD3_MARK, RXD2_MARK, RXD1_MARK, RXD0_MARK, - - CE2B_MARK, CE2A_MARK, IOIS16_MARK, - STATUS1_MARK, STATUS0_MARK, - - IRQOUT_MARK, - - PINMUX_MARK_END, -}; - -static pinmux_enum_t shx3_pinmux_data[] = { - - /* PA GPIO */ - PINMUX_DATA(PA7_DATA, PA7_IN, PA7_OUT, PA7_IN_PU), - PINMUX_DATA(PA6_DATA, PA6_IN, PA6_OUT, PA6_IN_PU), - PINMUX_DATA(PA5_DATA, PA5_IN, PA5_OUT, PA5_IN_PU), - PINMUX_DATA(PA4_DATA, PA4_IN, PA4_OUT, PA4_IN_PU), - PINMUX_DATA(PA3_DATA, PA3_IN, PA3_OUT, PA3_IN_PU), - PINMUX_DATA(PA2_DATA, PA2_IN, PA2_OUT, PA2_IN_PU), - PINMUX_DATA(PA1_DATA, PA1_IN, PA1_OUT, PA1_IN_PU), - PINMUX_DATA(PA0_DATA, PA0_IN, PA0_OUT, PA0_IN_PU), - - /* PB GPIO */ - PINMUX_DATA(PB7_DATA, PB7_IN, PB7_OUT, PB7_IN_PU), - PINMUX_DATA(PB6_DATA, PB6_IN, PB6_OUT, PB6_IN_PU), - PINMUX_DATA(PB5_DATA, PB5_IN, PB5_OUT, PB5_IN_PU), - PINMUX_DATA(PB4_DATA, PB4_IN, PB4_OUT, PB4_IN_PU), - PINMUX_DATA(PB3_DATA, PB3_IN, PB3_OUT, PB3_IN_PU), - PINMUX_DATA(PB2_DATA, PB2_IN, PB2_OUT, PB2_IN_PU), - PINMUX_DATA(PB1_DATA, PB1_IN, PB1_OUT, PB1_IN_PU), - PINMUX_DATA(PB0_DATA, PB0_IN, PB0_OUT, PB0_IN_PU), - - /* PC GPIO */ - PINMUX_DATA(PC7_DATA, PC7_IN, PC7_OUT, PC7_IN_PU), - PINMUX_DATA(PC6_DATA, PC6_IN, PC6_OUT, PC6_IN_PU), - PINMUX_DATA(PC5_DATA, PC5_IN, PC5_OUT, PC5_IN_PU), - PINMUX_DATA(PC4_DATA, PC4_IN, PC4_OUT, PC4_IN_PU), - PINMUX_DATA(PC3_DATA, PC3_IN, PC3_OUT, PC3_IN_PU), - PINMUX_DATA(PC2_DATA, PC2_IN, PC2_OUT, PC2_IN_PU), - PINMUX_DATA(PC1_DATA, PC1_IN, PC1_OUT, PC1_IN_PU), - PINMUX_DATA(PC0_DATA, PC0_IN, PC0_OUT, PC0_IN_PU), - - /* PD GPIO */ - PINMUX_DATA(PD7_DATA, PD7_IN, PD7_OUT, PD7_IN_PU), - PINMUX_DATA(PD6_DATA, PD6_IN, PD6_OUT, PD6_IN_PU), - PINMUX_DATA(PD5_DATA, PD5_IN, PD5_OUT, PD5_IN_PU), - PINMUX_DATA(PD4_DATA, PD4_IN, PD4_OUT, PD4_IN_PU), - PINMUX_DATA(PD3_DATA, PD3_IN, PD3_OUT, PD3_IN_PU), - PINMUX_DATA(PD2_DATA, PD2_IN, PD2_OUT, PD2_IN_PU), - PINMUX_DATA(PD1_DATA, PD1_IN, PD1_OUT, PD1_IN_PU), - PINMUX_DATA(PD0_DATA, PD0_IN, PD0_OUT, PD0_IN_PU), - - /* PE GPIO */ - PINMUX_DATA(PE7_DATA, PE7_IN, PE7_OUT, PE7_IN_PU), - PINMUX_DATA(PE6_DATA, PE6_IN, PE6_OUT, PE6_IN_PU), - PINMUX_DATA(PE5_DATA, PE5_IN, PE5_OUT, PE5_IN_PU), - PINMUX_DATA(PE4_DATA, PE4_IN, PE4_OUT, PE4_IN_PU), - PINMUX_DATA(PE3_DATA, PE3_IN, PE3_OUT, PE3_IN_PU), - PINMUX_DATA(PE2_DATA, PE2_IN, PE2_OUT, PE2_IN_PU), - PINMUX_DATA(PE1_DATA, PE1_IN, PE1_OUT, PE1_IN_PU), - PINMUX_DATA(PE0_DATA, PE0_IN, PE0_OUT, PE0_IN_PU), - - /* PF GPIO */ - PINMUX_DATA(PF7_DATA, PF7_IN, PF7_OUT, PF7_IN_PU), - PINMUX_DATA(PF6_DATA, PF6_IN, PF6_OUT, PF6_IN_PU), - PINMUX_DATA(PF5_DATA, PF5_IN, PF5_OUT, PF5_IN_PU), - PINMUX_DATA(PF4_DATA, PF4_IN, PF4_OUT, PF4_IN_PU), - PINMUX_DATA(PF3_DATA, PF3_IN, PF3_OUT, PF3_IN_PU), - PINMUX_DATA(PF2_DATA, PF2_IN, PF2_OUT, PF2_IN_PU), - PINMUX_DATA(PF1_DATA, PF1_IN, PF1_OUT, PF1_IN_PU), - PINMUX_DATA(PF0_DATA, PF0_IN, PF0_OUT, PF0_IN_PU), - - /* PG GPIO */ - PINMUX_DATA(PG7_DATA, PG7_IN, PG7_OUT, PG7_IN_PU), - PINMUX_DATA(PG6_DATA, PG6_IN, PG6_OUT, PG6_IN_PU), - PINMUX_DATA(PG5_DATA, PG5_IN, PG5_OUT, PG5_IN_PU), - PINMUX_DATA(PG4_DATA, PG4_IN, PG4_OUT, PG4_IN_PU), - PINMUX_DATA(PG3_DATA, PG3_IN, PG3_OUT, PG3_IN_PU), - PINMUX_DATA(PG2_DATA, PG2_IN, PG2_OUT, PG2_IN_PU), - PINMUX_DATA(PG1_DATA, PG1_IN, PG1_OUT, PG1_IN_PU), - PINMUX_DATA(PG0_DATA, PG0_IN, PG0_OUT, PG0_IN_PU), - - /* PH GPIO */ - PINMUX_DATA(PH5_DATA, PH5_IN, PH5_OUT, PH5_IN_PU), - PINMUX_DATA(PH4_DATA, PH4_IN, PH4_OUT, PH4_IN_PU), - PINMUX_DATA(PH3_DATA, PH3_IN, PH3_OUT, PH3_IN_PU), - PINMUX_DATA(PH2_DATA, PH2_IN, PH2_OUT, PH2_IN_PU), - PINMUX_DATA(PH1_DATA, PH1_IN, PH1_OUT, PH1_IN_PU), - PINMUX_DATA(PH0_DATA, PH0_IN, PH0_OUT, PH0_IN_PU), - - /* PA FN */ - PINMUX_DATA(D31_MARK, PA7_FN), - PINMUX_DATA(D30_MARK, PA6_FN), - PINMUX_DATA(D29_MARK, PA5_FN), - PINMUX_DATA(D28_MARK, PA4_FN), - PINMUX_DATA(D27_MARK, PA3_FN), - PINMUX_DATA(D26_MARK, PA2_FN), - PINMUX_DATA(D25_MARK, PA1_FN), - PINMUX_DATA(D24_MARK, PA0_FN), - - /* PB FN */ - PINMUX_DATA(D23_MARK, PB7_FN), - PINMUX_DATA(D22_MARK, PB6_FN), - PINMUX_DATA(D21_MARK, PB5_FN), - PINMUX_DATA(D20_MARK, PB4_FN), - PINMUX_DATA(D19_MARK, PB3_FN), - PINMUX_DATA(D18_MARK, PB2_FN), - PINMUX_DATA(D17_MARK, PB1_FN), - PINMUX_DATA(D16_MARK, PB0_FN), - - /* PC FN */ - PINMUX_DATA(BACK_MARK, PC7_FN), - PINMUX_DATA(BREQ_MARK, PC6_FN), - PINMUX_DATA(WE3_MARK, PC5_FN), - PINMUX_DATA(WE2_MARK, PC4_FN), - PINMUX_DATA(CS6_MARK, PC3_FN), - PINMUX_DATA(CS5_MARK, PC2_FN), - PINMUX_DATA(CS4_MARK, PC1_FN), - PINMUX_DATA(CLKOUTENB_MARK, PC0_FN), - - /* PD FN */ - PINMUX_DATA(DACK3_MARK, PD7_FN), - PINMUX_DATA(DACK2_MARK, PD6_FN), - PINMUX_DATA(DACK1_MARK, PD5_FN), - PINMUX_DATA(DACK0_MARK, PD4_FN), - PINMUX_DATA(DREQ3_MARK, PD3_FN), - PINMUX_DATA(DREQ2_MARK, PD2_FN), - PINMUX_DATA(DREQ1_MARK, PD1_FN), - PINMUX_DATA(DREQ0_MARK, PD0_FN), - - /* PE FN */ - PINMUX_DATA(IRQ3_MARK, PE7_FN), - PINMUX_DATA(IRQ2_MARK, PE6_FN), - PINMUX_DATA(IRQ1_MARK, PE5_FN), - PINMUX_DATA(IRQ0_MARK, PE4_FN), - PINMUX_DATA(DRAK3_MARK, PE3_FN), - PINMUX_DATA(DRAK2_MARK, PE2_FN), - PINMUX_DATA(DRAK1_MARK, PE1_FN), - PINMUX_DATA(DRAK0_MARK, PE0_FN), - - /* PF FN */ - PINMUX_DATA(SCK3_MARK, PF7_FN), - PINMUX_DATA(SCK2_MARK, PF6_FN), - PINMUX_DATA(SCK1_MARK, PF5_FN), - PINMUX_DATA(SCK0_MARK, PF4_FN), - PINMUX_DATA(IRL3_MARK, PF3_FN), - PINMUX_DATA(IRL2_MARK, PF2_FN), - PINMUX_DATA(IRL1_MARK, PF1_FN), - PINMUX_DATA(IRL0_MARK, PF0_FN), - - /* PG FN */ - PINMUX_DATA(TXD3_MARK, PG7_FN), - PINMUX_DATA(TXD2_MARK, PG6_FN), - PINMUX_DATA(TXD1_MARK, PG5_FN), - PINMUX_DATA(TXD0_MARK, PG4_FN), - PINMUX_DATA(RXD3_MARK, PG3_FN), - PINMUX_DATA(RXD2_MARK, PG2_FN), - PINMUX_DATA(RXD1_MARK, PG1_FN), - PINMUX_DATA(RXD0_MARK, PG0_FN), - - /* PH FN */ - PINMUX_DATA(CE2B_MARK, PH5_FN), - PINMUX_DATA(CE2A_MARK, PH4_FN), - PINMUX_DATA(IOIS16_MARK, PH3_FN), - PINMUX_DATA(STATUS1_MARK, PH2_FN), - PINMUX_DATA(STATUS0_MARK, PH1_FN), - PINMUX_DATA(IRQOUT_MARK, PH0_FN), -}; - -static struct pinmux_gpio shx3_pinmux_gpios[] = { - /* PA */ - PINMUX_GPIO(GPIO_PA7, PA7_DATA), - PINMUX_GPIO(GPIO_PA6, PA6_DATA), - PINMUX_GPIO(GPIO_PA5, PA5_DATA), - PINMUX_GPIO(GPIO_PA4, PA4_DATA), - PINMUX_GPIO(GPIO_PA3, PA3_DATA), - PINMUX_GPIO(GPIO_PA2, PA2_DATA), - PINMUX_GPIO(GPIO_PA1, PA1_DATA), - PINMUX_GPIO(GPIO_PA0, PA0_DATA), - - /* PB */ - PINMUX_GPIO(GPIO_PB7, PB7_DATA), - PINMUX_GPIO(GPIO_PB6, PB6_DATA), - PINMUX_GPIO(GPIO_PB5, PB5_DATA), - PINMUX_GPIO(GPIO_PB4, PB4_DATA), - PINMUX_GPIO(GPIO_PB3, PB3_DATA), - PINMUX_GPIO(GPIO_PB2, PB2_DATA), - PINMUX_GPIO(GPIO_PB1, PB1_DATA), - PINMUX_GPIO(GPIO_PB0, PB0_DATA), - - /* PC */ - PINMUX_GPIO(GPIO_PC7, PC7_DATA), - PINMUX_GPIO(GPIO_PC6, PC6_DATA), - PINMUX_GPIO(GPIO_PC5, PC5_DATA), - PINMUX_GPIO(GPIO_PC4, PC4_DATA), - PINMUX_GPIO(GPIO_PC3, PC3_DATA), - PINMUX_GPIO(GPIO_PC2, PC2_DATA), - PINMUX_GPIO(GPIO_PC1, PC1_DATA), - PINMUX_GPIO(GPIO_PC0, PC0_DATA), - - /* PD */ - PINMUX_GPIO(GPIO_PD7, PD7_DATA), - PINMUX_GPIO(GPIO_PD6, PD6_DATA), - PINMUX_GPIO(GPIO_PD5, PD5_DATA), - PINMUX_GPIO(GPIO_PD4, PD4_DATA), - PINMUX_GPIO(GPIO_PD3, PD3_DATA), - PINMUX_GPIO(GPIO_PD2, PD2_DATA), - PINMUX_GPIO(GPIO_PD1, PD1_DATA), - PINMUX_GPIO(GPIO_PD0, PD0_DATA), - - /* PE */ - PINMUX_GPIO(GPIO_PE7, PE7_DATA), - PINMUX_GPIO(GPIO_PE6, PE6_DATA), - PINMUX_GPIO(GPIO_PE5, PE5_DATA), - PINMUX_GPIO(GPIO_PE4, PE4_DATA), - PINMUX_GPIO(GPIO_PE3, PE3_DATA), - PINMUX_GPIO(GPIO_PE2, PE2_DATA), - PINMUX_GPIO(GPIO_PE1, PE1_DATA), - PINMUX_GPIO(GPIO_PE0, PE0_DATA), - - /* PF */ - PINMUX_GPIO(GPIO_PF7, PF7_DATA), - PINMUX_GPIO(GPIO_PF6, PF6_DATA), - PINMUX_GPIO(GPIO_PF5, PF5_DATA), - PINMUX_GPIO(GPIO_PF4, PF4_DATA), - PINMUX_GPIO(GPIO_PF3, PF3_DATA), - PINMUX_GPIO(GPIO_PF2, PF2_DATA), - PINMUX_GPIO(GPIO_PF1, PF1_DATA), - PINMUX_GPIO(GPIO_PF0, PF0_DATA), - - /* PG */ - PINMUX_GPIO(GPIO_PG7, PG7_DATA), - PINMUX_GPIO(GPIO_PG6, PG6_DATA), - PINMUX_GPIO(GPIO_PG5, PG5_DATA), - PINMUX_GPIO(GPIO_PG4, PG4_DATA), - PINMUX_GPIO(GPIO_PG3, PG3_DATA), - PINMUX_GPIO(GPIO_PG2, PG2_DATA), - PINMUX_GPIO(GPIO_PG1, PG1_DATA), - PINMUX_GPIO(GPIO_PG0, PG0_DATA), - - /* PH */ - PINMUX_GPIO(GPIO_PH5, PH5_DATA), - PINMUX_GPIO(GPIO_PH4, PH4_DATA), - PINMUX_GPIO(GPIO_PH3, PH3_DATA), - PINMUX_GPIO(GPIO_PH2, PH2_DATA), - PINMUX_GPIO(GPIO_PH1, PH1_DATA), - PINMUX_GPIO(GPIO_PH0, PH0_DATA), - - /* FN */ - PINMUX_GPIO(GPIO_FN_D31, D31_MARK), - PINMUX_GPIO(GPIO_FN_D30, D30_MARK), - PINMUX_GPIO(GPIO_FN_D29, D29_MARK), - PINMUX_GPIO(GPIO_FN_D28, D28_MARK), - PINMUX_GPIO(GPIO_FN_D27, D27_MARK), - PINMUX_GPIO(GPIO_FN_D26, D26_MARK), - PINMUX_GPIO(GPIO_FN_D25, D25_MARK), - PINMUX_GPIO(GPIO_FN_D24, D24_MARK), - PINMUX_GPIO(GPIO_FN_D23, D23_MARK), - PINMUX_GPIO(GPIO_FN_D22, D22_MARK), - PINMUX_GPIO(GPIO_FN_D21, D21_MARK), - PINMUX_GPIO(GPIO_FN_D20, D20_MARK), - PINMUX_GPIO(GPIO_FN_D19, D19_MARK), - PINMUX_GPIO(GPIO_FN_D18, D18_MARK), - PINMUX_GPIO(GPIO_FN_D17, D17_MARK), - PINMUX_GPIO(GPIO_FN_D16, D16_MARK), - PINMUX_GPIO(GPIO_FN_BACK, BACK_MARK), - PINMUX_GPIO(GPIO_FN_BREQ, BREQ_MARK), - PINMUX_GPIO(GPIO_FN_WE3, WE3_MARK), - PINMUX_GPIO(GPIO_FN_WE2, WE2_MARK), - PINMUX_GPIO(GPIO_FN_CS6, CS6_MARK), - PINMUX_GPIO(GPIO_FN_CS5, CS5_MARK), - PINMUX_GPIO(GPIO_FN_CS4, CS4_MARK), - PINMUX_GPIO(GPIO_FN_CLKOUTENB, CLKOUTENB_MARK), - PINMUX_GPIO(GPIO_FN_DACK3, DACK3_MARK), - PINMUX_GPIO(GPIO_FN_DACK2, DACK2_MARK), - PINMUX_GPIO(GPIO_FN_DACK1, DACK1_MARK), - PINMUX_GPIO(GPIO_FN_DACK0, DACK0_MARK), - PINMUX_GPIO(GPIO_FN_DREQ3, DREQ3_MARK), - PINMUX_GPIO(GPIO_FN_DREQ2, DREQ2_MARK), - PINMUX_GPIO(GPIO_FN_DREQ1, DREQ1_MARK), - PINMUX_GPIO(GPIO_FN_DREQ0, DREQ0_MARK), - PINMUX_GPIO(GPIO_FN_IRQ3, IRQ3_MARK), - PINMUX_GPIO(GPIO_FN_IRQ2, IRQ2_MARK), - PINMUX_GPIO(GPIO_FN_IRQ1, IRQ1_MARK), - PINMUX_GPIO(GPIO_FN_IRQ0, IRQ0_MARK), - PINMUX_GPIO(GPIO_FN_DRAK3, DRAK3_MARK), - PINMUX_GPIO(GPIO_FN_DRAK2, DRAK2_MARK), - PINMUX_GPIO(GPIO_FN_DRAK1, DRAK1_MARK), - PINMUX_GPIO(GPIO_FN_DRAK0, DRAK0_MARK), - PINMUX_GPIO(GPIO_FN_SCK3, SCK3_MARK), - PINMUX_GPIO(GPIO_FN_SCK2, SCK2_MARK), - PINMUX_GPIO(GPIO_FN_SCK1, SCK1_MARK), - PINMUX_GPIO(GPIO_FN_SCK0, SCK0_MARK), - PINMUX_GPIO(GPIO_FN_IRL3, IRL3_MARK), - PINMUX_GPIO(GPIO_FN_IRL2, IRL2_MARK), - PINMUX_GPIO(GPIO_FN_IRL1, IRL1_MARK), - PINMUX_GPIO(GPIO_FN_IRL0, IRL0_MARK), - PINMUX_GPIO(GPIO_FN_TXD3, TXD3_MARK), - PINMUX_GPIO(GPIO_FN_TXD2, TXD2_MARK), - PINMUX_GPIO(GPIO_FN_TXD1, TXD1_MARK), - PINMUX_GPIO(GPIO_FN_TXD0, TXD0_MARK), - PINMUX_GPIO(GPIO_FN_RXD3, RXD3_MARK), - PINMUX_GPIO(GPIO_FN_RXD2, RXD2_MARK), - PINMUX_GPIO(GPIO_FN_RXD1, RXD1_MARK), - PINMUX_GPIO(GPIO_FN_RXD0, RXD0_MARK), - PINMUX_GPIO(GPIO_FN_CE2B, CE2B_MARK), - PINMUX_GPIO(GPIO_FN_CE2A, CE2A_MARK), - PINMUX_GPIO(GPIO_FN_IOIS16, IOIS16_MARK), - PINMUX_GPIO(GPIO_FN_STATUS1, STATUS1_MARK), - PINMUX_GPIO(GPIO_FN_STATUS0, STATUS0_MARK), - PINMUX_GPIO(GPIO_FN_IRQOUT, IRQOUT_MARK), -}; - -static struct pinmux_cfg_reg shx3_pinmux_config_regs[] = { - { PINMUX_CFG_REG("PABCR", 0xffc70000, 32, 2) { - PA7_FN, PA7_OUT, PA7_IN, PA7_IN_PU, - PA6_FN, PA6_OUT, PA6_IN, PA6_IN_PU, - PA5_FN, PA5_OUT, PA5_IN, PA5_IN_PU, - PA4_FN, PA4_OUT, PA4_IN, PA4_IN_PU, - PA3_FN, PA3_OUT, PA3_IN, PA3_IN_PU, - PA2_FN, PA2_OUT, PA2_IN, PA2_IN_PU, - PA1_FN, PA1_OUT, PA1_IN, PA1_IN_PU, - PA0_FN, PA0_OUT, PA0_IN, PA0_IN_PU, - PB7_FN, PB7_OUT, PB7_IN, PB7_IN_PU, - PB6_FN, PB6_OUT, PB6_IN, PB6_IN_PU, - PB5_FN, PB5_OUT, PB5_IN, PB5_IN_PU, - PB4_FN, PB4_OUT, PB4_IN, PB4_IN_PU, - PB3_FN, PB3_OUT, PB3_IN, PB3_IN_PU, - PB2_FN, PB2_OUT, PB2_IN, PB2_IN_PU, - PB1_FN, PB1_OUT, PB1_IN, PB1_IN_PU, - PB0_FN, PB0_OUT, PB0_IN, PB0_IN_PU, }, - }, - { PINMUX_CFG_REG("PCDCR", 0xffc70004, 32, 2) { - PC7_FN, PC7_OUT, PC7_IN, PC7_IN_PU, - PC6_FN, PC6_OUT, PC6_IN, PC6_IN_PU, - PC5_FN, PC5_OUT, PC5_IN, PC5_IN_PU, - PC4_FN, PC4_OUT, PC4_IN, PC4_IN_PU, - PC3_FN, PC3_OUT, PC3_IN, PC3_IN_PU, - PC2_FN, PC2_OUT, PC2_IN, PC2_IN_PU, - PC1_FN, PC1_OUT, PC1_IN, PC1_IN_PU, - PC0_FN, PC0_OUT, PC0_IN, PC0_IN_PU, - PD7_FN, PD7_OUT, PD7_IN, PD7_IN_PU, - PD6_FN, PD6_OUT, PD6_IN, PD6_IN_PU, - PD5_FN, PD5_OUT, PD5_IN, PD5_IN_PU, - PD4_FN, PD4_OUT, PD4_IN, PD4_IN_PU, - PD3_FN, PD3_OUT, PD3_IN, PD3_IN_PU, - PD2_FN, PD2_OUT, PD2_IN, PD2_IN_PU, - PD1_FN, PD1_OUT, PD1_IN, PD1_IN_PU, - PD0_FN, PD0_OUT, PD0_IN, PD0_IN_PU, }, - }, - { PINMUX_CFG_REG("PEFCR", 0xffc70008, 32, 2) { - PE7_FN, PE7_OUT, PE7_IN, PE7_IN_PU, - PE6_FN, PE6_OUT, PE6_IN, PE6_IN_PU, - PE5_FN, PE5_OUT, PE5_IN, PE5_IN_PU, - PE4_FN, PE4_OUT, PE4_IN, PE4_IN_PU, - PE3_FN, PE3_OUT, PE3_IN, PE3_IN_PU, - PE2_FN, PE2_OUT, PE2_IN, PE2_IN_PU, - PE1_FN, PE1_OUT, PE1_IN, PE1_IN_PU, - PE0_FN, PE0_OUT, PE0_IN, PE0_IN_PU, - PF7_FN, PF7_OUT, PF7_IN, PF7_IN_PU, - PF6_FN, PF6_OUT, PF6_IN, PF6_IN_PU, - PF5_FN, PF5_OUT, PF5_IN, PF5_IN_PU, - PF4_FN, PF4_OUT, PF4_IN, PF4_IN_PU, - PF3_FN, PF3_OUT, PF3_IN, PF3_IN_PU, - PF2_FN, PF2_OUT, PF2_IN, PF2_IN_PU, - PF1_FN, PF1_OUT, PF1_IN, PF1_IN_PU, - PF0_FN, PF0_OUT, PF0_IN, PF0_IN_PU, }, - }, - { PINMUX_CFG_REG("PGHCR", 0xffc7000c, 32, 2) { - PG7_FN, PG7_OUT, PG7_IN, PG7_IN_PU, - PG6_FN, PG6_OUT, PG6_IN, PG6_IN_PU, - PG5_FN, PG5_OUT, PG5_IN, PG5_IN_PU, - PG4_FN, PG4_OUT, PG4_IN, PG4_IN_PU, - PG3_FN, PG3_OUT, PG3_IN, PG3_IN_PU, - PG2_FN, PG2_OUT, PG2_IN, PG2_IN_PU, - PG1_FN, PG1_OUT, PG1_IN, PG1_IN_PU, - PG0_FN, PG0_OUT, PG0_IN, PG0_IN_PU, - 0, 0, 0, 0, - 0, 0, 0, 0, - PH5_FN, PH5_OUT, PH5_IN, PH5_IN_PU, - PH4_FN, PH4_OUT, PH4_IN, PH4_IN_PU, - PH3_FN, PH3_OUT, PH3_IN, PH3_IN_PU, - PH2_FN, PH2_OUT, PH2_IN, PH2_IN_PU, - PH1_FN, PH1_OUT, PH1_IN, PH1_IN_PU, - PH0_FN, PH0_OUT, PH0_IN, PH0_IN_PU, }, - }, - { }, -}; - -static struct pinmux_data_reg shx3_pinmux_data_regs[] = { - { PINMUX_DATA_REG("PABDR", 0xffc70010, 32) { - 0, 0, 0, 0, 0, 0, 0, 0, - PA7_DATA, PA6_DATA, PA5_DATA, PA4_DATA, - PA3_DATA, PA2_DATA, PA1_DATA, PA0_DATA, - 0, 0, 0, 0, 0, 0, 0, 0, - PB7_DATA, PB6_DATA, PB5_DATA, PB4_DATA, - PB3_DATA, PB2_DATA, PB1_DATA, PB0_DATA, }, - }, - { PINMUX_DATA_REG("PCDDR", 0xffc70014, 32) { - 0, 0, 0, 0, 0, 0, 0, 0, - PC7_DATA, PC6_DATA, PC5_DATA, PC4_DATA, - PC3_DATA, PC2_DATA, PC1_DATA, PC0_DATA, - 0, 0, 0, 0, 0, 0, 0, 0, - PD7_DATA, PD6_DATA, PD5_DATA, PD4_DATA, - PD3_DATA, PD2_DATA, PD1_DATA, PD0_DATA, }, - }, - { PINMUX_DATA_REG("PEFDR", 0xffc70018, 32) { - 0, 0, 0, 0, 0, 0, 0, 0, - PE7_DATA, PE6_DATA, PE5_DATA, PE4_DATA, - PE3_DATA, PE2_DATA, PE1_DATA, PE0_DATA, - 0, 0, 0, 0, 0, 0, 0, 0, - PF7_DATA, PF6_DATA, PF5_DATA, PF4_DATA, - PF3_DATA, PF2_DATA, PF1_DATA, PF0_DATA, }, - }, - { PINMUX_DATA_REG("PGHDR", 0xffc7001c, 32) { - 0, 0, 0, 0, 0, 0, 0, 0, - PG7_DATA, PG6_DATA, PG5_DATA, PG4_DATA, - PG3_DATA, PG2_DATA, PG1_DATA, PG0_DATA, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, PH5_DATA, PH4_DATA, - PH3_DATA, PH2_DATA, PH1_DATA, PH0_DATA, }, - }, - { }, -}; - -static struct pinmux_info shx3_pinmux_info = { - .name = "shx3_pfc", - .reserved_id = PINMUX_RESERVED, - .data = { PINMUX_DATA_BEGIN, PINMUX_DATA_END }, - .input = { PINMUX_INPUT_BEGIN, PINMUX_INPUT_END }, - .input_pu = { PINMUX_INPUT_PULLUP_BEGIN, - PINMUX_INPUT_PULLUP_END }, - .output = { PINMUX_OUTPUT_BEGIN, PINMUX_OUTPUT_END }, - .mark = { PINMUX_MARK_BEGIN, PINMUX_MARK_END }, - .function = { PINMUX_FUNCTION_BEGIN, PINMUX_FUNCTION_END }, - .first_gpio = GPIO_PA7, - .last_gpio = GPIO_FN_IRQOUT, - .gpios = shx3_pinmux_gpios, - .gpio_data = shx3_pinmux_data, - .gpio_data_size = ARRAY_SIZE(shx3_pinmux_data), - .cfg_regs = shx3_pinmux_config_regs, - .data_regs = shx3_pinmux_data_regs, -}; - -static int __init shx3_pinmux_setup(void) -{ - return register_pinmux(&shx3_pinmux_info); -} -arch_initcall(shx3_pinmux_setup); diff --git a/trunk/arch/sh/kernel/cpu/sh4a/setup-sh7722.c b/trunk/arch/sh/kernel/cpu/sh4a/setup-sh7722.c index d551ed8dea95..156ccc960015 100644 --- a/trunk/arch/sh/kernel/cpu/sh4a/setup-sh7722.c +++ b/trunk/arch/sh/kernel/cpu/sh4a/setup-sh7722.c @@ -551,7 +551,7 @@ static struct resource siu_resources[] = { }; static struct platform_device siu_device = { - .name = "siu-pcm-audio", + .name = "sh_siu", .id = -1, .dev = { .platform_data = &siu_platform_data, diff --git a/trunk/arch/sh/kernel/cpu/sh4a/setup-sh7724.c b/trunk/arch/sh/kernel/cpu/sh4a/setup-sh7724.c index 828c9657eb52..79c556e56262 100644 --- a/trunk/arch/sh/kernel/cpu/sh4a/setup-sh7724.c +++ b/trunk/arch/sh/kernel/cpu/sh4a/setup-sh7724.c @@ -524,70 +524,6 @@ static struct platform_device veu1_device = { }, }; -/* BEU0 */ -static struct uio_info beu0_platform_data = { - .name = "BEU0", - .version = "0", - .irq = evt2irq(0x8A0), -}; - -static struct resource beu0_resources[] = { - [0] = { - .name = "BEU0", - .start = 0xfe930000, - .end = 0xfe933400, - .flags = IORESOURCE_MEM, - }, - [1] = { - /* place holder for contiguous memory */ - }, -}; - -static struct platform_device beu0_device = { - .name = "uio_pdrv_genirq", - .id = 6, - .dev = { - .platform_data = &beu0_platform_data, - }, - .resource = beu0_resources, - .num_resources = ARRAY_SIZE(beu0_resources), - .archdata = { - .hwblk_id = HWBLK_BEU0, - }, -}; - -/* BEU1 */ -static struct uio_info beu1_platform_data = { - .name = "BEU1", - .version = "0", - .irq = evt2irq(0xA00), -}; - -static struct resource beu1_resources[] = { - [0] = { - .name = "BEU1", - .start = 0xfe940000, - .end = 0xfe943400, - .flags = IORESOURCE_MEM, - }, - [1] = { - /* place holder for contiguous memory */ - }, -}; - -static struct platform_device beu1_device = { - .name = "uio_pdrv_genirq", - .id = 7, - .dev = { - .platform_data = &beu1_platform_data, - }, - .resource = beu1_resources, - .num_resources = ARRAY_SIZE(beu1_resources), - .archdata = { - .hwblk_id = HWBLK_BEU1, - }, -}; - static struct sh_timer_config cmt_platform_data = { .channel_offset = 0x60, .timer_bit = 5, @@ -921,8 +857,6 @@ static struct platform_device *sh7724_devices[] __initdata = { &vpu_device, &veu0_device, &veu1_device, - &beu0_device, - &beu1_device, &jpu_device, &spu0_device, &spu1_device, diff --git a/trunk/arch/sh/kernel/cpu/sh4a/setup-sh7757.c b/trunk/arch/sh/kernel/cpu/sh4a/setup-sh7757.c index 749c6388d5a5..444aca95b20d 100644 --- a/trunk/arch/sh/kernel/cpu/sh4a/setup-sh7757.c +++ b/trunk/arch/sh/kernel/cpu/sh4a/setup-sh7757.c @@ -26,7 +26,7 @@ static struct plat_sci_port scif2_platform_data = { static struct platform_device scif2_device = { .name = "sh-sci", - .id = 0, + .id = 2, .dev = { .platform_data = &scif2_platform_data, }, @@ -41,7 +41,7 @@ static struct plat_sci_port scif3_platform_data = { static struct platform_device scif3_device = { .name = "sh-sci", - .id = 1, + .id = 3, .dev = { .platform_data = &scif3_platform_data, }, @@ -56,7 +56,7 @@ static struct plat_sci_port scif4_platform_data = { static struct platform_device scif4_device = { .name = "sh-sci", - .id = 2, + .id = 4, .dev = { .platform_data = &scif4_platform_data, }, @@ -163,23 +163,39 @@ enum { IRL4_HHLL, IRL4_HHLH, IRL4_HHHL, IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7, - SDHI, DVC, - IRQ8, IRQ9, IRQ11, IRQ10, IRQ12, IRQ13, IRQ14, IRQ15, - TMU0, TMU1, TMU2, TMU2_TICPI, TMU3, TMU4, TMU5, + SDHI, + DVC, + IRQ8, IRQ9, IRQ10, + WDT0, + TMU0, TMU1, TMU2, TMU2_TICPI, HUDI, + ARC4, - DMAC0_5, DMAC6_7, DMAC8_11, - SCIF0, SCIF1, SCIF2, SCIF3, SCIF4, - USB0, USB1, + DMAC0, + IRQ11, + SCIF2, + DMAC1_6, + USB0, + IRQ12, JMC, - SPI0, SPI1, + SPI1, + IRQ13, IRQ14, + USB1, TMR01, TMR23, TMR45, + WDT1, FRT, - LPC, LPC5, LPC6, LPC7, LPC8, - PECI0, PECI1, PECI2, PECI3, PECI4, PECI5, + LPC, + SCIF0, SCIF1, SCIF3, + PECI0I, PECI1I, PECI2I, + IRQ15, ETHERC, - ADC0, ADC1, + SPI0, + ADC1, + DMAC1_8, SIM, + TMU3, TMU4, TMU5, + ADC0, + SCIF4, IIC0_0, IIC0_1, IIC0_2, IIC0_3, IIC1_0, IIC1_1, IIC1_2, IIC1_3, IIC2_0, IIC2_1, IIC2_2, IIC2_3, @@ -190,23 +206,9 @@ enum { IIC7_0, IIC7_1, IIC7_2, IIC7_3, IIC8_0, IIC8_1, IIC8_2, IIC8_3, IIC9_0, IIC9_1, IIC9_2, IIC9_3, - ONFICTL, - MMC1, MMC2, - ECCU, - PCIC, - G200, - RSPI, + PCIINTA, + PCIE, SGPIO, - DMINT12, DMINT13, DMINT14, DMINT15, DMINT16, DMINT17, DMINT18, DMINT19, - DMINT20, DMINT21, DMINT22, DMINT23, - DDRECC, - TSIP, - PCIE_BRIDGE, - WDT0B, WDT1B, WDT2B, WDT3B, WDT4B, WDT5B, WDT6B, WDT7B, WDT8B, - GETHER0, GETHER1, GETHER2, - PBIA, PBIB, PBIC, - DMAE2, DMAE3, - SERMUX2, SERMUX3, /* interrupt groups */ @@ -219,18 +221,19 @@ static struct intc_vect vectors[] __initdata = { INTC_VECT(DVC, 0x4e0), INTC_VECT(IRQ8, 0x500), INTC_VECT(IRQ9, 0x520), INTC_VECT(IRQ10, 0x540), + INTC_VECT(WDT0, 0x560), INTC_VECT(TMU0, 0x580), INTC_VECT(TMU1, 0x5a0), INTC_VECT(TMU2, 0x5c0), INTC_VECT(TMU2_TICPI, 0x5e0), INTC_VECT(HUDI, 0x600), INTC_VECT(ARC4, 0x620), - INTC_VECT(DMAC0_5, 0x640), INTC_VECT(DMAC0_5, 0x660), - INTC_VECT(DMAC0_5, 0x680), INTC_VECT(DMAC0_5, 0x6a0), - INTC_VECT(DMAC0_5, 0x6c0), + INTC_VECT(DMAC0, 0x640), INTC_VECT(DMAC0, 0x660), + INTC_VECT(DMAC0, 0x680), INTC_VECT(DMAC0, 0x6a0), + INTC_VECT(DMAC0, 0x6c0), INTC_VECT(IRQ11, 0x6e0), INTC_VECT(SCIF2, 0x700), INTC_VECT(SCIF2, 0x720), INTC_VECT(SCIF2, 0x740), INTC_VECT(SCIF2, 0x760), - INTC_VECT(DMAC0_5, 0x780), INTC_VECT(DMAC0_5, 0x7a0), - INTC_VECT(DMAC6_7, 0x7c0), INTC_VECT(DMAC6_7, 0x7e0), + INTC_VECT(DMAC0, 0x780), INTC_VECT(DMAC0, 0x7a0), + INTC_VECT(DMAC1_6, 0x7c0), INTC_VECT(DMAC1_6, 0x7e0), INTC_VECT(USB0, 0x840), INTC_VECT(IRQ12, 0x880), INTC_VECT(JMC, 0x8a0), @@ -239,6 +242,7 @@ static struct intc_vect vectors[] __initdata = { INTC_VECT(USB1, 0x920), INTC_VECT(TMR01, 0xa00), INTC_VECT(TMR23, 0xa20), INTC_VECT(TMR45, 0xa40), + INTC_VECT(WDT1, 0xa60), INTC_VECT(FRT, 0xa80), INTC_VECT(LPC, 0xaa0), INTC_VECT(LPC, 0xac0), INTC_VECT(LPC, 0xae0), INTC_VECT(LPC, 0xb00), @@ -246,14 +250,14 @@ static struct intc_vect vectors[] __initdata = { INTC_VECT(SCIF0, 0xb40), INTC_VECT(SCIF1, 0xb60), INTC_VECT(SCIF3, 0xb80), INTC_VECT(SCIF3, 0xba0), INTC_VECT(SCIF3, 0xbc0), INTC_VECT(SCIF3, 0xbe0), - INTC_VECT(PECI0, 0xc00), INTC_VECT(PECI1, 0xc20), - INTC_VECT(PECI2, 0xc40), + INTC_VECT(PECI0I, 0xc00), INTC_VECT(PECI1I, 0xc20), + INTC_VECT(PECI2I, 0xc40), INTC_VECT(IRQ15, 0xc60), INTC_VECT(ETHERC, 0xc80), INTC_VECT(ETHERC, 0xca0), INTC_VECT(SPI0, 0xcc0), INTC_VECT(ADC1, 0xce0), - INTC_VECT(DMAC8_11, 0xd00), INTC_VECT(DMAC8_11, 0xd20), - INTC_VECT(DMAC8_11, 0xd40), INTC_VECT(DMAC8_11, 0xd60), + INTC_VECT(DMAC1_8, 0xd00), INTC_VECT(DMAC1_8, 0xd20), + INTC_VECT(DMAC1_8, 0xd40), INTC_VECT(DMAC1_8, 0xd60), INTC_VECT(SIM, 0xd80), INTC_VECT(SIM, 0xda0), INTC_VECT(SIM, 0xdc0), INTC_VECT(SIM, 0xde0), INTC_VECT(TMU3, 0xe00), INTC_VECT(TMU4, 0xe20), @@ -274,47 +278,17 @@ static struct intc_vect vectors[] __initdata = { INTC_VECT(IIC5_0, 0x1860), INTC_VECT(IIC5_1, 0x1880), INTC_VECT(IIC5_2, 0x18a0), INTC_VECT(IIC5_3, 0x18c0), INTC_VECT(IIC6_0, 0x18e0), INTC_VECT(IIC6_1, 0x1900), - INTC_VECT(IIC6_2, 0x1920), - INTC_VECT(ONFICTL, 0x1960), - INTC_VECT(IIC6_3, 0x1980), + INTC_VECT(IIC6_2, 0x1920), INTC_VECT(IIC6_3, 0x1980), INTC_VECT(IIC7_0, 0x19a0), INTC_VECT(IIC7_1, 0x1a00), INTC_VECT(IIC7_2, 0x1a20), INTC_VECT(IIC7_3, 0x1a40), INTC_VECT(IIC8_0, 0x1a60), INTC_VECT(IIC8_1, 0x1a80), INTC_VECT(IIC8_2, 0x1aa0), INTC_VECT(IIC8_3, 0x1b40), INTC_VECT(IIC9_0, 0x1b60), INTC_VECT(IIC9_1, 0x1b80), INTC_VECT(IIC9_2, 0x1c00), INTC_VECT(IIC9_3, 0x1c20), - INTC_VECT(MMC1, 0x1c60), INTC_VECT(MMC2, 0x1c80), - INTC_VECT(ECCU, 0x1cc0), - INTC_VECT(PCIC, 0x1ce0), - INTC_VECT(G200, 0x1d00), - INTC_VECT(RSPI, 0x1d80), INTC_VECT(RSPI, 0x1da0), - INTC_VECT(RSPI, 0x1dc0), INTC_VECT(RSPI, 0x1de0), - INTC_VECT(PECI3, 0x1ec0), INTC_VECT(PECI4, 0x1ee0), - INTC_VECT(PECI5, 0x1f00), - INTC_VECT(SGPIO, 0x1f80), INTC_VECT(SGPIO, 0x1fa0), - INTC_VECT(SGPIO, 0x1fc0), - INTC_VECT(DMINT12, 0x2400), INTC_VECT(DMINT13, 0x2420), - INTC_VECT(DMINT14, 0x2440), INTC_VECT(DMINT15, 0x2460), - INTC_VECT(DMINT16, 0x2480), INTC_VECT(DMINT17, 0x24e0), - INTC_VECT(DMINT18, 0x2500), INTC_VECT(DMINT19, 0x2520), - INTC_VECT(DMINT20, 0x2540), INTC_VECT(DMINT21, 0x2560), - INTC_VECT(DMINT22, 0x2580), INTC_VECT(DMINT23, 0x2600), - INTC_VECT(DDRECC, 0x2620), - INTC_VECT(TSIP, 0x2640), - INTC_VECT(PCIE_BRIDGE, 0x27c0), - INTC_VECT(WDT0B, 0x2800), INTC_VECT(WDT1B, 0x2820), - INTC_VECT(WDT2B, 0x2840), INTC_VECT(WDT3B, 0x2860), - INTC_VECT(WDT4B, 0x2880), INTC_VECT(WDT5B, 0x28a0), - INTC_VECT(WDT6B, 0x28c0), INTC_VECT(WDT7B, 0x28e0), - INTC_VECT(WDT8B, 0x2900), - INTC_VECT(GETHER0, 0x2960), INTC_VECT(GETHER1, 0x2980), - INTC_VECT(GETHER2, 0x29a0), - INTC_VECT(PBIA, 0x2a00), INTC_VECT(PBIB, 0x2a20), - INTC_VECT(PBIC, 0x2a40), - INTC_VECT(DMAE2, 0x2a60), INTC_VECT(DMAE3, 0x2a80), - INTC_VECT(SERMUX2, 0x2aa0), INTC_VECT(SERMUX3, 0x2b40), - INTC_VECT(LPC5, 0x2b60), INTC_VECT(LPC6, 0x2b80), - INTC_VECT(LPC7, 0x2c00), INTC_VECT(LPC8, 0x2c20), + INTC_VECT(PCIINTA, 0x1ce0), + INTC_VECT(PCIE, 0x1e00), + INTC_VECT(SGPIO, 0x1f80), + INTC_VECT(SGPIO, 0x1fa0), }; static struct intc_group groups[] __initdata = { @@ -338,45 +312,31 @@ static struct intc_mask_reg mask_registers[] __initdata = { { 0xffd40038, 0xffd4003c, 32, /* INT2MSKR / INT2MSKCR */ { 0, 0, 0, 0, 0, 0, 0, 0, - 0, DMAC8_11, 0, PECI0, LPC, FRT, 0, TMR45, - TMR23, TMR01, 0, 0, 0, 0, 0, DMAC0_5, - HUDI, 0, 0, SCIF3, SCIF2, SDHI, TMU345, TMU012 + 0, DMAC1_8, 0, PECI0I, LPC, FRT, WDT1, TMR45, + TMR23, TMR01, 0, 0, 0, 0, 0, DMAC0, + HUDI, 0, WDT0, SCIF3, SCIF2, SDHI, TMU345, TMU012 } }, { 0xffd400d0, 0xffd400d4, 32, /* INT2MSKR1 / INT2MSKCR1 */ { IRQ15, IRQ14, IRQ13, IRQ12, IRQ11, IRQ10, SCIF4, ETHERC, IRQ9, IRQ8, SCIF1, SCIF0, USB0, 0, 0, USB1, - ADC1, 0, DMAC6_7, ADC0, SPI0, SIM, PECI2, PECI1, + ADC1, 0, DMAC1_6, ADC0, SPI0, SIM, PECI2I, PECI1I, ARC4, 0, SPI1, JMC, 0, 0, 0, DVC } }, { 0xffd10038, 0xffd1003c, 32, /* INT2MSKR2 / INT2MSKCR2 */ - { IIC4_1, IIC4_2, IIC5_0, ONFICTL, 0, 0, SGPIO, 0, - 0, G200, 0, IIC9_2, IIC8_2, IIC8_1, IIC8_0, IIC7_3, + { IIC4_1, IIC4_2, IIC5_0, 0, 0, 0, SGPIO, 0, + 0, 0, 0, IIC9_2, IIC8_2, IIC8_1, IIC8_0, IIC7_3, IIC7_2, IIC7_1, IIC6_3, IIC0_0, IIC0_1, IIC0_2, IIC0_3, IIC3_1, - IIC2_3, 0, IIC2_1, IIC9_1, IIC3_3, IIC1_0, 0, IIC2_2 + IIC2_3, 0, IIC2_1, IIC9_1, IIC3_3, IIC1_0, PCIE, IIC2_2 } }, - { 0xffd100d0, 0xffd100d4, 32, /* INT2MSKR3 / INT2MSKCR3 */ - { MMC1, IIC6_1, IIC6_0, IIC5_1, IIC3_2, IIC2_0, PECI5, MMC2, + { 0xffd100d0, 0xff1400d4, 32, /* INT2MSKR3 / INT2MSKCR4 */ + { 0, IIC6_1, IIC6_0, IIC5_1, IIC3_2, IIC2_0, 0, 0, IIC1_3, IIC1_2, IIC9_0, IIC8_3, IIC4_3, IIC7_0, 0, IIC6_2, - PCIC, 0, IIC4_0, 0, ECCU, RSPI, 0, IIC9_3, + PCIINTA, 0, IIC4_0, 0, 0, 0, 0, IIC9_3, IIC3_0, 0, IIC5_3, IIC5_2, 0, 0, 0, IIC1_1 } }, - - { 0xffd20038, 0xffd2003c, 32, /* INT2MSKR4 / INT2MSKCR4 */ - { WDT0B, WDT1B, WDT3B, GETHER0, 0, 0, 0, 0, - 0, 0, 0, LPC7, SERMUX2, DMAE3, DMAE2, PBIC, - PBIB, PBIA, GETHER1, DMINT12, DMINT13, DMINT14, DMINT15, TSIP, - DMINT23, 0, DMINT21, LPC6, 0, DMINT16, 0, DMINT22 - } }, - - { 0xffd200d0, 0xffd200d4, 32, /* INT2MSKR5 / INT2MSKCR5 */ - { 0, WDT8B, WDT7B, WDT4B, 0, DMINT20, 0, 0, - DMINT19, DMINT18, LPC5, SERMUX3, WDT2B, GETHER2, 0, 0, - 0, 0, PCIE_BRIDGE, 0, 0, 0, 0, LPC8, - DDRECC, 0, WDT6B, WDT5B, 0, 0, 0, DMINT17 - } }, }; #define INTPRI 0xffd00010 @@ -412,22 +372,6 @@ static struct intc_mask_reg mask_registers[] __initdata = { #define INT2PRI29 0xffd100b4 #define INT2PRI30 0xffd100b8 #define INT2PRI31 0xffd100bc -#define INT2PRI32 0xffd20000 -#define INT2PRI33 0xffd20004 -#define INT2PRI34 0xffd20008 -#define INT2PRI35 0xffd2000c -#define INT2PRI36 0xffd20010 -#define INT2PRI37 0xffd20014 -#define INT2PRI38 0xffd20018 -#define INT2PRI39 0xffd2001c -#define INT2PRI40 0xffd200a0 -#define INT2PRI41 0xffd200a4 -#define INT2PRI42 0xffd200a8 -#define INT2PRI43 0xffd200ac -#define INT2PRI44 0xffd200b0 -#define INT2PRI45 0xffd200b4 -#define INT2PRI46 0xffd200b8 -#define INT2PRI47 0xffd200bc static struct intc_prio_reg prio_registers[] __initdata = { { INTPRI, 0, 32, 4, { IRQ0, IRQ1, IRQ2, IRQ3, @@ -435,61 +379,39 @@ static struct intc_prio_reg prio_registers[] __initdata = { { INT2PRI0, 0, 32, 8, { TMU0, TMU1, TMU2, TMU2_TICPI } }, { INT2PRI1, 0, 32, 8, { TMU3, TMU4, TMU5, SDHI } }, - { INT2PRI2, 0, 32, 8, { SCIF2, SCIF3, 0, IRQ8 } }, - { INT2PRI3, 0, 32, 8, { HUDI, DMAC0_5, ADC0, IRQ9 } }, + { INT2PRI2, 0, 32, 8, { SCIF2, SCIF3, WDT0, IRQ8 } }, + { INT2PRI3, 0, 32, 8, { HUDI, DMAC0, ADC0, IRQ9 } }, { INT2PRI4, 0, 32, 8, { IRQ10, 0, TMR01, TMR23 } }, - { INT2PRI5, 0, 32, 8, { TMR45, 0, FRT, LPC } }, - { INT2PRI6, 0, 32, 8, { PECI0, ETHERC, DMAC8_11, 0 } }, + { INT2PRI5, 0, 32, 8, { TMR45, WDT1, FRT, LPC } }, + { INT2PRI6, 0, 32, 8, { PECI0I, ETHERC, DMAC1_8, 0 } }, { INT2PRI7, 0, 32, 8, { SCIF4, 0, IRQ11, IRQ12 } }, { INT2PRI8, 0, 32, 8, { 0, 0, 0, DVC } }, { INT2PRI9, 0, 32, 8, { ARC4, 0, SPI1, JMC } }, - { INT2PRI10, 0, 32, 8, { SPI0, SIM, PECI2, PECI1 } }, - { INT2PRI11, 0, 32, 8, { ADC1, IRQ13, DMAC6_7, IRQ14 } }, + { INT2PRI10, 0, 32, 8, { SPI0, SIM, PECI2I, PECI1I } }, + { INT2PRI11, 0, 32, 8, { ADC1, IRQ13, DMAC1_6, IRQ14 } }, { INT2PRI12, 0, 32, 8, { USB0, 0, IRQ15, USB1 } }, { INT2PRI13, 0, 32, 8, { 0, 0, SCIF1, SCIF0 } }, { INT2PRI16, 0, 32, 8, { IIC2_2, 0, 0, 0 } }, - { INT2PRI17, 0, 32, 8, { 0, 0, 0, IIC1_0 } }, + { INT2PRI17, 0, 32, 8, { PCIE, 0, 0, IIC1_0 } }, { INT2PRI18, 0, 32, 8, { IIC3_3, IIC9_1, IIC2_1, IIC1_2 } }, { INT2PRI19, 0, 32, 8, { IIC2_3, IIC3_1, 0, IIC1_3 } }, { INT2PRI20, 0, 32, 8, { IIC2_0, IIC6_3, IIC7_1, IIC7_2 } }, { INT2PRI21, 0, 32, 8, { IIC7_3, IIC8_0, IIC8_1, IIC8_2 } }, - { INT2PRI22, 0, 32, 8, { IIC9_2, MMC2, G200, 0 } }, - { INT2PRI23, 0, 32, 8, { PECI5, SGPIO, IIC3_2, IIC5_1 } }, - { INT2PRI24, 0, 32, 8, { PECI4, PECI3, 0, IIC1_1 } }, + { INT2PRI22, 0, 32, 8, { IIC9_2, 0, 0, 0 } }, + { INT2PRI23, 0, 32, 8, { 0, SGPIO, IIC3_2, IIC5_1 } }, + { INT2PRI24, 0, 32, 8, { 0, 0, 0, IIC1_1 } }, { INT2PRI25, 0, 32, 8, { IIC3_0, 0, IIC5_3, IIC5_2 } }, - { INT2PRI26, 0, 32, 8, { ECCU, RSPI, 0, IIC9_3 } }, - { INT2PRI27, 0, 32, 8, { PCIC, IIC6_0, IIC4_0, IIC6_1 } }, - { INT2PRI28, 0, 32, 8, { IIC4_3, IIC7_0, MMC1, IIC6_2 } }, + { INT2PRI26, 0, 32, 8, { 0, 0, 0, IIC9_3 } }, + { INT2PRI27, 0, 32, 8, { PCIINTA, IIC6_0, IIC4_0, IIC6_1 } }, + { INT2PRI28, 0, 32, 8, { IIC4_3, IIC7_0, 0, IIC6_2 } }, { INT2PRI29, 0, 32, 8, { 0, 0, IIC9_0, IIC8_3 } }, - { INT2PRI30, 0, 32, 8, { IIC4_1, IIC4_2, IIC5_0, ONFICTL } }, + { INT2PRI30, 0, 32, 8, { IIC4_1, IIC4_2, IIC5_0, 0 } }, { INT2PRI31, 0, 32, 8, { IIC0_0, IIC0_1, IIC0_2, IIC0_3 } }, - { INT2PRI32, 0, 32, 8, { DMINT22, 0, 0, 0 } }, - { INT2PRI33, 0, 32, 8, { 0, 0, 0, DMINT16 } }, - { INT2PRI34, 0, 32, 8, { 0, LPC6, DMINT21, DMINT18 } }, - { INT2PRI35, 0, 32, 8, { DMINT23, TSIP, 0, DMINT19 } }, - { INT2PRI36, 0, 32, 8, { DMINT20, GETHER1, PBIA, PBIB } }, - { INT2PRI37, 0, 32, 8, { PBIC, DMAE2, DMAE3, SERMUX2 } }, - { INT2PRI38, 0, 32, 8, { LPC7, 0, 0, 0 } }, - { INT2PRI39, 0, 32, 8, { 0, 0, 0, WDT4B } }, - { INT2PRI40, 0, 32, 8, { 0, 0, 0, DMINT17 } }, - { INT2PRI41, 0, 32, 8, { DDRECC, 0, WDT6B, WDT5B } }, - { INT2PRI42, 0, 32, 8, { 0, 0, 0, LPC8 } }, - { INT2PRI43, 0, 32, 8, { 0, WDT7B, PCIE_BRIDGE, WDT8B } }, - { INT2PRI44, 0, 32, 8, { WDT2B, GETHER2, 0, 0 } }, - { INT2PRI45, 0, 32, 8, { 0, 0, LPC5, SERMUX3 } }, - { INT2PRI46, 0, 32, 8, { WDT0B, WDT1B, WDT3B, GETHER0 } }, - { INT2PRI47, 0, 32, 8, { DMINT12, DMINT13, DMINT14, DMINT15 } }, -}; - -static struct intc_sense_reg sense_registers_irq8to15[] __initdata = { - { 0xffd100f8, 32, 2, /* ICR2 */ { IRQ15, IRQ14, IRQ13, IRQ12, - IRQ11, IRQ10, IRQ9, IRQ8 } }, }; static DECLARE_INTC_DESC(intc_desc, "sh7757", vectors, groups, - mask_registers, prio_registers, - sense_registers_irq8to15); + mask_registers, prio_registers, NULL); /* Support for external interrupt pins in IRQ mode */ static struct intc_vect vectors_irq0123[] __initdata = { diff --git a/trunk/arch/sh/kernel/cpu/sh4a/setup-sh7786.c b/trunk/arch/sh/kernel/cpu/sh4a/setup-sh7786.c index c016c0004714..8797723231ea 100644 --- a/trunk/arch/sh/kernel/cpu/sh4a/setup-sh7786.c +++ b/trunk/arch/sh/kernel/cpu/sh4a/setup-sh7786.c @@ -629,10 +629,33 @@ static void __init sh7786_usb_setup(void) } } +static int __init sh7786_devices_setup(void) +{ + int ret; + + sh7786_usb_setup(); + + ret = platform_add_devices(sh7786_early_devices, + ARRAY_SIZE(sh7786_early_devices)); + if (unlikely(ret != 0)) + return ret; + + return platform_add_devices(sh7786_devices, + ARRAY_SIZE(sh7786_devices)); +} +arch_initcall(sh7786_devices_setup); + +void __init plat_early_device_setup(void) +{ + early_platform_add_devices(sh7786_early_devices, + ARRAY_SIZE(sh7786_early_devices)); +} + enum { UNUSED = 0, /* interrupt sources */ + IRL0_LLLL, IRL0_LLLH, IRL0_LLHL, IRL0_LLHH, IRL0_LHLL, IRL0_LHLH, IRL0_LHHL, IRL0_LHHH, IRL0_HLLL, IRL0_HLLH, IRL0_HLHL, IRL0_HLHH, @@ -670,12 +693,9 @@ enum { Thermal, INTICI0, INTICI1, INTICI2, INTICI3, INTICI4, INTICI5, INTICI6, INTICI7, - - /* Muxed sub-events */ - TXI1, BRI1, RXI1, ERI1, }; -static struct intc_vect sh7786_vectors[] __initdata = { +static struct intc_vect vectors[] __initdata = { INTC_VECT(WDT, 0x3e0), INTC_VECT(TMU0_0, 0x400), INTC_VECT(TMU0_1, 0x420), INTC_VECT(TMU0_2, 0x440), INTC_VECT(TMU0_3, 0x460), @@ -736,12 +756,14 @@ static struct intc_vect sh7786_vectors[] __initdata = { #define INTDISTCR0 0xfe4100b0 #define INTDISTCR1 0xfe4100b4 +#define INTACK 0xfe4100b8 +#define INTACKCLR 0xfe4100bc #define INT2DISTCR0 0xfe410900 #define INT2DISTCR1 0xfe410904 #define INT2DISTCR2 0xfe410908 #define INT2DISTCR3 0xfe41090c -static struct intc_mask_reg sh7786_mask_registers[] __initdata = { +static struct intc_mask_reg mask_registers[] __initdata = { { CnINTMSK0, CnINTMSKCLR0, 32, { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 }, INTC_SMP_BALANCING(INTDISTCR0) }, @@ -785,7 +807,7 @@ static struct intc_mask_reg sh7786_mask_registers[] __initdata = { 0, 0, 0, 0, 0, 0, 0, 0 }, INTC_SMP_BALANCING(INT2DISTCR3) }, }; -static struct intc_prio_reg sh7786_prio_registers[] __initdata = { +static struct intc_prio_reg prio_registers[] __initdata = { { 0xfe410010, 0, 32, 4, /* INTPRI */ { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, { 0xfe410800, 0, 32, 8, /* INT2PRI0 */ { 0, 0, 0, WDT } }, @@ -829,27 +851,11 @@ static struct intc_prio_reg sh7786_prio_registers[] __initdata = { INTICI3, INTICI2, INTICI1, INTICI0 }, INTC_SMP(4, 2) }, }; -static struct intc_subgroup sh7786_subgroups[] __initdata = { - { 0xfe410c20, 32, SCIF1, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, TXI1, BRI1, RXI1, ERI1 } }, -}; - -static struct intc_desc sh7786_intc_desc __initdata = { - .name = "sh7786", - .hw = { - .vectors = sh7786_vectors, - .nr_vectors = ARRAY_SIZE(sh7786_vectors), - .mask_regs = sh7786_mask_registers, - .nr_mask_regs = ARRAY_SIZE(sh7786_mask_registers), - .subgroups = sh7786_subgroups, - .nr_subgroups = ARRAY_SIZE(sh7786_subgroups), - .prio_regs = sh7786_prio_registers, - .nr_prio_regs = ARRAY_SIZE(sh7786_prio_registers), - }, -}; +static DECLARE_INTC_DESC(intc_desc, "sh7786", vectors, NULL, + mask_registers, prio_registers, NULL); /* Support for external interrupt pins in IRQ mode */ + static struct intc_vect vectors_irq0123[] __initdata = { INTC_VECT(IRQ0, 0x200), INTC_VECT(IRQ1, 0x240), INTC_VECT(IRQ2, 0x280), INTC_VECT(IRQ3, 0x2c0), @@ -860,25 +866,23 @@ static struct intc_vect vectors_irq4567[] __initdata = { INTC_VECT(IRQ6, 0x380), INTC_VECT(IRQ7, 0x3c0), }; -static struct intc_sense_reg sh7786_sense_registers[] __initdata = { +static struct intc_sense_reg sense_registers[] __initdata = { { 0xfe41001c, 32, 2, /* ICR1 */ { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, }; -static struct intc_mask_reg sh7786_ack_registers[] __initdata = { +static struct intc_mask_reg ack_registers[] __initdata = { { 0xfe410024, 0, 32, /* INTREQ */ { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, }; static DECLARE_INTC_DESC_ACK(intc_desc_irq0123, "sh7786-irq0123", - vectors_irq0123, NULL, sh7786_mask_registers, - sh7786_prio_registers, sh7786_sense_registers, - sh7786_ack_registers); + vectors_irq0123, NULL, mask_registers, + prio_registers, sense_registers, ack_registers); static DECLARE_INTC_DESC_ACK(intc_desc_irq4567, "sh7786-irq4567", - vectors_irq4567, NULL, sh7786_mask_registers, - sh7786_prio_registers, sh7786_sense_registers, - sh7786_ack_registers); + vectors_irq4567, NULL, mask_registers, + prio_registers, sense_registers, ack_registers); /* External interrupt pins in IRL mode */ @@ -905,10 +909,10 @@ static struct intc_vect vectors_irl4567[] __initdata = { }; static DECLARE_INTC_DESC(intc_desc_irl0123, "sh7786-irl0123", vectors_irl0123, - NULL, sh7786_mask_registers, NULL, NULL); + NULL, mask_registers, NULL, NULL); static DECLARE_INTC_DESC(intc_desc_irl4567, "sh7786-irl4567", vectors_irl4567, - NULL, sh7786_mask_registers, NULL, NULL); + NULL, mask_registers, NULL, NULL); #define INTC_ICR0 0xfe410000 #define INTC_INTMSK0 CnINTMSK0 @@ -916,6 +920,19 @@ static DECLARE_INTC_DESC(intc_desc_irl4567, "sh7786-irl4567", vectors_irl4567, #define INTC_INTMSK2 INTMSK2 #define INTC_INTMSKCLR1 CnINTMSKCLR1 #define INTC_INTMSKCLR2 INTMSKCLR2 +#define INTC_USERIMASK 0xfe411000 + +#ifdef CONFIG_INTC_BALANCING +unsigned int irq_lookup(unsigned int irq) +{ + return __raw_readl(INTACK) & 1 ? irq : NO_IRQ_IGNORE; +} + +void irq_finish(unsigned int irq) +{ + __raw_writel(irq2evt(irq), INTACKCLR); +} +#endif void __init plat_irq_setup(void) { @@ -929,7 +946,8 @@ void __init plat_irq_setup(void) /* select IRL mode for IRL3-0 + IRL7-4 */ __raw_writel(__raw_readl(INTC_ICR0) & ~0x00c00000, INTC_ICR0); - register_intc_controller(&sh7786_intc_desc); + register_intc_controller(&intc_desc); + register_intc_userimask(INTC_USERIMASK); } void __init plat_irq_setup_pins(int mode) @@ -973,39 +991,3 @@ void __init plat_irq_setup_pins(int mode) void __init plat_mem_setup(void) { } - -static int __init sh7786_devices_setup(void) -{ - int ret, irq; - - sh7786_usb_setup(); - - /* - * De-mux SCIF1 IRQs if possible - */ - irq = intc_irq_lookup(sh7786_intc_desc.name, TXI1); - if (irq > 0) { - scif1_platform_data.irqs[SCIx_TXI_IRQ] = irq; - scif1_platform_data.irqs[SCIx_ERI_IRQ] = - intc_irq_lookup(sh7786_intc_desc.name, ERI1); - scif1_platform_data.irqs[SCIx_BRI_IRQ] = - intc_irq_lookup(sh7786_intc_desc.name, BRI1); - scif1_platform_data.irqs[SCIx_RXI_IRQ] = - intc_irq_lookup(sh7786_intc_desc.name, RXI1); - } - - ret = platform_add_devices(sh7786_early_devices, - ARRAY_SIZE(sh7786_early_devices)); - if (unlikely(ret != 0)) - return ret; - - return platform_add_devices(sh7786_devices, - ARRAY_SIZE(sh7786_devices)); -} -arch_initcall(sh7786_devices_setup); - -void __init plat_early_device_setup(void) -{ - early_platform_add_devices(sh7786_early_devices, - ARRAY_SIZE(sh7786_early_devices)); -} diff --git a/trunk/arch/sh/kernel/cpu/sh4a/setup-shx3.c b/trunk/arch/sh/kernel/cpu/sh4a/setup-shx3.c index 013f0b144489..9158bc5ea38b 100644 --- a/trunk/arch/sh/kernel/cpu/sh4a/setup-shx3.c +++ b/trunk/arch/sh/kernel/cpu/sh4a/setup-shx3.c @@ -1,7 +1,7 @@ /* * SH-X3 Prototype Setup * - * Copyright (C) 2007 - 2010 Paul Mundt + * Copyright (C) 2007 - 2009 Paul Mundt * * 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 @@ -12,9 +12,7 @@ #include #include #include -#include #include -#include #include /* @@ -356,10 +354,6 @@ static struct intc_group groups[] __initdata = { DMAC1_DMINT9, DMAC1_DMINT10, DMAC1_DMINT11), }; -#define INT2DISTCR0 0xfe4108a0 -#define INT2DISTCR1 0xfe4108a4 -#define INT2DISTCR2 0xfe4108a8 - static struct intc_mask_reg mask_registers[] __initdata = { { 0xfe410030, 0xfe410050, 32, /* CnINTMSK0 / CnINTMSKCLR0 */ { IRQ0, IRQ1, IRQ2, IRQ3 } }, @@ -369,23 +363,20 @@ static struct intc_mask_reg mask_registers[] __initdata = { { FE1, FE0, 0, ATAPI, VCORE0, VIN1, VIN0, IIC, DU, GPIO3, GPIO2, GPIO1, GPIO0, PAM, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* HUDI bits ignored */ - 0, TMU5, TMU4, TMU3, TMU2, TMU1, TMU0, 0, }, - INTC_SMP_BALANCING(INT2DISTCR0) }, + 0, TMU5, TMU4, TMU3, TMU2, TMU1, TMU0, 0, } }, { 0xfe410830, 0xfe410860, 32, /* CnINT2MSK1 / CnINT2MSKCLR1 */ { 0, 0, 0, 0, DTU3, DTU2, DTU1, DTU0, /* IRM bits ignored */ PCII9, PCII8, PCII7, PCII6, PCII5, PCII4, PCII3, PCII2, PCII1, PCII0, DMAC1_DMAE, DMAC1_DMINT11, DMAC1_DMINT10, DMAC1_DMINT9, DMAC1_DMINT8, DMAC1_DMINT7, DMAC1_DMINT6, DMAC0_DMAE, DMAC0_DMINT5, DMAC0_DMINT4, - DMAC0_DMINT3, DMAC0_DMINT2, DMAC0_DMINT1, DMAC0_DMINT0 }, - INTC_SMP_BALANCING(INT2DISTCR1) }, + DMAC0_DMINT3, DMAC0_DMINT2, DMAC0_DMINT1, DMAC0_DMINT0 } }, { 0xfe410840, 0xfe410870, 32, /* CnINT2MSK2 / CnINT2MSKCLR2 */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, SCIF3_TXI, SCIF3_BRI, SCIF3_RXI, SCIF3_ERI, SCIF2_TXI, SCIF2_BRI, SCIF2_RXI, SCIF2_ERI, SCIF1_TXI, SCIF1_BRI, SCIF1_RXI, SCIF1_ERI, - SCIF0_TXI, SCIF0_BRI, SCIF0_RXI, SCIF0_ERI }, - INTC_SMP_BALANCING(INT2DISTCR2) }, + SCIF0_TXI, SCIF0_BRI, SCIF0_RXI, SCIF0_ERI } }, }; static struct intc_prio_reg prio_registers[] __initdata = { @@ -442,33 +433,11 @@ static DECLARE_INTC_DESC(intc_desc_irl, "shx3-irl", vectors_irl, groups, void __init plat_irq_setup_pins(int mode) { - int ret = 0; - switch (mode) { case IRQ_MODE_IRQ: - ret |= gpio_request(GPIO_FN_IRQ3, intc_desc_irq.name); - ret |= gpio_request(GPIO_FN_IRQ2, intc_desc_irq.name); - ret |= gpio_request(GPIO_FN_IRQ1, intc_desc_irq.name); - ret |= gpio_request(GPIO_FN_IRQ0, intc_desc_irq.name); - - if (unlikely(ret)) { - pr_err("Failed to set IRQ mode\n"); - return; - } - register_intc_controller(&intc_desc_irq); break; case IRQ_MODE_IRL3210: - ret |= gpio_request(GPIO_FN_IRL3, intc_desc_irl.name); - ret |= gpio_request(GPIO_FN_IRL2, intc_desc_irl.name); - ret |= gpio_request(GPIO_FN_IRL1, intc_desc_irl.name); - ret |= gpio_request(GPIO_FN_IRL0, intc_desc_irl.name); - - if (unlikely(ret)) { - pr_err("Failed to set IRL mode\n"); - return; - } - register_intc_controller(&intc_desc_irl); break; default: @@ -478,9 +447,6 @@ void __init plat_irq_setup_pins(int mode) void __init plat_irq_setup(void) { - reserve_intc_vectors(vectors_irq, ARRAY_SIZE(vectors_irq)); - reserve_intc_vectors(vectors_irl, ARRAY_SIZE(vectors_irl)); - register_intc_controller(&intc_desc); } diff --git a/trunk/arch/sh/kernel/head_32.S b/trunk/arch/sh/kernel/head_32.S index 7db248936b60..6e35f012cc03 100644 --- a/trunk/arch/sh/kernel/head_32.S +++ b/trunk/arch/sh/kernel/head_32.S @@ -330,7 +330,7 @@ ENTRY(_stext) #if defined(CONFIG_CPU_SH2) 1: .long 0x000000F0 ! IMASK=0xF #else -1: .long 0x500080F0 ! MD=1, RB=0, BL=1, FD=1, IMASK=0xF +1: .long 0x400080F0 ! MD=1, RB=0, BL=0, FD=1, IMASK=0xF #endif ENTRY(stack_start) 2: .long init_thread_union+THREAD_SIZE diff --git a/trunk/arch/sh/kernel/io_trapped.c b/trunk/arch/sh/kernel/io_trapped.c index 32c385ef1011..2947d2bd1291 100644 --- a/trunk/arch/sh/kernel/io_trapped.c +++ b/trunk/arch/sh/kernel/io_trapped.c @@ -291,7 +291,7 @@ int handle_trapped_io(struct pt_regs *regs, unsigned long address) } tmp = handle_unaligned_access(instruction, regs, - &trapped_io_access, 1, address); + &trapped_io_access, 1); set_fs(oldfs); return tmp == 0; } diff --git a/trunk/arch/sh/kernel/irq.c b/trunk/arch/sh/kernel/irq.c index 9dc447db8a44..ae5bac39b896 100644 --- a/trunk/arch/sh/kernel/irq.c +++ b/trunk/arch/sh/kernel/irq.c @@ -283,8 +283,6 @@ void __init init_IRQ(void) if (sh_mv.mv_init_irq) sh_mv.mv_init_irq(); - intc_finalize(); - irq_ctx_init(smp_processor_id()); } diff --git a/trunk/arch/sh/kernel/kdebugfs.c b/trunk/arch/sh/kernel/kdebugfs.c deleted file mode 100644 index e11c30bb100c..000000000000 --- a/trunk/arch/sh/kernel/kdebugfs.c +++ /dev/null @@ -1,16 +0,0 @@ -#include -#include -#include - -struct dentry *arch_debugfs_dir; -EXPORT_SYMBOL(arch_debugfs_dir); - -static int __init arch_kdebugfs_init(void) -{ - arch_debugfs_dir = debugfs_create_dir("sh", NULL); - if (!arch_debugfs_dir) - return -ENOMEM; - - return 0; -} -arch_initcall(arch_kdebugfs_init); diff --git a/trunk/arch/sh/kernel/kprobes.c b/trunk/arch/sh/kernel/kprobes.c index 1208b09e95c3..4049d99f76e1 100644 --- a/trunk/arch/sh/kernel/kprobes.c +++ b/trunk/arch/sh/kernel/kprobes.c @@ -20,9 +20,9 @@ DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); -static DEFINE_PER_CPU(struct kprobe, saved_current_opcode); -static DEFINE_PER_CPU(struct kprobe, saved_next_opcode); -static DEFINE_PER_CPU(struct kprobe, saved_next_opcode2); +static struct kprobe saved_current_opcode; +static struct kprobe saved_next_opcode; +static struct kprobe saved_next_opcode2; #define OPCODE_JMP(x) (((x) & 0xF0FF) == 0x402b) #define OPCODE_JSR(x) (((x) & 0xF0FF) == 0x400b) @@ -102,21 +102,16 @@ int __kprobes kprobe_handle_illslot(unsigned long pc) void __kprobes arch_remove_kprobe(struct kprobe *p) { - struct kprobe *saved = &__get_cpu_var(saved_next_opcode); - - if (saved->addr) { + if (saved_next_opcode.addr != 0x0) { arch_disarm_kprobe(p); - arch_disarm_kprobe(saved); - - saved->addr = NULL; - saved->opcode = 0; - - saved = &__get_cpu_var(saved_next_opcode2); - if (saved->addr) { - arch_disarm_kprobe(saved); - - saved->addr = NULL; - saved->opcode = 0; + arch_disarm_kprobe(&saved_next_opcode); + saved_next_opcode.addr = 0x0; + saved_next_opcode.opcode = 0x0; + + if (saved_next_opcode2.addr != 0x0) { + arch_disarm_kprobe(&saved_next_opcode2); + saved_next_opcode2.addr = 0x0; + saved_next_opcode2.opcode = 0x0; } } } @@ -146,59 +141,57 @@ static void __kprobes set_current_kprobe(struct kprobe *p, struct pt_regs *regs, */ static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs) { - __get_cpu_var(saved_current_opcode).addr = (kprobe_opcode_t *)regs->pc; + kprobe_opcode_t *addr = NULL; + saved_current_opcode.addr = (kprobe_opcode_t *) (regs->pc); + addr = saved_current_opcode.addr; if (p != NULL) { - struct kprobe *op1, *op2; - arch_disarm_kprobe(p); - op1 = &__get_cpu_var(saved_next_opcode); - op2 = &__get_cpu_var(saved_next_opcode2); - if (OPCODE_JSR(p->opcode) || OPCODE_JMP(p->opcode)) { unsigned int reg_nr = ((p->opcode >> 8) & 0x000F); - op1->addr = (kprobe_opcode_t *) regs->regs[reg_nr]; + saved_next_opcode.addr = + (kprobe_opcode_t *) regs->regs[reg_nr]; } else if (OPCODE_BRA(p->opcode) || OPCODE_BSR(p->opcode)) { unsigned long disp = (p->opcode & 0x0FFF); - op1->addr = + saved_next_opcode.addr = (kprobe_opcode_t *) (regs->pc + 4 + disp * 2); } else if (OPCODE_BRAF(p->opcode) || OPCODE_BSRF(p->opcode)) { unsigned int reg_nr = ((p->opcode >> 8) & 0x000F); - op1->addr = + saved_next_opcode.addr = (kprobe_opcode_t *) (regs->pc + 4 + regs->regs[reg_nr]); } else if (OPCODE_RTS(p->opcode)) { - op1->addr = (kprobe_opcode_t *) regs->pr; + saved_next_opcode.addr = (kprobe_opcode_t *) regs->pr; } else if (OPCODE_BF(p->opcode) || OPCODE_BT(p->opcode)) { unsigned long disp = (p->opcode & 0x00FF); /* case 1 */ - op1->addr = p->addr + 1; + saved_next_opcode.addr = p->addr + 1; /* case 2 */ - op2->addr = + saved_next_opcode2.addr = (kprobe_opcode_t *) (regs->pc + 4 + disp * 2); - op2->opcode = *(op2->addr); - arch_arm_kprobe(op2); + saved_next_opcode2.opcode = *(saved_next_opcode2.addr); + arch_arm_kprobe(&saved_next_opcode2); } else if (OPCODE_BF_S(p->opcode) || OPCODE_BT_S(p->opcode)) { unsigned long disp = (p->opcode & 0x00FF); /* case 1 */ - op1->addr = p->addr + 2; + saved_next_opcode.addr = p->addr + 2; /* case 2 */ - op2->addr = + saved_next_opcode2.addr = (kprobe_opcode_t *) (regs->pc + 4 + disp * 2); - op2->opcode = *(op2->addr); - arch_arm_kprobe(op2); + saved_next_opcode2.opcode = *(saved_next_opcode2.addr); + arch_arm_kprobe(&saved_next_opcode2); } else { - op1->addr = p->addr + 1; + saved_next_opcode.addr = p->addr + 1; } - op1->opcode = *(op1->addr); - arch_arm_kprobe(op1); + saved_next_opcode.opcode = *(saved_next_opcode.addr); + arch_arm_kprobe(&saved_next_opcode); } } @@ -383,23 +376,21 @@ static int __kprobes post_kprobe_handler(struct pt_regs *regs) cur->post_handler(cur, regs, 0); } - p = &__get_cpu_var(saved_next_opcode); - if (p->addr) { - arch_disarm_kprobe(p); - p->addr = NULL; - p->opcode = 0; + if (saved_next_opcode.addr != 0x0) { + arch_disarm_kprobe(&saved_next_opcode); + saved_next_opcode.addr = 0x0; + saved_next_opcode.opcode = 0x0; - addr = __get_cpu_var(saved_current_opcode).addr; - __get_cpu_var(saved_current_opcode).addr = NULL; + addr = saved_current_opcode.addr; + saved_current_opcode.addr = 0x0; p = get_kprobe(addr); arch_arm_kprobe(p); - p = &__get_cpu_var(saved_next_opcode2); - if (p->addr) { - arch_disarm_kprobe(p); - p->addr = NULL; - p->opcode = 0; + if (saved_next_opcode2.addr != 0x0) { + arch_disarm_kprobe(&saved_next_opcode2); + saved_next_opcode2.addr = 0x0; + saved_next_opcode2.opcode = 0x0; } } @@ -581,5 +572,14 @@ static struct kprobe trampoline_p = { int __init arch_init_kprobes(void) { + saved_next_opcode.addr = 0x0; + saved_next_opcode.opcode = 0x0; + + saved_current_opcode.addr = 0x0; + saved_current_opcode.opcode = 0x0; + + saved_next_opcode2.addr = 0x0; + saved_next_opcode2.opcode = 0x0; + return register_kprobe(&trampoline_p); } diff --git a/trunk/arch/sh/kernel/ptrace.c b/trunk/arch/sh/kernel/ptrace.c deleted file mode 100644 index 0a05983633ca..000000000000 --- a/trunk/arch/sh/kernel/ptrace.c +++ /dev/null @@ -1,33 +0,0 @@ -#include - -/** - * regs_query_register_offset() - query register offset from its name - * @name: the name of a register - * - * regs_query_register_offset() returns the offset of a register in struct - * pt_regs from its name. If the name is invalid, this returns -EINVAL; - */ -int regs_query_register_offset(const char *name) -{ - const struct pt_regs_offset *roff; - for (roff = regoffset_table; roff->name != NULL; roff++) - if (!strcmp(roff->name, name)) - return roff->offset; - return -EINVAL; -} - -/** - * regs_query_register_name() - query register name from its offset - * @offset: the offset of a register in struct pt_regs. - * - * regs_query_register_name() returns the name of a register from its - * offset in struct pt_regs. If the @offset is invalid, this returns NULL; - */ -const char *regs_query_register_name(unsigned int offset) -{ - const struct pt_regs_offset *roff; - for (roff = regoffset_table; roff->name != NULL; roff++) - if (roff->offset == offset) - return roff->name; - return NULL; -} diff --git a/trunk/arch/sh/kernel/ptrace_32.c b/trunk/arch/sh/kernel/ptrace_32.c index 2cd42b58cb20..6c4bbba2a675 100644 --- a/trunk/arch/sh/kernel/ptrace_32.c +++ b/trunk/arch/sh/kernel/ptrace_32.c @@ -274,33 +274,6 @@ static int dspregs_active(struct task_struct *target, } #endif -const struct pt_regs_offset regoffset_table[] = { - REGS_OFFSET_NAME(0), - REGS_OFFSET_NAME(1), - REGS_OFFSET_NAME(2), - REGS_OFFSET_NAME(3), - REGS_OFFSET_NAME(4), - REGS_OFFSET_NAME(5), - REGS_OFFSET_NAME(6), - REGS_OFFSET_NAME(7), - REGS_OFFSET_NAME(8), - REGS_OFFSET_NAME(9), - REGS_OFFSET_NAME(10), - REGS_OFFSET_NAME(11), - REGS_OFFSET_NAME(12), - REGS_OFFSET_NAME(13), - REGS_OFFSET_NAME(14), - REGS_OFFSET_NAME(15), - REG_OFFSET_NAME(pc), - REG_OFFSET_NAME(pr), - REG_OFFSET_NAME(sr), - REG_OFFSET_NAME(gbr), - REG_OFFSET_NAME(mach), - REG_OFFSET_NAME(macl), - REG_OFFSET_NAME(tra), - REG_OFFSET_END, -}; - /* * These are our native regset flavours. */ diff --git a/trunk/arch/sh/kernel/ptrace_64.c b/trunk/arch/sh/kernel/ptrace_64.c index e0fb065914aa..5fd644da7f02 100644 --- a/trunk/arch/sh/kernel/ptrace_64.c +++ b/trunk/arch/sh/kernel/ptrace_64.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include @@ -252,85 +252,6 @@ static int fpregs_active(struct task_struct *target, } #endif -const struct pt_regs_offset regoffset_table[] = { - REG_OFFSET_NAME(pc), - REG_OFFSET_NAME(sr), - REG_OFFSET_NAME(syscall_nr), - REGS_OFFSET_NAME(0), - REGS_OFFSET_NAME(1), - REGS_OFFSET_NAME(2), - REGS_OFFSET_NAME(3), - REGS_OFFSET_NAME(4), - REGS_OFFSET_NAME(5), - REGS_OFFSET_NAME(6), - REGS_OFFSET_NAME(7), - REGS_OFFSET_NAME(8), - REGS_OFFSET_NAME(9), - REGS_OFFSET_NAME(10), - REGS_OFFSET_NAME(11), - REGS_OFFSET_NAME(12), - REGS_OFFSET_NAME(13), - REGS_OFFSET_NAME(14), - REGS_OFFSET_NAME(15), - REGS_OFFSET_NAME(16), - REGS_OFFSET_NAME(17), - REGS_OFFSET_NAME(18), - REGS_OFFSET_NAME(19), - REGS_OFFSET_NAME(20), - REGS_OFFSET_NAME(21), - REGS_OFFSET_NAME(22), - REGS_OFFSET_NAME(23), - REGS_OFFSET_NAME(24), - REGS_OFFSET_NAME(25), - REGS_OFFSET_NAME(26), - REGS_OFFSET_NAME(27), - REGS_OFFSET_NAME(28), - REGS_OFFSET_NAME(29), - REGS_OFFSET_NAME(30), - REGS_OFFSET_NAME(31), - REGS_OFFSET_NAME(32), - REGS_OFFSET_NAME(33), - REGS_OFFSET_NAME(34), - REGS_OFFSET_NAME(35), - REGS_OFFSET_NAME(36), - REGS_OFFSET_NAME(37), - REGS_OFFSET_NAME(38), - REGS_OFFSET_NAME(39), - REGS_OFFSET_NAME(40), - REGS_OFFSET_NAME(41), - REGS_OFFSET_NAME(42), - REGS_OFFSET_NAME(43), - REGS_OFFSET_NAME(44), - REGS_OFFSET_NAME(45), - REGS_OFFSET_NAME(46), - REGS_OFFSET_NAME(47), - REGS_OFFSET_NAME(48), - REGS_OFFSET_NAME(49), - REGS_OFFSET_NAME(50), - REGS_OFFSET_NAME(51), - REGS_OFFSET_NAME(52), - REGS_OFFSET_NAME(53), - REGS_OFFSET_NAME(54), - REGS_OFFSET_NAME(55), - REGS_OFFSET_NAME(56), - REGS_OFFSET_NAME(57), - REGS_OFFSET_NAME(58), - REGS_OFFSET_NAME(59), - REGS_OFFSET_NAME(60), - REGS_OFFSET_NAME(61), - REGS_OFFSET_NAME(62), - REGS_OFFSET_NAME(63), - TREGS_OFFSET_NAME(0), - TREGS_OFFSET_NAME(1), - TREGS_OFFSET_NAME(2), - TREGS_OFFSET_NAME(3), - TREGS_OFFSET_NAME(4), - TREGS_OFFSET_NAME(5), - TREGS_OFFSET_NAME(6), - TREGS_OFFSET_NAME(7), - REG_OFFSET_END, -}; - /* * These are our native regset flavours. */ @@ -474,9 +395,10 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) asmlinkage int sh64_ptrace(long request, long pid, long addr, long data) { #define WPC_DBRMODE 0x0d104008 - static unsigned long first_call; + static int first_call = 1; - if (!test_and_set_bit(0, &first_call)) { + lock_kernel(); + if (first_call) { /* Set WPC.DBRMODE to 0. This makes all debug events get * delivered through RESVEC, i.e. into the handlers in entry.S. * (If the kernel was downloaded using a remote gdb, WPC.DBRMODE @@ -486,7 +408,9 @@ asmlinkage int sh64_ptrace(long request, long pid, long addr, long data) * the remote gdb.) */ printk("DBRMODE set to 0 to permit native debugging\n"); poke_real_address_q(WPC_DBRMODE, 0); + first_call = 0; } + unlock_kernel(); return sys_ptrace(request, pid, addr, data); } diff --git a/trunk/arch/sh/kernel/reboot.c b/trunk/arch/sh/kernel/reboot.c index ca6a5ca64015..b1fca66bb92e 100644 --- a/trunk/arch/sh/kernel/reboot.c +++ b/trunk/arch/sh/kernel/reboot.c @@ -9,7 +9,6 @@ #include #include #include -#include void (*pm_power_off)(void); EXPORT_SYMBOL(pm_power_off); @@ -26,9 +25,6 @@ static void native_machine_restart(char * __unused) { local_irq_disable(); - /* Destroy all of the TLBs in preparation for reset by MMU */ - __flush_tlb_global(); - /* Address error with SR.BL=1 first. */ trigger_address_error(); diff --git a/trunk/arch/sh/kernel/setup.c b/trunk/arch/sh/kernel/setup.c index 4e278467f76c..e769401a78ba 100644 --- a/trunk/arch/sh/kernel/setup.c +++ b/trunk/arch/sh/kernel/setup.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -135,9 +136,8 @@ void __init check_for_initrd(void) goto disable; } - if (unlikely(start < __MEMORY_START)) { - pr_err("initrd start (%08lx) < __MEMORY_START(%x)\n", - start, __MEMORY_START); + if (unlikely(start < PAGE_OFFSET)) { + pr_err("initrd start < PAGE_OFFSET\n"); goto disable; } @@ -158,7 +158,7 @@ void __init check_for_initrd(void) /* * Address sanitization */ - initrd_start = (unsigned long)__va(start); + initrd_start = (unsigned long)__va(__pa(start)); initrd_end = initrd_start + INITRD_SIZE; memblock_reserve(__pa(initrd_start), INITRD_SIZE); @@ -458,3 +458,17 @@ const struct seq_operations cpuinfo_op = { .show = show_cpuinfo, }; #endif /* CONFIG_PROC_FS */ + +struct dentry *sh_debugfs_root; + +static int __init sh_debugfs_init(void) +{ + sh_debugfs_root = debugfs_create_dir("sh", NULL); + if (!sh_debugfs_root) + return -ENOMEM; + if (IS_ERR(sh_debugfs_root)) + return PTR_ERR(sh_debugfs_root); + + return 0; +} +arch_initcall(sh_debugfs_init); diff --git a/trunk/arch/sh/kernel/syscalls_32.S b/trunk/arch/sh/kernel/syscalls_32.S index e872e81add8a..19fd11dd9871 100644 --- a/trunk/arch/sh/kernel/syscalls_32.S +++ b/trunk/arch/sh/kernel/syscalls_32.S @@ -353,25 +353,3 @@ ENTRY(sys_call_table) .long sys_pwritev .long sys_rt_tgsigqueueinfo /* 335 */ .long sys_perf_event_open - .long sys_fanotify_init - .long sys_fanotify_mark - .long sys_prlimit64 - /* Broken-out socket family */ - .long sys_socket /* 340 */ - .long sys_bind - .long sys_connect - .long sys_listen - .long sys_accept - .long sys_getsockname /* 345 */ - .long sys_getpeername - .long sys_socketpair - .long sys_send - .long sys_sendto - .long sys_recv /* 350 */ - .long sys_recvfrom - .long sys_shutdown - .long sys_setsockopt - .long sys_getsockopt - .long sys_sendmsg /* 355 */ - .long sys_recvmsg - .long sys_recvmmsg diff --git a/trunk/arch/sh/kernel/syscalls_64.S b/trunk/arch/sh/kernel/syscalls_64.S index 66585708ce90..2048a20d7c80 100644 --- a/trunk/arch/sh/kernel/syscalls_64.S +++ b/trunk/arch/sh/kernel/syscalls_64.S @@ -393,6 +393,3 @@ sys_call_table: .long sys_perf_event_open .long sys_recvmmsg /* 365 */ .long sys_accept4 - .long sys_fanotify_init - .long sys_fanotify_mark - .long sys_prlimit64 diff --git a/trunk/arch/sh/kernel/traps_32.c b/trunk/arch/sh/kernel/traps_32.c index 3484c2f65aba..c3d86fa71ddf 100644 --- a/trunk/arch/sh/kernel/traps_32.c +++ b/trunk/arch/sh/kernel/traps_32.c @@ -5,7 +5,7 @@ * SuperH version: Copyright (C) 1999 Niibe Yutaka * Copyright (C) 2000 Philipp Rumpf * Copyright (C) 2000 David Howells - * Copyright (C) 2002 - 2010 Paul Mundt + * Copyright (C) 2002 - 2007 Paul Mundt * * 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 @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -370,8 +369,7 @@ static inline int handle_delayslot(struct pt_regs *regs, #define SH_PC_12BIT_OFFSET(instr) ((((signed short)(instr<<4))>>3) + 4) int handle_unaligned_access(insn_size_t instruction, struct pt_regs *regs, - struct mem_access *ma, int expected, - unsigned long address) + struct mem_access *ma, int expected) { u_int rm; int ret, index; @@ -385,18 +383,9 @@ int handle_unaligned_access(insn_size_t instruction, struct pt_regs *regs, index = (instruction>>8)&15; /* 0x0F00 */ rm = regs->regs[index]; - /* - * Log the unexpected fixups, and then pass them on to perf. - * - * We intentionally don't report the expected cases to perf as - * otherwise the trapped I/O case will skew the results too much - * to be useful. - */ - if (!expected) { + /* shout about fixups */ + if (!expected) unaligned_fixups_notify(current, instruction, regs); - perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, 0, - regs, address); - } ret = -EFAULT; switch (instruction&0xF000) { @@ -585,8 +574,7 @@ asmlinkage void do_address_error(struct pt_regs *regs, set_fs(USER_DS); tmp = handle_unaligned_access(instruction, regs, - &user_mem_access, 0, - address); + &user_mem_access, 0); set_fs(oldfs); if (tmp == 0) @@ -619,8 +607,8 @@ asmlinkage void do_address_error(struct pt_regs *regs, unaligned_fixups_notify(current, instruction, regs); - handle_unaligned_access(instruction, regs, &user_mem_access, - 0, address); + handle_unaligned_access(instruction, regs, + &user_mem_access, 0); set_fs(oldfs); } } @@ -814,9 +802,6 @@ void __cpuinit per_cpu_trap_init(void) : /* no output */ : "r" (&vbr_base) : "memory"); - - /* disable exception blocking now when the vbr has been setup */ - clear_bl_bit(); } void *set_exception_table_vec(unsigned int vec, void *handler) diff --git a/trunk/arch/sh/kernel/traps_64.c b/trunk/arch/sh/kernel/traps_64.c index 6713ca97e553..e67e140bf1f6 100644 --- a/trunk/arch/sh/kernel/traps_64.c +++ b/trunk/arch/sh/kernel/traps_64.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -51,7 +50,7 @@ asmlinkage void do_##name(unsigned long error_code, struct pt_regs *regs) \ do_unhandled_exception(trapnr, signr, str, __stringify(name), error_code, regs, current); \ } -static DEFINE_SPINLOCK(die_lock); +spinlock_t die_lock; void die(const char * str, struct pt_regs * regs, long err) { @@ -434,8 +433,6 @@ static int misaligned_load(struct pt_regs *regs, return error; } - perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, 0, regs, address); - destreg = (opcode >> 4) & 0x3f; if (user_mode(regs)) { __u64 buffer; @@ -512,8 +509,6 @@ static int misaligned_store(struct pt_regs *regs, return error; } - perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, 0, regs, address); - srcreg = (opcode >> 4) & 0x3f; if (user_mode(regs)) { __u64 buffer; @@ -588,8 +583,6 @@ static int misaligned_fpu_load(struct pt_regs *regs, return error; } - perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, 0, regs, address); - destreg = (opcode >> 4) & 0x3f; if (user_mode(regs)) { __u64 buffer; @@ -665,8 +658,6 @@ static int misaligned_fpu_store(struct pt_regs *regs, return error; } - perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, 0, regs, address); - srcreg = (opcode >> 4) & 0x3f; if (user_mode(regs)) { __u64 buffer; diff --git a/trunk/arch/sh/lib/Makefile b/trunk/arch/sh/lib/Makefile index 7b95f29e3174..dab4d2129812 100644 --- a/trunk/arch/sh/lib/Makefile +++ b/trunk/arch/sh/lib/Makefile @@ -30,4 +30,4 @@ lib-$(CONFIG_MMU) += copy_page.o __clear_user.o lib-$(CONFIG_MCOUNT) += mcount.o lib-y += $(memcpy-y) $(memset-y) $(udivsi3-y) -ccflags-y := -Werror +EXTRA_CFLAGS += -Werror diff --git a/trunk/arch/sh/math-emu/math.c b/trunk/arch/sh/math-emu/math.c index f76a5090d5d1..1fcdb1220975 100644 --- a/trunk/arch/sh/math-emu/math.c +++ b/trunk/arch/sh/math-emu/math.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include @@ -620,8 +619,6 @@ int do_fpu_inst(unsigned short inst, struct pt_regs *regs) struct task_struct *tsk = current; struct sh_fpu_soft_struct *fpu = &(tsk->thread.xstate->softfpu); - perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, 0, regs, 0); - if (!(task_thread_info(tsk)->status & TS_USEDFPU)) { /* initialize once. */ fpu_init(fpu); diff --git a/trunk/arch/sh/mm/Kconfig b/trunk/arch/sh/mm/Kconfig index 09370392aff1..1445ca6257df 100644 --- a/trunk/arch/sh/mm/Kconfig +++ b/trunk/arch/sh/mm/Kconfig @@ -168,10 +168,6 @@ config IOREMAP_FIXED config UNCACHED_MAPPING bool -config HAVE_SRAM_POOL - bool - select GENERIC_ALLOCATOR - choice prompt "Kernel page size" default PAGE_SIZE_4KB diff --git a/trunk/arch/sh/mm/Makefile b/trunk/arch/sh/mm/Makefile index ab89ea4f9414..53f7c684afb2 100644 --- a/trunk/arch/sh/mm/Makefile +++ b/trunk/arch/sh/mm/Makefile @@ -40,7 +40,6 @@ obj-$(CONFIG_PMB) += pmb.o obj-$(CONFIG_NUMA) += numa.o obj-$(CONFIG_IOREMAP_FIXED) += ioremap_fixed.o obj-$(CONFIG_UNCACHED_MAPPING) += uncached.o -obj-$(CONFIG_HAVE_SRAM_POOL) += sram.o # Special flags for fault_64.o. This puts restrictions on the number of # caller-save registers that the compiler can target when building this file. @@ -67,4 +66,4 @@ CFLAGS_fault_64.o += -ffixed-r7 \ -ffixed-r60 -ffixed-r61 -ffixed-r62 \ -fomit-frame-pointer -ccflags-y := -Werror +EXTRA_CFLAGS += -Werror diff --git a/trunk/arch/sh/mm/asids-debugfs.c b/trunk/arch/sh/mm/asids-debugfs.c index 74c03ecc4871..cd8c3bf39b5a 100644 --- a/trunk/arch/sh/mm/asids-debugfs.c +++ b/trunk/arch/sh/mm/asids-debugfs.c @@ -63,7 +63,7 @@ static int __init asids_debugfs_init(void) { struct dentry *asids_dentry; - asids_dentry = debugfs_create_file("asids", S_IRUSR, arch_debugfs_dir, + asids_dentry = debugfs_create_file("asids", S_IRUSR, sh_debugfs_root, NULL, &asids_debugfs_fops); if (!asids_dentry) return -ENOMEM; diff --git a/trunk/arch/sh/mm/cache-debugfs.c b/trunk/arch/sh/mm/cache-debugfs.c index 52411462c409..690ed010d002 100644 --- a/trunk/arch/sh/mm/cache-debugfs.c +++ b/trunk/arch/sh/mm/cache-debugfs.c @@ -126,19 +126,25 @@ static int __init cache_debugfs_init(void) { struct dentry *dcache_dentry, *icache_dentry; - dcache_dentry = debugfs_create_file("dcache", S_IRUSR, arch_debugfs_dir, + dcache_dentry = debugfs_create_file("dcache", S_IRUSR, sh_debugfs_root, (unsigned int *)CACHE_TYPE_DCACHE, &cache_debugfs_fops); if (!dcache_dentry) return -ENOMEM; + if (IS_ERR(dcache_dentry)) + return PTR_ERR(dcache_dentry); - icache_dentry = debugfs_create_file("icache", S_IRUSR, arch_debugfs_dir, + icache_dentry = debugfs_create_file("icache", S_IRUSR, sh_debugfs_root, (unsigned int *)CACHE_TYPE_ICACHE, &cache_debugfs_fops); if (!icache_dentry) { debugfs_remove(dcache_dentry); return -ENOMEM; } + if (IS_ERR(icache_dentry)) { + debugfs_remove(dcache_dentry); + return PTR_ERR(icache_dentry); + } return 0; } diff --git a/trunk/arch/sh/mm/consistent.c b/trunk/arch/sh/mm/consistent.c index 038793286990..c86a08540258 100644 --- a/trunk/arch/sh/mm/consistent.c +++ b/trunk/arch/sh/mm/consistent.c @@ -38,12 +38,11 @@ void *dma_generic_alloc_coherent(struct device *dev, size_t size, void *ret, *ret_nocache; int order = get_order(size); - gfp |= __GFP_ZERO; - ret = (void *)__get_free_pages(gfp, order); if (!ret) return NULL; + memset(ret, 0, size); /* * Pages from the page allocator may have data present in * cache. So flush the cache before using uncached memory. diff --git a/trunk/arch/sh/mm/init.c b/trunk/arch/sh/mm/init.c index 3385b28acaac..552bea5113f5 100644 --- a/trunk/arch/sh/mm/init.c +++ b/trunk/arch/sh/mm/init.c @@ -47,6 +47,7 @@ static pte_t *__get_pte_phys(unsigned long addr) pgd_t *pgd; pud_t *pud; pmd_t *pmd; + pte_t *pte; pgd = pgd_offset_k(addr); if (pgd_none(*pgd)) { @@ -66,7 +67,8 @@ static pte_t *__get_pte_phys(unsigned long addr) return NULL; } - return pte_offset_kernel(pmd, addr); + pte = pte_offset_kernel(pmd, addr); + return pte; } static void set_pte_phys(unsigned long addr, unsigned long phys, pgprot_t prot) @@ -123,45 +125,13 @@ void __clear_fixmap(enum fixed_addresses idx, pgprot_t prot) clear_pte_phys(address, prot); } -static pmd_t * __init one_md_table_init(pud_t *pud) -{ - if (pud_none(*pud)) { - pmd_t *pmd; - - pmd = alloc_bootmem_pages(PAGE_SIZE); - pud_populate(&init_mm, pud, pmd); - BUG_ON(pmd != pmd_offset(pud, 0)); - } - - return pmd_offset(pud, 0); -} - -static pte_t * __init one_page_table_init(pmd_t *pmd) -{ - if (pmd_none(*pmd)) { - pte_t *pte; - - pte = alloc_bootmem_pages(PAGE_SIZE); - pmd_populate_kernel(&init_mm, pmd, pte); - BUG_ON(pte != pte_offset_kernel(pmd, 0)); - } - - return pte_offset_kernel(pmd, 0); -} - -static pte_t * __init page_table_kmap_check(pte_t *pte, pmd_t *pmd, - unsigned long vaddr, pte_t *lastpte) -{ - return pte; -} - void __init page_table_range_init(unsigned long start, unsigned long end, pgd_t *pgd_base) { pgd_t *pgd; pud_t *pud; pmd_t *pmd; - pte_t *pte = NULL; + pte_t *pte; int i, j, k; unsigned long vaddr; @@ -174,13 +144,19 @@ void __init page_table_range_init(unsigned long start, unsigned long end, for ( ; (i < PTRS_PER_PGD) && (vaddr != end); pgd++, i++) { pud = (pud_t *)pgd; for ( ; (j < PTRS_PER_PUD) && (vaddr != end); pud++, j++) { - pmd = one_md_table_init(pud); -#ifndef __PAGETABLE_PMD_FOLDED +#ifdef __PAGETABLE_PMD_FOLDED + pmd = (pmd_t *)pud; +#else + pmd = (pmd_t *)alloc_bootmem_low_pages(PAGE_SIZE); + pud_populate(&init_mm, pud, pmd); pmd += k; #endif for (; (k < PTRS_PER_PMD) && (vaddr != end); pmd++, k++) { - pte = page_table_kmap_check(one_page_table_init(pmd), - pmd, vaddr, pte); + if (pmd_none(*pmd)) { + pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE); + pmd_populate_kernel(&init_mm, pmd, pte); + BUG_ON(pte != pte_offset_kernel(pmd, 0)); + } vaddr += PMD_SIZE; } k = 0; diff --git a/trunk/arch/sh/mm/nommu.c b/trunk/arch/sh/mm/nommu.c index 36312d254faf..7694f50c9034 100644 --- a/trunk/arch/sh/mm/nommu.c +++ b/trunk/arch/sh/mm/nommu.c @@ -67,10 +67,6 @@ void local_flush_tlb_kernel_range(unsigned long start, unsigned long end) BUG(); } -void __flush_tlb_global(void) -{ -} - void __update_tlb(struct vm_area_struct *vma, unsigned long address, pte_t pte) { } diff --git a/trunk/arch/sh/mm/pmb.c b/trunk/arch/sh/mm/pmb.c index b20b1b3eee4b..6379091a1647 100644 --- a/trunk/arch/sh/mm/pmb.c +++ b/trunk/arch/sh/mm/pmb.c @@ -40,7 +40,7 @@ struct pmb_entry { unsigned long flags; unsigned long size; - raw_spinlock_t lock; + spinlock_t lock; /* * 0 .. NR_PMB_ENTRIES for specific entry selection, or @@ -265,7 +265,7 @@ static struct pmb_entry *pmb_alloc(unsigned long vpn, unsigned long ppn, memset(pmbe, 0, sizeof(struct pmb_entry)); - raw_spin_lock_init(&pmbe->lock); + spin_lock_init(&pmbe->lock); pmbe->vpn = vpn; pmbe->ppn = ppn; @@ -327,9 +327,9 @@ static void set_pmb_entry(struct pmb_entry *pmbe) { unsigned long flags; - raw_spin_lock_irqsave(&pmbe->lock, flags); + spin_lock_irqsave(&pmbe->lock, flags); __set_pmb_entry(pmbe); - raw_spin_unlock_irqrestore(&pmbe->lock, flags); + spin_unlock_irqrestore(&pmbe->lock, flags); } #endif /* CONFIG_PM */ @@ -368,7 +368,7 @@ int pmb_bolt_mapping(unsigned long vaddr, phys_addr_t phys, return PTR_ERR(pmbe); } - raw_spin_lock_irqsave(&pmbe->lock, flags); + spin_lock_irqsave(&pmbe->lock, flags); pmbe->size = pmb_sizes[i].size; @@ -383,10 +383,9 @@ int pmb_bolt_mapping(unsigned long vaddr, phys_addr_t phys, * entries for easier tear-down. */ if (likely(pmbp)) { - raw_spin_lock_nested(&pmbp->lock, - SINGLE_DEPTH_NESTING); + spin_lock(&pmbp->lock); pmbp->link = pmbe; - raw_spin_unlock(&pmbp->lock); + spin_unlock(&pmbp->lock); } pmbp = pmbe; @@ -399,7 +398,7 @@ int pmb_bolt_mapping(unsigned long vaddr, phys_addr_t phys, i--; mapped++; - raw_spin_unlock_irqrestore(&pmbe->lock, flags); + spin_unlock_irqrestore(&pmbe->lock, flags); } } while (size >= SZ_16M); @@ -628,14 +627,15 @@ static void __init pmb_synchronize(void) continue; } - raw_spin_lock_irqsave(&pmbe->lock, irqflags); + spin_lock_irqsave(&pmbe->lock, irqflags); for (j = 0; j < ARRAY_SIZE(pmb_sizes); j++) if (pmb_sizes[j].flag == size) pmbe->size = pmb_sizes[j].size; if (pmbp) { - raw_spin_lock_nested(&pmbp->lock, SINGLE_DEPTH_NESTING); + spin_lock(&pmbp->lock); + /* * Compare the previous entry against the current one to * see if the entries span a contiguous mapping. If so, @@ -644,12 +644,13 @@ static void __init pmb_synchronize(void) */ if (pmb_can_merge(pmbp, pmbe)) pmbp->link = pmbe; - raw_spin_unlock(&pmbp->lock); + + spin_unlock(&pmbp->lock); } pmbp = pmbe; - raw_spin_unlock_irqrestore(&pmbe->lock, irqflags); + spin_unlock_irqrestore(&pmbe->lock, irqflags); } } @@ -756,7 +757,7 @@ static void __init pmb_resize(void) /* * Found it, now resize it. */ - raw_spin_lock_irqsave(&pmbe->lock, flags); + spin_lock_irqsave(&pmbe->lock, flags); pmbe->size = SZ_16M; pmbe->flags &= ~PMB_SZ_MASK; @@ -766,7 +767,7 @@ static void __init pmb_resize(void) __set_pmb_entry(pmbe); - raw_spin_unlock_irqrestore(&pmbe->lock, flags); + spin_unlock_irqrestore(&pmbe->lock, flags); } read_unlock(&pmb_rwlock); @@ -865,9 +866,11 @@ static int __init pmb_debugfs_init(void) struct dentry *dentry; dentry = debugfs_create_file("pmb", S_IFREG | S_IRUGO, - arch_debugfs_dir, NULL, &pmb_debugfs_fops); + sh_debugfs_root, NULL, &pmb_debugfs_fops); if (!dentry) return -ENOMEM; + if (IS_ERR(dentry)) + return PTR_ERR(dentry); return 0; } diff --git a/trunk/arch/sh/mm/sram.c b/trunk/arch/sh/mm/sram.c deleted file mode 100644 index bc156ec4545e..000000000000 --- a/trunk/arch/sh/mm/sram.c +++ /dev/null @@ -1,34 +0,0 @@ -/* - * SRAM pool for tiny memories not otherwise managed. - * - * Copyright (C) 2010 Paul Mundt - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - */ -#include -#include -#include - -/* - * This provides a standard SRAM pool for tiny memories that can be - * added either by the CPU or the platform code. Typical SRAM sizes - * to be inserted in to the pool will generally be less than the page - * size, with anything more reasonably sized handled as a NUMA memory - * node. - */ -struct gen_pool *sram_pool; - -static int __init sram_pool_init(void) -{ - /* - * This is a global pool, we don't care about node locality. - */ - sram_pool = gen_pool_create(1, -1); - if (unlikely(!sram_pool)) - return -ENOMEM; - - return 0; -} -core_initcall(sram_pool_init); diff --git a/trunk/arch/sh/mm/tlb-debugfs.c b/trunk/arch/sh/mm/tlb-debugfs.c index dea637a09246..229bf75f28df 100644 --- a/trunk/arch/sh/mm/tlb-debugfs.c +++ b/trunk/arch/sh/mm/tlb-debugfs.c @@ -151,13 +151,15 @@ static int __init tlb_debugfs_init(void) { struct dentry *itlb, *utlb; - itlb = debugfs_create_file("itlb", S_IRUSR, arch_debugfs_dir, + itlb = debugfs_create_file("itlb", S_IRUSR, sh_debugfs_root, (unsigned int *)TLB_TYPE_ITLB, &tlb_debugfs_fops); if (unlikely(!itlb)) return -ENOMEM; + if (IS_ERR(itlb)) + return PTR_ERR(itlb); - utlb = debugfs_create_file("utlb", S_IRUSR, arch_debugfs_dir, + utlb = debugfs_create_file("utlb", S_IRUSR, sh_debugfs_root, (unsigned int *)TLB_TYPE_UTLB, &tlb_debugfs_fops); if (unlikely(!utlb)) { @@ -165,6 +167,11 @@ static int __init tlb_debugfs_init(void) return -ENOMEM; } + if (IS_ERR(utlb)) { + debugfs_remove(itlb); + return PTR_ERR(utlb); + } + return 0; } module_init(tlb_debugfs_init); diff --git a/trunk/arch/sh/mm/tlbflush_32.c b/trunk/arch/sh/mm/tlbflush_32.c index a6a20d6de4c0..3fbe03ce8fe3 100644 --- a/trunk/arch/sh/mm/tlbflush_32.c +++ b/trunk/arch/sh/mm/tlbflush_32.c @@ -119,19 +119,3 @@ void local_flush_tlb_mm(struct mm_struct *mm) local_irq_restore(flags); } } - -void __flush_tlb_global(void) -{ - unsigned long flags; - - local_irq_save(flags); - - /* - * This is the most destructive of the TLB flushing options, - * and will tear down all of the UTLB/ITLB mappings, including - * wired entries. - */ - __raw_writel(__raw_readl(MMUCR) | MMUCR_TI, MMUCR); - - local_irq_restore(flags); -} diff --git a/trunk/arch/sh/mm/tlbflush_64.c b/trunk/arch/sh/mm/tlbflush_64.c index 7f5810f5dfdc..03db41cc1268 100644 --- a/trunk/arch/sh/mm/tlbflush_64.c +++ b/trunk/arch/sh/mm/tlbflush_64.c @@ -455,11 +455,6 @@ void local_flush_tlb_kernel_range(unsigned long start, unsigned long end) flush_tlb_all(); } -void __flush_tlb_global(void) -{ - flush_tlb_all(); -} - void __update_tlb(struct vm_area_struct *vma, unsigned long address, pte_t pte) { } diff --git a/trunk/arch/sh/tools/mach-types b/trunk/arch/sh/tools/mach-types index 9f56eb978024..b25aa554ee5e 100644 --- a/trunk/arch/sh/tools/mach-types +++ b/trunk/arch/sh/tools/mach-types @@ -52,8 +52,6 @@ MIGOR SH_MIGOR RSK7201 SH_RSK7201 RSK7203 SH_RSK7203 AP325RXA SH_AP325RXA -SH2007 SH_SH2007 -SH7757LCR SH_SH7757LCR SH7763RDP SH_SH7763RDP SH7785LCR SH_SH7785LCR SH7785LCR_PT SH_SH7785LCR_PT diff --git a/trunk/arch/sparc/Kconfig b/trunk/arch/sparc/Kconfig index 8e7bafc5dd0e..3e9d31401fb2 100644 --- a/trunk/arch/sparc/Kconfig +++ b/trunk/arch/sparc/Kconfig @@ -19,7 +19,6 @@ config SPARC bool default y select OF - select OF_PROMTREE select HAVE_IDE select HAVE_OPROFILE select HAVE_ARCH_KGDB if !SMP || SPARC64 diff --git a/trunk/arch/sparc/include/asm/Kbuild b/trunk/arch/sparc/include/asm/Kbuild index 3c93f08ce187..deeb0fba8029 100644 --- a/trunk/arch/sparc/include/asm/Kbuild +++ b/trunk/arch/sparc/include/asm/Kbuild @@ -7,6 +7,7 @@ header-y += display7seg.h header-y += envctrl.h header-y += fbio.h header-y += jsflash.h +header-y += openprom.h header-y += openpromio.h header-y += perfctr.h header-y += psrcompat.h diff --git a/trunk/arch/sparc/include/asm/floppy_32.h b/trunk/arch/sparc/include/asm/floppy_32.h index 86666f70322e..c792830636de 100644 --- a/trunk/arch/sparc/include/asm/floppy_32.h +++ b/trunk/arch/sparc/include/asm/floppy_32.h @@ -304,8 +304,7 @@ static struct linux_prom_registers fd_regs[2]; static int sun_floppy_init(void) { char state[128]; - phandle tnode, fd_node; - int num_regs; + int tnode, fd_node, num_regs; struct resource r; use_virtual_dma = 1; diff --git a/trunk/arch/sparc/include/asm/openprom.h b/trunk/arch/sparc/include/asm/openprom.h index 81cd43432dc0..963e1a45c35f 100644 --- a/trunk/arch/sparc/include/asm/openprom.h +++ b/trunk/arch/sparc/include/asm/openprom.h @@ -11,8 +11,6 @@ #define LINUX_OPPROM_MAGIC 0x10010407 #ifndef __ASSEMBLY__ -#include - /* V0 prom device operations. */ struct linux_dev_v0_funcs { int (*v0_devopen)(char *device_str); @@ -28,7 +26,7 @@ struct linux_dev_v0_funcs { /* V2 and later prom device operations. */ struct linux_dev_v2_funcs { - phandle (*v2_inst2pkg)(int d); /* Convert ihandle to phandle */ + int (*v2_inst2pkg)(int d); /* Convert ihandle to phandle */ char * (*v2_dumb_mem_alloc)(char *va, unsigned sz); void (*v2_dumb_mem_free)(char *va, unsigned sz); @@ -170,12 +168,12 @@ struct linux_romvec { /* Routines for traversing the prom device tree. */ struct linux_nodeops { - phandle (*no_nextnode)(phandle node); - phandle (*no_child)(phandle node); - int (*no_proplen)(phandle node, const char *name); - int (*no_getprop)(phandle node, const char *name, char *val); - int (*no_setprop)(phandle node, const char *name, char *val, int len); - char * (*no_nextprop)(phandle node, char *name); + int (*no_nextnode)(int node); + int (*no_child)(int node); + int (*no_proplen)(int node, const char *name); + int (*no_getprop)(int node, const char *name, char *val); + int (*no_setprop)(int node, const char *name, char *val, int len); + char * (*no_nextprop)(int node, char *name); }; /* More fun PROM structures for device probing. */ diff --git a/trunk/arch/sparc/include/asm/oplib_32.h b/trunk/arch/sparc/include/asm/oplib_32.h index 51296a6f5005..33e31ce6b31f 100644 --- a/trunk/arch/sparc/include/asm/oplib_32.h +++ b/trunk/arch/sparc/include/asm/oplib_32.h @@ -30,7 +30,7 @@ extern unsigned int prom_rev, prom_prev; /* Root node of the prom device tree, this stays constant after * initialization is complete. */ -extern phandle prom_root_node; +extern int prom_root_node; /* Pointer to prom structure containing the device tree traversal * and usage utility functions. Only prom-lib should use these, @@ -178,68 +178,68 @@ extern void prom_putsegment(int context, unsigned long virt_addr, /* PROM device tree traversal functions... */ /* Get the child node of the given node, or zero if no child exists. */ -extern phandle prom_getchild(phandle parent_node); +extern int prom_getchild(int parent_node); /* Get the next sibling node of the given node, or zero if no further * siblings exist. */ -extern phandle prom_getsibling(phandle node); +extern int prom_getsibling(int node); /* Get the length, at the passed node, of the given property type. * Returns -1 on error (ie. no such property at this node). */ -extern int prom_getproplen(phandle thisnode, const char *property); +extern int prom_getproplen(int thisnode, const char *property); /* Fetch the requested property using the given buffer. Returns * the number of bytes the prom put into your buffer or -1 on error. */ -extern int __must_check prom_getproperty(phandle thisnode, const char *property, +extern int __must_check prom_getproperty(int thisnode, const char *property, char *prop_buffer, int propbuf_size); /* Acquire an integer property. */ -extern int prom_getint(phandle node, char *property); +extern int prom_getint(int node, char *property); /* Acquire an integer property, with a default value. */ -extern int prom_getintdefault(phandle node, char *property, int defval); +extern int prom_getintdefault(int node, char *property, int defval); /* Acquire a boolean property, 0=FALSE 1=TRUE. */ -extern int prom_getbool(phandle node, char *prop); +extern int prom_getbool(int node, char *prop); /* Acquire a string property, null string on error. */ -extern void prom_getstring(phandle node, char *prop, char *buf, int bufsize); +extern void prom_getstring(int node, char *prop, char *buf, int bufsize); /* Does the passed node have the given "name"? YES=1 NO=0 */ -extern int prom_nodematch(phandle thisnode, char *name); +extern int prom_nodematch(int thisnode, char *name); /* Search all siblings starting at the passed node for "name" matching * the given string. Returns the node on success, zero on failure. */ -extern phandle prom_searchsiblings(phandle node_start, char *name); +extern int prom_searchsiblings(int node_start, char *name); /* Return the first property type, as a string, for the given node. * Returns a null string on error. */ -extern char *prom_firstprop(phandle node, char *buffer); +extern char *prom_firstprop(int node, char *buffer); /* Returns the next property after the passed property for the given * node. Returns null string on failure. */ -extern char *prom_nextprop(phandle node, char *prev_property, char *buffer); +extern char *prom_nextprop(int node, char *prev_property, char *buffer); /* Returns phandle of the path specified */ -extern phandle prom_finddevice(char *name); +extern int prom_finddevice(char *name); /* Returns 1 if the specified node has given property. */ -extern int prom_node_has_property(phandle node, char *property); +extern int prom_node_has_property(int node, char *property); /* Set the indicated property at the given node with the passed value. * Returns the number of bytes of your value that the prom took. */ -extern int prom_setprop(phandle node, const char *prop_name, char *prop_value, +extern int prom_setprop(int node, const char *prop_name, char *prop_value, int value_size); -extern phandle prom_pathtoinode(char *path); -extern phandle prom_inst2pkg(int); +extern int prom_pathtoinode(char *path); +extern int prom_inst2pkg(int); /* Dorking with Bus ranges... */ @@ -247,13 +247,13 @@ extern phandle prom_inst2pkg(int); extern void prom_apply_obio_ranges(struct linux_prom_registers *obioregs, int nregs); /* Apply ranges of any prom node (and optionally parent node as well) to registers. */ -extern void prom_apply_generic_ranges(phandle node, phandle parent, +extern void prom_apply_generic_ranges(int node, int parent, struct linux_prom_registers *sbusregs, int nregs); /* CPU probing helpers. */ -int cpu_find_by_instance(int instance, phandle *prom_node, int *mid); -int cpu_find_by_mid(int mid, phandle *prom_node); -int cpu_get_hwmid(phandle prom_node); +int cpu_find_by_instance(int instance, int *prom_node, int *mid); +int cpu_find_by_mid(int mid, int *prom_node); +int cpu_get_hwmid(int prom_node); extern spinlock_t prom_lock; diff --git a/trunk/arch/sparc/include/asm/oplib_64.h b/trunk/arch/sparc/include/asm/oplib_64.h index c9cc078e3e31..3e0b2d62303d 100644 --- a/trunk/arch/sparc/include/asm/oplib_64.h +++ b/trunk/arch/sparc/include/asm/oplib_64.h @@ -16,7 +16,7 @@ extern char prom_version[]; /* Root node of the prom device tree, this stays constant after * initialization is complete. */ -extern phandle prom_root_node; +extern int prom_root_node; /* PROM stdin and stdout */ extern int prom_stdin, prom_stdout; @@ -24,7 +24,7 @@ extern int prom_stdin, prom_stdout; /* /chosen node of the prom device tree, this stays constant after * initialization is complete. */ -extern phandle prom_chosen_node; +extern int prom_chosen_node; /* Helper values and strings in arch/sparc64/kernel/head.S */ extern const char prom_peer_name[]; @@ -218,69 +218,68 @@ extern void prom_unmap(unsigned long size, unsigned long vaddr); /* PROM device tree traversal functions... */ /* Get the child node of the given node, or zero if no child exists. */ -extern phandle prom_getchild(phandle parent_node); +extern int prom_getchild(int parent_node); /* Get the next sibling node of the given node, or zero if no further * siblings exist. */ -extern phandle prom_getsibling(phandle node); +extern int prom_getsibling(int node); /* Get the length, at the passed node, of the given property type. * Returns -1 on error (ie. no such property at this node). */ -extern int prom_getproplen(phandle thisnode, const char *property); +extern int prom_getproplen(int thisnode, const char *property); /* Fetch the requested property using the given buffer. Returns * the number of bytes the prom put into your buffer or -1 on error. */ -extern int prom_getproperty(phandle thisnode, const char *property, +extern int prom_getproperty(int thisnode, const char *property, char *prop_buffer, int propbuf_size); /* Acquire an integer property. */ -extern int prom_getint(phandle node, const char *property); +extern int prom_getint(int node, const char *property); /* Acquire an integer property, with a default value. */ -extern int prom_getintdefault(phandle node, const char *property, int defval); +extern int prom_getintdefault(int node, const char *property, int defval); /* Acquire a boolean property, 0=FALSE 1=TRUE. */ -extern int prom_getbool(phandle node, const char *prop); +extern int prom_getbool(int node, const char *prop); /* Acquire a string property, null string on error. */ -extern void prom_getstring(phandle node, const char *prop, char *buf, - int bufsize); +extern void prom_getstring(int node, const char *prop, char *buf, int bufsize); /* Does the passed node have the given "name"? YES=1 NO=0 */ -extern int prom_nodematch(phandle thisnode, const char *name); +extern int prom_nodematch(int thisnode, const char *name); /* Search all siblings starting at the passed node for "name" matching * the given string. Returns the node on success, zero on failure. */ -extern phandle prom_searchsiblings(phandle node_start, const char *name); +extern int prom_searchsiblings(int node_start, const char *name); /* Return the first property type, as a string, for the given node. * Returns a null string on error. Buffer should be at least 32B long. */ -extern char *prom_firstprop(phandle node, char *buffer); +extern char *prom_firstprop(int node, char *buffer); /* Returns the next property after the passed property for the given * node. Returns null string on failure. Buffer should be at least 32B long. */ -extern char *prom_nextprop(phandle node, const char *prev_property, char *buf); +extern char *prom_nextprop(int node, const char *prev_property, char *buffer); /* Returns 1 if the specified node has given property. */ -extern int prom_node_has_property(phandle node, const char *property); +extern int prom_node_has_property(int node, const char *property); /* Returns phandle of the path specified */ -extern phandle prom_finddevice(const char *name); +extern int prom_finddevice(const char *name); /* Set the indicated property at the given node with the passed value. * Returns the number of bytes of your value that the prom took. */ -extern int prom_setprop(phandle node, const char *prop_name, char *prop_value, +extern int prom_setprop(int node, const char *prop_name, char *prop_value, int value_size); -extern phandle prom_pathtoinode(const char *path); -extern phandle prom_inst2pkg(int); +extern int prom_pathtoinode(const char *path); +extern int prom_inst2pkg(int); extern int prom_service_exists(const char *service_name); extern void prom_sun4v_guest_soft_state(void); diff --git a/trunk/arch/sparc/include/asm/prom.h b/trunk/arch/sparc/include/asm/prom.h index 56bbaadef646..291f12575edd 100644 --- a/trunk/arch/sparc/include/asm/prom.h +++ b/trunk/arch/sparc/include/asm/prom.h @@ -18,7 +18,6 @@ * 2 of the License, or (at your option) any later version. */ #include -#include #include #include #include @@ -68,8 +67,8 @@ extern struct device_node *of_console_device; extern char *of_console_path; extern char *of_console_options; -extern void irq_trans_init(struct device_node *dp); -extern char *build_path_component(struct device_node *dp); +extern void (*prom_build_more)(struct device_node *dp, struct device_node ***nextp); +extern char *build_full_name(struct device_node *dp); #endif /* __KERNEL__ */ #endif /* _SPARC_PROM_H */ diff --git a/trunk/arch/sparc/kernel/auxio_32.c b/trunk/arch/sparc/kernel/auxio_32.c index 35f48837871a..ee8d214cae1e 100644 --- a/trunk/arch/sparc/kernel/auxio_32.c +++ b/trunk/arch/sparc/kernel/auxio_32.c @@ -23,7 +23,7 @@ static DEFINE_SPINLOCK(auxio_lock); void __init auxio_probe(void) { - phandle node, auxio_nd; + int node, auxio_nd; struct linux_prom_registers auxregs[1]; struct resource r; @@ -113,7 +113,7 @@ volatile unsigned char * auxio_power_register = NULL; void __init auxio_power_probe(void) { struct linux_prom_registers regs; - phandle node; + int node; struct resource r; /* Attempt to find the sun4m power control node. */ diff --git a/trunk/arch/sparc/kernel/btext.c b/trunk/arch/sparc/kernel/btext.c index 89aa4eb20cf5..8cc2d56ffe9a 100644 --- a/trunk/arch/sparc/kernel/btext.c +++ b/trunk/arch/sparc/kernel/btext.c @@ -40,7 +40,7 @@ static unsigned char *dispDeviceBase __force_data; static unsigned char vga_font[cmapsz]; -static int __init btext_initialize(phandle node) +static int __init btext_initialize(unsigned int node) { unsigned int width, height, depth, pitch; unsigned long address = 0; @@ -309,7 +309,7 @@ static struct console btext_console = { int __init btext_find_display(void) { - phandle node; + unsigned int node; char type[32]; int ret; diff --git a/trunk/arch/sparc/kernel/devices.c b/trunk/arch/sparc/kernel/devices.c index d2eddd6647cd..62dc7a021413 100644 --- a/trunk/arch/sparc/kernel/devices.c +++ b/trunk/arch/sparc/kernel/devices.c @@ -31,9 +31,9 @@ static char *cpu_mid_prop(void) return "mid"; } -static int check_cpu_node(phandle nd, int *cur_inst, - int (*compare)(phandle, int, void *), void *compare_arg, - phandle *prom_node, int *mid) +static int check_cpu_node(int nd, int *cur_inst, + int (*compare)(int, int, void *), void *compare_arg, + int *prom_node, int *mid) { if (!compare(nd, *cur_inst, compare_arg)) { if (prom_node) @@ -51,8 +51,8 @@ static int check_cpu_node(phandle nd, int *cur_inst, return -ENODEV; } -static int __cpu_find_by(int (*compare)(phandle, int, void *), - void *compare_arg, phandle *prom_node, int *mid) +static int __cpu_find_by(int (*compare)(int, int, void *), void *compare_arg, + int *prom_node, int *mid) { struct device_node *dp; int cur_inst; @@ -71,7 +71,7 @@ static int __cpu_find_by(int (*compare)(phandle, int, void *), return -ENODEV; } -static int cpu_instance_compare(phandle nd, int instance, void *_arg) +static int cpu_instance_compare(int nd, int instance, void *_arg) { int desired_instance = (int) _arg; @@ -80,13 +80,13 @@ static int cpu_instance_compare(phandle nd, int instance, void *_arg) return -ENODEV; } -int cpu_find_by_instance(int instance, phandle *prom_node, int *mid) +int cpu_find_by_instance(int instance, int *prom_node, int *mid) { return __cpu_find_by(cpu_instance_compare, (void *)instance, prom_node, mid); } -static int cpu_mid_compare(phandle nd, int instance, void *_arg) +static int cpu_mid_compare(int nd, int instance, void *_arg) { int desired_mid = (int) _arg; int this_mid; @@ -98,7 +98,7 @@ static int cpu_mid_compare(phandle nd, int instance, void *_arg) return -ENODEV; } -int cpu_find_by_mid(int mid, phandle *prom_node) +int cpu_find_by_mid(int mid, int *prom_node) { return __cpu_find_by(cpu_mid_compare, (void *)mid, prom_node, NULL); @@ -108,7 +108,7 @@ int cpu_find_by_mid(int mid, phandle *prom_node) * address (0-3). This gives us the true hardware mid, which might have * some other bits set. On 4d hardware and software mids are the same. */ -int cpu_get_hwmid(phandle prom_node) +int cpu_get_hwmid(int prom_node) { return prom_getintdefault(prom_node, cpu_mid_prop(), -ENODEV); } @@ -119,8 +119,7 @@ void __init device_scan(void) #ifndef CONFIG_SMP { - phandle cpu_node; - int err; + int err, cpu_node; err = cpu_find_by_instance(0, &cpu_node, NULL); if (err) { /* Probably a sun4e, Sun is trying to trick us ;-) */ diff --git a/trunk/arch/sparc/kernel/leon_kernel.c b/trunk/arch/sparc/kernel/leon_kernel.c index 2d51527d810f..6a7b4dbc8e09 100644 --- a/trunk/arch/sparc/kernel/leon_kernel.c +++ b/trunk/arch/sparc/kernel/leon_kernel.c @@ -282,5 +282,5 @@ void __init leon_init_IRQ(void) void __init leon_init(void) { - of_pdt_build_more = &leon_node_init; + prom_build_more = &leon_node_init; } diff --git a/trunk/arch/sparc/kernel/pcic.c b/trunk/arch/sparc/kernel/pcic.c index aeaa09a3c655..d36a8d391ca0 100644 --- a/trunk/arch/sparc/kernel/pcic.c +++ b/trunk/arch/sparc/kernel/pcic.c @@ -284,7 +284,7 @@ int __init pcic_probe(void) struct linux_prom_registers regs[PROMREG_MAX]; struct linux_pbm_info* pbm; char namebuf[64]; - phandle node; + int node; int err; if (pcic0_up) { @@ -440,7 +440,7 @@ static int __devinit pdev_to_pnode(struct linux_pbm_info *pbm, { struct linux_prom_pci_registers regs[PROMREG_MAX]; int err; - phandle node = prom_getchild(pbm->prom_node); + int node = prom_getchild(pbm->prom_node); while(node) { err = prom_getproperty(node, "reg", diff --git a/trunk/arch/sparc/kernel/prom.h b/trunk/arch/sparc/kernel/prom.h index cf5fe1c0b024..eeb04a782ec8 100644 --- a/trunk/arch/sparc/kernel/prom.h +++ b/trunk/arch/sparc/kernel/prom.h @@ -4,6 +4,12 @@ #include #include +extern void * prom_early_alloc(unsigned long size); +extern void irq_trans_init(struct device_node *dp); + +extern unsigned int prom_unique_id; + +extern char *build_path_component(struct device_node *dp); extern void of_console_init(void); extern unsigned int prom_early_allocated; diff --git a/trunk/arch/sparc/kernel/prom_common.c b/trunk/arch/sparc/kernel/prom_common.c index ed25834328f4..1f830da2ddf2 100644 --- a/trunk/arch/sparc/kernel/prom_common.c +++ b/trunk/arch/sparc/kernel/prom_common.c @@ -20,13 +20,14 @@ #include #include #include -#include #include #include #include #include "prom.h" +void (*prom_build_more)(struct device_node *dp, struct device_node ***nextp); + struct device_node *of_console_device; EXPORT_SYMBOL(of_console_device); @@ -118,47 +119,192 @@ int of_find_in_proplist(const char *list, const char *match, int len) } EXPORT_SYMBOL(of_find_in_proplist); -/* - * SPARC32 and SPARC64's prom_nextprop() do things differently - * here, despite sharing the same interface. SPARC32 doesn't fill in 'buf', - * returning NULL on an error. SPARC64 fills in 'buf', but sets it to an - * empty string upon error. - */ -static int __init handle_nextprop_quirks(char *buf, const char *name) +unsigned int prom_unique_id; + +static struct property * __init build_one_prop(phandle node, char *prev, + char *special_name, + void *special_val, + int special_len) { - if (!name || strlen(name) == 0) - return -1; + static struct property *tmp = NULL; + struct property *p; + const char *name; + + if (tmp) { + p = tmp; + memset(p, 0, sizeof(*p) + 32); + tmp = NULL; + } else { + p = prom_early_alloc(sizeof(struct property) + 32); + p->unique_id = prom_unique_id++; + } + + p->name = (char *) (p + 1); + if (special_name) { + strcpy(p->name, special_name); + p->length = special_len; + p->value = prom_early_alloc(special_len); + memcpy(p->value, special_val, special_len); + } else { + if (prev == NULL) { + name = prom_firstprop(node, p->name); + } else { + name = prom_nextprop(node, prev, p->name); + } + if (!name || strlen(name) == 0) { + tmp = p; + return NULL; + } #ifdef CONFIG_SPARC32 - strcpy(buf, name); + strcpy(p->name, name); #endif - return 0; + p->length = prom_getproplen(node, p->name); + if (p->length <= 0) { + p->length = 0; + } else { + int len; + + p->value = prom_early_alloc(p->length + 1); + len = prom_getproperty(node, p->name, p->value, + p->length); + if (len <= 0) + p->length = 0; + ((unsigned char *)p->value)[p->length] = '\0'; + } + } + return p; } -static int __init prom_common_nextprop(phandle node, char *prev, char *buf) +static struct property * __init build_prop_list(phandle node) { - const char *name; + struct property *head, *tail; + + head = tail = build_one_prop(node, NULL, + ".node", &node, sizeof(node)); - buf[0] = '\0'; - name = prom_nextprop(node, prev, buf); - return handle_nextprop_quirks(buf, name); + tail->next = build_one_prop(node, NULL, NULL, NULL, 0); + tail = tail->next; + while(tail) { + tail->next = build_one_prop(node, tail->name, + NULL, NULL, 0); + tail = tail->next; + } + + return head; } -unsigned int prom_early_allocated __initdata; +static char * __init get_one_property(phandle node, const char *name) +{ + char *buf = ""; + int len; + + len = prom_getproplen(node, name); + if (len > 0) { + buf = prom_early_alloc(len); + len = prom_getproperty(node, name, buf, len); + } + + return buf; +} + +static struct device_node * __init prom_create_node(phandle node, + struct device_node *parent) +{ + struct device_node *dp; + + if (!node) + return NULL; + + dp = prom_early_alloc(sizeof(*dp)); + dp->unique_id = prom_unique_id++; + dp->parent = parent; + + kref_init(&dp->kref); -static struct of_pdt_ops prom_sparc_ops __initdata = { - .nextprop = prom_common_nextprop, - .getproplen = prom_getproplen, - .getproperty = prom_getproperty, - .getchild = prom_getchild, - .getsibling = prom_getsibling, -}; + dp->name = get_one_property(node, "name"); + dp->type = get_one_property(node, "device_type"); + dp->phandle = node; + + dp->properties = build_prop_list(node); + + irq_trans_init(dp); + + return dp; +} + +char * __init build_full_name(struct device_node *dp) +{ + int len, ourlen, plen; + char *n; + + plen = strlen(dp->parent->full_name); + ourlen = strlen(dp->path_component_name); + len = ourlen + plen + 2; + + n = prom_early_alloc(len); + strcpy(n, dp->parent->full_name); + if (!of_node_is_root(dp->parent)) { + strcpy(n + plen, "/"); + plen++; + } + strcpy(n + plen, dp->path_component_name); + + return n; +} + +static struct device_node * __init prom_build_tree(struct device_node *parent, + phandle node, + struct device_node ***nextp) +{ + struct device_node *ret = NULL, *prev_sibling = NULL; + struct device_node *dp; + + while (1) { + dp = prom_create_node(node, parent); + if (!dp) + break; + + if (prev_sibling) + prev_sibling->sibling = dp; + + if (!ret) + ret = dp; + prev_sibling = dp; + + *(*nextp) = dp; + *nextp = &dp->allnext; + + dp->path_component_name = build_path_component(dp); + dp->full_name = build_full_name(dp); + + dp->child = prom_build_tree(dp, prom_getchild(node), nextp); + + if (prom_build_more) + prom_build_more(dp, nextp); + + node = prom_getsibling(node); + } + + return ret; +} + +unsigned int prom_early_allocated __initdata; void __init prom_build_devicetree(void) { - of_pdt_build_devicetree(prom_root_node, &prom_sparc_ops); + struct device_node **nextp; + + allnodes = prom_create_node(prom_root_node, NULL); + allnodes->path_component_name = ""; + allnodes->full_name = "/"; + + nextp = &allnodes->allnext; + allnodes->child = prom_build_tree(allnodes, + prom_getchild(allnodes->phandle), + &nextp); of_console_init(); - pr_info("PROM: Built device tree with %u bytes of memory.\n", - prom_early_allocated); + printk("PROM: Built device tree with %u bytes of memory.\n", + prom_early_allocated); } diff --git a/trunk/arch/sparc/kernel/setup_64.c b/trunk/arch/sparc/kernel/setup_64.c index 29bafe051bb1..5f72de67588b 100644 --- a/trunk/arch/sparc/kernel/setup_64.c +++ b/trunk/arch/sparc/kernel/setup_64.c @@ -315,7 +315,7 @@ void __init setup_arch(char **cmdline_p) #ifdef CONFIG_IP_PNP if (!ic_set_manually) { - phandle chosen = prom_finddevice("/chosen"); + int chosen = prom_finddevice ("/chosen"); u32 cl, sv, gw; cl = prom_getintdefault (chosen, "client-ip", 0); diff --git a/trunk/arch/sparc/kernel/starfire.c b/trunk/arch/sparc/kernel/starfire.c index a4446c0fb7a1..060d0f3a6151 100644 --- a/trunk/arch/sparc/kernel/starfire.c +++ b/trunk/arch/sparc/kernel/starfire.c @@ -23,7 +23,7 @@ int this_is_starfire = 0; void check_if_starfire(void) { - phandle ssnode = prom_finddevice("/ssp-serial"); + int ssnode = prom_finddevice("/ssp-serial"); if (ssnode != 0 && ssnode != -1) this_is_starfire = 1; } diff --git a/trunk/arch/sparc/kernel/tadpole.c b/trunk/arch/sparc/kernel/tadpole.c index 9aba8bd5a78b..f476a5f4af6a 100644 --- a/trunk/arch/sparc/kernel/tadpole.c +++ b/trunk/arch/sparc/kernel/tadpole.c @@ -100,7 +100,7 @@ static void swift_clockstop(void) void __init clock_stop_probe(void) { - phandle node, clk_nd; + unsigned int node, clk_nd; char name[20]; prom_getstring(prom_root_node, "name", name, sizeof(name)); diff --git a/trunk/arch/sparc/mm/init_64.c b/trunk/arch/sparc/mm/init_64.c index 2f6ae1d1fb6b..4c2572773b55 100644 --- a/trunk/arch/sparc/mm/init_64.c +++ b/trunk/arch/sparc/mm/init_64.c @@ -88,7 +88,7 @@ static void __init read_obp_memory(const char *property, struct linux_prom64_registers *regs, int *num_ents) { - phandle node = prom_finddevice("/memory"); + int node = prom_finddevice("/memory"); int prop_size = prom_getproplen(node, property); int ents, ret, i; diff --git a/trunk/arch/sparc/mm/srmmu.c b/trunk/arch/sparc/mm/srmmu.c index 92319aa8b662..b0b43aa5e45a 100644 --- a/trunk/arch/sparc/mm/srmmu.c +++ b/trunk/arch/sparc/mm/srmmu.c @@ -1262,8 +1262,7 @@ extern unsigned long bootmem_init(unsigned long *pages_avail); void __init srmmu_paging_init(void) { - int i; - phandle cpunode; + int i, cpunode; char node_str[128]; pgd_t *pgd; pmd_t *pmd; @@ -1399,8 +1398,7 @@ static void __init srmmu_is_bad(void) static void __init init_vac_layout(void) { - phandle nd; - int cache_lines; + int nd, cache_lines; char node_str[128]; #ifdef CONFIG_SMP int cpu = 0; @@ -2084,7 +2082,7 @@ static void __init get_srmmu_type(void) /* Next check for Fujitsu Swift. */ if(psr_typ == 0 && psr_vers == 4) { - phandle cpunode; + int cpunode; char node_str[128]; /* Look if it is not a TurboSparc emulating Swift... */ diff --git a/trunk/arch/sparc/mm/sun4c.c b/trunk/arch/sparc/mm/sun4c.c index ddd0d86e508e..4289f90f8697 100644 --- a/trunk/arch/sparc/mm/sun4c.c +++ b/trunk/arch/sparc/mm/sun4c.c @@ -420,7 +420,7 @@ volatile unsigned long __iomem *sun4c_memerr_reg = NULL; void __init sun4c_probe_memerr_reg(void) { - phandle node; + int node; struct linux_prom_registers regs[1]; node = prom_getchild(prom_root_node); diff --git a/trunk/arch/sparc/prom/init_32.c b/trunk/arch/sparc/prom/init_32.c index d342dba4dd54..ccb36c7f9b8c 100644 --- a/trunk/arch/sparc/prom/init_32.c +++ b/trunk/arch/sparc/prom/init_32.c @@ -20,7 +20,7 @@ enum prom_major_version prom_vers; unsigned int prom_rev, prom_prev; /* The root node of the prom device tree. */ -phandle prom_root_node; +int prom_root_node; EXPORT_SYMBOL(prom_root_node); /* Pointer to the device tree operations structure. */ diff --git a/trunk/arch/sparc/prom/init_64.c b/trunk/arch/sparc/prom/init_64.c index 3ff911e7d25b..7b00f89490a4 100644 --- a/trunk/arch/sparc/prom/init_64.c +++ b/trunk/arch/sparc/prom/init_64.c @@ -19,7 +19,7 @@ char prom_version[80]; /* The root node of the prom device tree. */ int prom_stdin, prom_stdout; -phandle prom_chosen_node; +int prom_chosen_node; /* You must call prom_init() before you attempt to use any of the * routines in the prom library. It returns 0 on success, 1 on @@ -30,7 +30,7 @@ extern void prom_cif_init(void *, void *); void __init prom_init(void *cif_handler, void *cif_stack) { - phandle node; + int node; prom_cif_init(cif_handler, cif_stack); diff --git a/trunk/arch/sparc/prom/memory.c b/trunk/arch/sparc/prom/memory.c index 3f263a64857d..fac7899a29c3 100644 --- a/trunk/arch/sparc/prom/memory.c +++ b/trunk/arch/sparc/prom/memory.c @@ -31,8 +31,7 @@ static int __init prom_meminit_v0(void) static int __init prom_meminit_v2(void) { struct linux_prom_registers reg[64]; - phandle node; - int size, num_ents, i; + int node, size, num_ents, i; node = prom_searchsiblings(prom_getchild(prom_root_node), "memory"); size = prom_getproperty(node, "available", (char *) reg, sizeof(reg)); diff --git a/trunk/arch/sparc/prom/misc_64.c b/trunk/arch/sparc/prom/misc_64.c index d24bc44e361e..6cb1581d6aef 100644 --- a/trunk/arch/sparc/prom/misc_64.c +++ b/trunk/arch/sparc/prom/misc_64.c @@ -183,8 +183,7 @@ unsigned char prom_get_idprom(char *idbuf, int num_bytes) int prom_get_mmu_ihandle(void) { - phandle node; - int ret; + int node, ret; if (prom_mmu_ihandle_cache != 0) return prom_mmu_ihandle_cache; @@ -202,8 +201,7 @@ int prom_get_mmu_ihandle(void) static int prom_get_memory_ihandle(void) { static int memory_ihandle_cache; - phandle node; - int ret; + int node, ret; if (memory_ihandle_cache != 0) return memory_ihandle_cache; diff --git a/trunk/arch/sparc/prom/ranges.c b/trunk/arch/sparc/prom/ranges.c index 541fc829c207..aeff43e44e45 100644 --- a/trunk/arch/sparc/prom/ranges.c +++ b/trunk/arch/sparc/prom/ranges.c @@ -68,7 +68,7 @@ EXPORT_SYMBOL(prom_apply_obio_ranges); void __init prom_ranges_init(void) { - phandle node, obio_node; + int node, obio_node; int success; num_obio_ranges = 0; @@ -89,8 +89,8 @@ void __init prom_ranges_init(void) prom_printf("PROMLIB: obio_ranges %d\n", num_obio_ranges); } -void prom_apply_generic_ranges(phandle node, phandle parent, - struct linux_prom_registers *regs, int nregs) +void +prom_apply_generic_ranges (int node, int parent, struct linux_prom_registers *regs, int nregs) { int success; int num_ranges; diff --git a/trunk/arch/sparc/prom/tree_32.c b/trunk/arch/sparc/prom/tree_32.c index 63e08e149774..b21592f8e3fe 100644 --- a/trunk/arch/sparc/prom/tree_32.c +++ b/trunk/arch/sparc/prom/tree_32.c @@ -20,10 +20,10 @@ extern void restore_current(void); static char promlib_buf[128]; /* Internal version of prom_getchild that does not alter return values. */ -phandle __prom_getchild(phandle node) +int __prom_getchild(int node) { unsigned long flags; - phandle cnode; + int cnode; spin_lock_irqsave(&prom_lock, flags); cnode = prom_nodeops->no_child(node); @@ -36,9 +36,9 @@ phandle __prom_getchild(phandle node) /* Return the child of node 'node' or zero if no this node has no * direct descendent. */ -phandle prom_getchild(phandle node) +int prom_getchild(int node) { - phandle cnode; + int cnode; if (node == -1) return 0; @@ -52,10 +52,10 @@ phandle prom_getchild(phandle node) EXPORT_SYMBOL(prom_getchild); /* Internal version of prom_getsibling that does not alter return values. */ -phandle __prom_getsibling(phandle node) +int __prom_getsibling(int node) { unsigned long flags; - phandle cnode; + int cnode; spin_lock_irqsave(&prom_lock, flags); cnode = prom_nodeops->no_nextnode(node); @@ -68,9 +68,9 @@ phandle __prom_getsibling(phandle node) /* Return the next sibling of node 'node' or zero if no more siblings * at this level of depth in the tree. */ -phandle prom_getsibling(phandle node) +int prom_getsibling(int node) { - phandle sibnode; + int sibnode; if (node == -1) return 0; @@ -86,7 +86,7 @@ EXPORT_SYMBOL(prom_getsibling); /* Return the length in bytes of property 'prop' at node 'node'. * Return -1 on error. */ -int prom_getproplen(phandle node, const char *prop) +int prom_getproplen(int node, const char *prop) { int ret; unsigned long flags; @@ -106,7 +106,7 @@ EXPORT_SYMBOL(prom_getproplen); * 'buffer' which has a size of 'bufsize'. If the acquisition * was successful the length will be returned, else -1 is returned. */ -int prom_getproperty(phandle node, const char *prop, char *buffer, int bufsize) +int prom_getproperty(int node, const char *prop, char *buffer, int bufsize) { int plen, ret; unsigned long flags; @@ -126,7 +126,7 @@ EXPORT_SYMBOL(prom_getproperty); /* Acquire an integer property and return its value. Returns -1 * on failure. */ -int prom_getint(phandle node, char *prop) +int prom_getint(int node, char *prop) { static int intprop; @@ -140,7 +140,7 @@ EXPORT_SYMBOL(prom_getint); /* Acquire an integer property, upon error return the passed default * integer. */ -int prom_getintdefault(phandle node, char *property, int deflt) +int prom_getintdefault(int node, char *property, int deflt) { int retval; @@ -152,7 +152,7 @@ int prom_getintdefault(phandle node, char *property, int deflt) EXPORT_SYMBOL(prom_getintdefault); /* Acquire a boolean property, 1=TRUE 0=FALSE. */ -int prom_getbool(phandle node, char *prop) +int prom_getbool(int node, char *prop) { int retval; @@ -166,7 +166,7 @@ EXPORT_SYMBOL(prom_getbool); * string on error. The char pointer is the user supplied string * buffer. */ -void prom_getstring(phandle node, char *prop, char *user_buf, int ubuf_size) +void prom_getstring(int node, char *prop, char *user_buf, int ubuf_size) { int len; @@ -180,7 +180,7 @@ EXPORT_SYMBOL(prom_getstring); /* Does the device at node 'node' have name 'name'? * YES = 1 NO = 0 */ -int prom_nodematch(phandle node, char *name) +int prom_nodematch(int node, char *name) { int error; @@ -194,11 +194,10 @@ int prom_nodematch(phandle node, char *name) /* Search siblings at 'node_start' for a node with name * 'nodename'. Return node if successful, zero if not. */ -phandle prom_searchsiblings(phandle node_start, char *nodename) +int prom_searchsiblings(int node_start, char *nodename) { - phandle thisnode; - int error; + int thisnode, error; for(thisnode = node_start; thisnode; thisnode=prom_getsibling(thisnode)) { @@ -214,7 +213,7 @@ phandle prom_searchsiblings(phandle node_start, char *nodename) EXPORT_SYMBOL(prom_searchsiblings); /* Interal version of nextprop that does not alter return values. */ -char *__prom_nextprop(phandle node, char * oprop) +char * __prom_nextprop(int node, char * oprop) { unsigned long flags; char *prop; @@ -229,7 +228,7 @@ char *__prom_nextprop(phandle node, char * oprop) /* Return the first property name for node 'node'. */ /* buffer is unused argument, but as v9 uses it, we need to have the same interface */ -char *prom_firstprop(phandle node, char *bufer) +char * prom_firstprop(int node, char *bufer) { if (node == 0 || node == -1) return ""; @@ -242,7 +241,7 @@ EXPORT_SYMBOL(prom_firstprop); * at node 'node' . Returns empty string if no more * property types for this node. */ -char *prom_nextprop(phandle node, char *oprop, char *buffer) +char * prom_nextprop(int node, char *oprop, char *buffer) { if (node == 0 || node == -1) return ""; @@ -251,11 +250,11 @@ char *prom_nextprop(phandle node, char *oprop, char *buffer) } EXPORT_SYMBOL(prom_nextprop); -phandle prom_finddevice(char *name) +int prom_finddevice(char *name) { char nbuf[128]; char *s = name, *d; - phandle node = prom_root_node, node2; + int node = prom_root_node, node2; unsigned int which_io, phys_addr; struct linux_prom_registers reg[PROMREG_MAX]; @@ -299,7 +298,7 @@ phandle prom_finddevice(char *name) } EXPORT_SYMBOL(prom_finddevice); -int prom_node_has_property(phandle node, char *prop) +int prom_node_has_property(int node, char *prop) { char *current_property = ""; @@ -315,7 +314,7 @@ EXPORT_SYMBOL(prom_node_has_property); /* Set property 'pname' at node 'node' to value 'value' which has a length * of 'size' bytes. Return the number of bytes the prom accepted. */ -int prom_setprop(phandle node, const char *pname, char *value, int size) +int prom_setprop(int node, const char *pname, char *value, int size) { unsigned long flags; int ret; @@ -330,9 +329,9 @@ int prom_setprop(phandle node, const char *pname, char *value, int size) } EXPORT_SYMBOL(prom_setprop); -phandle prom_inst2pkg(int inst) +int prom_inst2pkg(int inst) { - phandle node; + int node; unsigned long flags; spin_lock_irqsave(&prom_lock, flags); @@ -346,10 +345,9 @@ phandle prom_inst2pkg(int inst) /* Return 'node' assigned to a particular prom 'path' * FIXME: Should work for v0 as well */ -phandle prom_pathtoinode(char *path) +int prom_pathtoinode(char *path) { - phandle node; - int inst; + int node, inst; inst = prom_devopen (path); if (inst == -1) return 0; diff --git a/trunk/arch/sparc/prom/tree_64.c b/trunk/arch/sparc/prom/tree_64.c index 691be68932f8..9d3f9137a43a 100644 --- a/trunk/arch/sparc/prom/tree_64.c +++ b/trunk/arch/sparc/prom/tree_64.c @@ -16,7 +16,7 @@ #include #include -static phandle prom_node_to_node(const char *type, phandle node) +static int prom_node_to_node(const char *type, int node) { unsigned long args[5]; @@ -28,20 +28,20 @@ static phandle prom_node_to_node(const char *type, phandle node) p1275_cmd_direct(args); - return (phandle) args[4]; + return (int) args[4]; } /* Return the child of node 'node' or zero if no this node has no * direct descendent. */ -inline phandle __prom_getchild(phandle node) +inline int __prom_getchild(int node) { return prom_node_to_node("child", node); } -inline phandle prom_getchild(phandle node) +inline int prom_getchild(int node) { - phandle cnode; + int cnode; if (node == -1) return 0; @@ -52,9 +52,9 @@ inline phandle prom_getchild(phandle node) } EXPORT_SYMBOL(prom_getchild); -inline phandle prom_getparent(phandle node) +inline int prom_getparent(int node) { - phandle cnode; + int cnode; if (node == -1) return 0; @@ -67,14 +67,14 @@ inline phandle prom_getparent(phandle node) /* Return the next sibling of node 'node' or zero if no more siblings * at this level of depth in the tree. */ -inline phandle __prom_getsibling(phandle node) +inline int __prom_getsibling(int node) { return prom_node_to_node(prom_peer_name, node); } -inline phandle prom_getsibling(phandle node) +inline int prom_getsibling(int node) { - phandle sibnode; + int sibnode; if (node == -1) return 0; @@ -89,7 +89,7 @@ EXPORT_SYMBOL(prom_getsibling); /* Return the length in bytes of property 'prop' at node 'node'. * Return -1 on error. */ -inline int prom_getproplen(phandle node, const char *prop) +inline int prom_getproplen(int node, const char *prop) { unsigned long args[6]; @@ -113,7 +113,7 @@ EXPORT_SYMBOL(prom_getproplen); * 'buffer' which has a size of 'bufsize'. If the acquisition * was successful the length will be returned, else -1 is returned. */ -inline int prom_getproperty(phandle node, const char *prop, +inline int prom_getproperty(int node, const char *prop, char *buffer, int bufsize) { unsigned long args[8]; @@ -141,7 +141,7 @@ EXPORT_SYMBOL(prom_getproperty); /* Acquire an integer property and return its value. Returns -1 * on failure. */ -inline int prom_getint(phandle node, const char *prop) +inline int prom_getint(int node, const char *prop) { int intprop; @@ -156,7 +156,7 @@ EXPORT_SYMBOL(prom_getint); * integer. */ -int prom_getintdefault(phandle node, const char *property, int deflt) +int prom_getintdefault(int node, const char *property, int deflt) { int retval; @@ -169,7 +169,7 @@ int prom_getintdefault(phandle node, const char *property, int deflt) EXPORT_SYMBOL(prom_getintdefault); /* Acquire a boolean property, 1=TRUE 0=FALSE. */ -int prom_getbool(phandle node, const char *prop) +int prom_getbool(int node, const char *prop) { int retval; @@ -184,8 +184,7 @@ EXPORT_SYMBOL(prom_getbool); * string on error. The char pointer is the user supplied string * buffer. */ -void prom_getstring(phandle node, const char *prop, char *user_buf, - int ubuf_size) +void prom_getstring(int node, const char *prop, char *user_buf, int ubuf_size) { int len; @@ -199,7 +198,7 @@ EXPORT_SYMBOL(prom_getstring); /* Does the device at node 'node' have name 'name'? * YES = 1 NO = 0 */ -int prom_nodematch(phandle node, const char *name) +int prom_nodematch(int node, const char *name) { char namebuf[128]; prom_getproperty(node, "name", namebuf, sizeof(namebuf)); @@ -211,10 +210,10 @@ int prom_nodematch(phandle node, const char *name) /* Search siblings at 'node_start' for a node with name * 'nodename'. Return node if successful, zero if not. */ -phandle prom_searchsiblings(phandle node_start, const char *nodename) +int prom_searchsiblings(int node_start, const char *nodename) { - phandle thisnode; - int error; + + int thisnode, error; char promlib_buf[128]; for(thisnode = node_start; thisnode; @@ -235,7 +234,7 @@ static const char *prom_nextprop_name = "nextprop"; /* Return the first property type for node 'node'. * buffer should be at least 32B in length */ -inline char *prom_firstprop(phandle node, char *buffer) +inline char *prom_firstprop(int node, char *buffer) { unsigned long args[7]; @@ -261,7 +260,7 @@ EXPORT_SYMBOL(prom_firstprop); * at node 'node' . Returns NULL string if no more * property types for this node. */ -inline char *prom_nextprop(phandle node, const char *oprop, char *buffer) +inline char *prom_nextprop(int node, const char *oprop, char *buffer) { unsigned long args[7]; char buf[32]; @@ -289,7 +288,8 @@ inline char *prom_nextprop(phandle node, const char *oprop, char *buffer) } EXPORT_SYMBOL(prom_nextprop); -phandle prom_finddevice(const char *name) +int +prom_finddevice(const char *name) { unsigned long args[5]; @@ -307,7 +307,7 @@ phandle prom_finddevice(const char *name) } EXPORT_SYMBOL(prom_finddevice); -int prom_node_has_property(phandle node, const char *prop) +int prom_node_has_property(int node, const char *prop) { char buf [32]; @@ -325,7 +325,7 @@ EXPORT_SYMBOL(prom_node_has_property); * of 'size' bytes. Return the number of bytes the prom accepted. */ int -prom_setprop(phandle node, const char *pname, char *value, int size) +prom_setprop(int node, const char *pname, char *value, int size) { unsigned long args[8]; @@ -355,10 +355,10 @@ prom_setprop(phandle node, const char *pname, char *value, int size) } EXPORT_SYMBOL(prom_setprop); -inline phandle prom_inst2pkg(int inst) +inline int prom_inst2pkg(int inst) { unsigned long args[5]; - phandle node; + int node; args[0] = (unsigned long) "instance-to-package"; args[1] = 1; @@ -377,10 +377,10 @@ inline phandle prom_inst2pkg(int inst) /* Return 'node' assigned to a particular prom 'path' * FIXME: Should work for v0 as well */ -phandle prom_pathtoinode(const char *path) +int +prom_pathtoinode(const char *path) { - phandle node; - int inst; + int node, inst; inst = prom_devopen (path); if (inst == 0) diff --git a/trunk/arch/x86/include/asm/olpc.h b/trunk/arch/x86/include/asm/olpc.h index 42a978c0c1b3..101229b0d8ed 100644 --- a/trunk/arch/x86/include/asm/olpc.h +++ b/trunk/arch/x86/include/asm/olpc.h @@ -89,8 +89,6 @@ extern int olpc_ec_mask_unset(uint8_t bits); /* EC commands */ #define EC_FIRMWARE_REV 0x08 -#define EC_WLAN_ENTER_RESET 0x35 -#define EC_WLAN_LEAVE_RESET 0x25 /* SCI source values */ diff --git a/trunk/block/blk-core.c b/trunk/block/blk-core.c index f0834e2f5727..881fe44ec7da 100644 --- a/trunk/block/blk-core.c +++ b/trunk/block/blk-core.c @@ -64,13 +64,15 @@ static void drive_stat_acct(struct request *rq, int new_io) return; cpu = part_stat_lock(); - part = disk_map_sector_rcu(rq->rq_disk, blk_rq_pos(rq)); - if (!new_io) + if (!new_io) { + part = rq->part; part_stat_inc(cpu, part, merges[rw]); - else { + } else { + part = disk_map_sector_rcu(rq->rq_disk, blk_rq_pos(rq)); part_round_stats(cpu, part); part_inc_in_flight(part, rw); + rq->part = part; } part_stat_unlock(); @@ -128,6 +130,7 @@ void blk_rq_init(struct request_queue *q, struct request *rq) rq->ref_count = 1; rq->start_time = jiffies; set_start_time_ns(rq); + rq->part = NULL; } EXPORT_SYMBOL(blk_rq_init); @@ -802,11 +805,16 @@ static struct request *get_request(struct request_queue *q, int rw_flags, rl->starved[is_sync] = 0; priv = !test_bit(QUEUE_FLAG_ELVSWITCH, &q->queue_flags); - if (priv) + if (priv) { rl->elvpriv++; - if (blk_queue_io_stat(q)) - rw_flags |= REQ_IO_STAT; + /* + * Don't do stats for non-priv requests + */ + if (blk_queue_io_stat(q)) + rw_flags |= REQ_IO_STAT; + } + spin_unlock_irq(q->queue_lock); rq = blk_alloc_request(q, rw_flags, priv, gfp_mask); @@ -1783,7 +1791,7 @@ static void blk_account_io_completion(struct request *req, unsigned int bytes) int cpu; cpu = part_stat_lock(); - part = disk_map_sector_rcu(req->rq_disk, blk_rq_pos(req)); + part = req->part; part_stat_add(cpu, part, sectors[rw], bytes >> 9); part_stat_unlock(); } @@ -1803,7 +1811,7 @@ static void blk_account_io_done(struct request *req) int cpu; cpu = part_stat_lock(); - part = disk_map_sector_rcu(req->rq_disk, blk_rq_pos(req)); + part = req->part; part_stat_inc(cpu, part, ios[rw]); part_stat_add(cpu, part, ticks[rw], duration); diff --git a/trunk/block/blk-merge.c b/trunk/block/blk-merge.c index 77b7c26df6b5..0a2fd8a48a38 100644 --- a/trunk/block/blk-merge.c +++ b/trunk/block/blk-merge.c @@ -351,7 +351,7 @@ static void blk_account_io_merge(struct request *req) int cpu; cpu = part_stat_lock(); - part = disk_map_sector_rcu(req->rq_disk, blk_rq_pos(req)); + part = req->part; part_round_stats(cpu, part); part_dec_in_flight(part, rq_data_dir(req)); diff --git a/trunk/block/blk.h b/trunk/block/blk.h index 2db8f32838e7..1e675e5ade02 100644 --- a/trunk/block/blk.h +++ b/trunk/block/blk.h @@ -116,10 +116,6 @@ void blk_queue_congestion_threshold(struct request_queue *q); int blk_dev_init(void); -void elv_quiesce_start(struct request_queue *q); -void elv_quiesce_end(struct request_queue *q); - - /* * Return the threshold (number of used requests) at which the queue is * considered to be congested. It include a little hysteresis to keep the diff --git a/trunk/block/genhd.c b/trunk/block/genhd.c index 5fa2b44a72ff..a8adf96a4b41 100644 --- a/trunk/block/genhd.c +++ b/trunk/block/genhd.c @@ -929,8 +929,15 @@ static void disk_free_ptbl_rcu_cb(struct rcu_head *head) { struct disk_part_tbl *ptbl = container_of(head, struct disk_part_tbl, rcu_head); + struct gendisk *disk = ptbl->disk; + struct request_queue *q = disk->queue; + unsigned long flags; kfree(ptbl); + + spin_lock_irqsave(q->queue_lock, flags); + elv_quiesce_end(q); + spin_unlock_irqrestore(q->queue_lock, flags); } /** @@ -948,11 +955,17 @@ static void disk_replace_part_tbl(struct gendisk *disk, struct disk_part_tbl *new_ptbl) { struct disk_part_tbl *old_ptbl = disk->part_tbl; + struct request_queue *q = disk->queue; rcu_assign_pointer(disk->part_tbl, new_ptbl); if (old_ptbl) { rcu_assign_pointer(old_ptbl->last_lookup, NULL); + + spin_lock_irq(q->queue_lock); + elv_quiesce_start(q); + spin_unlock_irq(q->queue_lock); + call_rcu(&old_ptbl->rcu_head, disk_free_ptbl_rcu_cb); } } @@ -993,6 +1006,7 @@ int disk_expand_part_tbl(struct gendisk *disk, int partno) return -ENOMEM; new_ptbl->len = target; + new_ptbl->disk = disk; for (i = 0; i < len; i++) rcu_assign_pointer(new_ptbl->part[i], old_ptbl->part[i]); diff --git a/trunk/drivers/Makefile b/trunk/drivers/Makefile index 14cf9077bb2b..a2aea53a75ed 100644 --- a/trunk/drivers/Makefile +++ b/trunk/drivers/Makefile @@ -51,6 +51,7 @@ obj-y += net/ obj-$(CONFIG_ATM) += atm/ obj-$(CONFIG_FUSION) += message/ obj-y += firewire/ +obj-y += ieee1394/ obj-$(CONFIG_UIO) += uio/ obj-y += cdrom/ obj-y += auxdisplay/ @@ -91,7 +92,6 @@ obj-$(CONFIG_EISA) += eisa/ obj-y += lguest/ obj-$(CONFIG_CPU_FREQ) += cpufreq/ obj-$(CONFIG_CPU_IDLE) += cpuidle/ -obj-$(CONFIG_DMA_ENGINE) += dma/ obj-$(CONFIG_MMC) += mmc/ obj-$(CONFIG_MEMSTICK) += memstick/ obj-$(CONFIG_NEW_LEDS) += leds/ @@ -104,6 +104,7 @@ obj-$(CONFIG_ARCH_SHMOBILE) += sh/ ifndef CONFIG_ARCH_USES_GETTIMEOFFSET obj-y += clocksource/ endif +obj-$(CONFIG_DMA_ENGINE) += dma/ obj-$(CONFIG_DCA) += dca/ obj-$(CONFIG_HID) += hid/ obj-$(CONFIG_PPC_PS3) += ps3/ diff --git a/trunk/drivers/base/platform.c b/trunk/drivers/base/platform.c index f051cfff18af..3966e62ad019 100644 --- a/trunk/drivers/base/platform.c +++ b/trunk/drivers/base/platform.c @@ -147,7 +147,6 @@ static void platform_device_release(struct device *dev) struct platform_object *pa = container_of(dev, struct platform_object, pdev.dev); - of_device_node_put(&pa->pdev.dev); kfree(pa->pdev.dev.platform_data); kfree(pa->pdev.resource); kfree(pa); diff --git a/trunk/drivers/block/xsysace.c b/trunk/drivers/block/xsysace.c index 829161edae53..6e968cd4893c 100644 --- a/trunk/drivers/block/xsysace.c +++ b/trunk/drivers/block/xsysace.c @@ -1225,8 +1225,7 @@ ace_of_probe(struct platform_device *op, const struct of_device_id *match) bus_width = ACE_BUS_WIDTH_8; /* Call the bus-independant setup code */ - return ace_alloc(&op->dev, id ? be32_to_cpup(id) : 0, - physaddr, irq, bus_width); + return ace_alloc(&op->dev, id ? *id : 0, physaddr, irq, bus_width); } static int __devexit ace_of_remove(struct platform_device *op) diff --git a/trunk/drivers/char/keyboard.c b/trunk/drivers/char/keyboard.c index e95d7876ca6b..a7ca75212bfe 100644 --- a/trunk/drivers/char/keyboard.c +++ b/trunk/drivers/char/keyboard.c @@ -175,7 +175,8 @@ EXPORT_SYMBOL_GPL(unregister_keyboard_notifier); */ struct getset_keycode_data { - struct input_keymap_entry ke; + unsigned int scancode; + unsigned int keycode; int error; }; @@ -183,50 +184,32 @@ static int getkeycode_helper(struct input_handle *handle, void *data) { struct getset_keycode_data *d = data; - d->error = input_get_keycode(handle->dev, &d->ke); + d->error = input_get_keycode(handle->dev, d->scancode, &d->keycode); return d->error == 0; /* stop as soon as we successfully get one */ } int getkeycode(unsigned int scancode) { - struct getset_keycode_data d = { - .ke = { - .flags = 0, - .len = sizeof(scancode), - .keycode = 0, - }, - .error = -ENODEV, - }; - - memcpy(d.ke.scancode, &scancode, sizeof(scancode)); + struct getset_keycode_data d = { scancode, 0, -ENODEV }; input_handler_for_each_handle(&kbd_handler, &d, getkeycode_helper); - return d.error ?: d.ke.keycode; + return d.error ?: d.keycode; } static int setkeycode_helper(struct input_handle *handle, void *data) { struct getset_keycode_data *d = data; - d->error = input_set_keycode(handle->dev, &d->ke); + d->error = input_set_keycode(handle->dev, d->scancode, d->keycode); return d->error == 0; /* stop as soon as we successfully set one */ } int setkeycode(unsigned int scancode, unsigned int keycode) { - struct getset_keycode_data d = { - .ke = { - .flags = 0, - .len = sizeof(scancode), - .keycode = keycode, - }, - .error = -ENODEV, - }; - - memcpy(d.ke.scancode, &scancode, sizeof(scancode)); + struct getset_keycode_data d = { scancode, keycode, -ENODEV }; input_handler_for_each_handle(&kbd_handler, &d, setkeycode_helper); diff --git a/trunk/drivers/char/sysrq.c b/trunk/drivers/char/sysrq.c index eaa5d3efa79d..f3019f53e875 100644 --- a/trunk/drivers/char/sysrq.c +++ b/trunk/drivers/char/sysrq.c @@ -566,16 +566,10 @@ static const unsigned char sysrq_xlate[KEY_MAX + 1] = static bool sysrq_down; static int sysrq_alt_use; static int sysrq_alt; -static DEFINE_SPINLOCK(sysrq_event_lock); static bool sysrq_filter(struct input_handle *handle, unsigned int type, unsigned int code, int value) { - bool suppress; - - /* We are called with interrupts disabled, just take the lock */ - spin_lock(&sysrq_event_lock); - if (type != EV_KEY) goto out; @@ -607,10 +601,7 @@ static bool sysrq_filter(struct input_handle *handle, unsigned int type, } out: - suppress = sysrq_down; - spin_unlock(&sysrq_event_lock); - - return suppress; + return sysrq_down; } static int sysrq_connect(struct input_handler *handler, @@ -661,8 +652,8 @@ static void sysrq_disconnect(struct input_handle *handle) } /* - * We are matching on KEY_LEFTALT instead of KEY_SYSRQ because not all - * keyboards have SysRq key predefined and so user may add it to keymap + * We are matching on KEY_LEFTALT insteard of KEY_SYSRQ because not all + * keyboards have SysRq ikey predefined and so user may add it to keymap * later, but we expect all such keyboards to have left alt. */ static const struct input_device_id sysrq_ids[] = { diff --git a/trunk/drivers/clocksource/sh_cmt.c b/trunk/drivers/clocksource/sh_cmt.c index a44611652282..717305d30444 100644 --- a/trunk/drivers/clocksource/sh_cmt.c +++ b/trunk/drivers/clocksource/sh_cmt.c @@ -308,7 +308,7 @@ static irqreturn_t sh_cmt_interrupt(int irq, void *dev_id) * isr before we end up here. */ if (p->flags & FLAG_CLOCKSOURCE) - p->total_cycles += p->match_value + 1; + p->total_cycles += p->match_value; if (!(p->flags & FLAG_REPROGRAM)) p->next_match_value = p->max_match_value; @@ -403,7 +403,7 @@ static cycle_t sh_cmt_clocksource_read(struct clocksource *cs) raw = sh_cmt_get_counter(p, &has_wrapped); if (unlikely(has_wrapped)) - raw += p->match_value + 1; + raw += p->match_value; spin_unlock_irqrestore(&p->lock, flags); return value + raw; @@ -445,7 +445,7 @@ static int sh_cmt_register_clocksource(struct sh_cmt_priv *p, /* clk_get_rate() needs an enabled clock */ clk_enable(p->clk); - p->rate = clk_get_rate(p->clk) / ((p->width == 16) ? 512 : 8); + p->rate = clk_get_rate(p->clk) / (p->width == 16) ? 512 : 8; clk_disable(p->clk); /* TODO: calculate good shift from rate and counter bit width */ @@ -478,7 +478,7 @@ static void sh_cmt_clock_event_start(struct sh_cmt_priv *p, int periodic) ced->min_delta_ns = clockevent_delta2ns(0x1f, ced); if (periodic) - sh_cmt_set_next(p, ((p->rate + HZ/2) / HZ) - 1); + sh_cmt_set_next(p, (p->rate + HZ/2) / HZ); else sh_cmt_set_next(p, p->max_match_value); } @@ -523,9 +523,9 @@ static int sh_cmt_clock_event_next(unsigned long delta, BUG_ON(ced->mode != CLOCK_EVT_MODE_ONESHOT); if (likely(p->flags & FLAG_IRQCONTEXT)) - p->next_match_value = delta - 1; + p->next_match_value = delta; else - sh_cmt_set_next(p, delta - 1); + sh_cmt_set_next(p, delta); return 0; } diff --git a/trunk/drivers/firewire/Kconfig b/trunk/drivers/firewire/Kconfig index 40a222e19b2d..fcf3ea28340b 100644 --- a/trunk/drivers/firewire/Kconfig +++ b/trunk/drivers/firewire/Kconfig @@ -3,6 +3,9 @@ menu "IEEE 1394 (FireWire) support" # firewire-core does not depend on PCI but is # not useful without PCI controller driver +comment "You can enable one or both FireWire driver stacks." +comment "The newer stack is recommended." + config FIREWIRE tristate "FireWire driver stack" select CRC_ITU_T @@ -61,6 +64,8 @@ config FIREWIRE_NET To compile this driver as a module, say M here: The module will be called firewire-net. +source "drivers/ieee1394/Kconfig" + config FIREWIRE_NOSY tristate "Nosy - a FireWire traffic sniffer for PCILynx cards" depends on PCI diff --git a/trunk/drivers/firewire/Makefile b/trunk/drivers/firewire/Makefile index e3870d5c43dd..3c6a7fb20aa7 100644 --- a/trunk/drivers/firewire/Makefile +++ b/trunk/drivers/firewire/Makefile @@ -13,4 +13,3 @@ obj-$(CONFIG_FIREWIRE_OHCI) += firewire-ohci.o obj-$(CONFIG_FIREWIRE_SBP2) += firewire-sbp2.o obj-$(CONFIG_FIREWIRE_NET) += firewire-net.o obj-$(CONFIG_FIREWIRE_NOSY) += nosy.o -obj-$(CONFIG_PROVIDE_OHCI1394_DMA_INIT) += init_ohci1394_dma.o diff --git a/trunk/drivers/gpio/xilinx_gpio.c b/trunk/drivers/gpio/xilinx_gpio.c index 846fbd5e31bf..709690995d0d 100644 --- a/trunk/drivers/gpio/xilinx_gpio.c +++ b/trunk/drivers/gpio/xilinx_gpio.c @@ -171,13 +171,13 @@ static int __devinit xgpio_of_probe(struct device_node *np) /* Update GPIO state shadow register with default value */ tree_info = of_get_property(np, "xlnx,dout-default", NULL); if (tree_info) - chip->gpio_state = be32_to_cpup(tree_info); + chip->gpio_state = *tree_info; /* Update GPIO direction shadow register with default value */ chip->gpio_dir = 0xFFFFFFFF; /* By default, all pins are inputs */ tree_info = of_get_property(np, "xlnx,tri-default", NULL); if (tree_info) - chip->gpio_dir = be32_to_cpup(tree_info); + chip->gpio_dir = *tree_info; /* Check device node and parent device node for device width */ chip->mmchip.gc.ngpio = 32; /* By default assume full GPIO controller */ @@ -186,7 +186,7 @@ static int __devinit xgpio_of_probe(struct device_node *np) tree_info = of_get_property(np->parent, "xlnx,gpio-width", NULL); if (tree_info) - chip->mmchip.gc.ngpio = be32_to_cpup(tree_info); + chip->mmchip.gc.ngpio = *tree_info; spin_lock_init(&chip->gpio_lock); diff --git a/trunk/drivers/hid/hid-core.c b/trunk/drivers/hid/hid-core.c index 515345b11ac9..7832b6e2478b 100644 --- a/trunk/drivers/hid/hid-core.c +++ b/trunk/drivers/hid/hid-core.c @@ -1780,11 +1780,6 @@ static bool hid_ignore(struct hid_device *hdev) hdev->product <= USB_DEVICE_ID_SOUNDGRAPH_IMON_LAST) return true; break; - case USB_VENDOR_ID_HANWANG: - if (hdev->product >= USB_DEVICE_ID_HANWANG_TABLET_FIRST && - hdev->product <= USB_DEVICE_ID_HANWANG_TABLET_LAST) - return true; - break; } if (hdev->type == HID_TYPE_USBMOUSE && diff --git a/trunk/drivers/hid/hid-ids.h b/trunk/drivers/hid/hid-ids.h index 3341baa86a30..3ee999d33004 100644 --- a/trunk/drivers/hid/hid-ids.h +++ b/trunk/drivers/hid/hid-ids.h @@ -299,10 +299,6 @@ #define USB_DEVICE_ID_GYRATION_REMOTE_2 0x0003 #define USB_DEVICE_ID_GYRATION_REMOTE_3 0x0008 -#define USB_VENDOR_ID_HANWANG 0x0b57 -#define USB_DEVICE_ID_HANWANG_TABLET_FIRST 0x5000 -#define USB_DEVICE_ID_HANWANG_TABLET_LAST 0x8fff - #define USB_VENDOR_ID_HAPP 0x078b #define USB_DEVICE_ID_UGCI_DRIVING 0x0010 #define USB_DEVICE_ID_UGCI_FLYING 0x0020 diff --git a/trunk/drivers/hid/hid-input.c b/trunk/drivers/hid/hid-input.c index bb0b3659437b..834ef47b76d6 100644 --- a/trunk/drivers/hid/hid-input.c +++ b/trunk/drivers/hid/hid-input.c @@ -68,52 +68,39 @@ static const struct { #define map_key_clear(c) hid_map_usage_clear(hidinput, usage, &bit, \ &max, EV_KEY, (c)) -static bool match_scancode(struct hid_usage *usage, - unsigned int cur_idx, unsigned int scancode) +static inline int match_scancode(unsigned int code, unsigned int scancode) { - return (usage->hid & (HID_USAGE_PAGE | HID_USAGE)) == scancode; -} + if (scancode == 0) + return 1; -static bool match_keycode(struct hid_usage *usage, - unsigned int cur_idx, unsigned int keycode) -{ - /* - * We should exclude unmapped usages when doing lookup by keycode. - */ - return (usage->type == EV_KEY && usage->code == keycode); + return (code & (HID_USAGE_PAGE | HID_USAGE)) == scancode; } -static bool match_index(struct hid_usage *usage, - unsigned int cur_idx, unsigned int idx) +static inline int match_keycode(unsigned int code, unsigned int keycode) { - return cur_idx == idx; -} + if (keycode == 0) + return 1; -typedef bool (*hid_usage_cmp_t)(struct hid_usage *usage, - unsigned int cur_idx, unsigned int val); + return code == keycode; +} static struct hid_usage *hidinput_find_key(struct hid_device *hid, - hid_usage_cmp_t match, - unsigned int value, - unsigned int *usage_idx) + unsigned int scancode, + unsigned int keycode) { - unsigned int i, j, k, cur_idx = 0; + int i, j, k; struct hid_report *report; struct hid_usage *usage; for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) { list_for_each_entry(report, &hid->report_enum[k].report_list, list) { for (i = 0; i < report->maxfield; i++) { - for (j = 0; j < report->field[i]->maxusage; j++) { + for ( j = 0; j < report->field[i]->maxusage; j++) { usage = report->field[i]->usage + j; - if (usage->type == EV_KEY || usage->type == 0) { - if (match(usage, cur_idx, value)) { - if (usage_idx) - *usage_idx = cur_idx; - return usage; - } - cur_idx++; - } + if (usage->type == EV_KEY && + match_scancode(usage->hid, scancode) && + match_keycode(usage->code, keycode)) + return usage; } } } @@ -121,68 +108,39 @@ static struct hid_usage *hidinput_find_key(struct hid_device *hid, return NULL; } -static struct hid_usage *hidinput_locate_usage(struct hid_device *hid, - const struct input_keymap_entry *ke, - unsigned int *index) -{ - struct hid_usage *usage; - unsigned int scancode; - - if (ke->flags & INPUT_KEYMAP_BY_INDEX) - usage = hidinput_find_key(hid, match_index, ke->index, index); - else if (input_scancode_to_scalar(ke, &scancode) == 0) - usage = hidinput_find_key(hid, match_scancode, scancode, index); - else - usage = NULL; - - return usage; -} - static int hidinput_getkeycode(struct input_dev *dev, - struct input_keymap_entry *ke) + unsigned int scancode, unsigned int *keycode) { struct hid_device *hid = input_get_drvdata(dev); struct hid_usage *usage; - unsigned int scancode, index; - usage = hidinput_locate_usage(hid, ke, &index); + usage = hidinput_find_key(hid, scancode, 0); if (usage) { - ke->keycode = usage->type == EV_KEY ? - usage->code : KEY_RESERVED; - ke->index = index; - scancode = usage->hid & (HID_USAGE_PAGE | HID_USAGE); - ke->len = sizeof(scancode); - memcpy(ke->scancode, &scancode, sizeof(scancode)); + *keycode = usage->code; return 0; } - return -EINVAL; } static int hidinput_setkeycode(struct input_dev *dev, - const struct input_keymap_entry *ke, - unsigned int *old_keycode) + unsigned int scancode, unsigned int keycode) { struct hid_device *hid = input_get_drvdata(dev); struct hid_usage *usage; + int old_keycode; - usage = hidinput_locate_usage(hid, ke, NULL); + usage = hidinput_find_key(hid, scancode, 0); if (usage) { - *old_keycode = usage->type == EV_KEY ? - usage->code : KEY_RESERVED; - usage->code = ke->keycode; + old_keycode = usage->code; + usage->code = keycode; - clear_bit(*old_keycode, dev->keybit); + clear_bit(old_keycode, dev->keybit); set_bit(usage->code, dev->keybit); - dbg_hid(KERN_DEBUG "Assigned keycode %d to HID usage code %x\n", - usage->code, usage->hid); - - /* - * Set the keybit for the old keycode if the old keycode is used - * by another key - */ - if (hidinput_find_key(hid, match_keycode, *old_keycode, NULL)) - set_bit(*old_keycode, dev->keybit); + dbg_hid(KERN_DEBUG "Assigned keycode %d to HID usage code %x\n", keycode, scancode); + /* Set the keybit for the old keycode if the old keycode is used + * by another key */ + if (hidinput_find_key (hid, 0, old_keycode)) + set_bit(old_keycode, dev->keybit); return 0; } @@ -877,8 +835,8 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) hid->ll_driver->hidinput_input_event; input_dev->open = hidinput_open; input_dev->close = hidinput_close; - input_dev->setkeycode_new = hidinput_setkeycode; - input_dev->getkeycode_new = hidinput_getkeycode; + input_dev->setkeycode = hidinput_setkeycode; + input_dev->getkeycode = hidinput_getkeycode; input_dev->name = hid->name; input_dev->phys = hid->phys; diff --git a/trunk/drivers/hwmon/Kconfig b/trunk/drivers/hwmon/Kconfig index e382da3122b7..97499d00615a 100644 --- a/trunk/drivers/hwmon/Kconfig +++ b/trunk/drivers/hwmon/Kconfig @@ -1088,6 +1088,26 @@ config SENSORS_ULTRA45 This driver provides support for the Ultra45 workstation environmental sensors. +config SENSORS_HDAPS + tristate "IBM Hard Drive Active Protection System (hdaps)" + depends on INPUT && X86 + select INPUT_POLLDEV + default n + help + This driver provides support for the IBM Hard Drive Active Protection + System (hdaps), which provides an accelerometer and other misc. data. + ThinkPads starting with the R50, T41, and X40 are supported. The + accelerometer data is readable via sysfs. + + This driver also provides an absolute input class device, allowing + the laptop to act as a pinball machine-esque joystick. + + If your ThinkPad is not recognized by the driver, please update to latest + BIOS. This is especially the case for some R52 ThinkPads. + + Say Y here if you have an applicable laptop and want to experience + the awesome power of hdaps. + config SENSORS_LIS3_SPI tristate "STMicroeletronics LIS3LV02Dx three-axis digital accelerometer (SPI)" depends on !ACPI && SPI_MASTER && INPUT diff --git a/trunk/drivers/hwmon/Makefile b/trunk/drivers/hwmon/Makefile index ec9cb735c898..e3c2484f6c5f 100644 --- a/trunk/drivers/hwmon/Makefile +++ b/trunk/drivers/hwmon/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_SENSORS_G760A) += g760a.o obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o +obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o diff --git a/trunk/drivers/platform/x86/hdaps.c b/trunk/drivers/hwmon/hdaps.c similarity index 99% rename from trunk/drivers/platform/x86/hdaps.c rename to trunk/drivers/hwmon/hdaps.c index 067bf36d32f3..bfd42f18924b 100644 --- a/trunk/drivers/platform/x86/hdaps.c +++ b/trunk/drivers/hwmon/hdaps.c @@ -1,5 +1,5 @@ /* - * hdaps.c - driver for IBM's Hard Drive Active Protection System + * drivers/hwmon/hdaps.c - driver for IBM's Hard Drive Active Protection System * * Copyright (C) 2005 Robert Love * Copyright (C) 2005 Jesper Juhl diff --git a/trunk/drivers/i2c/busses/i2c-sh7760.c b/trunk/drivers/i2c/busses/i2c-sh7760.c index 3cad8fecc3d3..4f93da31d3ad 100644 --- a/trunk/drivers/i2c/busses/i2c-sh7760.c +++ b/trunk/drivers/i2c/busses/i2c-sh7760.c @@ -101,12 +101,12 @@ struct cami2c { static inline void OUT32(struct cami2c *cam, int reg, unsigned long val) { - __raw_writel(val, (unsigned long)cam->iobase + reg); + ctrl_outl(val, (unsigned long)cam->iobase + reg); } static inline unsigned long IN32(struct cami2c *cam, int reg) { - return __raw_readl((unsigned long)cam->iobase + reg); + return ctrl_inl((unsigned long)cam->iobase + reg); } static irqreturn_t sh7760_i2c_irq(int irq, void *ptr) diff --git a/trunk/drivers/i2c/busses/i2c-sh_mobile.c b/trunk/drivers/i2c/busses/i2c-sh_mobile.c index 2707f5e17158..598c49acaeb5 100644 --- a/trunk/drivers/i2c/busses/i2c-sh_mobile.c +++ b/trunk/drivers/i2c/busses/i2c-sh_mobile.c @@ -538,17 +538,15 @@ static int sh_mobile_i2c_hook_irqs(struct platform_device *dev, int hook) { struct resource *res; int ret = -ENXIO; - int n, k = 0; + int q, m; + int k = 0; + int n = 0; while ((res = platform_get_resource(dev, IORESOURCE_IRQ, k))) { for (n = res->start; hook && n <= res->end; n++) { if (request_irq(n, sh_mobile_i2c_isr, IRQF_DISABLED, - dev_name(&dev->dev), dev)) { - for (n--; n >= res->start; n--) - free_irq(n, dev); - + dev_name(&dev->dev), dev)) goto rollback; - } } k++; } @@ -556,17 +554,16 @@ static int sh_mobile_i2c_hook_irqs(struct platform_device *dev, int hook) if (hook) return k > 0 ? 0 : -ENOENT; + k--; ret = 0; rollback: - k--; - - while (k >= 0) { - res = platform_get_resource(dev, IORESOURCE_IRQ, k); - for (n = res->start; n <= res->end; n++) - free_irq(n, dev); + for (q = k; k >= 0; k--) { + for (m = n; m >= res->start; m--) + free_irq(m, dev); - k--; + res = platform_get_resource(dev, IORESOURCE_IRQ, k - 1); + m = res->end; } return ret; diff --git a/trunk/drivers/ieee1394/Kconfig b/trunk/drivers/ieee1394/Kconfig new file mode 100644 index 000000000000..e02096cf7d95 --- /dev/null +++ b/trunk/drivers/ieee1394/Kconfig @@ -0,0 +1,182 @@ +config IEEE1394 + tristate "Legacy alternative FireWire driver stack" + depends on PCI || BROKEN + help + IEEE 1394 describes a high performance serial bus, which is also + known as FireWire(tm) or i.Link(tm) and is used for connecting all + sorts of devices (most notably digital video cameras) to your + computer. + + If you have FireWire hardware and want to use it, say Y here. This + is the core support only, you will also need to select a driver for + your IEEE 1394 adapter. + + To compile this driver as a module, say M here: the module will be + called ieee1394. + + NOTE: + ieee1394 is superseded by the newer firewire-core driver. See + http://ieee1394.wiki.kernel.org/index.php/Juju_Migration for + further information on how to switch to the new FireWire drivers. + +config IEEE1394_OHCI1394 + tristate "OHCI-1394 controllers" + depends on PCI && IEEE1394 + help + Enable this driver if you have an IEEE 1394 controller based on the + OHCI-1394 specification. The current driver is only tested with OHCI + chipsets made by Texas Instruments and NEC. Most third-party vendors + use one of these chipsets. It should work with any OHCI-1394 + compliant card, however. + + To compile this driver as a module, say M here: the module will be + called ohci1394. + + NOTE: + ohci1394 is superseded by the newer firewire-ohci driver. See + http://ieee1394.wiki.kernel.org/index.php/Juju_Migration for + further information on how to switch to the new FireWire drivers. + + If you want to install firewire-ohci and ohci1394 together, you + should configure them only as modules and blacklist the driver(s) + which you don't want to have auto-loaded. Add either + + blacklist ohci1394 + blacklist video1394 + blacklist dv1394 + or + blacklist firewire-ohci + + to /etc/modprobe.conf or /etc/modprobe.d/* and update modprobe.conf + depending on your distribution. + +comment "PCILynx controller requires I2C" + depends on IEEE1394 && I2C=n + +config IEEE1394_PCILYNX + tristate "PCILynx controller" + depends on PCI && IEEE1394 && I2C + select I2C_ALGOBIT + help + Say Y here if you have an IEEE-1394 controller with the Texas + Instruments PCILynx chip. Note: this driver is written for revision + 2 of this chip and may not work with revision 0. + + To compile this driver as a module, say M here: the module will be + called pcilynx. + + Only some old and now very rare PCI and CardBus cards and + PowerMacs G3 B&W contain the PCILynx controller. Therefore + almost everybody can say N here. + +comment "SBP-2 support (for storage devices) requires SCSI" + depends on IEEE1394 && SCSI=n + +config IEEE1394_SBP2 + tristate "Storage devices (SBP-2 protocol)" + depends on IEEE1394 && SCSI + help + This option enables you to use SBP-2 devices connected to an IEEE + 1394 bus. SBP-2 devices include storage devices like harddisks and + DVD drives, also some other FireWire devices like scanners. + + You should also enable support for disks, CD-ROMs, etc. in the SCSI + configuration section. + + To compile this driver as a module, say M here: the module will be + called sbp2. + + NOTE: + sbp2 is superseded by the newer firewire-sbp2 driver. See + http://ieee1394.wiki.kernel.org/index.php/Juju_Migration for + further information on how to switch to the new FireWire drivers. + +config IEEE1394_SBP2_PHYS_DMA + bool "Enable replacement for physical DMA in SBP2" + depends on IEEE1394_SBP2 && VIRT_TO_BUS && EXPERIMENTAL + help + This builds sbp2 for use with non-OHCI host adapters which do not + support physical DMA or for when ohci1394 is run with phys_dma=0. + Physical DMA is data movement without assistance of the drivers' + interrupt handlers. This option includes the interrupt handlers + that are required in absence of this hardware feature. + + This option is buggy and currently broken on some architectures. + If unsure, say N. + +config IEEE1394_ETH1394_ROM_ENTRY + depends on IEEE1394 + bool + default n + +config IEEE1394_ETH1394 + tristate "IP networking over 1394 (experimental)" + depends on IEEE1394 && EXPERIMENTAL && INET + select IEEE1394_ETH1394_ROM_ENTRY + help + This driver implements a functional majority of RFC 2734: IPv4 over + 1394. It will provide IP connectivity with implementations of RFC + 2734 found on other operating systems. It will not communicate with + older versions of this driver found in stock kernels prior to 2.6.3. + This driver is still considered experimental. It does not yet support + MCAP, therefore multicast support is significantly limited. + + The module is called eth1394 although it does not emulate Ethernet. + + NOTE: + eth1394 is superseded by the newer firewire-net driver. See + http://ieee1394.wiki.kernel.org/index.php/Juju_Migration for + further information on how to switch to the new FireWire drivers. + +config IEEE1394_RAWIO + tristate "raw1394 userspace interface" + depends on IEEE1394 + help + This option adds support for the raw1394 device file which enables + direct communication of user programs with IEEE 1394 devices + (isochronous and asynchronous). Almost all application programs + which access FireWire require this option. + + To compile this driver as a module, say M here: the module will be + called raw1394. + + NOTE: + raw1394 is superseded by the newer firewire-core driver. See + http://ieee1394.wiki.kernel.org/index.php/Juju_Migration for + further information on how to switch to the new FireWire drivers. + +config IEEE1394_VIDEO1394 + tristate "video1394 userspace interface" + depends on IEEE1394 && IEEE1394_OHCI1394 + help + This option adds support for the video1394 device files which enable + isochronous communication of user programs with IEEE 1394 devices, + especially video capture or export. This interface is used by all + libdc1394 based programs and by several other programs, in addition to + the raw1394 interface. It is generally not required for DV capture. + + To compile this driver as a module, say M here: the module will be + called video1394. + + NOTE: + video1394 is superseded by the newer firewire-core driver. See + http://ieee1394.wiki.kernel.org/index.php/Juju_Migration for + further information on how to switch to the new FireWire drivers. + +config IEEE1394_DV1394 + tristate "dv1394 userspace interface (deprecated)" + depends on IEEE1394 && IEEE1394_OHCI1394 + help + The dv1394 driver is unsupported and may be removed from Linux in a + future release. Its functionality is now provided by either + raw1394 or firewire-core together with libraries such as libiec61883. + +config IEEE1394_VERBOSEDEBUG + bool "Excessive debugging output" + depends on IEEE1394 + help + If you say Y here, you will get very verbose debugging logs from the + ieee1394 drivers, including sent and received packet headers. This + will quickly result in large amounts of data sent to the system log. + + Say Y if you really need the debugging output. Everyone else says N. diff --git a/trunk/drivers/ieee1394/Makefile b/trunk/drivers/ieee1394/Makefile new file mode 100644 index 000000000000..1f8153b57503 --- /dev/null +++ b/trunk/drivers/ieee1394/Makefile @@ -0,0 +1,18 @@ +# +# Makefile for the Linux IEEE 1394 implementation +# + +ieee1394-objs := ieee1394_core.o ieee1394_transactions.o hosts.o \ + highlevel.o csr.o nodemgr.o dma.o iso.o \ + csr1212.o config_roms.o + +obj-$(CONFIG_IEEE1394) += ieee1394.o +obj-$(CONFIG_IEEE1394_PCILYNX) += pcilynx.o +obj-$(CONFIG_IEEE1394_OHCI1394) += ohci1394.o +obj-$(CONFIG_IEEE1394_VIDEO1394) += video1394.o +obj-$(CONFIG_IEEE1394_RAWIO) += raw1394.o +obj-$(CONFIG_IEEE1394_SBP2) += sbp2.o +obj-$(CONFIG_IEEE1394_DV1394) += dv1394.o +obj-$(CONFIG_IEEE1394_ETH1394) += eth1394.o + +obj-$(CONFIG_PROVIDE_OHCI1394_DMA_INIT) += init_ohci1394_dma.o diff --git a/trunk/drivers/ieee1394/config_roms.c b/trunk/drivers/ieee1394/config_roms.c new file mode 100644 index 000000000000..1b981207fa76 --- /dev/null +++ b/trunk/drivers/ieee1394/config_roms.c @@ -0,0 +1,194 @@ +/* + * IEEE 1394 for Linux + * + * ConfigROM entries + * + * Copyright (C) 2004 Ben Collins + * + * This code is licensed under the GPL. See the file COPYING in the root + * directory of the kernel sources for details. + */ + +#include + +#include "csr1212.h" +#include "ieee1394.h" +#include "ieee1394_types.h" +#include "hosts.h" +#include "ieee1394_core.h" +#include "highlevel.h" +#include "csr.h" +#include "config_roms.h" + +struct hpsb_config_rom_entry { + const char *name; + + /* Base initialization, called at module load */ + int (*init)(void); + + /* Cleanup called at module exit */ + void (*cleanup)(void); + + /* The flag added to host->config_roms */ + unsigned int flag; +}; + +/* The default host entry. This must succeed. */ +int hpsb_default_host_entry(struct hpsb_host *host) +{ + struct csr1212_keyval *root; + struct csr1212_keyval *vend_id = NULL; + struct csr1212_keyval *text = NULL; + char csr_name[128]; + int ret; + + sprintf(csr_name, "Linux - %s", host->driver->name); + root = host->csr.rom->root_kv; + + vend_id = csr1212_new_immediate(CSR1212_KV_ID_VENDOR, host->csr.guid_hi >> 8); + text = csr1212_new_string_descriptor_leaf(csr_name); + + if (!vend_id || !text) { + if (vend_id) + csr1212_release_keyval(vend_id); + if (text) + csr1212_release_keyval(text); + csr1212_destroy_csr(host->csr.rom); + return -ENOMEM; + } + + csr1212_associate_keyval(vend_id, text); + csr1212_release_keyval(text); + ret = csr1212_attach_keyval_to_directory(root, vend_id); + csr1212_release_keyval(vend_id); + if (ret != CSR1212_SUCCESS) { + csr1212_destroy_csr(host->csr.rom); + return -ENOMEM; + } + + host->update_config_rom = 1; + + return 0; +} + + +#ifdef CONFIG_IEEE1394_ETH1394_ROM_ENTRY +#include "eth1394.h" + +static struct csr1212_keyval *ip1394_ud; + +static int config_rom_ip1394_init(void) +{ + struct csr1212_keyval *spec_id = NULL; + struct csr1212_keyval *spec_desc = NULL; + struct csr1212_keyval *ver = NULL; + struct csr1212_keyval *ver_desc = NULL; + int ret = -ENOMEM; + + ip1394_ud = csr1212_new_directory(CSR1212_KV_ID_UNIT); + + spec_id = csr1212_new_immediate(CSR1212_KV_ID_SPECIFIER_ID, + ETHER1394_GASP_SPECIFIER_ID); + spec_desc = csr1212_new_string_descriptor_leaf("IANA"); + ver = csr1212_new_immediate(CSR1212_KV_ID_VERSION, + ETHER1394_GASP_VERSION); + ver_desc = csr1212_new_string_descriptor_leaf("IPv4"); + + if (!ip1394_ud || !spec_id || !spec_desc || !ver || !ver_desc) + goto ip1394_fail; + + csr1212_associate_keyval(spec_id, spec_desc); + csr1212_associate_keyval(ver, ver_desc); + if (csr1212_attach_keyval_to_directory(ip1394_ud, spec_id) + == CSR1212_SUCCESS && + csr1212_attach_keyval_to_directory(ip1394_ud, ver) + == CSR1212_SUCCESS) + ret = 0; + +ip1394_fail: + if (ret && ip1394_ud) { + csr1212_release_keyval(ip1394_ud); + ip1394_ud = NULL; + } + + if (spec_id) + csr1212_release_keyval(spec_id); + if (spec_desc) + csr1212_release_keyval(spec_desc); + if (ver) + csr1212_release_keyval(ver); + if (ver_desc) + csr1212_release_keyval(ver_desc); + + return ret; +} + +static void config_rom_ip1394_cleanup(void) +{ + if (ip1394_ud) { + csr1212_release_keyval(ip1394_ud); + ip1394_ud = NULL; + } +} + +int hpsb_config_rom_ip1394_add(struct hpsb_host *host) +{ + if (!ip1394_ud) + return -ENODEV; + + if (csr1212_attach_keyval_to_directory(host->csr.rom->root_kv, + ip1394_ud) != CSR1212_SUCCESS) + return -ENOMEM; + + host->config_roms |= HPSB_CONFIG_ROM_ENTRY_IP1394; + host->update_config_rom = 1; + return 0; +} +EXPORT_SYMBOL_GPL(hpsb_config_rom_ip1394_add); + +void hpsb_config_rom_ip1394_remove(struct hpsb_host *host) +{ + csr1212_detach_keyval_from_directory(host->csr.rom->root_kv, ip1394_ud); + host->config_roms &= ~HPSB_CONFIG_ROM_ENTRY_IP1394; + host->update_config_rom = 1; +} +EXPORT_SYMBOL_GPL(hpsb_config_rom_ip1394_remove); + +static struct hpsb_config_rom_entry ip1394_entry = { + .name = "ip1394", + .init = config_rom_ip1394_init, + .cleanup = config_rom_ip1394_cleanup, + .flag = HPSB_CONFIG_ROM_ENTRY_IP1394, +}; + +#endif /* CONFIG_IEEE1394_ETH1394_ROM_ENTRY */ + +static struct hpsb_config_rom_entry *const config_rom_entries[] = { +#ifdef CONFIG_IEEE1394_ETH1394_ROM_ENTRY + &ip1394_entry, +#endif +}; + +/* Initialize all config roms */ +int hpsb_init_config_roms(void) +{ + int i, error = 0; + + for (i = 0; i < ARRAY_SIZE(config_rom_entries); i++) + if (config_rom_entries[i]->init()) { + HPSB_ERR("Failed to initialize config rom entry `%s'", + config_rom_entries[i]->name); + error = -1; + } + + return error; +} + +/* Cleanup all config roms */ +void hpsb_cleanup_config_roms(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(config_rom_entries); i++) + config_rom_entries[i]->cleanup(); +} diff --git a/trunk/drivers/ieee1394/config_roms.h b/trunk/drivers/ieee1394/config_roms.h new file mode 100644 index 000000000000..1f5cd1f16c44 --- /dev/null +++ b/trunk/drivers/ieee1394/config_roms.h @@ -0,0 +1,19 @@ +#ifndef _IEEE1394_CONFIG_ROMS_H +#define _IEEE1394_CONFIG_ROMS_H + +struct hpsb_host; + +int hpsb_default_host_entry(struct hpsb_host *host); +int hpsb_init_config_roms(void); +void hpsb_cleanup_config_roms(void); + +/* List of flags to check if a host contains a certain extra config rom + * entry. Available in the host->config_roms member. */ +#define HPSB_CONFIG_ROM_ENTRY_IP1394 0x00000001 + +#ifdef CONFIG_IEEE1394_ETH1394_ROM_ENTRY +int hpsb_config_rom_ip1394_add(struct hpsb_host *host); +void hpsb_config_rom_ip1394_remove(struct hpsb_host *host); +#endif + +#endif /* _IEEE1394_CONFIG_ROMS_H */ diff --git a/trunk/drivers/ieee1394/csr.c b/trunk/drivers/ieee1394/csr.c new file mode 100644 index 000000000000..d696f69ebce5 --- /dev/null +++ b/trunk/drivers/ieee1394/csr.c @@ -0,0 +1,843 @@ +/* + * IEEE 1394 for Linux + * + * CSR implementation, iso/bus manager implementation. + * + * Copyright (C) 1999 Andreas E. Bombe + * 2002 Manfred Weihs + * + * This code is licensed under the GPL. See the file COPYING in the root + * directory of the kernel sources for details. + * + * + * Contributions: + * + * Manfred Weihs + * configuration ROM manipulation + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "csr1212.h" +#include "ieee1394_types.h" +#include "hosts.h" +#include "ieee1394.h" +#include "highlevel.h" +#include "ieee1394_core.h" + +/* Module Parameters */ +/* this module parameter can be used to disable mapping of the FCP registers */ + +static int fcp = 1; +module_param(fcp, int, 0444); +MODULE_PARM_DESC(fcp, "Map FCP registers (default = 1, disable = 0)."); + +static struct csr1212_keyval *node_cap = NULL; + +static void add_host(struct hpsb_host *host); +static void remove_host(struct hpsb_host *host); +static void host_reset(struct hpsb_host *host); +static int read_maps(struct hpsb_host *host, int nodeid, quadlet_t *buffer, + u64 addr, size_t length, u16 fl); +static int write_fcp(struct hpsb_host *host, int nodeid, int dest, + quadlet_t *data, u64 addr, size_t length, u16 flags); +static int read_regs(struct hpsb_host *host, int nodeid, quadlet_t *buf, + u64 addr, size_t length, u16 flags); +static int write_regs(struct hpsb_host *host, int nodeid, int destid, + quadlet_t *data, u64 addr, size_t length, u16 flags); +static int lock_regs(struct hpsb_host *host, int nodeid, quadlet_t *store, + u64 addr, quadlet_t data, quadlet_t arg, int extcode, u16 fl); +static int lock64_regs(struct hpsb_host *host, int nodeid, octlet_t * store, + u64 addr, octlet_t data, octlet_t arg, int extcode, u16 fl); +static int read_config_rom(struct hpsb_host *host, int nodeid, quadlet_t *buffer, + u64 addr, size_t length, u16 fl); +static u64 allocate_addr_range(u64 size, u32 alignment, void *__host); +static void release_addr_range(u64 addr, void *__host); + +static struct hpsb_highlevel csr_highlevel = { + .name = "standard registers", + .add_host = add_host, + .remove_host = remove_host, + .host_reset = host_reset, +}; + +static const struct hpsb_address_ops map_ops = { + .read = read_maps, +}; + +static const struct hpsb_address_ops fcp_ops = { + .write = write_fcp, +}; + +static const struct hpsb_address_ops reg_ops = { + .read = read_regs, + .write = write_regs, + .lock = lock_regs, + .lock64 = lock64_regs, +}; + +static const struct hpsb_address_ops config_rom_ops = { + .read = read_config_rom, +}; + +struct csr1212_bus_ops csr_bus_ops = { + .allocate_addr_range = allocate_addr_range, + .release_addr = release_addr_range, +}; + + +static u16 csr_crc16(unsigned *data, int length) +{ + int check=0, i; + int shift, sum, next=0; + + for (i = length; i; i--) { + for (next = check, shift = 28; shift >= 0; shift -= 4 ) { + sum = ((next >> 12) ^ (be32_to_cpu(*data) >> shift)) & 0xf; + next = (next << 4) ^ (sum << 12) ^ (sum << 5) ^ (sum); + } + check = next & 0xffff; + data++; + } + + return check; +} + +static void host_reset(struct hpsb_host *host) +{ + host->csr.state &= 0x300; + + host->csr.bus_manager_id = 0x3f; + host->csr.bandwidth_available = 4915; + host->csr.channels_available_hi = 0xfffffffe; /* pre-alloc ch 31 per 1394a-2000 */ + host->csr.channels_available_lo = ~0; + host->csr.broadcast_channel = 0x80000000 | 31; + + if (host->is_irm) { + if (host->driver->hw_csr_reg) { + host->driver->hw_csr_reg(host, 2, 0xfffffffe, ~0); + } + } + + host->csr.node_ids = host->node_id << 16; + + if (!host->is_root) { + /* clear cmstr bit */ + host->csr.state &= ~0x100; + } + + be32_add_cpu(&host->csr.topology_map[1], 1); + host->csr.topology_map[2] = cpu_to_be32(host->node_count << 16 + | host->selfid_count); + host->csr.topology_map[0] = + cpu_to_be32((host->selfid_count + 2) << 16 + | csr_crc16(host->csr.topology_map + 1, + host->selfid_count + 2)); + + be32_add_cpu(&host->csr.speed_map[1], 1); + host->csr.speed_map[0] = cpu_to_be32(0x3f1 << 16 + | csr_crc16(host->csr.speed_map+1, + 0x3f1)); +} + +/* + * HI == seconds (bits 0:2) + * LO == fractions of a second in units of 125usec (bits 19:31) + * + * Convert SPLIT_TIMEOUT to jiffies. + * The default and minimum as per 1394a-2000 clause 8.3.2.2.6 is 100ms. + */ +static inline void calculate_expire(struct csr_control *csr) +{ + unsigned int usecs = (csr->split_timeout_hi & 7) * 1000000 + + (csr->split_timeout_lo >> 19) * 125; + + csr->expire = usecs_to_jiffies(usecs > 100000 ? usecs : 100000); + HPSB_VERBOSE("CSR: setting expire to %lu, HZ=%u", csr->expire, HZ); +} + + +static void add_host(struct hpsb_host *host) +{ + struct csr1212_keyval *root; + quadlet_t bus_info[CSR_BUS_INFO_SIZE]; + + hpsb_register_addrspace(&csr_highlevel, host, ®_ops, + CSR_REGISTER_BASE, + CSR_REGISTER_BASE + CSR_CONFIG_ROM); + hpsb_register_addrspace(&csr_highlevel, host, &config_rom_ops, + CSR_REGISTER_BASE + CSR_CONFIG_ROM, + CSR_REGISTER_BASE + CSR_CONFIG_ROM_END); + if (fcp) { + hpsb_register_addrspace(&csr_highlevel, host, &fcp_ops, + CSR_REGISTER_BASE + CSR_FCP_COMMAND, + CSR_REGISTER_BASE + CSR_FCP_END); + } + hpsb_register_addrspace(&csr_highlevel, host, &map_ops, + CSR_REGISTER_BASE + CSR_TOPOLOGY_MAP, + CSR_REGISTER_BASE + CSR_TOPOLOGY_MAP_END); + hpsb_register_addrspace(&csr_highlevel, host, &map_ops, + CSR_REGISTER_BASE + CSR_SPEED_MAP, + CSR_REGISTER_BASE + CSR_SPEED_MAP_END); + + spin_lock_init(&host->csr.lock); + + host->csr.state = 0; + host->csr.node_ids = 0; + host->csr.split_timeout_hi = 0; + host->csr.split_timeout_lo = 800 << 19; + calculate_expire(&host->csr); + host->csr.cycle_time = 0; + host->csr.bus_time = 0; + host->csr.bus_manager_id = 0x3f; + host->csr.bandwidth_available = 4915; + host->csr.channels_available_hi = 0xfffffffe; /* pre-alloc ch 31 per 1394a-2000 */ + host->csr.channels_available_lo = ~0; + host->csr.broadcast_channel = 0x80000000 | 31; + + if (host->is_irm) { + if (host->driver->hw_csr_reg) { + host->driver->hw_csr_reg(host, 2, 0xfffffffe, ~0); + } + } + + if (host->csr.max_rec >= 9) + host->csr.max_rom = 2; + else if (host->csr.max_rec >= 5) + host->csr.max_rom = 1; + else + host->csr.max_rom = 0; + + host->csr.generation = 2; + + bus_info[1] = IEEE1394_BUSID_MAGIC; + bus_info[2] = cpu_to_be32((hpsb_disable_irm ? 0 : 1 << CSR_IRMC_SHIFT) | + (1 << CSR_CMC_SHIFT) | + (1 << CSR_ISC_SHIFT) | + (0 << CSR_BMC_SHIFT) | + (0 << CSR_PMC_SHIFT) | + (host->csr.cyc_clk_acc << CSR_CYC_CLK_ACC_SHIFT) | + (host->csr.max_rec << CSR_MAX_REC_SHIFT) | + (host->csr.max_rom << CSR_MAX_ROM_SHIFT) | + (host->csr.generation << CSR_GENERATION_SHIFT) | + host->csr.lnk_spd); + + bus_info[3] = cpu_to_be32(host->csr.guid_hi); + bus_info[4] = cpu_to_be32(host->csr.guid_lo); + + /* The hardware copy of the bus info block will be set later when a + * bus reset is issued. */ + + csr1212_init_local_csr(host->csr.rom, bus_info, host->csr.max_rom); + + root = host->csr.rom->root_kv; + + if(csr1212_attach_keyval_to_directory(root, node_cap) != CSR1212_SUCCESS) { + HPSB_ERR("Failed to attach Node Capabilities to root directory"); + } + + host->update_config_rom = 1; +} + +static void remove_host(struct hpsb_host *host) +{ + quadlet_t bus_info[CSR_BUS_INFO_SIZE]; + + bus_info[1] = IEEE1394_BUSID_MAGIC; + bus_info[2] = cpu_to_be32((0 << CSR_IRMC_SHIFT) | + (0 << CSR_CMC_SHIFT) | + (0 << CSR_ISC_SHIFT) | + (0 << CSR_BMC_SHIFT) | + (0 << CSR_PMC_SHIFT) | + (host->csr.cyc_clk_acc << CSR_CYC_CLK_ACC_SHIFT) | + (host->csr.max_rec << CSR_MAX_REC_SHIFT) | + (0 << CSR_MAX_ROM_SHIFT) | + (0 << CSR_GENERATION_SHIFT) | + host->csr.lnk_spd); + + bus_info[3] = cpu_to_be32(host->csr.guid_hi); + bus_info[4] = cpu_to_be32(host->csr.guid_lo); + + csr1212_detach_keyval_from_directory(host->csr.rom->root_kv, node_cap); + + csr1212_init_local_csr(host->csr.rom, bus_info, 0); + host->update_config_rom = 1; +} + + +int hpsb_update_config_rom(struct hpsb_host *host, const quadlet_t *new_rom, + size_t buffersize, unsigned char rom_version) +{ + unsigned long flags; + int ret; + + HPSB_NOTICE("hpsb_update_config_rom() is deprecated"); + + spin_lock_irqsave(&host->csr.lock, flags); + if (rom_version != host->csr.generation) + ret = -1; + else if (buffersize > host->csr.rom->cache_head->size) + ret = -2; + else { + /* Just overwrite the generated ConfigROM image with new data, + * it can be regenerated later. */ + memcpy(host->csr.rom->cache_head->data, new_rom, buffersize); + host->csr.rom->cache_head->len = buffersize; + + if (host->driver->set_hw_config_rom) + host->driver->set_hw_config_rom(host, host->csr.rom->bus_info_data); + /* Increment the generation number to keep some sort of sync + * with the newer ConfigROM manipulation method. */ + host->csr.generation++; + if (host->csr.generation > 0xf || host->csr.generation < 2) + host->csr.generation = 2; + ret=0; + } + spin_unlock_irqrestore(&host->csr.lock, flags); + return ret; +} + + +/* Read topology / speed maps and configuration ROM */ +static int read_maps(struct hpsb_host *host, int nodeid, quadlet_t *buffer, + u64 addr, size_t length, u16 fl) +{ + unsigned long flags; + int csraddr = addr - CSR_REGISTER_BASE; + const char *src; + + spin_lock_irqsave(&host->csr.lock, flags); + + if (csraddr < CSR_SPEED_MAP) { + src = ((char *)host->csr.topology_map) + csraddr + - CSR_TOPOLOGY_MAP; + } else { + src = ((char *)host->csr.speed_map) + csraddr - CSR_SPEED_MAP; + } + + memcpy(buffer, src, length); + spin_unlock_irqrestore(&host->csr.lock, flags); + return RCODE_COMPLETE; +} + + +#define out if (--length == 0) break + +static int read_regs(struct hpsb_host *host, int nodeid, quadlet_t *buf, + u64 addr, size_t length, u16 flags) +{ + int csraddr = addr - CSR_REGISTER_BASE; + int oldcycle; + quadlet_t ret; + + if ((csraddr | length) & 0x3) + return RCODE_TYPE_ERROR; + + length /= 4; + + switch (csraddr) { + case CSR_STATE_CLEAR: + *(buf++) = cpu_to_be32(host->csr.state); + out; + case CSR_STATE_SET: + *(buf++) = cpu_to_be32(host->csr.state); + out; + case CSR_NODE_IDS: + *(buf++) = cpu_to_be32(host->csr.node_ids); + out; + + case CSR_RESET_START: + return RCODE_TYPE_ERROR; + + /* address gap - handled by default below */ + + case CSR_SPLIT_TIMEOUT_HI: + *(buf++) = cpu_to_be32(host->csr.split_timeout_hi); + out; + case CSR_SPLIT_TIMEOUT_LO: + *(buf++) = cpu_to_be32(host->csr.split_timeout_lo); + out; + + /* address gap */ + return RCODE_ADDRESS_ERROR; + + case CSR_CYCLE_TIME: + oldcycle = host->csr.cycle_time; + host->csr.cycle_time = + host->driver->devctl(host, GET_CYCLE_COUNTER, 0); + + if (oldcycle > host->csr.cycle_time) { + /* cycle time wrapped around */ + host->csr.bus_time += 1 << 7; + } + *(buf++) = cpu_to_be32(host->csr.cycle_time); + out; + case CSR_BUS_TIME: + oldcycle = host->csr.cycle_time; + host->csr.cycle_time = + host->driver->devctl(host, GET_CYCLE_COUNTER, 0); + + if (oldcycle > host->csr.cycle_time) { + /* cycle time wrapped around */ + host->csr.bus_time += (1 << 7); + } + *(buf++) = cpu_to_be32(host->csr.bus_time + | (host->csr.cycle_time >> 25)); + out; + + /* address gap */ + return RCODE_ADDRESS_ERROR; + + case CSR_BUSY_TIMEOUT: + /* not yet implemented */ + return RCODE_ADDRESS_ERROR; + + case CSR_BUS_MANAGER_ID: + if (host->driver->hw_csr_reg) + ret = host->driver->hw_csr_reg(host, 0, 0, 0); + else + ret = host->csr.bus_manager_id; + + *(buf++) = cpu_to_be32(ret); + out; + case CSR_BANDWIDTH_AVAILABLE: + if (host->driver->hw_csr_reg) + ret = host->driver->hw_csr_reg(host, 1, 0, 0); + else + ret = host->csr.bandwidth_available; + + *(buf++) = cpu_to_be32(ret); + out; + case CSR_CHANNELS_AVAILABLE_HI: + if (host->driver->hw_csr_reg) + ret = host->driver->hw_csr_reg(host, 2, 0, 0); + else + ret = host->csr.channels_available_hi; + + *(buf++) = cpu_to_be32(ret); + out; + case CSR_CHANNELS_AVAILABLE_LO: + if (host->driver->hw_csr_reg) + ret = host->driver->hw_csr_reg(host, 3, 0, 0); + else + ret = host->csr.channels_available_lo; + + *(buf++) = cpu_to_be32(ret); + out; + + case CSR_BROADCAST_CHANNEL: + *(buf++) = cpu_to_be32(host->csr.broadcast_channel); + out; + + /* address gap to end - fall through to default */ + default: + return RCODE_ADDRESS_ERROR; + } + + return RCODE_COMPLETE; +} + +static int write_regs(struct hpsb_host *host, int nodeid, int destid, + quadlet_t *data, u64 addr, size_t length, u16 flags) +{ + int csraddr = addr - CSR_REGISTER_BASE; + + if ((csraddr | length) & 0x3) + return RCODE_TYPE_ERROR; + + length /= 4; + + switch (csraddr) { + case CSR_STATE_CLEAR: + /* FIXME FIXME FIXME */ + printk("doh, someone wants to mess with state clear\n"); + out; + case CSR_STATE_SET: + printk("doh, someone wants to mess with state set\n"); + out; + + case CSR_NODE_IDS: + host->csr.node_ids &= NODE_MASK << 16; + host->csr.node_ids |= be32_to_cpu(*(data++)) & (BUS_MASK << 16); + host->node_id = host->csr.node_ids >> 16; + host->driver->devctl(host, SET_BUS_ID, host->node_id >> 6); + out; + + case CSR_RESET_START: + /* FIXME - perform command reset */ + out; + + /* address gap */ + return RCODE_ADDRESS_ERROR; + + case CSR_SPLIT_TIMEOUT_HI: + host->csr.split_timeout_hi = + be32_to_cpu(*(data++)) & 0x00000007; + calculate_expire(&host->csr); + out; + case CSR_SPLIT_TIMEOUT_LO: + host->csr.split_timeout_lo = + be32_to_cpu(*(data++)) & 0xfff80000; + calculate_expire(&host->csr); + out; + + /* address gap */ + return RCODE_ADDRESS_ERROR; + + case CSR_CYCLE_TIME: + /* should only be set by cycle start packet, automatically */ + host->csr.cycle_time = be32_to_cpu(*data); + host->driver->devctl(host, SET_CYCLE_COUNTER, + be32_to_cpu(*(data++))); + out; + case CSR_BUS_TIME: + host->csr.bus_time = be32_to_cpu(*(data++)) & 0xffffff80; + out; + + /* address gap */ + return RCODE_ADDRESS_ERROR; + + case CSR_BUSY_TIMEOUT: + /* not yet implemented */ + return RCODE_ADDRESS_ERROR; + + case CSR_BUS_MANAGER_ID: + case CSR_BANDWIDTH_AVAILABLE: + case CSR_CHANNELS_AVAILABLE_HI: + case CSR_CHANNELS_AVAILABLE_LO: + /* these are not writable, only lockable */ + return RCODE_TYPE_ERROR; + + case CSR_BROADCAST_CHANNEL: + /* only the valid bit can be written */ + host->csr.broadcast_channel = (host->csr.broadcast_channel & ~0x40000000) + | (be32_to_cpu(*data) & 0x40000000); + out; + + /* address gap to end - fall through */ + default: + return RCODE_ADDRESS_ERROR; + } + + return RCODE_COMPLETE; +} + +#undef out + + +static int lock_regs(struct hpsb_host *host, int nodeid, quadlet_t *store, + u64 addr, quadlet_t data, quadlet_t arg, int extcode, u16 fl) +{ + int csraddr = addr - CSR_REGISTER_BASE; + unsigned long flags; + quadlet_t *regptr = NULL; + + if (csraddr & 0x3) + return RCODE_TYPE_ERROR; + + if (csraddr < CSR_BUS_MANAGER_ID || csraddr > CSR_CHANNELS_AVAILABLE_LO + || extcode != EXTCODE_COMPARE_SWAP) + goto unsupported_lockreq; + + data = be32_to_cpu(data); + arg = be32_to_cpu(arg); + + /* Is somebody releasing the broadcast_channel on us? */ + if (csraddr == CSR_CHANNELS_AVAILABLE_HI && (data & 0x1)) { + /* Note: this is may not be the right way to handle + * the problem, so we should look into the proper way + * eventually. */ + HPSB_WARN("Node [" NODE_BUS_FMT "] wants to release " + "broadcast channel 31. Ignoring.", + NODE_BUS_ARGS(host, nodeid)); + + data &= ~0x1; /* keep broadcast channel allocated */ + } + + if (host->driver->hw_csr_reg) { + quadlet_t old; + + old = host->driver-> + hw_csr_reg(host, (csraddr - CSR_BUS_MANAGER_ID) >> 2, + data, arg); + + *store = cpu_to_be32(old); + return RCODE_COMPLETE; + } + + spin_lock_irqsave(&host->csr.lock, flags); + + switch (csraddr) { + case CSR_BUS_MANAGER_ID: + regptr = &host->csr.bus_manager_id; + *store = cpu_to_be32(*regptr); + if (*regptr == arg) + *regptr = data; + break; + + case CSR_BANDWIDTH_AVAILABLE: + { + quadlet_t bandwidth; + quadlet_t old; + quadlet_t new; + + regptr = &host->csr.bandwidth_available; + old = *regptr; + + /* bandwidth available algorithm adapted from IEEE 1394a-2000 spec */ + if (arg > 0x1fff) { + *store = cpu_to_be32(old); /* change nothing */ + break; + } + data &= 0x1fff; + if (arg >= data) { + /* allocate bandwidth */ + bandwidth = arg - data; + if (old >= bandwidth) { + new = old - bandwidth; + *store = cpu_to_be32(arg); + *regptr = new; + } else { + *store = cpu_to_be32(old); + } + } else { + /* deallocate bandwidth */ + bandwidth = data - arg; + if (old + bandwidth < 0x2000) { + new = old + bandwidth; + *store = cpu_to_be32(arg); + *regptr = new; + } else { + *store = cpu_to_be32(old); + } + } + break; + } + + case CSR_CHANNELS_AVAILABLE_HI: + { + /* Lock algorithm for CHANNELS_AVAILABLE as recommended by 1394a-2000 */ + quadlet_t affected_channels = arg ^ data; + + regptr = &host->csr.channels_available_hi; + + if ((arg & affected_channels) == (*regptr & affected_channels)) { + *regptr ^= affected_channels; + *store = cpu_to_be32(arg); + } else { + *store = cpu_to_be32(*regptr); + } + + break; + } + + case CSR_CHANNELS_AVAILABLE_LO: + { + /* Lock algorithm for CHANNELS_AVAILABLE as recommended by 1394a-2000 */ + quadlet_t affected_channels = arg ^ data; + + regptr = &host->csr.channels_available_lo; + + if ((arg & affected_channels) == (*regptr & affected_channels)) { + *regptr ^= affected_channels; + *store = cpu_to_be32(arg); + } else { + *store = cpu_to_be32(*regptr); + } + break; + } + } + + spin_unlock_irqrestore(&host->csr.lock, flags); + + return RCODE_COMPLETE; + + unsupported_lockreq: + switch (csraddr) { + case CSR_STATE_CLEAR: + case CSR_STATE_SET: + case CSR_RESET_START: + case CSR_NODE_IDS: + case CSR_SPLIT_TIMEOUT_HI: + case CSR_SPLIT_TIMEOUT_LO: + case CSR_CYCLE_TIME: + case CSR_BUS_TIME: + case CSR_BROADCAST_CHANNEL: + return RCODE_TYPE_ERROR; + + case CSR_BUSY_TIMEOUT: + /* not yet implemented - fall through */ + default: + return RCODE_ADDRESS_ERROR; + } +} + +static int lock64_regs(struct hpsb_host *host, int nodeid, octlet_t * store, + u64 addr, octlet_t data, octlet_t arg, int extcode, u16 fl) +{ + int csraddr = addr - CSR_REGISTER_BASE; + unsigned long flags; + + data = be64_to_cpu(data); + arg = be64_to_cpu(arg); + + if (csraddr & 0x3) + return RCODE_TYPE_ERROR; + + if (csraddr != CSR_CHANNELS_AVAILABLE + || extcode != EXTCODE_COMPARE_SWAP) + goto unsupported_lock64req; + + /* Is somebody releasing the broadcast_channel on us? */ + if (csraddr == CSR_CHANNELS_AVAILABLE_HI && (data & 0x100000000ULL)) { + /* Note: this is may not be the right way to handle + * the problem, so we should look into the proper way + * eventually. */ + HPSB_WARN("Node [" NODE_BUS_FMT "] wants to release " + "broadcast channel 31. Ignoring.", + NODE_BUS_ARGS(host, nodeid)); + + data &= ~0x100000000ULL; /* keep broadcast channel allocated */ + } + + if (host->driver->hw_csr_reg) { + quadlet_t data_hi, data_lo; + quadlet_t arg_hi, arg_lo; + quadlet_t old_hi, old_lo; + + data_hi = data >> 32; + data_lo = data & 0xFFFFFFFF; + arg_hi = arg >> 32; + arg_lo = arg & 0xFFFFFFFF; + + old_hi = host->driver->hw_csr_reg(host, (csraddr - CSR_BUS_MANAGER_ID) >> 2, + data_hi, arg_hi); + + old_lo = host->driver->hw_csr_reg(host, ((csraddr + 4) - CSR_BUS_MANAGER_ID) >> 2, + data_lo, arg_lo); + + *store = cpu_to_be64(((octlet_t)old_hi << 32) | old_lo); + } else { + octlet_t old; + octlet_t affected_channels = arg ^ data; + + spin_lock_irqsave(&host->csr.lock, flags); + + old = ((octlet_t)host->csr.channels_available_hi << 32) | host->csr.channels_available_lo; + + if ((arg & affected_channels) == (old & affected_channels)) { + host->csr.channels_available_hi ^= (affected_channels >> 32); + host->csr.channels_available_lo ^= (affected_channels & 0xffffffff); + *store = cpu_to_be64(arg); + } else { + *store = cpu_to_be64(old); + } + + spin_unlock_irqrestore(&host->csr.lock, flags); + } + + /* Is somebody erroneously releasing the broadcast_channel on us? */ + if (host->csr.channels_available_hi & 0x1) + host->csr.channels_available_hi &= ~0x1; + + return RCODE_COMPLETE; + + unsupported_lock64req: + switch (csraddr) { + case CSR_STATE_CLEAR: + case CSR_STATE_SET: + case CSR_RESET_START: + case CSR_NODE_IDS: + case CSR_SPLIT_TIMEOUT_HI: + case CSR_SPLIT_TIMEOUT_LO: + case CSR_CYCLE_TIME: + case CSR_BUS_TIME: + case CSR_BUS_MANAGER_ID: + case CSR_BROADCAST_CHANNEL: + case CSR_BUSY_TIMEOUT: + case CSR_BANDWIDTH_AVAILABLE: + return RCODE_TYPE_ERROR; + + default: + return RCODE_ADDRESS_ERROR; + } +} + +static int write_fcp(struct hpsb_host *host, int nodeid, int dest, + quadlet_t *data, u64 addr, size_t length, u16 flags) +{ + int csraddr = addr - CSR_REGISTER_BASE; + + if (length > 512) + return RCODE_TYPE_ERROR; + + switch (csraddr) { + case CSR_FCP_COMMAND: + highlevel_fcp_request(host, nodeid, 0, (u8 *)data, length); + break; + case CSR_FCP_RESPONSE: + highlevel_fcp_request(host, nodeid, 1, (u8 *)data, length); + break; + default: + return RCODE_TYPE_ERROR; + } + + return RCODE_COMPLETE; +} + +static int read_config_rom(struct hpsb_host *host, int nodeid, quadlet_t *buffer, + u64 addr, size_t length, u16 fl) +{ + u32 offset = addr - CSR1212_REGISTER_SPACE_BASE; + + if (csr1212_read(host->csr.rom, offset, buffer, length) == CSR1212_SUCCESS) + return RCODE_COMPLETE; + else + return RCODE_ADDRESS_ERROR; +} + +static u64 allocate_addr_range(u64 size, u32 alignment, void *__host) +{ + struct hpsb_host *host = (struct hpsb_host*)__host; + + return hpsb_allocate_and_register_addrspace(&csr_highlevel, + host, + &config_rom_ops, + size, alignment, + CSR1212_UNITS_SPACE_BASE, + CSR1212_UNITS_SPACE_END); +} + +static void release_addr_range(u64 addr, void *__host) +{ + struct hpsb_host *host = (struct hpsb_host*)__host; + hpsb_unregister_addrspace(&csr_highlevel, host, addr); +} + + +int init_csr(void) +{ + node_cap = csr1212_new_immediate(CSR1212_KV_ID_NODE_CAPABILITIES, 0x0083c0); + if (!node_cap) { + HPSB_ERR("Failed to allocate memory for Node Capabilties ConfigROM entry!"); + return -ENOMEM; + } + + hpsb_register_highlevel(&csr_highlevel); + + return 0; +} + +void cleanup_csr(void) +{ + if (node_cap) + csr1212_release_keyval(node_cap); + hpsb_unregister_highlevel(&csr_highlevel); +} diff --git a/trunk/drivers/ieee1394/csr.h b/trunk/drivers/ieee1394/csr.h new file mode 100644 index 000000000000..90fb3f2192c3 --- /dev/null +++ b/trunk/drivers/ieee1394/csr.h @@ -0,0 +1,99 @@ +#ifndef _IEEE1394_CSR_H +#define _IEEE1394_CSR_H + +#include + +#include "csr1212.h" +#include "ieee1394_types.h" + +#define CSR_REGISTER_BASE 0xfffff0000000ULL + +/* register offsets relative to CSR_REGISTER_BASE */ +#define CSR_STATE_CLEAR 0x0 +#define CSR_STATE_SET 0x4 +#define CSR_NODE_IDS 0x8 +#define CSR_RESET_START 0xc +#define CSR_SPLIT_TIMEOUT_HI 0x18 +#define CSR_SPLIT_TIMEOUT_LO 0x1c +#define CSR_CYCLE_TIME 0x200 +#define CSR_BUS_TIME 0x204 +#define CSR_BUSY_TIMEOUT 0x210 +#define CSR_BUS_MANAGER_ID 0x21c +#define CSR_BANDWIDTH_AVAILABLE 0x220 +#define CSR_CHANNELS_AVAILABLE 0x224 +#define CSR_CHANNELS_AVAILABLE_HI 0x224 +#define CSR_CHANNELS_AVAILABLE_LO 0x228 +#define CSR_BROADCAST_CHANNEL 0x234 +#define CSR_CONFIG_ROM 0x400 +#define CSR_CONFIG_ROM_END 0x800 +#define CSR_FCP_COMMAND 0xB00 +#define CSR_FCP_RESPONSE 0xD00 +#define CSR_FCP_END 0xF00 +#define CSR_TOPOLOGY_MAP 0x1000 +#define CSR_TOPOLOGY_MAP_END 0x1400 +#define CSR_SPEED_MAP 0x2000 +#define CSR_SPEED_MAP_END 0x3000 + +/* IEEE 1394 bus specific Configuration ROM Key IDs */ +#define IEEE1394_KV_ID_POWER_REQUIREMENTS (0x30) + +/* IEEE 1394 Bus Information Block specifics */ +#define CSR_BUS_INFO_SIZE (5 * sizeof(quadlet_t)) + +#define CSR_IRMC_SHIFT 31 +#define CSR_CMC_SHIFT 30 +#define CSR_ISC_SHIFT 29 +#define CSR_BMC_SHIFT 28 +#define CSR_PMC_SHIFT 27 +#define CSR_CYC_CLK_ACC_SHIFT 16 +#define CSR_MAX_REC_SHIFT 12 +#define CSR_MAX_ROM_SHIFT 8 +#define CSR_GENERATION_SHIFT 4 + +static inline void csr_set_bus_info_generation(struct csr1212_csr *csr, u8 gen) +{ + csr->bus_info_data[2] &= ~cpu_to_be32(0xf << CSR_GENERATION_SHIFT); + csr->bus_info_data[2] |= cpu_to_be32((u32)gen << CSR_GENERATION_SHIFT); +} + +struct csr_control { + spinlock_t lock; + + quadlet_t state; + quadlet_t node_ids; + quadlet_t split_timeout_hi, split_timeout_lo; + unsigned long expire; /* Calculated from split_timeout */ + quadlet_t cycle_time; + quadlet_t bus_time; + quadlet_t bus_manager_id; + quadlet_t bandwidth_available; + quadlet_t channels_available_hi, channels_available_lo; + quadlet_t broadcast_channel; + + /* Bus Info */ + quadlet_t guid_hi, guid_lo; + u8 cyc_clk_acc; + u8 max_rec; + u8 max_rom; + u8 generation; /* Only use values between 0x2 and 0xf */ + u8 lnk_spd; + + unsigned long gen_timestamp[16]; + + struct csr1212_csr *rom; + + quadlet_t topology_map[256]; + quadlet_t speed_map[1024]; +}; + +extern struct csr1212_bus_ops csr_bus_ops; + +int init_csr(void); +void cleanup_csr(void); + +/* hpsb_update_config_rom() is deprecated */ +struct hpsb_host; +int hpsb_update_config_rom(struct hpsb_host *host, const quadlet_t *new_rom, + size_t size, unsigned char rom_version); + +#endif /* _IEEE1394_CSR_H */ diff --git a/trunk/drivers/ieee1394/csr1212.c b/trunk/drivers/ieee1394/csr1212.c new file mode 100644 index 000000000000..e76cac64c533 --- /dev/null +++ b/trunk/drivers/ieee1394/csr1212.c @@ -0,0 +1,1467 @@ +/* + * csr1212.c -- IEEE 1212 Control and Status Register support for Linux + * + * Copyright (C) 2003 Francois Retief + * Steve Kinneberg + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +/* TODO List: + * - Verify interface consistency: i.e., public functions that take a size + * parameter expect size to be in bytes. + */ + +#include +#include +#include +#include +#include +#include + +#include "csr1212.h" + + +/* Permitted key type for each key id */ +#define __I (1 << CSR1212_KV_TYPE_IMMEDIATE) +#define __C (1 << CSR1212_KV_TYPE_CSR_OFFSET) +#define __D (1 << CSR1212_KV_TYPE_DIRECTORY) +#define __L (1 << CSR1212_KV_TYPE_LEAF) +static const u8 csr1212_key_id_type_map[0x30] = { + __C, /* used by Apple iSight */ + __D | __L, /* Descriptor */ + __I | __D | __L, /* Bus_Dependent_Info */ + __I | __D | __L, /* Vendor */ + __I, /* Hardware_Version */ + 0, 0, /* Reserved */ + __D | __L | __I, /* Module */ + __I, 0, 0, 0, /* used by Apple iSight, Reserved */ + __I, /* Node_Capabilities */ + __L, /* EUI_64 */ + 0, 0, 0, /* Reserved */ + __D, /* Unit */ + __I, /* Specifier_ID */ + __I, /* Version */ + __I | __C | __D | __L, /* Dependent_Info */ + __L, /* Unit_Location */ + 0, /* Reserved */ + __I, /* Model */ + __D, /* Instance */ + __L, /* Keyword */ + __D, /* Feature */ + __L, /* Extended_ROM */ + __I, /* Extended_Key_Specifier_ID */ + __I, /* Extended_Key */ + __I | __C | __D | __L, /* Extended_Data */ + __L, /* Modifiable_Descriptor */ + __I, /* Directory_ID */ + __I, /* Revision */ +}; +#undef __I +#undef __C +#undef __D +#undef __L + + +#define quads_to_bytes(_q) ((_q) * sizeof(u32)) +#define bytes_to_quads(_b) DIV_ROUND_UP(_b, sizeof(u32)) + +static void free_keyval(struct csr1212_keyval *kv) +{ + if ((kv->key.type == CSR1212_KV_TYPE_LEAF) && + (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM)) + CSR1212_FREE(kv->value.leaf.data); + + CSR1212_FREE(kv); +} + +static u16 csr1212_crc16(const u32 *buffer, size_t length) +{ + int shift; + u32 data; + u16 sum, crc = 0; + + for (; length; length--) { + data = be32_to_cpu(*buffer); + buffer++; + for (shift = 28; shift >= 0; shift -= 4 ) { + sum = ((crc >> 12) ^ (data >> shift)) & 0xf; + crc = (crc << 4) ^ (sum << 12) ^ (sum << 5) ^ (sum); + } + crc &= 0xffff; + } + + return cpu_to_be16(crc); +} + +/* Microsoft computes the CRC with the bytes in reverse order. */ +static u16 csr1212_msft_crc16(const u32 *buffer, size_t length) +{ + int shift; + u32 data; + u16 sum, crc = 0; + + for (; length; length--) { + data = le32_to_cpu(*buffer); + buffer++; + for (shift = 28; shift >= 0; shift -= 4 ) { + sum = ((crc >> 12) ^ (data >> shift)) & 0xf; + crc = (crc << 4) ^ (sum << 12) ^ (sum << 5) ^ (sum); + } + crc &= 0xffff; + } + + return cpu_to_be16(crc); +} + +static struct csr1212_dentry * +csr1212_find_keyval(struct csr1212_keyval *dir, struct csr1212_keyval *kv) +{ + struct csr1212_dentry *pos; + + for (pos = dir->value.directory.dentries_head; + pos != NULL; pos = pos->next) + if (pos->kv == kv) + return pos; + return NULL; +} + +static struct csr1212_keyval * +csr1212_find_keyval_offset(struct csr1212_keyval *kv_list, u32 offset) +{ + struct csr1212_keyval *kv; + + for (kv = kv_list->next; kv && (kv != kv_list); kv = kv->next) + if (kv->offset == offset) + return kv; + return NULL; +} + + +/* Creation Routines */ + +struct csr1212_csr *csr1212_create_csr(struct csr1212_bus_ops *ops, + size_t bus_info_size, void *private) +{ + struct csr1212_csr *csr; + + csr = CSR1212_MALLOC(sizeof(*csr)); + if (!csr) + return NULL; + + csr->cache_head = + csr1212_rom_cache_malloc(CSR1212_CONFIG_ROM_SPACE_OFFSET, + CSR1212_CONFIG_ROM_SPACE_SIZE); + if (!csr->cache_head) { + CSR1212_FREE(csr); + return NULL; + } + + /* The keyval key id is not used for the root node, but a valid key id + * that can be used for a directory needs to be passed to + * csr1212_new_directory(). */ + csr->root_kv = csr1212_new_directory(CSR1212_KV_ID_VENDOR); + if (!csr->root_kv) { + CSR1212_FREE(csr->cache_head); + CSR1212_FREE(csr); + return NULL; + } + + csr->bus_info_data = csr->cache_head->data; + csr->bus_info_len = bus_info_size; + csr->crc_len = bus_info_size; + csr->ops = ops; + csr->private = private; + csr->cache_tail = csr->cache_head; + + return csr; +} + +void csr1212_init_local_csr(struct csr1212_csr *csr, + const u32 *bus_info_data, int max_rom) +{ + static const int mr_map[] = { 4, 64, 1024, 0 }; + + BUG_ON(max_rom & ~0x3); + csr->max_rom = mr_map[max_rom]; + memcpy(csr->bus_info_data, bus_info_data, csr->bus_info_len); +} + +static struct csr1212_keyval *csr1212_new_keyval(u8 type, u8 key) +{ + struct csr1212_keyval *kv; + + if (key < 0x30 && ((csr1212_key_id_type_map[key] & (1 << type)) == 0)) + return NULL; + + kv = CSR1212_MALLOC(sizeof(*kv)); + if (!kv) + return NULL; + + atomic_set(&kv->refcnt, 1); + kv->key.type = type; + kv->key.id = key; + kv->associate = NULL; + kv->next = NULL; + kv->prev = NULL; + kv->offset = 0; + kv->valid = 0; + return kv; +} + +struct csr1212_keyval *csr1212_new_immediate(u8 key, u32 value) +{ + struct csr1212_keyval *kv; + + kv = csr1212_new_keyval(CSR1212_KV_TYPE_IMMEDIATE, key); + if (!kv) + return NULL; + + kv->value.immediate = value; + kv->valid = 1; + return kv; +} + +static struct csr1212_keyval * +csr1212_new_leaf(u8 key, const void *data, size_t data_len) +{ + struct csr1212_keyval *kv; + + kv = csr1212_new_keyval(CSR1212_KV_TYPE_LEAF, key); + if (!kv) + return NULL; + + if (data_len > 0) { + kv->value.leaf.data = CSR1212_MALLOC(data_len); + if (!kv->value.leaf.data) { + CSR1212_FREE(kv); + return NULL; + } + + if (data) + memcpy(kv->value.leaf.data, data, data_len); + } else { + kv->value.leaf.data = NULL; + } + + kv->value.leaf.len = bytes_to_quads(data_len); + kv->offset = 0; + kv->valid = 1; + + return kv; +} + +static struct csr1212_keyval * +csr1212_new_csr_offset(u8 key, u32 csr_offset) +{ + struct csr1212_keyval *kv; + + kv = csr1212_new_keyval(CSR1212_KV_TYPE_CSR_OFFSET, key); + if (!kv) + return NULL; + + kv->value.csr_offset = csr_offset; + + kv->offset = 0; + kv->valid = 1; + return kv; +} + +struct csr1212_keyval *csr1212_new_directory(u8 key) +{ + struct csr1212_keyval *kv; + + kv = csr1212_new_keyval(CSR1212_KV_TYPE_DIRECTORY, key); + if (!kv) + return NULL; + + kv->value.directory.len = 0; + kv->offset = 0; + kv->value.directory.dentries_head = NULL; + kv->value.directory.dentries_tail = NULL; + kv->valid = 1; + return kv; +} + +void csr1212_associate_keyval(struct csr1212_keyval *kv, + struct csr1212_keyval *associate) +{ + BUG_ON(!kv || !associate || kv->key.id == CSR1212_KV_ID_DESCRIPTOR || + (associate->key.id != CSR1212_KV_ID_DESCRIPTOR && + associate->key.id != CSR1212_KV_ID_DEPENDENT_INFO && + associate->key.id != CSR1212_KV_ID_EXTENDED_KEY && + associate->key.id != CSR1212_KV_ID_EXTENDED_DATA && + associate->key.id < 0x30) || + (kv->key.id == CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID && + associate->key.id != CSR1212_KV_ID_EXTENDED_KEY) || + (kv->key.id == CSR1212_KV_ID_EXTENDED_KEY && + associate->key.id != CSR1212_KV_ID_EXTENDED_DATA) || + (associate->key.id == CSR1212_KV_ID_EXTENDED_KEY && + kv->key.id != CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) || + (associate->key.id == CSR1212_KV_ID_EXTENDED_DATA && + kv->key.id != CSR1212_KV_ID_EXTENDED_KEY)); + + if (kv->associate) + csr1212_release_keyval(kv->associate); + + csr1212_keep_keyval(associate); + kv->associate = associate; +} + +static int __csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir, + struct csr1212_keyval *kv, + bool keep_keyval) +{ + struct csr1212_dentry *dentry; + + BUG_ON(!kv || !dir || dir->key.type != CSR1212_KV_TYPE_DIRECTORY); + + dentry = CSR1212_MALLOC(sizeof(*dentry)); + if (!dentry) + return -ENOMEM; + + if (keep_keyval) + csr1212_keep_keyval(kv); + dentry->kv = kv; + + dentry->next = NULL; + dentry->prev = dir->value.directory.dentries_tail; + + if (!dir->value.directory.dentries_head) + dir->value.directory.dentries_head = dentry; + + if (dir->value.directory.dentries_tail) + dir->value.directory.dentries_tail->next = dentry; + dir->value.directory.dentries_tail = dentry; + + return CSR1212_SUCCESS; +} + +int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir, + struct csr1212_keyval *kv) +{ + return __csr1212_attach_keyval_to_directory(dir, kv, true); +} + +#define CSR1212_DESCRIPTOR_LEAF_DATA(kv) \ + (&((kv)->value.leaf.data[1])) + +#define CSR1212_DESCRIPTOR_LEAF_SET_TYPE(kv, type) \ + ((kv)->value.leaf.data[0] = \ + cpu_to_be32(CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) | \ + ((type) << CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT))) +#define CSR1212_DESCRIPTOR_LEAF_SET_SPECIFIER_ID(kv, spec_id) \ + ((kv)->value.leaf.data[0] = \ + cpu_to_be32((CSR1212_DESCRIPTOR_LEAF_TYPE(kv) << \ + CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT) | \ + ((spec_id) & CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK))) + +static struct csr1212_keyval * +csr1212_new_descriptor_leaf(u8 dtype, u32 specifier_id, + const void *data, size_t data_len) +{ + struct csr1212_keyval *kv; + + kv = csr1212_new_leaf(CSR1212_KV_ID_DESCRIPTOR, NULL, + data_len + CSR1212_DESCRIPTOR_LEAF_OVERHEAD); + if (!kv) + return NULL; + + kmemcheck_annotate_variable(kv->value.leaf.data[0]); + CSR1212_DESCRIPTOR_LEAF_SET_TYPE(kv, dtype); + CSR1212_DESCRIPTOR_LEAF_SET_SPECIFIER_ID(kv, specifier_id); + + if (data) + memcpy(CSR1212_DESCRIPTOR_LEAF_DATA(kv), data, data_len); + + return kv; +} + +/* Check if string conforms to minimal ASCII as per IEEE 1212 clause 7.4 */ +static int csr1212_check_minimal_ascii(const char *s) +{ + static const char minimal_ascii_table[] = { + /* 1 2 4 8 16 32 64 128 */ + 128, /* --, --, --, --, --, --, --, 07, */ + 4 + 16 + 32, /* --, --, 0a, --, 0C, 0D, --, --, */ + 0, /* --, --, --, --, --, --, --, --, */ + 0, /* --, --, --, --, --, --, --, --, */ + 255 - 8 - 16, /* 20, 21, 22, --, --, 25, 26, 27, */ + 255, /* 28, 29, 2a, 2b, 2c, 2d, 2e, 2f, */ + 255, /* 30, 31, 32, 33, 34, 35, 36, 37, */ + 255, /* 38, 39, 3a, 3b, 3c, 3d, 3e, 3f, */ + 255, /* 40, 41, 42, 43, 44, 45, 46, 47, */ + 255, /* 48, 49, 4a, 4b, 4c, 4d, 4e, 4f, */ + 255, /* 50, 51, 52, 53, 54, 55, 56, 57, */ + 1 + 2 + 4 + 128, /* 58, 59, 5a, --, --, --, --, 5f, */ + 255 - 1, /* --, 61, 62, 63, 64, 65, 66, 67, */ + 255, /* 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, */ + 255, /* 70, 71, 72, 73, 74, 75, 76, 77, */ + 1 + 2 + 4, /* 78, 79, 7a, --, --, --, --, --, */ + }; + int i, j; + + for (; *s; s++) { + i = *s >> 3; /* i = *s / 8; */ + j = 1 << (*s & 3); /* j = 1 << (*s % 8); */ + + if (i >= ARRAY_SIZE(minimal_ascii_table) || + !(minimal_ascii_table[i] & j)) + return -EINVAL; + } + return 0; +} + +/* IEEE 1212 clause 7.5.4.1 textual descriptors (English, minimal ASCII) */ +struct csr1212_keyval *csr1212_new_string_descriptor_leaf(const char *s) +{ + struct csr1212_keyval *kv; + u32 *text; + size_t str_len, quads; + + if (!s || !*s || csr1212_check_minimal_ascii(s)) + return NULL; + + str_len = strlen(s); + quads = bytes_to_quads(str_len); + kv = csr1212_new_descriptor_leaf(0, 0, NULL, quads_to_bytes(quads) + + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_OVERHEAD); + if (!kv) + return NULL; + + kv->value.leaf.data[1] = 0; /* width, character_set, language */ + text = CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(kv); + text[quads - 1] = 0; /* padding */ + memcpy(text, s, str_len); + + return kv; +} + + +/* Destruction Routines */ + +void csr1212_detach_keyval_from_directory(struct csr1212_keyval *dir, + struct csr1212_keyval *kv) +{ + struct csr1212_dentry *dentry; + + if (!kv || !dir || dir->key.type != CSR1212_KV_TYPE_DIRECTORY) + return; + + dentry = csr1212_find_keyval(dir, kv); + + if (!dentry) + return; + + if (dentry->prev) + dentry->prev->next = dentry->next; + if (dentry->next) + dentry->next->prev = dentry->prev; + if (dir->value.directory.dentries_head == dentry) + dir->value.directory.dentries_head = dentry->next; + if (dir->value.directory.dentries_tail == dentry) + dir->value.directory.dentries_tail = dentry->prev; + + CSR1212_FREE(dentry); + + csr1212_release_keyval(kv); +} + +/* This function is used to free the memory taken by a keyval. If the given + * keyval is a directory type, then any keyvals contained in that directory + * will be destroyed as well if noone holds a reference on them. By means of + * list manipulation, this routine will descend a directory structure in a + * non-recursive manner. */ +void csr1212_release_keyval(struct csr1212_keyval *kv) +{ + struct csr1212_keyval *k, *a; + struct csr1212_dentry dentry; + struct csr1212_dentry *head, *tail; + + if (!atomic_dec_and_test(&kv->refcnt)) + return; + + dentry.kv = kv; + dentry.next = NULL; + dentry.prev = NULL; + + head = &dentry; + tail = head; + + while (head) { + k = head->kv; + + while (k) { + /* must not dec_and_test kv->refcnt again */ + if (k != kv && !atomic_dec_and_test(&k->refcnt)) + break; + + a = k->associate; + + if (k->key.type == CSR1212_KV_TYPE_DIRECTORY) { + /* If the current entry is a directory, move all + * the entries to the destruction list. */ + if (k->value.directory.dentries_head) { + tail->next = + k->value.directory.dentries_head; + k->value.directory.dentries_head->prev = + tail; + tail = k->value.directory.dentries_tail; + } + } + free_keyval(k); + k = a; + } + + head = head->next; + if (head) { + if (head->prev && head->prev != &dentry) + CSR1212_FREE(head->prev); + head->prev = NULL; + } else if (tail != &dentry) { + CSR1212_FREE(tail); + } + } +} + +void csr1212_destroy_csr(struct csr1212_csr *csr) +{ + struct csr1212_csr_rom_cache *c, *oc; + struct csr1212_cache_region *cr, *ocr; + + csr1212_release_keyval(csr->root_kv); + + c = csr->cache_head; + while (c) { + oc = c; + cr = c->filled_head; + while (cr) { + ocr = cr; + cr = cr->next; + CSR1212_FREE(ocr); + } + c = c->next; + CSR1212_FREE(oc); + } + + CSR1212_FREE(csr); +} + + +/* CSR Image Creation */ + +static int csr1212_append_new_cache(struct csr1212_csr *csr, size_t romsize) +{ + struct csr1212_csr_rom_cache *cache; + u64 csr_addr; + + BUG_ON(!csr || !csr->ops || !csr->ops->allocate_addr_range || + !csr->ops->release_addr || csr->max_rom < 1); + + /* ROM size must be a multiple of csr->max_rom */ + romsize = (romsize + (csr->max_rom - 1)) & ~(csr->max_rom - 1); + + csr_addr = csr->ops->allocate_addr_range(romsize, csr->max_rom, + csr->private); + if (csr_addr == CSR1212_INVALID_ADDR_SPACE) + return -ENOMEM; + + if (csr_addr < CSR1212_REGISTER_SPACE_BASE) { + /* Invalid address returned from allocate_addr_range(). */ + csr->ops->release_addr(csr_addr, csr->private); + return -ENOMEM; + } + + cache = csr1212_rom_cache_malloc(csr_addr - CSR1212_REGISTER_SPACE_BASE, + romsize); + if (!cache) { + csr->ops->release_addr(csr_addr, csr->private); + return -ENOMEM; + } + + cache->ext_rom = csr1212_new_keyval(CSR1212_KV_TYPE_LEAF, + CSR1212_KV_ID_EXTENDED_ROM); + if (!cache->ext_rom) { + csr->ops->release_addr(csr_addr, csr->private); + CSR1212_FREE(cache); + return -ENOMEM; + } + + if (csr1212_attach_keyval_to_directory(csr->root_kv, cache->ext_rom) != + CSR1212_SUCCESS) { + csr1212_release_keyval(cache->ext_rom); + csr->ops->release_addr(csr_addr, csr->private); + CSR1212_FREE(cache); + return -ENOMEM; + } + cache->ext_rom->offset = csr_addr - CSR1212_REGISTER_SPACE_BASE; + cache->ext_rom->value.leaf.len = -1; + cache->ext_rom->value.leaf.data = cache->data; + + /* Add cache to tail of cache list */ + cache->prev = csr->cache_tail; + csr->cache_tail->next = cache; + csr->cache_tail = cache; + return CSR1212_SUCCESS; +} + +static void csr1212_remove_cache(struct csr1212_csr *csr, + struct csr1212_csr_rom_cache *cache) +{ + if (csr->cache_head == cache) + csr->cache_head = cache->next; + if (csr->cache_tail == cache) + csr->cache_tail = cache->prev; + + if (cache->prev) + cache->prev->next = cache->next; + if (cache->next) + cache->next->prev = cache->prev; + + if (cache->ext_rom) { + csr1212_detach_keyval_from_directory(csr->root_kv, + cache->ext_rom); + csr1212_release_keyval(cache->ext_rom); + } + + CSR1212_FREE(cache); +} + +static int csr1212_generate_layout_subdir(struct csr1212_keyval *dir, + struct csr1212_keyval **layout_tail) +{ + struct csr1212_dentry *dentry; + struct csr1212_keyval *dkv; + struct csr1212_keyval *last_extkey_spec = NULL; + struct csr1212_keyval *last_extkey = NULL; + int num_entries = 0; + + for (dentry = dir->value.directory.dentries_head; dentry; + dentry = dentry->next) { + for (dkv = dentry->kv; dkv; dkv = dkv->associate) { + /* Special Case: Extended Key Specifier_ID */ + if (dkv->key.id == + CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) { + if (last_extkey_spec == NULL) + last_extkey_spec = dkv; + else if (dkv->value.immediate != + last_extkey_spec->value.immediate) + last_extkey_spec = dkv; + else + continue; + /* Special Case: Extended Key */ + } else if (dkv->key.id == CSR1212_KV_ID_EXTENDED_KEY) { + if (last_extkey == NULL) + last_extkey = dkv; + else if (dkv->value.immediate != + last_extkey->value.immediate) + last_extkey = dkv; + else + continue; + } + + num_entries += 1; + + switch (dkv->key.type) { + default: + case CSR1212_KV_TYPE_IMMEDIATE: + case CSR1212_KV_TYPE_CSR_OFFSET: + break; + case CSR1212_KV_TYPE_LEAF: + case CSR1212_KV_TYPE_DIRECTORY: + /* Remove from list */ + if (dkv->prev && (dkv->prev->next == dkv)) + dkv->prev->next = dkv->next; + if (dkv->next && (dkv->next->prev == dkv)) + dkv->next->prev = dkv->prev; + //if (dkv == *layout_tail) + // *layout_tail = dkv->prev; + + /* Special case: Extended ROM leafs */ + if (dkv->key.id == CSR1212_KV_ID_EXTENDED_ROM) { + dkv->value.leaf.len = -1; + /* Don't add Extended ROM leafs in the + * layout list, they are handled + * differently. */ + break; + } + + /* Add to tail of list */ + dkv->next = NULL; + dkv->prev = *layout_tail; + (*layout_tail)->next = dkv; + *layout_tail = dkv; + break; + } + } + } + return num_entries; +} + +static size_t csr1212_generate_layout_order(struct csr1212_keyval *kv) +{ + struct csr1212_keyval *ltail = kv; + size_t agg_size = 0; + + while (kv) { + switch (kv->key.type) { + case CSR1212_KV_TYPE_LEAF: + /* Add 1 quadlet for crc/len field */ + agg_size += kv->value.leaf.len + 1; + break; + + case CSR1212_KV_TYPE_DIRECTORY: + kv->value.directory.len = + csr1212_generate_layout_subdir(kv, <ail); + /* Add 1 quadlet for crc/len field */ + agg_size += kv->value.directory.len + 1; + break; + } + kv = kv->next; + } + return quads_to_bytes(agg_size); +} + +static struct csr1212_keyval * +csr1212_generate_positions(struct csr1212_csr_rom_cache *cache, + struct csr1212_keyval *start_kv, int start_pos) +{ + struct csr1212_keyval *kv = start_kv; + struct csr1212_keyval *okv = start_kv; + int pos = start_pos; + int kv_len = 0, okv_len = 0; + + cache->layout_head = kv; + + while (kv && pos < cache->size) { + /* Special case: Extended ROM leafs */ + if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) + kv->offset = cache->offset + pos; + + switch (kv->key.type) { + case CSR1212_KV_TYPE_LEAF: + kv_len = kv->value.leaf.len; + break; + + case CSR1212_KV_TYPE_DIRECTORY: + kv_len = kv->value.directory.len; + break; + + default: + /* Should never get here */ + WARN_ON(1); + break; + } + + pos += quads_to_bytes(kv_len + 1); + + if (pos <= cache->size) { + okv = kv; + okv_len = kv_len; + kv = kv->next; + } + } + + cache->layout_tail = okv; + cache->len = okv->offset - cache->offset + quads_to_bytes(okv_len + 1); + + return kv; +} + +#define CSR1212_KV_KEY_SHIFT 24 +#define CSR1212_KV_KEY_TYPE_SHIFT 6 +#define CSR1212_KV_KEY_ID_MASK 0x3f +#define CSR1212_KV_KEY_TYPE_MASK 0x3 /* after shift */ + +static void +csr1212_generate_tree_subdir(struct csr1212_keyval *dir, u32 *data_buffer) +{ + struct csr1212_dentry *dentry; + struct csr1212_keyval *last_extkey_spec = NULL; + struct csr1212_keyval *last_extkey = NULL; + int index = 0; + + for (dentry = dir->value.directory.dentries_head; + dentry; + dentry = dentry->next) { + struct csr1212_keyval *a; + + for (a = dentry->kv; a; a = a->associate) { + u32 value = 0; + + /* Special Case: Extended Key Specifier_ID */ + if (a->key.id == + CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) { + if (last_extkey_spec == NULL) + last_extkey_spec = a; + else if (a->value.immediate != + last_extkey_spec->value.immediate) + last_extkey_spec = a; + else + continue; + + /* Special Case: Extended Key */ + } else if (a->key.id == CSR1212_KV_ID_EXTENDED_KEY) { + if (last_extkey == NULL) + last_extkey = a; + else if (a->value.immediate != + last_extkey->value.immediate) + last_extkey = a; + else + continue; + } + + switch (a->key.type) { + case CSR1212_KV_TYPE_IMMEDIATE: + value = a->value.immediate; + break; + case CSR1212_KV_TYPE_CSR_OFFSET: + value = a->value.csr_offset; + break; + case CSR1212_KV_TYPE_LEAF: + value = a->offset; + value -= dir->offset + quads_to_bytes(1+index); + value = bytes_to_quads(value); + break; + case CSR1212_KV_TYPE_DIRECTORY: + value = a->offset; + value -= dir->offset + quads_to_bytes(1+index); + value = bytes_to_quads(value); + break; + default: + /* Should never get here */ + WARN_ON(1); + break; + } + + value |= (a->key.id & CSR1212_KV_KEY_ID_MASK) << + CSR1212_KV_KEY_SHIFT; + value |= (a->key.type & CSR1212_KV_KEY_TYPE_MASK) << + (CSR1212_KV_KEY_SHIFT + + CSR1212_KV_KEY_TYPE_SHIFT); + data_buffer[index] = cpu_to_be32(value); + index++; + } + } +} + +struct csr1212_keyval_img { + u16 length; + u16 crc; + + /* Must be last */ + u32 data[0]; /* older gcc can't handle [] which is standard */ +}; + +static void csr1212_fill_cache(struct csr1212_csr_rom_cache *cache) +{ + struct csr1212_keyval *kv, *nkv; + struct csr1212_keyval_img *kvi; + + for (kv = cache->layout_head; + kv != cache->layout_tail->next; + kv = nkv) { + kvi = (struct csr1212_keyval_img *)(cache->data + + bytes_to_quads(kv->offset - cache->offset)); + switch (kv->key.type) { + default: + case CSR1212_KV_TYPE_IMMEDIATE: + case CSR1212_KV_TYPE_CSR_OFFSET: + /* Should never get here */ + WARN_ON(1); + break; + + case CSR1212_KV_TYPE_LEAF: + /* Don't copy over Extended ROM areas, they are + * already filled out! */ + if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) + memcpy(kvi->data, kv->value.leaf.data, + quads_to_bytes(kv->value.leaf.len)); + + kvi->length = cpu_to_be16(kv->value.leaf.len); + kvi->crc = csr1212_crc16(kvi->data, kv->value.leaf.len); + break; + + case CSR1212_KV_TYPE_DIRECTORY: + csr1212_generate_tree_subdir(kv, kvi->data); + + kvi->length = cpu_to_be16(kv->value.directory.len); + kvi->crc = csr1212_crc16(kvi->data, + kv->value.directory.len); + break; + } + + nkv = kv->next; + if (kv->prev) + kv->prev->next = NULL; + if (kv->next) + kv->next->prev = NULL; + kv->prev = NULL; + kv->next = NULL; + } +} + +/* This size is arbitrarily chosen. + * The struct overhead is subtracted for more economic allocations. */ +#define CSR1212_EXTENDED_ROM_SIZE (2048 - sizeof(struct csr1212_csr_rom_cache)) + +int csr1212_generate_csr_image(struct csr1212_csr *csr) +{ + struct csr1212_bus_info_block_img *bi; + struct csr1212_csr_rom_cache *cache; + struct csr1212_keyval *kv; + size_t agg_size; + int ret; + int init_offset; + + BUG_ON(!csr); + + cache = csr->cache_head; + + bi = (struct csr1212_bus_info_block_img*)cache->data; + + bi->length = bytes_to_quads(csr->bus_info_len) - 1; + bi->crc_length = bi->length; + bi->crc = csr1212_crc16(bi->data, bi->crc_length); + + csr->root_kv->next = NULL; + csr->root_kv->prev = NULL; + + agg_size = csr1212_generate_layout_order(csr->root_kv); + + init_offset = csr->bus_info_len; + + for (kv = csr->root_kv, cache = csr->cache_head; + kv; + cache = cache->next) { + if (!cache) { + /* Estimate approximate number of additional cache + * regions needed (it assumes that the cache holding + * the first 1K Config ROM space always exists). */ + int est_c = agg_size / (CSR1212_EXTENDED_ROM_SIZE - + (2 * sizeof(u32))) + 1; + + /* Add additional cache regions, extras will be + * removed later */ + for (; est_c; est_c--) { + ret = csr1212_append_new_cache(csr, + CSR1212_EXTENDED_ROM_SIZE); + if (ret != CSR1212_SUCCESS) + return ret; + } + /* Need to re-layout for additional cache regions */ + agg_size = csr1212_generate_layout_order(csr->root_kv); + kv = csr->root_kv; + cache = csr->cache_head; + init_offset = csr->bus_info_len; + } + kv = csr1212_generate_positions(cache, kv, init_offset); + agg_size -= cache->len; + init_offset = sizeof(u32); + } + + /* Remove unused, excess cache regions */ + while (cache) { + struct csr1212_csr_rom_cache *oc = cache; + + cache = cache->next; + csr1212_remove_cache(csr, oc); + } + + /* Go through the list backward so that when done, the correct CRC + * will be calculated for the Extended ROM areas. */ + for (cache = csr->cache_tail; cache; cache = cache->prev) { + /* Only Extended ROM caches should have this set. */ + if (cache->ext_rom) { + int leaf_size; + + /* Make sure the Extended ROM leaf is a multiple of + * max_rom in size. */ + BUG_ON(csr->max_rom < 1); + leaf_size = (cache->len + (csr->max_rom - 1)) & + ~(csr->max_rom - 1); + + /* Zero out the unused ROM region */ + memset(cache->data + bytes_to_quads(cache->len), 0x00, + leaf_size - cache->len); + + /* Subtract leaf header */ + leaf_size -= sizeof(u32); + + /* Update the Extended ROM leaf length */ + cache->ext_rom->value.leaf.len = + bytes_to_quads(leaf_size); + } else { + /* Zero out the unused ROM region */ + memset(cache->data + bytes_to_quads(cache->len), 0x00, + cache->size - cache->len); + } + + /* Copy the data into the cache buffer */ + csr1212_fill_cache(cache); + + if (cache != csr->cache_head) { + /* Set the length and CRC of the extended ROM. */ + struct csr1212_keyval_img *kvi = + (struct csr1212_keyval_img*)cache->data; + u16 len = bytes_to_quads(cache->len) - 1; + + kvi->length = cpu_to_be16(len); + kvi->crc = csr1212_crc16(kvi->data, len); + } + } + + return CSR1212_SUCCESS; +} + +int csr1212_read(struct csr1212_csr *csr, u32 offset, void *buffer, u32 len) +{ + struct csr1212_csr_rom_cache *cache; + + for (cache = csr->cache_head; cache; cache = cache->next) + if (offset >= cache->offset && + (offset + len) <= (cache->offset + cache->size)) { + memcpy(buffer, &cache->data[ + bytes_to_quads(offset - cache->offset)], + len); + return CSR1212_SUCCESS; + } + + return -ENOENT; +} + +/* + * Apparently there are many different wrong implementations of the CRC + * algorithm. We don't fail, we just warn... approximately once per GUID. + */ +static void +csr1212_check_crc(const u32 *buffer, size_t length, u16 crc, __be32 *guid) +{ + static u64 last_bad_eui64; + u64 eui64 = ((u64)be32_to_cpu(guid[0]) << 32) | be32_to_cpu(guid[1]); + + if (csr1212_crc16(buffer, length) == crc || + csr1212_msft_crc16(buffer, length) == crc || + eui64 == last_bad_eui64) + return; + + printk(KERN_DEBUG "ieee1394: config ROM CRC error\n"); + last_bad_eui64 = eui64; +} + +/* Parse a chunk of data as a Config ROM */ + +static int csr1212_parse_bus_info_block(struct csr1212_csr *csr) +{ + struct csr1212_bus_info_block_img *bi; + struct csr1212_cache_region *cr; + int i; + int ret; + + for (i = 0; i < csr->bus_info_len; i += sizeof(u32)) { + ret = csr->ops->bus_read(csr, CSR1212_CONFIG_ROM_SPACE_BASE + i, + &csr->cache_head->data[bytes_to_quads(i)], + csr->private); + if (ret != CSR1212_SUCCESS) + return ret; + + /* check ROM header's info_length */ + if (i == 0 && + be32_to_cpu(csr->cache_head->data[0]) >> 24 != + bytes_to_quads(csr->bus_info_len) - 1) + return -EINVAL; + } + + bi = (struct csr1212_bus_info_block_img*)csr->cache_head->data; + csr->crc_len = quads_to_bytes(bi->crc_length); + + /* IEEE 1212 recommends that crc_len be equal to bus_info_len, but that + * is not always the case, so read the rest of the crc area 1 quadlet at + * a time. */ + for (i = csr->bus_info_len; i <= csr->crc_len; i += sizeof(u32)) { + ret = csr->ops->bus_read(csr, CSR1212_CONFIG_ROM_SPACE_BASE + i, + &csr->cache_head->data[bytes_to_quads(i)], + csr->private); + if (ret != CSR1212_SUCCESS) + return ret; + } + + csr1212_check_crc(bi->data, bi->crc_length, bi->crc, + &csr->bus_info_data[3]); + + cr = CSR1212_MALLOC(sizeof(*cr)); + if (!cr) + return -ENOMEM; + + cr->next = NULL; + cr->prev = NULL; + cr->offset_start = 0; + cr->offset_end = csr->crc_len + 4; + + csr->cache_head->filled_head = cr; + csr->cache_head->filled_tail = cr; + + return CSR1212_SUCCESS; +} + +#define CSR1212_KV_KEY(q) (be32_to_cpu(q) >> CSR1212_KV_KEY_SHIFT) +#define CSR1212_KV_KEY_TYPE(q) (CSR1212_KV_KEY(q) >> CSR1212_KV_KEY_TYPE_SHIFT) +#define CSR1212_KV_KEY_ID(q) (CSR1212_KV_KEY(q) & CSR1212_KV_KEY_ID_MASK) +#define CSR1212_KV_VAL_MASK 0xffffff +#define CSR1212_KV_VAL(q) (be32_to_cpu(q) & CSR1212_KV_VAL_MASK) + +static int +csr1212_parse_dir_entry(struct csr1212_keyval *dir, u32 ki, u32 kv_pos) +{ + int ret = CSR1212_SUCCESS; + struct csr1212_keyval *k = NULL; + u32 offset; + bool keep_keyval = true; + + switch (CSR1212_KV_KEY_TYPE(ki)) { + case CSR1212_KV_TYPE_IMMEDIATE: + k = csr1212_new_immediate(CSR1212_KV_KEY_ID(ki), + CSR1212_KV_VAL(ki)); + if (!k) { + ret = -ENOMEM; + goto out; + } + /* Don't keep local reference when parsing. */ + keep_keyval = false; + break; + + case CSR1212_KV_TYPE_CSR_OFFSET: + k = csr1212_new_csr_offset(CSR1212_KV_KEY_ID(ki), + CSR1212_KV_VAL(ki)); + if (!k) { + ret = -ENOMEM; + goto out; + } + /* Don't keep local reference when parsing. */ + keep_keyval = false; + break; + + default: + /* Compute the offset from 0xffff f000 0000. */ + offset = quads_to_bytes(CSR1212_KV_VAL(ki)) + kv_pos; + if (offset == kv_pos) { + /* Uh-oh. Can't have a relative offset of 0 for Leaves + * or Directories. The Config ROM image is most likely + * messed up, so we'll just abort here. */ + ret = -EIO; + goto out; + } + + k = csr1212_find_keyval_offset(dir, offset); + + if (k) + break; /* Found it. */ + + if (CSR1212_KV_KEY_TYPE(ki) == CSR1212_KV_TYPE_DIRECTORY) + k = csr1212_new_directory(CSR1212_KV_KEY_ID(ki)); + else + k = csr1212_new_leaf(CSR1212_KV_KEY_ID(ki), NULL, 0); + + if (!k) { + ret = -ENOMEM; + goto out; + } + /* Don't keep local reference when parsing. */ + keep_keyval = false; + /* Contents not read yet so it's not valid. */ + k->valid = 0; + k->offset = offset; + + k->prev = dir; + k->next = dir->next; + dir->next->prev = k; + dir->next = k; + } + ret = __csr1212_attach_keyval_to_directory(dir, k, keep_keyval); +out: + if (ret != CSR1212_SUCCESS && k != NULL) + free_keyval(k); + return ret; +} + +int csr1212_parse_keyval(struct csr1212_keyval *kv, + struct csr1212_csr_rom_cache *cache) +{ + struct csr1212_keyval_img *kvi; + int i; + int ret = CSR1212_SUCCESS; + int kvi_len; + + kvi = (struct csr1212_keyval_img*) + &cache->data[bytes_to_quads(kv->offset - cache->offset)]; + kvi_len = be16_to_cpu(kvi->length); + + /* GUID is wrong in here in case of extended ROM. We don't care. */ + csr1212_check_crc(kvi->data, kvi_len, kvi->crc, &cache->data[3]); + + switch (kv->key.type) { + case CSR1212_KV_TYPE_DIRECTORY: + for (i = 0; i < kvi_len; i++) { + u32 ki = kvi->data[i]; + + /* Some devices put null entries in their unit + * directories. If we come across such an entry, + * then skip it. */ + if (ki == 0x0) + continue; + ret = csr1212_parse_dir_entry(kv, ki, + kv->offset + quads_to_bytes(i + 1)); + } + kv->value.directory.len = kvi_len; + break; + + case CSR1212_KV_TYPE_LEAF: + if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) { + size_t size = quads_to_bytes(kvi_len); + + kv->value.leaf.data = CSR1212_MALLOC(size); + if (!kv->value.leaf.data) { + ret = -ENOMEM; + goto out; + } + + kv->value.leaf.len = kvi_len; + memcpy(kv->value.leaf.data, kvi->data, size); + } + break; + } + + kv->valid = 1; +out: + return ret; +} + +static int +csr1212_read_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv) +{ + struct csr1212_cache_region *cr, *ncr, *newcr = NULL; + struct csr1212_keyval_img *kvi = NULL; + struct csr1212_csr_rom_cache *cache; + int cache_index; + u64 addr; + u32 *cache_ptr; + u16 kv_len = 0; + + BUG_ON(!csr || !kv || csr->max_rom < 1); + + /* First find which cache the data should be in (or go in if not read + * yet). */ + for (cache = csr->cache_head; cache; cache = cache->next) + if (kv->offset >= cache->offset && + kv->offset < (cache->offset + cache->size)) + break; + + if (!cache) { + u32 q, cache_size; + + /* Only create a new cache for Extended ROM leaves. */ + if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) + return -EINVAL; + + if (csr->ops->bus_read(csr, + CSR1212_REGISTER_SPACE_BASE + kv->offset, + &q, csr->private)) + return -EIO; + + kv->value.leaf.len = be32_to_cpu(q) >> 16; + + cache_size = (quads_to_bytes(kv->value.leaf.len + 1) + + (csr->max_rom - 1)) & ~(csr->max_rom - 1); + + cache = csr1212_rom_cache_malloc(kv->offset, cache_size); + if (!cache) + return -ENOMEM; + + kv->value.leaf.data = &cache->data[1]; + csr->cache_tail->next = cache; + cache->prev = csr->cache_tail; + cache->next = NULL; + csr->cache_tail = cache; + cache->filled_head = + CSR1212_MALLOC(sizeof(*cache->filled_head)); + if (!cache->filled_head) + return -ENOMEM; + + cache->filled_head->offset_start = 0; + cache->filled_head->offset_end = sizeof(u32); + cache->filled_tail = cache->filled_head; + cache->filled_head->next = NULL; + cache->filled_head->prev = NULL; + cache->data[0] = q; + + /* Don't read the entire extended ROM now. Pieces of it will + * be read when entries inside it are read. */ + return csr1212_parse_keyval(kv, cache); + } + + cache_index = kv->offset - cache->offset; + + /* Now seach read portions of the cache to see if it is there. */ + for (cr = cache->filled_head; cr; cr = cr->next) { + if (cache_index < cr->offset_start) { + newcr = CSR1212_MALLOC(sizeof(*newcr)); + if (!newcr) + return -ENOMEM; + + newcr->offset_start = cache_index & ~(csr->max_rom - 1); + newcr->offset_end = newcr->offset_start; + newcr->next = cr; + newcr->prev = cr->prev; + cr->prev = newcr; + cr = newcr; + break; + } else if ((cache_index >= cr->offset_start) && + (cache_index < cr->offset_end)) { + kvi = (struct csr1212_keyval_img*) + (&cache->data[bytes_to_quads(cache_index)]); + kv_len = quads_to_bytes(be16_to_cpu(kvi->length) + 1); + break; + } else if (cache_index == cr->offset_end) { + break; + } + } + + if (!cr) { + cr = cache->filled_tail; + newcr = CSR1212_MALLOC(sizeof(*newcr)); + if (!newcr) + return -ENOMEM; + + newcr->offset_start = cache_index & ~(csr->max_rom - 1); + newcr->offset_end = newcr->offset_start; + newcr->prev = cr; + newcr->next = cr->next; + cr->next = newcr; + cr = newcr; + cache->filled_tail = newcr; + } + + while(!kvi || cr->offset_end < cache_index + kv_len) { + cache_ptr = &cache->data[bytes_to_quads(cr->offset_end & + ~(csr->max_rom - 1))]; + + addr = (CSR1212_CSR_ARCH_REG_SPACE_BASE + cache->offset + + cr->offset_end) & ~(csr->max_rom - 1); + + if (csr->ops->bus_read(csr, addr, cache_ptr, csr->private)) + return -EIO; + + cr->offset_end += csr->max_rom - (cr->offset_end & + (csr->max_rom - 1)); + + if (!kvi && (cr->offset_end > cache_index)) { + kvi = (struct csr1212_keyval_img*) + (&cache->data[bytes_to_quads(cache_index)]); + kv_len = quads_to_bytes(be16_to_cpu(kvi->length) + 1); + } + + if ((kv_len + (kv->offset - cache->offset)) > cache->size) { + /* The Leaf or Directory claims its length extends + * beyond the ConfigROM image region and thus beyond the + * end of our cache region. Therefore, we abort now + * rather than seg faulting later. */ + return -EIO; + } + + ncr = cr->next; + + if (ncr && (cr->offset_end >= ncr->offset_start)) { + /* consolidate region entries */ + ncr->offset_start = cr->offset_start; + + if (cr->prev) + cr->prev->next = cr->next; + ncr->prev = cr->prev; + if (cache->filled_head == cr) + cache->filled_head = ncr; + CSR1212_FREE(cr); + cr = ncr; + } + } + + return csr1212_parse_keyval(kv, cache); +} + +struct csr1212_keyval * +csr1212_get_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv) +{ + if (!kv) + return NULL; + if (!kv->valid) + if (csr1212_read_keyval(csr, kv) != CSR1212_SUCCESS) + return NULL; + return kv; +} + +int csr1212_parse_csr(struct csr1212_csr *csr) +{ + struct csr1212_dentry *dentry; + int ret; + + BUG_ON(!csr || !csr->ops || !csr->ops->bus_read); + + ret = csr1212_parse_bus_info_block(csr); + if (ret != CSR1212_SUCCESS) + return ret; + + /* + * There has been a buggy firmware with bus_info_block.max_rom > 0 + * spotted which actually only supported quadlet read requests to the + * config ROM. Therefore read everything quadlet by quadlet regardless + * of what the bus info block says. + */ + csr->max_rom = 4; + + csr->cache_head->layout_head = csr->root_kv; + csr->cache_head->layout_tail = csr->root_kv; + + csr->root_kv->offset = (CSR1212_CONFIG_ROM_SPACE_BASE & 0xffff) + + csr->bus_info_len; + + csr->root_kv->valid = 0; + csr->root_kv->next = csr->root_kv; + csr->root_kv->prev = csr->root_kv; + ret = csr1212_read_keyval(csr, csr->root_kv); + if (ret != CSR1212_SUCCESS) + return ret; + + /* Scan through the Root directory finding all extended ROM regions + * and make cache regions for them */ + for (dentry = csr->root_kv->value.directory.dentries_head; + dentry; dentry = dentry->next) { + if (dentry->kv->key.id == CSR1212_KV_ID_EXTENDED_ROM && + !dentry->kv->valid) { + ret = csr1212_read_keyval(csr, dentry->kv); + if (ret != CSR1212_SUCCESS) + return ret; + } + } + + return CSR1212_SUCCESS; +} diff --git a/trunk/drivers/ieee1394/csr1212.h b/trunk/drivers/ieee1394/csr1212.h new file mode 100644 index 000000000000..a892d922dbc9 --- /dev/null +++ b/trunk/drivers/ieee1394/csr1212.h @@ -0,0 +1,383 @@ +/* + * csr1212.h -- IEEE 1212 Control and Status Register support for Linux + * + * Copyright (C) 2003 Francois Retief + * Steve Kinneberg + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __CSR1212_H__ +#define __CSR1212_H__ + +#include +#include +#include + +#define CSR1212_MALLOC(size) kmalloc((size), GFP_KERNEL) +#define CSR1212_FREE(ptr) kfree(ptr) + +#define CSR1212_SUCCESS (0) + + +/* CSR 1212 key types */ +#define CSR1212_KV_TYPE_IMMEDIATE 0 +#define CSR1212_KV_TYPE_CSR_OFFSET 1 +#define CSR1212_KV_TYPE_LEAF 2 +#define CSR1212_KV_TYPE_DIRECTORY 3 + + +/* CSR 1212 key ids */ +#define CSR1212_KV_ID_DESCRIPTOR 0x01 +#define CSR1212_KV_ID_BUS_DEPENDENT_INFO 0x02 +#define CSR1212_KV_ID_VENDOR 0x03 +#define CSR1212_KV_ID_HARDWARE_VERSION 0x04 +#define CSR1212_KV_ID_MODULE 0x07 +#define CSR1212_KV_ID_NODE_CAPABILITIES 0x0C +#define CSR1212_KV_ID_EUI_64 0x0D +#define CSR1212_KV_ID_UNIT 0x11 +#define CSR1212_KV_ID_SPECIFIER_ID 0x12 +#define CSR1212_KV_ID_VERSION 0x13 +#define CSR1212_KV_ID_DEPENDENT_INFO 0x14 +#define CSR1212_KV_ID_UNIT_LOCATION 0x15 +#define CSR1212_KV_ID_MODEL 0x17 +#define CSR1212_KV_ID_INSTANCE 0x18 +#define CSR1212_KV_ID_KEYWORD 0x19 +#define CSR1212_KV_ID_FEATURE 0x1A +#define CSR1212_KV_ID_EXTENDED_ROM 0x1B +#define CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID 0x1C +#define CSR1212_KV_ID_EXTENDED_KEY 0x1D +#define CSR1212_KV_ID_EXTENDED_DATA 0x1E +#define CSR1212_KV_ID_MODIFIABLE_DESCRIPTOR 0x1F +#define CSR1212_KV_ID_DIRECTORY_ID 0x20 +#define CSR1212_KV_ID_REVISION 0x21 + + +/* IEEE 1212 Address space map */ +#define CSR1212_ALL_SPACE_BASE (0x000000000000ULL) +#define CSR1212_ALL_SPACE_SIZE (1ULL << 48) +#define CSR1212_ALL_SPACE_END (CSR1212_ALL_SPACE_BASE + CSR1212_ALL_SPACE_SIZE) + +#define CSR1212_MEMORY_SPACE_BASE (0x000000000000ULL) +#define CSR1212_MEMORY_SPACE_SIZE ((256ULL * (1ULL << 40)) - (512ULL * (1ULL << 20))) +#define CSR1212_MEMORY_SPACE_END (CSR1212_MEMORY_SPACE_BASE + CSR1212_MEMORY_SPACE_SIZE) + +#define CSR1212_PRIVATE_SPACE_BASE (0xffffe0000000ULL) +#define CSR1212_PRIVATE_SPACE_SIZE (256ULL * (1ULL << 20)) +#define CSR1212_PRIVATE_SPACE_END (CSR1212_PRIVATE_SPACE_BASE + CSR1212_PRIVATE_SPACE_SIZE) + +#define CSR1212_REGISTER_SPACE_BASE (0xfffff0000000ULL) +#define CSR1212_REGISTER_SPACE_SIZE (256ULL * (1ULL << 20)) +#define CSR1212_REGISTER_SPACE_END (CSR1212_REGISTER_SPACE_BASE + CSR1212_REGISTER_SPACE_SIZE) + +#define CSR1212_CSR_ARCH_REG_SPACE_BASE (0xfffff0000000ULL) +#define CSR1212_CSR_ARCH_REG_SPACE_SIZE (512) +#define CSR1212_CSR_ARCH_REG_SPACE_END (CSR1212_CSR_ARCH_REG_SPACE_BASE + CSR1212_CSR_ARCH_REG_SPACE_SIZE) +#define CSR1212_CSR_ARCH_REG_SPACE_OFFSET (CSR1212_CSR_ARCH_REG_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE) + +#define CSR1212_CSR_BUS_DEP_REG_SPACE_BASE (0xfffff0000200ULL) +#define CSR1212_CSR_BUS_DEP_REG_SPACE_SIZE (512) +#define CSR1212_CSR_BUS_DEP_REG_SPACE_END (CSR1212_CSR_BUS_DEP_REG_SPACE_BASE + CSR1212_CSR_BUS_DEP_REG_SPACE_SIZE) +#define CSR1212_CSR_BUS_DEP_REG_SPACE_OFFSET (CSR1212_CSR_BUS_DEP_REG_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE) + +#define CSR1212_CONFIG_ROM_SPACE_BASE (0xfffff0000400ULL) +#define CSR1212_CONFIG_ROM_SPACE_SIZE (1024) +#define CSR1212_CONFIG_ROM_SPACE_END (CSR1212_CONFIG_ROM_SPACE_BASE + CSR1212_CONFIG_ROM_SPACE_SIZE) +#define CSR1212_CONFIG_ROM_SPACE_OFFSET (CSR1212_CONFIG_ROM_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE) + +#define CSR1212_UNITS_SPACE_BASE (0xfffff0000800ULL) +#define CSR1212_UNITS_SPACE_SIZE ((256ULL * (1ULL << 20)) - 2048) +#define CSR1212_UNITS_SPACE_END (CSR1212_UNITS_SPACE_BASE + CSR1212_UNITS_SPACE_SIZE) +#define CSR1212_UNITS_SPACE_OFFSET (CSR1212_UNITS_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE) + +#define CSR1212_INVALID_ADDR_SPACE -1 + + +/* Config ROM image structures */ +struct csr1212_bus_info_block_img { + u8 length; + u8 crc_length; + u16 crc; + + /* Must be last */ + u32 data[0]; /* older gcc can't handle [] which is standard */ +}; + +struct csr1212_leaf { + int len; + u32 *data; +}; + +struct csr1212_dentry { + struct csr1212_dentry *next, *prev; + struct csr1212_keyval *kv; +}; + +struct csr1212_directory { + int len; + struct csr1212_dentry *dentries_head, *dentries_tail; +}; + +struct csr1212_keyval { + struct { + u8 type; + u8 id; + } key; + union { + u32 immediate; + u32 csr_offset; + struct csr1212_leaf leaf; + struct csr1212_directory directory; + } value; + struct csr1212_keyval *associate; + atomic_t refcnt; + + /* used in generating and/or parsing CSR image */ + struct csr1212_keyval *next, *prev; /* flat list of CSR elements */ + u32 offset; /* position in CSR from 0xffff f000 0000 */ + u8 valid; /* flag indicating keyval has valid data*/ +}; + + +struct csr1212_cache_region { + struct csr1212_cache_region *next, *prev; + u32 offset_start; /* inclusive */ + u32 offset_end; /* exclusive */ +}; + +struct csr1212_csr_rom_cache { + struct csr1212_csr_rom_cache *next, *prev; + struct csr1212_cache_region *filled_head, *filled_tail; + struct csr1212_keyval *layout_head, *layout_tail; + size_t size; + u32 offset; + struct csr1212_keyval *ext_rom; + size_t len; + + /* Must be last */ + u32 data[0]; /* older gcc can't handle [] which is standard */ +}; + +struct csr1212_csr { + size_t bus_info_len; /* bus info block length in bytes */ + size_t crc_len; /* crc length in bytes */ + __be32 *bus_info_data; /* bus info data incl bus name and EUI */ + + void *private; /* private, bus specific data */ + struct csr1212_bus_ops *ops; + + struct csr1212_keyval *root_kv; + + int max_rom; /* max bytes readable in Config ROM region */ + + /* Items below used for image parsing and generation */ + struct csr1212_csr_rom_cache *cache_head, *cache_tail; +}; + +struct csr1212_bus_ops { + /* This function is used by csr1212 to read additional information + * from remote nodes when parsing a Config ROM (i.e., read Config ROM + * entries located in the Units Space. Must return 0 on success + * anything else indicates an error. */ + int (*bus_read) (struct csr1212_csr *csr, u64 addr, + void *buffer, void *private); + + /* This function is used by csr1212 to allocate a region in units space + * in the event that Config ROM entries don't all fit in the predefined + * 1K region. The void *private parameter is private member of struct + * csr1212_csr. */ + u64 (*allocate_addr_range) (u64 size, u32 alignment, void *private); + + /* This function is used by csr1212 to release a region in units space + * that is no longer needed. */ + void (*release_addr) (u64 addr, void *private); +}; + + +/* Descriptor Leaf manipulation macros */ +#define CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT 24 +#define CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK 0xffffff +#define CSR1212_DESCRIPTOR_LEAF_OVERHEAD (1 * sizeof(u32)) + +#define CSR1212_DESCRIPTOR_LEAF_TYPE(kv) \ + (be32_to_cpu((kv)->value.leaf.data[0]) >> \ + CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT) +#define CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) \ + (be32_to_cpu((kv)->value.leaf.data[0]) & \ + CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK) + + +/* Text Descriptor Leaf manipulation macros */ +#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT 28 +#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_MASK 0xf /* after shift */ +#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT 16 +#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK 0xfff /* after shift */ +#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK 0xffff +#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_OVERHEAD (1 * sizeof(u32)) + +#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH(kv) \ + (be32_to_cpu((kv)->value.leaf.data[1]) >> \ + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT) +#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET(kv) \ + ((be32_to_cpu((kv)->value.leaf.data[1]) >> \ + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT) & \ + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK) +#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) \ + (be32_to_cpu((kv)->value.leaf.data[1]) & \ + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK) +#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(kv) \ + (&((kv)->value.leaf.data[2])) + + +/* The following 2 function are for creating new Configuration ROM trees. The + * first function is used for both creating local trees and parsing remote + * trees. The second function adds pertinent information to local Configuration + * ROM trees - namely data for the bus information block. */ +extern struct csr1212_csr *csr1212_create_csr(struct csr1212_bus_ops *ops, + size_t bus_info_size, + void *private); +extern void csr1212_init_local_csr(struct csr1212_csr *csr, + const u32 *bus_info_data, int max_rom); + + +/* Destroy a Configuration ROM tree and release all memory taken by the tree. */ +extern void csr1212_destroy_csr(struct csr1212_csr *csr); + + +/* The following set of functions are fore creating new keyvals for placement in + * a Configuration ROM tree. Code that creates new keyvals with these functions + * must release those keyvals with csr1212_release_keyval() when they are no + * longer needed. */ +extern struct csr1212_keyval *csr1212_new_immediate(u8 key, u32 value); +extern struct csr1212_keyval *csr1212_new_directory(u8 key); +extern struct csr1212_keyval *csr1212_new_string_descriptor_leaf(const char *s); + + +/* The following function manages association between keyvals. Typically, + * Descriptor Leaves and Directories will be associated with another keyval and + * it is desirable for the Descriptor keyval to be place immediately after the + * keyval that it is associated with. + * Take care with subsequent ROM modifications: There is no function to remove + * previously specified associations. + */ +extern void csr1212_associate_keyval(struct csr1212_keyval *kv, + struct csr1212_keyval *associate); + + +/* The following functions manage the association of a keyval and directories. + * A keyval may be attached to more than one directory. */ +extern int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir, + struct csr1212_keyval *kv); +extern void csr1212_detach_keyval_from_directory(struct csr1212_keyval *dir, + struct csr1212_keyval *kv); + + +/* Creates a complete Configuration ROM image in the list of caches available + * via csr->cache_head. */ +extern int csr1212_generate_csr_image(struct csr1212_csr *csr); + + +/* This is a convience function for reading a block of data out of one of the + * caches in the csr->cache_head list. */ +extern int csr1212_read(struct csr1212_csr *csr, u32 offset, void *buffer, + u32 len); + + +/* The following functions are in place for parsing Configuration ROM images. + * csr1212_parse_keyval() is used should there be a need to directly parse a + * Configuration ROM directly. */ +extern int csr1212_parse_keyval(struct csr1212_keyval *kv, + struct csr1212_csr_rom_cache *cache); +extern int csr1212_parse_csr(struct csr1212_csr *csr); + + +/* This function allocates a new cache which may be used for either parsing or + * generating sub-sets of Configuration ROM images. */ +static inline struct csr1212_csr_rom_cache * +csr1212_rom_cache_malloc(u32 offset, size_t size) +{ + struct csr1212_csr_rom_cache *cache; + + cache = CSR1212_MALLOC(sizeof(*cache) + size); + if (!cache) + return NULL; + + cache->next = NULL; + cache->prev = NULL; + cache->filled_head = NULL; + cache->filled_tail = NULL; + cache->layout_head = NULL; + cache->layout_tail = NULL; + cache->offset = offset; + cache->size = size; + cache->ext_rom = NULL; + + return cache; +} + + +/* This function ensures that a keyval contains data when referencing a keyval + * created by parsing a Configuration ROM. */ +extern struct csr1212_keyval * +csr1212_get_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv); + + +/* This function increments the reference count for a keyval should there be a + * need for code to retain a keyval that has been parsed. */ +static inline void csr1212_keep_keyval(struct csr1212_keyval *kv) +{ + atomic_inc(&kv->refcnt); + smp_mb__after_atomic_inc(); +} + + +/* This function decrements a keyval's reference count and will destroy the + * keyval when there are no more users of the keyval. This should be called by + * any code that calls csr1212_keep_keyval() or any of the keyval creation + * routines csr1212_new_*(). */ +extern void csr1212_release_keyval(struct csr1212_keyval *kv); + + +/* + * This macro allows for looping over the keyval entries in a directory and it + * ensures that keyvals from remote ConfigROMs are parsed properly. + * + * struct csr1212_csr *_csr points to the CSR associated with dir. + * struct csr1212_keyval *_kv points to the current keyval (loop index). + * struct csr1212_keyval *_dir points to the directory to be looped. + * struct csr1212_dentry *_pos is used internally for indexing. + * + * kv will be NULL upon exit of the loop. + */ +#define csr1212_for_each_dir_entry(_csr, _kv, _dir, _pos) \ + for (csr1212_get_keyval((_csr), (_dir)), \ + _pos = (_dir)->value.directory.dentries_head, \ + _kv = (_pos) ? csr1212_get_keyval((_csr), _pos->kv) : NULL;\ + (_kv) && (_pos); \ + (_kv->associate == NULL) ? \ + ((_pos = _pos->next), (_kv = (_pos) ? \ + csr1212_get_keyval((_csr), _pos->kv) : \ + NULL)) : \ + (_kv = csr1212_get_keyval((_csr), _kv->associate))) + +#endif /* __CSR1212_H__ */ diff --git a/trunk/drivers/ieee1394/dma.c b/trunk/drivers/ieee1394/dma.c new file mode 100644 index 000000000000..d178699b194a --- /dev/null +++ b/trunk/drivers/ieee1394/dma.c @@ -0,0 +1,289 @@ +/* + * DMA region bookkeeping routines + * + * Copyright (C) 2002 Maas Digital LLC + * + * This code is licensed under the GPL. See the file COPYING in the root + * directory of the kernel sources for details. + */ + +#include +#include +#include +#include +#include + +#include "dma.h" + +/* dma_prog_region */ + +void dma_prog_region_init(struct dma_prog_region *prog) +{ + prog->kvirt = NULL; + prog->dev = NULL; + prog->n_pages = 0; + prog->bus_addr = 0; +} + +int dma_prog_region_alloc(struct dma_prog_region *prog, unsigned long n_bytes, + struct pci_dev *dev) +{ + /* round up to page size */ + n_bytes = PAGE_ALIGN(n_bytes); + + prog->n_pages = n_bytes >> PAGE_SHIFT; + + prog->kvirt = pci_alloc_consistent(dev, n_bytes, &prog->bus_addr); + if (!prog->kvirt) { + printk(KERN_ERR + "dma_prog_region_alloc: pci_alloc_consistent() failed\n"); + dma_prog_region_free(prog); + return -ENOMEM; + } + + prog->dev = dev; + + return 0; +} + +void dma_prog_region_free(struct dma_prog_region *prog) +{ + if (prog->kvirt) { + pci_free_consistent(prog->dev, prog->n_pages << PAGE_SHIFT, + prog->kvirt, prog->bus_addr); + } + + prog->kvirt = NULL; + prog->dev = NULL; + prog->n_pages = 0; + prog->bus_addr = 0; +} + +/* dma_region */ + +/** + * dma_region_init - clear out all fields but do not allocate anything + */ +void dma_region_init(struct dma_region *dma) +{ + dma->kvirt = NULL; + dma->dev = NULL; + dma->n_pages = 0; + dma->n_dma_pages = 0; + dma->sglist = NULL; +} + +/** + * dma_region_alloc - allocate the buffer and map it to the IOMMU + */ +int dma_region_alloc(struct dma_region *dma, unsigned long n_bytes, + struct pci_dev *dev, int direction) +{ + unsigned int i; + + /* round up to page size */ + n_bytes = PAGE_ALIGN(n_bytes); + + dma->n_pages = n_bytes >> PAGE_SHIFT; + + dma->kvirt = vmalloc_32(n_bytes); + if (!dma->kvirt) { + printk(KERN_ERR "dma_region_alloc: vmalloc_32() failed\n"); + goto err; + } + + /* Clear the ram out, no junk to the user */ + memset(dma->kvirt, 0, n_bytes); + + /* allocate scatter/gather list */ + dma->sglist = vmalloc(dma->n_pages * sizeof(*dma->sglist)); + if (!dma->sglist) { + printk(KERN_ERR "dma_region_alloc: vmalloc(sglist) failed\n"); + goto err; + } + + sg_init_table(dma->sglist, dma->n_pages); + + /* fill scatter/gather list with pages */ + for (i = 0; i < dma->n_pages; i++) { + unsigned long va = + (unsigned long)dma->kvirt + (i << PAGE_SHIFT); + + sg_set_page(&dma->sglist[i], vmalloc_to_page((void *)va), + PAGE_SIZE, 0); + } + + /* map sglist to the IOMMU */ + dma->n_dma_pages = + pci_map_sg(dev, dma->sglist, dma->n_pages, direction); + + if (dma->n_dma_pages == 0) { + printk(KERN_ERR "dma_region_alloc: pci_map_sg() failed\n"); + goto err; + } + + dma->dev = dev; + dma->direction = direction; + + return 0; + + err: + dma_region_free(dma); + return -ENOMEM; +} + +/** + * dma_region_free - unmap and free the buffer + */ +void dma_region_free(struct dma_region *dma) +{ + if (dma->n_dma_pages) { + pci_unmap_sg(dma->dev, dma->sglist, dma->n_pages, + dma->direction); + dma->n_dma_pages = 0; + dma->dev = NULL; + } + + vfree(dma->sglist); + dma->sglist = NULL; + + vfree(dma->kvirt); + dma->kvirt = NULL; + dma->n_pages = 0; +} + +/* find the scatterlist index and remaining offset corresponding to a + given offset from the beginning of the buffer */ +static inline int dma_region_find(struct dma_region *dma, unsigned long offset, + unsigned int start, unsigned long *rem) +{ + int i; + unsigned long off = offset; + + for (i = start; i < dma->n_dma_pages; i++) { + if (off < sg_dma_len(&dma->sglist[i])) { + *rem = off; + break; + } + + off -= sg_dma_len(&dma->sglist[i]); + } + + BUG_ON(i >= dma->n_dma_pages); + + return i; +} + +/** + * dma_region_offset_to_bus - get bus address of an offset within a DMA region + * + * Returns the DMA bus address of the byte with the given @offset relative to + * the beginning of the @dma. + */ +dma_addr_t dma_region_offset_to_bus(struct dma_region * dma, + unsigned long offset) +{ + unsigned long rem = 0; + + struct scatterlist *sg = + &dma->sglist[dma_region_find(dma, offset, 0, &rem)]; + return sg_dma_address(sg) + rem; +} + +/** + * dma_region_sync_for_cpu - sync the CPU's view of the buffer + */ +void dma_region_sync_for_cpu(struct dma_region *dma, unsigned long offset, + unsigned long len) +{ + int first, last; + unsigned long rem = 0; + + if (!len) + len = 1; + + first = dma_region_find(dma, offset, 0, &rem); + last = dma_region_find(dma, rem + len - 1, first, &rem); + + pci_dma_sync_sg_for_cpu(dma->dev, &dma->sglist[first], last - first + 1, + dma->direction); +} + +/** + * dma_region_sync_for_device - sync the IO bus' view of the buffer + */ +void dma_region_sync_for_device(struct dma_region *dma, unsigned long offset, + unsigned long len) +{ + int first, last; + unsigned long rem = 0; + + if (!len) + len = 1; + + first = dma_region_find(dma, offset, 0, &rem); + last = dma_region_find(dma, rem + len - 1, first, &rem); + + pci_dma_sync_sg_for_device(dma->dev, &dma->sglist[first], + last - first + 1, dma->direction); +} + +#ifdef CONFIG_MMU + +static int dma_region_pagefault(struct vm_area_struct *vma, + struct vm_fault *vmf) +{ + struct dma_region *dma = (struct dma_region *)vma->vm_private_data; + + if (!dma->kvirt) + return VM_FAULT_SIGBUS; + + if (vmf->pgoff >= dma->n_pages) + return VM_FAULT_SIGBUS; + + vmf->page = vmalloc_to_page(dma->kvirt + (vmf->pgoff << PAGE_SHIFT)); + get_page(vmf->page); + return 0; +} + +static const struct vm_operations_struct dma_region_vm_ops = { + .fault = dma_region_pagefault, +}; + +/** + * dma_region_mmap - map the buffer into a user space process + */ +int dma_region_mmap(struct dma_region *dma, struct file *file, + struct vm_area_struct *vma) +{ + unsigned long size; + + if (!dma->kvirt) + return -EINVAL; + + /* must be page-aligned (XXX: comment is wrong, we could allow pgoff) */ + if (vma->vm_pgoff != 0) + return -EINVAL; + + /* check the length */ + size = vma->vm_end - vma->vm_start; + if (size > (dma->n_pages << PAGE_SHIFT)) + return -EINVAL; + + vma->vm_ops = &dma_region_vm_ops; + vma->vm_private_data = dma; + vma->vm_file = file; + vma->vm_flags |= VM_RESERVED | VM_ALWAYSDUMP; + + return 0; +} + +#else /* CONFIG_MMU */ + +int dma_region_mmap(struct dma_region *dma, struct file *file, + struct vm_area_struct *vma) +{ + return -EINVAL; +} + +#endif /* CONFIG_MMU */ diff --git a/trunk/drivers/ieee1394/dma.h b/trunk/drivers/ieee1394/dma.h new file mode 100644 index 000000000000..467373cab8e5 --- /dev/null +++ b/trunk/drivers/ieee1394/dma.h @@ -0,0 +1,89 @@ +/* + * DMA region bookkeeping routines + * + * Copyright (C) 2002 Maas Digital LLC + * + * This code is licensed under the GPL. See the file COPYING in the root + * directory of the kernel sources for details. + */ + +#ifndef IEEE1394_DMA_H +#define IEEE1394_DMA_H + +#include + +struct file; +struct pci_dev; +struct scatterlist; +struct vm_area_struct; + +/** + * struct dma_prog_region - small contiguous DMA buffer + * @kvirt: kernel virtual address + * @dev: PCI device + * @n_pages: number of kernel pages + * @bus_addr: base bus address + * + * a small, physically contiguous DMA buffer with random-access, synchronous + * usage characteristics + */ +struct dma_prog_region { + unsigned char *kvirt; + struct pci_dev *dev; + unsigned int n_pages; + dma_addr_t bus_addr; +}; + +/* clear out all fields but do not allocate any memory */ +void dma_prog_region_init(struct dma_prog_region *prog); +int dma_prog_region_alloc(struct dma_prog_region *prog, unsigned long n_bytes, + struct pci_dev *dev); +void dma_prog_region_free(struct dma_prog_region *prog); + +static inline dma_addr_t dma_prog_region_offset_to_bus( + struct dma_prog_region *prog, unsigned long offset) +{ + return prog->bus_addr + offset; +} + +/** + * struct dma_region - large non-contiguous DMA buffer + * @virt: kernel virtual address + * @dev: PCI device + * @n_pages: number of kernel pages + * @n_dma_pages: number of IOMMU pages + * @sglist: IOMMU mapping + * @direction: PCI_DMA_TODEVICE, etc. + * + * a large, non-physically-contiguous DMA buffer with streaming, asynchronous + * usage characteristics + */ +struct dma_region { + unsigned char *kvirt; + struct pci_dev *dev; + unsigned int n_pages; + unsigned int n_dma_pages; + struct scatterlist *sglist; + int direction; +}; + +void dma_region_init(struct dma_region *dma); +int dma_region_alloc(struct dma_region *dma, unsigned long n_bytes, + struct pci_dev *dev, int direction); +void dma_region_free(struct dma_region *dma); +void dma_region_sync_for_cpu(struct dma_region *dma, unsigned long offset, + unsigned long len); +void dma_region_sync_for_device(struct dma_region *dma, unsigned long offset, + unsigned long len); +int dma_region_mmap(struct dma_region *dma, struct file *file, + struct vm_area_struct *vma); +dma_addr_t dma_region_offset_to_bus(struct dma_region *dma, + unsigned long offset); + +/** + * dma_region_i - macro to index into a DMA region (or dma_prog_region) + */ +#define dma_region_i(_dma, _type, _index) \ + ( ((_type*) ((_dma)->kvirt)) + (_index) ) + +#endif /* IEEE1394_DMA_H */ diff --git a/trunk/drivers/ieee1394/dv1394-private.h b/trunk/drivers/ieee1394/dv1394-private.h new file mode 100644 index 000000000000..18b92cbf4a9f --- /dev/null +++ b/trunk/drivers/ieee1394/dv1394-private.h @@ -0,0 +1,587 @@ +/* + * dv1394-private.h - DV input/output over IEEE 1394 on OHCI chips + * Copyright (C)2001 Daniel Maas + * receive by Dan Dennedy + * + * based on: + * video1394.h - driver for OHCI 1394 boards + * Copyright (C)1999,2000 Sebastien Rougeaux + * Peter Schlaile + * + * 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. + */ + +#ifndef _DV_1394_PRIVATE_H +#define _DV_1394_PRIVATE_H + +#include "ieee1394.h" +#include "ohci1394.h" +#include "dma.h" + +/* data structures private to the dv1394 driver */ +/* none of this is exposed to user-space */ + + +/* + the 8-byte CIP (Common Isochronous Packet) header that precedes + each packet of DV data. + + See the IEC 61883 standard. +*/ + +struct CIP_header { unsigned char b[8]; }; + +static inline void fill_cip_header(struct CIP_header *cip, + unsigned char source_node_id, + unsigned long counter, + enum pal_or_ntsc format, + unsigned long timestamp) +{ + cip->b[0] = source_node_id; + cip->b[1] = 0x78; /* packet size in quadlets (480/4) - even for empty packets! */ + cip->b[2] = 0x00; + cip->b[3] = counter; + + cip->b[4] = 0x80; /* const */ + + switch(format) { + case DV1394_PAL: + cip->b[5] = 0x80; + break; + case DV1394_NTSC: + cip->b[5] = 0x00; + break; + } + + cip->b[6] = timestamp >> 8; + cip->b[7] = timestamp & 0xFF; +} + + + +/* + DMA commands used to program the OHCI's DMA engine + + See the Texas Instruments OHCI 1394 chipset documentation. +*/ + +struct output_more_immediate { __le32 q[8]; }; +struct output_more { __le32 q[4]; }; +struct output_last { __le32 q[4]; }; +struct input_more { __le32 q[4]; }; +struct input_last { __le32 q[4]; }; + +/* outputs */ + +static inline void fill_output_more_immediate(struct output_more_immediate *omi, + unsigned char tag, + unsigned char channel, + unsigned char sync_tag, + unsigned int payload_size) +{ + omi->q[0] = cpu_to_le32(0x02000000 | 8); /* OUTPUT_MORE_IMMEDIATE; 8 is the size of the IT header */ + omi->q[1] = cpu_to_le32(0); + omi->q[2] = cpu_to_le32(0); + omi->q[3] = cpu_to_le32(0); + + /* IT packet header */ + omi->q[4] = cpu_to_le32( (0x0 << 16) /* IEEE1394_SPEED_100 */ + | (tag << 14) + | (channel << 8) + | (TCODE_ISO_DATA << 4) + | (sync_tag) ); + + /* reserved field; mimic behavior of my Sony DSR-40 */ + omi->q[5] = cpu_to_le32((payload_size << 16) | (0x7F << 8) | 0xA0); + + omi->q[6] = cpu_to_le32(0); + omi->q[7] = cpu_to_le32(0); +} + +static inline void fill_output_more(struct output_more *om, + unsigned int data_size, + unsigned long data_phys_addr) +{ + om->q[0] = cpu_to_le32(data_size); + om->q[1] = cpu_to_le32(data_phys_addr); + om->q[2] = cpu_to_le32(0); + om->q[3] = cpu_to_le32(0); +} + +static inline void fill_output_last(struct output_last *ol, + int want_timestamp, + int want_interrupt, + unsigned int data_size, + unsigned long data_phys_addr) +{ + u32 temp = 0; + temp |= 1 << 28; /* OUTPUT_LAST */ + + if (want_timestamp) /* controller will update timestamp at DMA time */ + temp |= 1 << 27; + + if (want_interrupt) + temp |= 3 << 20; + + temp |= 3 << 18; /* must take branch */ + temp |= data_size; + + ol->q[0] = cpu_to_le32(temp); + ol->q[1] = cpu_to_le32(data_phys_addr); + ol->q[2] = cpu_to_le32(0); + ol->q[3] = cpu_to_le32(0); +} + +/* inputs */ + +static inline void fill_input_more(struct input_more *im, + int want_interrupt, + unsigned int data_size, + unsigned long data_phys_addr) +{ + u32 temp = 2 << 28; /* INPUT_MORE */ + temp |= 8 << 24; /* s = 1, update xferStatus and resCount */ + if (want_interrupt) + temp |= 0 << 20; /* interrupts, i=0 in packet-per-buffer mode */ + temp |= 0x0 << 16; /* disable branch to address for packet-per-buffer mode */ + /* disable wait on sync field, not used in DV :-( */ + temp |= data_size; + + im->q[0] = cpu_to_le32(temp); + im->q[1] = cpu_to_le32(data_phys_addr); + im->q[2] = cpu_to_le32(0); /* branchAddress and Z not use in packet-per-buffer mode */ + im->q[3] = cpu_to_le32(0); /* xferStatus & resCount, resCount must be initialize to data_size */ +} + +static inline void fill_input_last(struct input_last *il, + int want_interrupt, + unsigned int data_size, + unsigned long data_phys_addr) +{ + u32 temp = 3 << 28; /* INPUT_LAST */ + temp |= 8 << 24; /* s = 1, update xferStatus and resCount */ + if (want_interrupt) + temp |= 3 << 20; /* enable interrupts */ + temp |= 0xC << 16; /* enable branch to address */ + /* disable wait on sync field, not used in DV :-( */ + temp |= data_size; + + il->q[0] = cpu_to_le32(temp); + il->q[1] = cpu_to_le32(data_phys_addr); + il->q[2] = cpu_to_le32(1); /* branchAddress (filled in later) and Z = 1 descriptor in next block */ + il->q[3] = cpu_to_le32(data_size); /* xferStatus & resCount, resCount must be initialize to data_size */ +} + + + +/* + A "DMA descriptor block" consists of several contiguous DMA commands. + struct DMA_descriptor_block encapsulates all of the commands necessary + to send one packet of DV data. + + There are three different types of these blocks: + + 1) command to send an empty packet (CIP header only, no DV data): + + OUTPUT_MORE-Immediate <-- contains the iso header in-line + OUTPUT_LAST <-- points to the CIP header + + 2) command to send a full packet when the DV data payload does NOT + cross a page boundary: + + OUTPUT_MORE-Immediate <-- contains the iso header in-line + OUTPUT_MORE <-- points to the CIP header + OUTPUT_LAST <-- points to entire DV data payload + + 3) command to send a full packet when the DV payload DOES cross + a page boundary: + + OUTPUT_MORE-Immediate <-- contains the iso header in-line + OUTPUT_MORE <-- points to the CIP header + OUTPUT_MORE <-- points to first part of DV data payload + OUTPUT_LAST <-- points to second part of DV data payload + + This struct describes all three block types using unions. + + !!! It is vital that an even number of these descriptor blocks fit on one + page of memory, since a block cannot cross a page boundary !!! + + */ + +struct DMA_descriptor_block { + + union { + struct { + /* iso header, common to all output block types */ + struct output_more_immediate omi; + + union { + /* empty packet */ + struct { + struct output_last ol; /* CIP header */ + } empty; + + /* full packet */ + struct { + struct output_more om; /* CIP header */ + + union { + /* payload does not cross page boundary */ + struct { + struct output_last ol; /* data payload */ + } nocross; + + /* payload crosses page boundary */ + struct { + struct output_more om; /* data payload */ + struct output_last ol; /* data payload */ + } cross; + } u; + + } full; + } u; + } out; + + struct { + struct input_last il; + } in; + + } u; + + /* ensure that PAGE_SIZE % sizeof(struct DMA_descriptor_block) == 0 + by padding out to 128 bytes */ + u32 __pad__[12]; +}; + + +/* struct frame contains all data associated with one frame in the + ringbuffer these are allocated when the DMA context is initialized + do_dv1394_init(). They are re-used after the card finishes + transmitting the frame. */ + +struct video_card; /* forward declaration */ + +struct frame { + + /* points to the struct video_card that owns this frame */ + struct video_card *video; + + /* index of this frame in video_card->frames[] */ + unsigned int frame_num; + + /* FRAME_CLEAR - DMA program not set up, waiting for data + FRAME_READY - DMA program written, ready to transmit + + Changes to these should be locked against the interrupt + */ + enum { + FRAME_CLEAR = 0, + FRAME_READY + } state; + + /* whether this frame has been DMA'ed already; used only from + the IRQ handler to determine whether the frame can be reset */ + int done; + + + /* kernel virtual pointer to the start of this frame's data in + the user ringbuffer. Use only for CPU access; to get the DMA + bus address you must go through the video->user_dma mapping */ + unsigned long data; + + /* Max # of packets per frame */ +#define MAX_PACKETS 500 + + + /* a PAGE_SIZE memory pool for allocating CIP headers + !header_pool must be aligned to PAGE_SIZE! */ + struct CIP_header *header_pool; + dma_addr_t header_pool_dma; + + + /* a physically contiguous memory pool for allocating DMA + descriptor blocks; usually around 64KB in size + !descriptor_pool must be aligned to PAGE_SIZE! */ + struct DMA_descriptor_block *descriptor_pool; + dma_addr_t descriptor_pool_dma; + unsigned long descriptor_pool_size; + + + /* # of packets allocated for this frame */ + unsigned int n_packets; + + + /* below are several pointers (kernel virtual addresses, not + DMA bus addresses) to parts of the DMA program. These are + set each time the DMA program is written in + frame_prepare(). They are used later on, e.g. from the + interrupt handler, to check the status of the frame */ + + /* points to status/timestamp field of first DMA packet */ + /* (we'll check it later to monitor timestamp accuracy) */ + __le32 *frame_begin_timestamp; + + /* the timestamp we assigned to the first packet in the frame */ + u32 assigned_timestamp; + + /* pointer to the first packet's CIP header (where the timestamp goes) */ + struct CIP_header *cip_syt1; + + /* pointer to the second packet's CIP header + (only set if the first packet was empty) */ + struct CIP_header *cip_syt2; + + /* in order to figure out what caused an interrupt, + store pointers to the status fields of the two packets + that can cause interrupts. We'll check these from the + interrupt handler. + */ + __le32 *mid_frame_timestamp; + __le32 *frame_end_timestamp; + + /* branch address field of final packet. This is effectively + the "tail" in the chain of DMA descriptor blocks. + We will fill it with the address of the first DMA descriptor + block in the subsequent frame, once it is ready. + */ + __le32 *frame_end_branch; + + /* the number of descriptors in the first descriptor block + of the frame. Needed to start DMA */ + int first_n_descriptors; +}; + + +struct packet { + __le16 timestamp; + u16 invalid; + u16 iso_header; + __le16 data_length; + u32 cip_h1; + u32 cip_h2; + unsigned char data[480]; + unsigned char padding[16]; /* force struct size =512 for page alignment */ +}; + + +/* allocate/free a frame */ +static struct frame* frame_new(unsigned int frame_num, struct video_card *video); +static void frame_delete(struct frame *f); + +/* reset f so that it can be used again */ +static void frame_reset(struct frame *f); + +/* struct video_card contains all data associated with one instance + of the dv1394 driver +*/ +enum modes { + MODE_RECEIVE, + MODE_TRANSMIT +}; + +struct video_card { + + /* ohci card to which this instance corresponds */ + struct ti_ohci *ohci; + + /* OHCI card id; the link between the VFS inode and a specific video_card + (essentially the device minor number) */ + int id; + + /* entry in dv1394_cards */ + struct list_head list; + + /* OHCI card IT DMA context number, -1 if not in use */ + int ohci_it_ctx; + struct ohci1394_iso_tasklet it_tasklet; + + /* register offsets for current IT DMA context, 0 if not in use */ + u32 ohci_IsoXmitContextControlSet; + u32 ohci_IsoXmitContextControlClear; + u32 ohci_IsoXmitCommandPtr; + + /* OHCI card IR DMA context number, -1 if not in use */ + struct ohci1394_iso_tasklet ir_tasklet; + int ohci_ir_ctx; + + /* register offsets for current IR DMA context, 0 if not in use */ + u32 ohci_IsoRcvContextControlSet; + u32 ohci_IsoRcvContextControlClear; + u32 ohci_IsoRcvCommandPtr; + u32 ohci_IsoRcvContextMatch; + + + /* CONCURRENCY CONTROL */ + + /* there are THREE levels of locking associated with video_card. */ + + /* + 1) the 'open' flag - this prevents more than one process from + opening the device. (the driver currently assumes only one opener). + This is a regular int, but use test_and_set_bit() (on bit zero) + for atomicity. + */ + unsigned long open; + + /* + 2) the spinlock - this provides mutual exclusion between the interrupt + handler and process-context operations. Generally you must take the + spinlock under the following conditions: + 1) DMA (and hence the interrupt handler) may be running + AND + 2) you need to operate on the video_card, especially active_frame + + It is OK to play with video_card without taking the spinlock if + you are certain that DMA is not running. Even if DMA is running, + it is OK to *read* active_frame with the lock, then drop it + immediately. This is safe because the interrupt handler will never + advance active_frame onto a frame that is not READY (and the spinlock + must be held while marking a frame READY). + + spinlock is also used to protect ohci_it_ctx and ohci_ir_ctx, + which can be accessed from both process and interrupt context + */ + spinlock_t spinlock; + + /* flag to prevent spurious interrupts (which OHCI seems to + generate a lot :) from accessing the struct */ + int dma_running; + + /* + 3) the sleeping mutex 'mtx' - this is used from process context only, + to serialize various operations on the video_card. Even though only one + open() is allowed, we still need to prevent multiple threads of execution + from entering calls like read, write, ioctl, etc. + + I honestly can't think of a good reason to use dv1394 from several threads + at once, but we need to serialize anyway to prevent oopses =). + + NOTE: if you need both spinlock and mtx, take mtx first to avoid deadlock! + */ + struct mutex mtx; + + /* people waiting for buffer space, please form a line here... */ + wait_queue_head_t waitq; + + /* support asynchronous I/O signals (SIGIO) */ + struct fasync_struct *fasync; + + /* the large, non-contiguous (rvmalloc()) ringbuffer for DV + data, exposed to user-space via mmap() */ + unsigned long dv_buf_size; + struct dma_region dv_buf; + + /* next byte in the ringbuffer that a write() call will fill */ + size_t write_off; + + struct frame *frames[DV1394_MAX_FRAMES]; + + /* n_frames also serves as an indicator that this struct video_card is + initialized and ready to run DMA buffers */ + + int n_frames; + + /* this is the frame that is currently "owned" by the OHCI DMA controller + (set to -1 iff DMA is not running) + + ! must lock against the interrupt handler when accessing it ! + + RULES: + + Only the interrupt handler may change active_frame if DMA + is running; if not, process may change it + + If the next frame is READY, the interrupt handler will advance + active_frame when the current frame is finished. + + If the next frame is CLEAR, the interrupt handler will re-transmit + the current frame, and the dropped_frames counter will be incremented. + + The interrupt handler will NEVER advance active_frame to a + frame that is not READY. + */ + int active_frame; + int first_run; + + /* the same locking rules apply to these three fields also: */ + + /* altered ONLY from process context. Must check first_clear_frame->state; + if it's READY, that means the ringbuffer is full with READY frames; + if it's CLEAR, that means one or more ringbuffer frames are CLEAR */ + unsigned int first_clear_frame; + + /* altered both by process and interrupt */ + unsigned int n_clear_frames; + + /* only altered by the interrupt */ + unsigned int dropped_frames; + + + + /* the CIP accumulator and continuity counter are properties + of the DMA stream as a whole (not a single frame), so they + are stored here in the video_card */ + + unsigned long cip_accum; + unsigned long cip_n, cip_d; + unsigned int syt_offset; + unsigned int continuity_counter; + + enum pal_or_ntsc pal_or_ntsc; + + /* redundant, but simplifies the code somewhat */ + unsigned int frame_size; /* in bytes */ + + /* the isochronous channel to use, -1 if video card is inactive */ + int channel; + + + /* physically contiguous packet ringbuffer for receive */ + struct dma_region packet_buf; + unsigned long packet_buf_size; + + unsigned int current_packet; + int first_frame; /* received first start frame marker? */ + enum modes mode; +}; + +/* + if the video_card is not initialized, then the ONLY fields that are valid are: + ohci + open + n_frames +*/ + +static inline int video_card_initialized(struct video_card *v) +{ + return v->n_frames > 0; +} + +static int do_dv1394_init(struct video_card *video, struct dv1394_init *init); +static int do_dv1394_init_default(struct video_card *video); +static void do_dv1394_shutdown(struct video_card *video, int free_user_buf); + + +/* NTSC empty packet rate accurate to within 0.01%, + calibrated against a Sony DSR-40 DVCAM deck */ + +#define CIP_N_NTSC 68000000 +#define CIP_D_NTSC 1068000000 + +#define CIP_N_PAL 1 +#define CIP_D_PAL 16 + +#endif /* _DV_1394_PRIVATE_H */ + diff --git a/trunk/drivers/ieee1394/dv1394.c b/trunk/drivers/ieee1394/dv1394.c new file mode 100644 index 000000000000..c5a031b79d03 --- /dev/null +++ b/trunk/drivers/ieee1394/dv1394.c @@ -0,0 +1,2584 @@ +/* + * dv1394.c - DV input/output over IEEE 1394 on OHCI chips + * Copyright (C)2001 Daniel Maas + * receive by Dan Dennedy + * + * based on: + * video1394.c - video driver for OHCI 1394 boards + * Copyright (C)1999,2000 Sebastien Rougeaux + * + * 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. + */ + +/* + OVERVIEW + + I designed dv1394 as a "pipe" that you can use to shoot DV onto a + FireWire bus. In transmission mode, dv1394 does the following: + + 1. accepts contiguous frames of DV data from user-space, via write() + or mmap() (see dv1394.h for the complete API) + 2. wraps IEC 61883 packets around the DV data, inserting + empty synchronization packets as necessary + 3. assigns accurate SYT timestamps to the outgoing packets + 4. shoots them out using the OHCI card's IT DMA engine + + Thanks to Dan Dennedy, we now have a receive mode that does the following: + + 1. accepts raw IEC 61883 packets from the OHCI card + 2. re-assembles the DV data payloads into contiguous frames, + discarding empty packets + 3. sends the DV data to user-space via read() or mmap() +*/ + +/* + TODO: + + - tunable frame-drop behavior: either loop last frame, or halt transmission + + - use a scatter/gather buffer for DMA programs (f->descriptor_pool) + so that we don't rely on allocating 64KB of contiguous kernel memory + via pci_alloc_consistent() + + DONE: + - during reception, better handling of dropped frames and continuity errors + - during reception, prevent DMA from bypassing the irq tasklets + - reduce irq rate during reception (1/250 packets). + - add many more internal buffers during reception with scatter/gather dma. + - add dbc (continuity) checking on receive, increment status.dropped_frames + if not continuous. + - restart IT DMA after a bus reset + - safely obtain and release ISO Tx channels in cooperation with OHCI driver + - map received DIF blocks to their proper location in DV frame (ensure + recovery if dropped packet) + - handle bus resets gracefully (OHCI card seems to take care of this itself(!)) + - do not allow resizing the user_buf once allocated; eliminate nuke_buffer_mappings + - eliminated #ifdef DV1394_DEBUG_LEVEL by inventing macros debug_printk and irq_printk + - added wmb() and mb() to places where PCI read/write ordering needs to be enforced + - set video->id correctly + - store video_cards in an array indexed by OHCI card ID, rather than a list + - implement DMA context allocation to cooperate with other users of the OHCI + - fix all XXX showstoppers + - disable IR/IT DMA interrupts on shutdown + - flush pci writes to the card by issuing a read + - character device dispatching + - switch over to the new kernel DMA API (pci_map_*()) (* needs testing on platforms with IOMMU!) + - keep all video_cards in a list (for open() via chardev), set file->private_data = video + - dv1394_poll should indicate POLLIN when receiving buffers are available + - add proc fs interface to set cip_n, cip_d, syt_offset, and video signal + - expose xmit and recv as separate devices (not exclusive) + - expose NTSC and PAL as separate devices (can be overridden) + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dv1394.h" +#include "dv1394-private.h" +#include "highlevel.h" +#include "hosts.h" +#include "ieee1394.h" +#include "ieee1394_core.h" +#include "ieee1394_hotplug.h" +#include "ieee1394_types.h" +#include "nodemgr.h" +#include "ohci1394.h" + +/* DEBUG LEVELS: + 0 - no debugging messages + 1 - some debugging messages, but none during DMA frame transmission + 2 - lots of messages, including during DMA frame transmission + (will cause underflows if your machine is too slow!) +*/ + +#define DV1394_DEBUG_LEVEL 0 + +/* for debugging use ONLY: allow more than one open() of the device */ +/* #define DV1394_ALLOW_MORE_THAN_ONE_OPEN 1 */ + +#if DV1394_DEBUG_LEVEL >= 2 +#define irq_printk( args... ) printk( args ) +#else +#define irq_printk( args... ) do {} while (0) +#endif + +#if DV1394_DEBUG_LEVEL >= 1 +#define debug_printk( args... ) printk( args) +#else +#define debug_printk( args... ) do {} while (0) +#endif + +/* issue a dummy PCI read to force the preceding write + to be posted to the PCI bus immediately */ + +static inline void flush_pci_write(struct ti_ohci *ohci) +{ + mb(); + reg_read(ohci, OHCI1394_IsochronousCycleTimer); +} + +static void it_tasklet_func(unsigned long data); +static void ir_tasklet_func(unsigned long data); + +#ifdef CONFIG_COMPAT +static long dv1394_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg); +#endif + +/* GLOBAL DATA */ + +/* list of all video_cards */ +static LIST_HEAD(dv1394_cards); +static DEFINE_SPINLOCK(dv1394_cards_lock); + +/* translate from a struct file* to the corresponding struct video_card* */ + +static inline struct video_card* file_to_video_card(struct file *file) +{ + return file->private_data; +} + +/*** FRAME METHODS *********************************************************/ + +static void frame_reset(struct frame *f) +{ + f->state = FRAME_CLEAR; + f->done = 0; + f->n_packets = 0; + f->frame_begin_timestamp = NULL; + f->assigned_timestamp = 0; + f->cip_syt1 = NULL; + f->cip_syt2 = NULL; + f->mid_frame_timestamp = NULL; + f->frame_end_timestamp = NULL; + f->frame_end_branch = NULL; +} + +static struct frame* frame_new(unsigned int frame_num, struct video_card *video) +{ + struct frame *f = kmalloc(sizeof(*f), GFP_KERNEL); + if (!f) + return NULL; + + f->video = video; + f->frame_num = frame_num; + + f->header_pool = pci_alloc_consistent(f->video->ohci->dev, PAGE_SIZE, &f->header_pool_dma); + if (!f->header_pool) { + printk(KERN_ERR "dv1394: failed to allocate CIP header pool\n"); + kfree(f); + return NULL; + } + + debug_printk("dv1394: frame_new: allocated CIP header pool at virt 0x%08lx (contig) dma 0x%08lx size %ld\n", + (unsigned long) f->header_pool, (unsigned long) f->header_pool_dma, PAGE_SIZE); + + f->descriptor_pool_size = MAX_PACKETS * sizeof(struct DMA_descriptor_block); + /* make it an even # of pages */ + f->descriptor_pool_size += PAGE_SIZE - (f->descriptor_pool_size%PAGE_SIZE); + + f->descriptor_pool = pci_alloc_consistent(f->video->ohci->dev, + f->descriptor_pool_size, + &f->descriptor_pool_dma); + if (!f->descriptor_pool) { + pci_free_consistent(f->video->ohci->dev, PAGE_SIZE, f->header_pool, f->header_pool_dma); + kfree(f); + return NULL; + } + + debug_printk("dv1394: frame_new: allocated DMA program memory at virt 0x%08lx (contig) dma 0x%08lx size %ld\n", + (unsigned long) f->descriptor_pool, (unsigned long) f->descriptor_pool_dma, f->descriptor_pool_size); + + f->data = 0; + frame_reset(f); + + return f; +} + +static void frame_delete(struct frame *f) +{ + pci_free_consistent(f->video->ohci->dev, PAGE_SIZE, f->header_pool, f->header_pool_dma); + pci_free_consistent(f->video->ohci->dev, f->descriptor_pool_size, f->descriptor_pool, f->descriptor_pool_dma); + kfree(f); +} + + + + +/* + frame_prepare() - build the DMA program for transmitting + + Frame_prepare() must be called OUTSIDE the video->spinlock. + However, frame_prepare() must still be serialized, so + it should be called WITH the video->mtx taken. + */ + +static void frame_prepare(struct video_card *video, unsigned int this_frame) +{ + struct frame *f = video->frames[this_frame]; + int last_frame; + + struct DMA_descriptor_block *block; + dma_addr_t block_dma; + struct CIP_header *cip; + dma_addr_t cip_dma; + + unsigned int n_descriptors, full_packets, packets_per_frame, payload_size; + + /* these flags denote packets that need special attention */ + int empty_packet, first_packet, last_packet, mid_packet; + + __le32 *branch_address, *last_branch_address = NULL; + unsigned long data_p; + int first_packet_empty = 0; + u32 cycleTimer, ct_sec, ct_cyc, ct_off; + unsigned long irq_flags; + + irq_printk("frame_prepare( %d ) ---------------------\n", this_frame); + + full_packets = 0; + + + + if (video->pal_or_ntsc == DV1394_PAL) + packets_per_frame = DV1394_PAL_PACKETS_PER_FRAME; + else + packets_per_frame = DV1394_NTSC_PACKETS_PER_FRAME; + + while ( full_packets < packets_per_frame ) { + empty_packet = first_packet = last_packet = mid_packet = 0; + + data_p = f->data + full_packets * 480; + + /************************************************/ + /* allocate a descriptor block and a CIP header */ + /************************************************/ + + /* note: these should NOT cross a page boundary (DMA restriction) */ + + if (f->n_packets >= MAX_PACKETS) { + printk(KERN_ERR "dv1394: FATAL ERROR: max packet count exceeded\n"); + return; + } + + /* the block surely won't cross a page boundary, + since an even number of descriptor_blocks fit on a page */ + block = &(f->descriptor_pool[f->n_packets]); + + /* DMA address of the block = offset of block relative + to the kernel base address of the descriptor pool + + DMA base address of the descriptor pool */ + block_dma = ((unsigned long) block - (unsigned long) f->descriptor_pool) + f->descriptor_pool_dma; + + + /* the whole CIP pool fits on one page, so no worries about boundaries */ + if ( ((unsigned long) &(f->header_pool[f->n_packets]) - (unsigned long) f->header_pool) + > PAGE_SIZE) { + printk(KERN_ERR "dv1394: FATAL ERROR: no room to allocate CIP header\n"); + return; + } + + cip = &(f->header_pool[f->n_packets]); + + /* DMA address of the CIP header = offset of cip + relative to kernel base address of the header pool + + DMA base address of the header pool */ + cip_dma = (unsigned long) cip % PAGE_SIZE + f->header_pool_dma; + + /* is this an empty packet? */ + + if (video->cip_accum > (video->cip_d - video->cip_n)) { + empty_packet = 1; + payload_size = 8; + video->cip_accum -= (video->cip_d - video->cip_n); + } else { + payload_size = 488; + video->cip_accum += video->cip_n; + } + + /* there are three important packets each frame: + + the first packet in the frame - we ask the card to record the timestamp when + this packet is actually sent, so we can monitor + how accurate our timestamps are. Also, the first + packet serves as a semaphore to let us know that + it's OK to free the *previous* frame's DMA buffer + + the last packet in the frame - this packet is used to detect buffer underflows. + if this is the last ready frame, the last DMA block + will have a branch back to the beginning of the frame + (so that the card will re-send the frame on underflow). + if this branch gets taken, we know that at least one + frame has been dropped. When the next frame is ready, + the branch is pointed to its first packet, and the + semaphore is disabled. + + a "mid" packet slightly before the end of the frame - this packet should trigger + an interrupt so we can go and assign a timestamp to the first packet + in the next frame. We don't use the very last packet in the frame + for this purpose, because that would leave very little time to set + the timestamp before DMA starts on the next frame. + */ + + if (f->n_packets == 0) { + first_packet = 1; + } else if ( full_packets == (packets_per_frame-1) ) { + last_packet = 1; + } else if (f->n_packets == packets_per_frame) { + mid_packet = 1; + } + + + /********************/ + /* setup CIP header */ + /********************/ + + /* the timestamp will be written later from the + mid-frame interrupt handler. For now we just + store the address of the CIP header(s) that + need a timestamp. */ + + /* first packet in the frame needs a timestamp */ + if (first_packet) { + f->cip_syt1 = cip; + if (empty_packet) + first_packet_empty = 1; + + } else if (first_packet_empty && (f->n_packets == 1) ) { + /* if the first packet was empty, the second + packet's CIP header also needs a timestamp */ + f->cip_syt2 = cip; + } + + fill_cip_header(cip, + /* the node ID number of the OHCI card */ + reg_read(video->ohci, OHCI1394_NodeID) & 0x3F, + video->continuity_counter, + video->pal_or_ntsc, + 0xFFFF /* the timestamp is filled in later */); + + /* advance counter, only for full packets */ + if ( ! empty_packet ) + video->continuity_counter++; + + /******************************/ + /* setup DMA descriptor block */ + /******************************/ + + /* first descriptor - OUTPUT_MORE_IMMEDIATE, for the controller's IT header */ + fill_output_more_immediate( &(block->u.out.omi), 1, video->channel, 0, payload_size); + + if (empty_packet) { + /* second descriptor - OUTPUT_LAST for CIP header */ + fill_output_last( &(block->u.out.u.empty.ol), + + /* want completion status on all interesting packets */ + (first_packet || mid_packet || last_packet) ? 1 : 0, + + /* want interrupts on all interesting packets */ + (first_packet || mid_packet || last_packet) ? 1 : 0, + + sizeof(struct CIP_header), /* data size */ + cip_dma); + + if (first_packet) + f->frame_begin_timestamp = &(block->u.out.u.empty.ol.q[3]); + else if (mid_packet) + f->mid_frame_timestamp = &(block->u.out.u.empty.ol.q[3]); + else if (last_packet) { + f->frame_end_timestamp = &(block->u.out.u.empty.ol.q[3]); + f->frame_end_branch = &(block->u.out.u.empty.ol.q[2]); + } + + branch_address = &(block->u.out.u.empty.ol.q[2]); + n_descriptors = 3; + if (first_packet) + f->first_n_descriptors = n_descriptors; + + } else { /* full packet */ + + /* second descriptor - OUTPUT_MORE for CIP header */ + fill_output_more( &(block->u.out.u.full.om), + sizeof(struct CIP_header), /* data size */ + cip_dma); + + + /* third (and possibly fourth) descriptor - for DV data */ + /* the 480-byte payload can cross a page boundary; if so, + we need to split it into two DMA descriptors */ + + /* does the 480-byte data payload cross a page boundary? */ + if ( (PAGE_SIZE- ((unsigned long)data_p % PAGE_SIZE) ) < 480 ) { + + /* page boundary crossed */ + + fill_output_more( &(block->u.out.u.full.u.cross.om), + /* data size - how much of data_p fits on the first page */ + PAGE_SIZE - (data_p % PAGE_SIZE), + + /* DMA address of data_p */ + dma_region_offset_to_bus(&video->dv_buf, + data_p - (unsigned long) video->dv_buf.kvirt)); + + fill_output_last( &(block->u.out.u.full.u.cross.ol), + + /* want completion status on all interesting packets */ + (first_packet || mid_packet || last_packet) ? 1 : 0, + + /* want interrupt on all interesting packets */ + (first_packet || mid_packet || last_packet) ? 1 : 0, + + /* data size - remaining portion of data_p */ + 480 - (PAGE_SIZE - (data_p % PAGE_SIZE)), + + /* DMA address of data_p + PAGE_SIZE - (data_p % PAGE_SIZE) */ + dma_region_offset_to_bus(&video->dv_buf, + data_p + PAGE_SIZE - (data_p % PAGE_SIZE) - (unsigned long) video->dv_buf.kvirt)); + + if (first_packet) + f->frame_begin_timestamp = &(block->u.out.u.full.u.cross.ol.q[3]); + else if (mid_packet) + f->mid_frame_timestamp = &(block->u.out.u.full.u.cross.ol.q[3]); + else if (last_packet) { + f->frame_end_timestamp = &(block->u.out.u.full.u.cross.ol.q[3]); + f->frame_end_branch = &(block->u.out.u.full.u.cross.ol.q[2]); + } + + branch_address = &(block->u.out.u.full.u.cross.ol.q[2]); + + n_descriptors = 5; + if (first_packet) + f->first_n_descriptors = n_descriptors; + + full_packets++; + + } else { + /* fits on one page */ + + fill_output_last( &(block->u.out.u.full.u.nocross.ol), + + /* want completion status on all interesting packets */ + (first_packet || mid_packet || last_packet) ? 1 : 0, + + /* want interrupt on all interesting packets */ + (first_packet || mid_packet || last_packet) ? 1 : 0, + + 480, /* data size (480 bytes of DV data) */ + + + /* DMA address of data_p */ + dma_region_offset_to_bus(&video->dv_buf, + data_p - (unsigned long) video->dv_buf.kvirt)); + + if (first_packet) + f->frame_begin_timestamp = &(block->u.out.u.full.u.nocross.ol.q[3]); + else if (mid_packet) + f->mid_frame_timestamp = &(block->u.out.u.full.u.nocross.ol.q[3]); + else if (last_packet) { + f->frame_end_timestamp = &(block->u.out.u.full.u.nocross.ol.q[3]); + f->frame_end_branch = &(block->u.out.u.full.u.nocross.ol.q[2]); + } + + branch_address = &(block->u.out.u.full.u.nocross.ol.q[2]); + + n_descriptors = 4; + if (first_packet) + f->first_n_descriptors = n_descriptors; + + full_packets++; + } + } + + /* link this descriptor block into the DMA program by filling in + the branch address of the previous block */ + + /* note: we are not linked into the active DMA chain yet */ + + if (last_branch_address) { + *(last_branch_address) = cpu_to_le32(block_dma | n_descriptors); + } + + last_branch_address = branch_address; + + + f->n_packets++; + + } + + /* when we first assemble a new frame, set the final branch + to loop back up to the top */ + *(f->frame_end_branch) = cpu_to_le32(f->descriptor_pool_dma | f->first_n_descriptors); + + /* make the latest version of this frame visible to the PCI card */ + dma_region_sync_for_device(&video->dv_buf, f->data - (unsigned long) video->dv_buf.kvirt, video->frame_size); + + /* lock against DMA interrupt */ + spin_lock_irqsave(&video->spinlock, irq_flags); + + f->state = FRAME_READY; + + video->n_clear_frames--; + + last_frame = video->first_clear_frame - 1; + if (last_frame == -1) + last_frame = video->n_frames-1; + + video->first_clear_frame = (video->first_clear_frame + 1) % video->n_frames; + + irq_printk(" frame %d prepared, active_frame = %d, n_clear_frames = %d, first_clear_frame = %d\n last=%d\n", + this_frame, video->active_frame, video->n_clear_frames, video->first_clear_frame, last_frame); + + irq_printk(" begin_ts %08lx mid_ts %08lx end_ts %08lx end_br %08lx\n", + (unsigned long) f->frame_begin_timestamp, + (unsigned long) f->mid_frame_timestamp, + (unsigned long) f->frame_end_timestamp, + (unsigned long) f->frame_end_branch); + + if (video->active_frame != -1) { + + /* if DMA is already active, we are almost done */ + /* just link us onto the active DMA chain */ + if (video->frames[last_frame]->frame_end_branch) { + u32 temp; + + /* point the previous frame's tail to this frame's head */ + *(video->frames[last_frame]->frame_end_branch) = cpu_to_le32(f->descriptor_pool_dma | f->first_n_descriptors); + + /* this write MUST precede the next one, or we could silently drop frames */ + wmb(); + + /* disable the want_status semaphore on the last packet */ + temp = le32_to_cpu(*(video->frames[last_frame]->frame_end_branch - 2)); + temp &= 0xF7CFFFFF; + *(video->frames[last_frame]->frame_end_branch - 2) = cpu_to_le32(temp); + + /* flush these writes to memory ASAP */ + flush_pci_write(video->ohci); + + /* NOTE: + ideally the writes should be "atomic": if + the OHCI card reads the want_status flag in + between them, we'll falsely report a + dropped frame. Hopefully this window is too + small to really matter, and the consequence + is rather harmless. */ + + + irq_printk(" new frame %d linked onto DMA chain\n", this_frame); + + } else { + printk(KERN_ERR "dv1394: last frame not ready???\n"); + } + + } else { + + u32 transmit_sec, transmit_cyc; + u32 ts_cyc; + + /* DMA is stopped, so this is the very first frame */ + video->active_frame = this_frame; + + /* set CommandPtr to address and size of first descriptor block */ + reg_write(video->ohci, video->ohci_IsoXmitCommandPtr, + video->frames[video->active_frame]->descriptor_pool_dma | + f->first_n_descriptors); + + /* assign a timestamp based on the current cycle time... + We'll tell the card to begin DMA 100 cycles from now, + and assign a timestamp 103 cycles from now */ + + cycleTimer = reg_read(video->ohci, OHCI1394_IsochronousCycleTimer); + + ct_sec = cycleTimer >> 25; + ct_cyc = (cycleTimer >> 12) & 0x1FFF; + ct_off = cycleTimer & 0xFFF; + + transmit_sec = ct_sec; + transmit_cyc = ct_cyc + 100; + + transmit_sec += transmit_cyc/8000; + transmit_cyc %= 8000; + + ts_cyc = transmit_cyc + 3; + ts_cyc %= 8000; + + f->assigned_timestamp = (ts_cyc&0xF) << 12; + + /* now actually write the timestamp into the appropriate CIP headers */ + if (f->cip_syt1) { + f->cip_syt1->b[6] = f->assigned_timestamp >> 8; + f->cip_syt1->b[7] = f->assigned_timestamp & 0xFF; + } + if (f->cip_syt2) { + f->cip_syt2->b[6] = f->assigned_timestamp >> 8; + f->cip_syt2->b[7] = f->assigned_timestamp & 0xFF; + } + + /* --- start DMA --- */ + + /* clear all bits in ContextControl register */ + + reg_write(video->ohci, video->ohci_IsoXmitContextControlClear, 0xFFFFFFFF); + wmb(); + + /* the OHCI card has the ability to start ISO transmission on a + particular cycle (start-on-cycle). This way we can ensure that + the first DV frame will have an accurate timestamp. + + However, start-on-cycle only appears to work if the OHCI card + is cycle master! Since the consequences of messing up the first + timestamp are minimal*, just disable start-on-cycle for now. + + * my DV deck drops the first few frames before it "locks in;" + so the first frame having an incorrect timestamp is inconsequential. + */ + +#if 0 + reg_write(video->ohci, video->ohci_IsoXmitContextControlSet, + (1 << 31) /* enable start-on-cycle */ + | ( (transmit_sec & 0x3) << 29) + | (transmit_cyc << 16)); + wmb(); +#endif + + video->dma_running = 1; + + /* set the 'run' bit */ + reg_write(video->ohci, video->ohci_IsoXmitContextControlSet, 0x8000); + flush_pci_write(video->ohci); + + /* --- DMA should be running now --- */ + + debug_printk(" Cycle = %4u ContextControl = %08x CmdPtr = %08x\n", + (reg_read(video->ohci, OHCI1394_IsochronousCycleTimer) >> 12) & 0x1FFF, + reg_read(video->ohci, video->ohci_IsoXmitContextControlSet), + reg_read(video->ohci, video->ohci_IsoXmitCommandPtr)); + + debug_printk(" DMA start - current cycle %4u, transmit cycle %4u (%2u), assigning ts cycle %2u\n", + ct_cyc, transmit_cyc, transmit_cyc & 0xF, ts_cyc & 0xF); + +#if DV1394_DEBUG_LEVEL >= 2 + { + /* check if DMA is really running */ + int i = 0; + while (i < 20) { + mb(); + mdelay(1); + if (reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & (1 << 10)) { + printk("DMA ACTIVE after %d msec\n", i); + break; + } + i++; + } + + printk("set = %08x, cmdPtr = %08x\n", + reg_read(video->ohci, video->ohci_IsoXmitContextControlSet), + reg_read(video->ohci, video->ohci_IsoXmitCommandPtr) + ); + + if ( ! (reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & (1 << 10)) ) { + printk("DMA did NOT go active after 20ms, event = %x\n", + reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & 0x1F); + } else + printk("DMA is RUNNING!\n"); + } +#endif + + } + + + spin_unlock_irqrestore(&video->spinlock, irq_flags); +} + + + +/*** RECEIVE FUNCTIONS *****************************************************/ + +/* + frame method put_packet + + map and copy the packet data to its location in the frame + based upon DIF section and sequence +*/ + +static void inline +frame_put_packet (struct frame *f, struct packet *p) +{ + int section_type = p->data[0] >> 5; /* section type is in bits 5 - 7 */ + int dif_sequence = p->data[1] >> 4; /* dif sequence number is in bits 4 - 7 */ + int dif_block = p->data[2]; + + /* sanity check */ + if (dif_sequence > 11 || dif_block > 149) return; + + switch (section_type) { + case 0: /* 1 Header block */ + memcpy( (void *) f->data + dif_sequence * 150 * 80, p->data, 480); + break; + + case 1: /* 2 Subcode blocks */ + memcpy( (void *) f->data + dif_sequence * 150 * 80 + (1 + dif_block) * 80, p->data, 480); + break; + + case 2: /* 3 VAUX blocks */ + memcpy( (void *) f->data + dif_sequence * 150 * 80 + (3 + dif_block) * 80, p->data, 480); + break; + + case 3: /* 9 Audio blocks interleaved with video */ + memcpy( (void *) f->data + dif_sequence * 150 * 80 + (6 + dif_block * 16) * 80, p->data, 480); + break; + + case 4: /* 135 Video blocks interleaved with audio */ + memcpy( (void *) f->data + dif_sequence * 150 * 80 + (7 + (dif_block / 15) + dif_block) * 80, p->data, 480); + break; + + default: /* we can not handle any other data */ + break; + } +} + + +static void start_dma_receive(struct video_card *video) +{ + if (video->first_run == 1) { + video->first_run = 0; + + /* start DMA once all of the frames are READY */ + video->n_clear_frames = 0; + video->first_clear_frame = -1; + video->current_packet = 0; + video->active_frame = 0; + + /* reset iso recv control register */ + reg_write(video->ohci, video->ohci_IsoRcvContextControlClear, 0xFFFFFFFF); + wmb(); + + /* clear bufferFill, set isochHeader and speed (0=100) */ + reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, 0x40000000); + + /* match on all tags, listen on channel */ + reg_write(video->ohci, video->ohci_IsoRcvContextMatch, 0xf0000000 | video->channel); + + /* address and first descriptor block + Z=1 */ + reg_write(video->ohci, video->ohci_IsoRcvCommandPtr, + video->frames[0]->descriptor_pool_dma | 1); /* Z=1 */ + wmb(); + + video->dma_running = 1; + + /* run */ + reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, 0x8000); + flush_pci_write(video->ohci); + + debug_printk("dv1394: DMA started\n"); + +#if DV1394_DEBUG_LEVEL >= 2 + { + int i; + + for (i = 0; i < 1000; ++i) { + mdelay(1); + if (reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 10)) { + printk("DMA ACTIVE after %d msec\n", i); + break; + } + } + if ( reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 11) ) { + printk("DEAD, event = %x\n", + reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & 0x1F); + } else + printk("RUNNING!\n"); + } +#endif + } else if ( reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 11) ) { + debug_printk("DEAD, event = %x\n", + reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & 0x1F); + + /* wake */ + reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, (1 << 12)); + } +} + + +/* + receive_packets() - build the DMA program for receiving +*/ + +static void receive_packets(struct video_card *video) +{ + struct DMA_descriptor_block *block = NULL; + dma_addr_t block_dma = 0; + struct packet *data = NULL; + dma_addr_t data_dma = 0; + __le32 *last_branch_address = NULL; + unsigned long irq_flags; + int want_interrupt = 0; + struct frame *f = NULL; + int i, j; + + spin_lock_irqsave(&video->spinlock, irq_flags); + + for (j = 0; j < video->n_frames; j++) { + + /* connect frames */ + if (j > 0 && f != NULL && f->frame_end_branch != NULL) + *(f->frame_end_branch) = cpu_to_le32(video->frames[j]->descriptor_pool_dma | 1); /* set Z=1 */ + + f = video->frames[j]; + + for (i = 0; i < MAX_PACKETS; i++) { + /* locate a descriptor block and packet from the buffer */ + block = &(f->descriptor_pool[i]); + block_dma = ((unsigned long) block - (unsigned long) f->descriptor_pool) + f->descriptor_pool_dma; + + data = ((struct packet*)video->packet_buf.kvirt) + f->frame_num * MAX_PACKETS + i; + data_dma = dma_region_offset_to_bus( &video->packet_buf, + ((unsigned long) data - (unsigned long) video->packet_buf.kvirt) ); + + /* setup DMA descriptor block */ + want_interrupt = ((i % (MAX_PACKETS/2)) == 0 || i == (MAX_PACKETS-1)); + fill_input_last( &(block->u.in.il), want_interrupt, 512, data_dma); + + /* link descriptors */ + last_branch_address = f->frame_end_branch; + + if (last_branch_address != NULL) + *(last_branch_address) = cpu_to_le32(block_dma | 1); /* set Z=1 */ + + f->frame_end_branch = &(block->u.in.il.q[2]); + } + + } /* next j */ + + spin_unlock_irqrestore(&video->spinlock, irq_flags); + +} + + + +/*** MANAGEMENT FUNCTIONS **************************************************/ + +static int do_dv1394_init(struct video_card *video, struct dv1394_init *init) +{ + unsigned long flags, new_buf_size; + int i; + u64 chan_mask; + int retval = -EINVAL; + + debug_printk("dv1394: initialising %d\n", video->id); + if (init->api_version != DV1394_API_VERSION) + return -EINVAL; + + /* first sanitize all the parameters */ + if ( (init->n_frames < 2) || (init->n_frames > DV1394_MAX_FRAMES) ) + return -EINVAL; + + if ( (init->format != DV1394_NTSC) && (init->format != DV1394_PAL) ) + return -EINVAL; + + if ( (init->syt_offset == 0) || (init->syt_offset > 50) ) + /* default SYT offset is 3 cycles */ + init->syt_offset = 3; + + if (init->channel > 63) + init->channel = 63; + + chan_mask = (u64)1 << init->channel; + + /* calculate what size DMA buffer is needed */ + if (init->format == DV1394_NTSC) + new_buf_size = DV1394_NTSC_FRAME_SIZE * init->n_frames; + else + new_buf_size = DV1394_PAL_FRAME_SIZE * init->n_frames; + + /* round up to PAGE_SIZE */ + if (new_buf_size % PAGE_SIZE) new_buf_size += PAGE_SIZE - (new_buf_size % PAGE_SIZE); + + /* don't allow the user to allocate the DMA buffer more than once */ + if (video->dv_buf.kvirt && video->dv_buf_size != new_buf_size) { + printk("dv1394: re-sizing the DMA buffer is not allowed\n"); + return -EINVAL; + } + + /* shutdown the card if it's currently active */ + /* (the card should not be reset if the parameters are screwy) */ + + do_dv1394_shutdown(video, 0); + + /* try to claim the ISO channel */ + spin_lock_irqsave(&video->ohci->IR_channel_lock, flags); + if (video->ohci->ISO_channel_usage & chan_mask) { + spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags); + retval = -EBUSY; + goto err; + } + video->ohci->ISO_channel_usage |= chan_mask; + spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags); + + video->channel = init->channel; + + /* initialize misc. fields of video */ + video->n_frames = init->n_frames; + video->pal_or_ntsc = init->format; + + video->cip_accum = 0; + video->continuity_counter = 0; + + video->active_frame = -1; + video->first_clear_frame = 0; + video->n_clear_frames = video->n_frames; + video->dropped_frames = 0; + + video->write_off = 0; + + video->first_run = 1; + video->current_packet = -1; + video->first_frame = 0; + + if (video->pal_or_ntsc == DV1394_NTSC) { + video->cip_n = init->cip_n != 0 ? init->cip_n : CIP_N_NTSC; + video->cip_d = init->cip_d != 0 ? init->cip_d : CIP_D_NTSC; + video->frame_size = DV1394_NTSC_FRAME_SIZE; + } else { + video->cip_n = init->cip_n != 0 ? init->cip_n : CIP_N_PAL; + video->cip_d = init->cip_d != 0 ? init->cip_d : CIP_D_PAL; + video->frame_size = DV1394_PAL_FRAME_SIZE; + } + + video->syt_offset = init->syt_offset; + + /* find and claim DMA contexts on the OHCI card */ + + if (video->ohci_it_ctx == -1) { + ohci1394_init_iso_tasklet(&video->it_tasklet, OHCI_ISO_TRANSMIT, + it_tasklet_func, (unsigned long) video); + + if (ohci1394_register_iso_tasklet(video->ohci, &video->it_tasklet) < 0) { + printk(KERN_ERR "dv1394: could not find an available IT DMA context\n"); + retval = -EBUSY; + goto err; + } + + video->ohci_it_ctx = video->it_tasklet.context; + debug_printk("dv1394: claimed IT DMA context %d\n", video->ohci_it_ctx); + } + + if (video->ohci_ir_ctx == -1) { + ohci1394_init_iso_tasklet(&video->ir_tasklet, OHCI_ISO_RECEIVE, + ir_tasklet_func, (unsigned long) video); + + if (ohci1394_register_iso_tasklet(video->ohci, &video->ir_tasklet) < 0) { + printk(KERN_ERR "dv1394: could not find an available IR DMA context\n"); + retval = -EBUSY; + goto err; + } + video->ohci_ir_ctx = video->ir_tasklet.context; + debug_printk("dv1394: claimed IR DMA context %d\n", video->ohci_ir_ctx); + } + + /* allocate struct frames */ + for (i = 0; i < init->n_frames; i++) { + video->frames[i] = frame_new(i, video); + + if (!video->frames[i]) { + printk(KERN_ERR "dv1394: Cannot allocate frame structs\n"); + retval = -ENOMEM; + goto err; + } + } + + if (!video->dv_buf.kvirt) { + /* allocate the ringbuffer */ + retval = dma_region_alloc(&video->dv_buf, new_buf_size, video->ohci->dev, PCI_DMA_TODEVICE); + if (retval) + goto err; + + video->dv_buf_size = new_buf_size; + + debug_printk("dv1394: Allocated %d frame buffers, total %u pages (%u DMA pages), %lu bytes\n", + video->n_frames, video->dv_buf.n_pages, + video->dv_buf.n_dma_pages, video->dv_buf_size); + } + + /* set up the frame->data pointers */ + for (i = 0; i < video->n_frames; i++) + video->frames[i]->data = (unsigned long) video->dv_buf.kvirt + i * video->frame_size; + + if (!video->packet_buf.kvirt) { + /* allocate packet buffer */ + video->packet_buf_size = sizeof(struct packet) * video->n_frames * MAX_PACKETS; + if (video->packet_buf_size % PAGE_SIZE) + video->packet_buf_size += PAGE_SIZE - (video->packet_buf_size % PAGE_SIZE); + + retval = dma_region_alloc(&video->packet_buf, video->packet_buf_size, + video->ohci->dev, PCI_DMA_FROMDEVICE); + if (retval) + goto err; + + debug_printk("dv1394: Allocated %d packets in buffer, total %u pages (%u DMA pages), %lu bytes\n", + video->n_frames*MAX_PACKETS, video->packet_buf.n_pages, + video->packet_buf.n_dma_pages, video->packet_buf_size); + } + + /* set up register offsets for IT context */ + /* IT DMA context registers are spaced 16 bytes apart */ + video->ohci_IsoXmitContextControlSet = OHCI1394_IsoXmitContextControlSet+16*video->ohci_it_ctx; + video->ohci_IsoXmitContextControlClear = OHCI1394_IsoXmitContextControlClear+16*video->ohci_it_ctx; + video->ohci_IsoXmitCommandPtr = OHCI1394_IsoXmitCommandPtr+16*video->ohci_it_ctx; + + /* enable interrupts for IT context */ + reg_write(video->ohci, OHCI1394_IsoXmitIntMaskSet, (1 << video->ohci_it_ctx)); + debug_printk("dv1394: interrupts enabled for IT context %d\n", video->ohci_it_ctx); + + /* set up register offsets for IR context */ + /* IR DMA context registers are spaced 32 bytes apart */ + video->ohci_IsoRcvContextControlSet = OHCI1394_IsoRcvContextControlSet+32*video->ohci_ir_ctx; + video->ohci_IsoRcvContextControlClear = OHCI1394_IsoRcvContextControlClear+32*video->ohci_ir_ctx; + video->ohci_IsoRcvCommandPtr = OHCI1394_IsoRcvCommandPtr+32*video->ohci_ir_ctx; + video->ohci_IsoRcvContextMatch = OHCI1394_IsoRcvContextMatch+32*video->ohci_ir_ctx; + + /* enable interrupts for IR context */ + reg_write(video->ohci, OHCI1394_IsoRecvIntMaskSet, (1 << video->ohci_ir_ctx) ); + debug_printk("dv1394: interrupts enabled for IR context %d\n", video->ohci_ir_ctx); + + return 0; + +err: + do_dv1394_shutdown(video, 1); + return retval; +} + +/* if the user doesn't bother to call ioctl(INIT) before starting + mmap() or read()/write(), just give him some default values */ + +static int do_dv1394_init_default(struct video_card *video) +{ + struct dv1394_init init; + + init.api_version = DV1394_API_VERSION; + init.n_frames = DV1394_MAX_FRAMES / 4; + init.channel = video->channel; + init.format = video->pal_or_ntsc; + init.cip_n = video->cip_n; + init.cip_d = video->cip_d; + init.syt_offset = video->syt_offset; + + return do_dv1394_init(video, &init); +} + +/* do NOT call from interrupt context */ +static void stop_dma(struct video_card *video) +{ + unsigned long flags; + int i; + + /* no interrupts */ + spin_lock_irqsave(&video->spinlock, flags); + + video->dma_running = 0; + + if ( (video->ohci_it_ctx == -1) && (video->ohci_ir_ctx == -1) ) + goto out; + + /* stop DMA if in progress */ + if ( (video->active_frame != -1) || + (reg_read(video->ohci, video->ohci_IsoXmitContextControlClear) & (1 << 10)) || + (reg_read(video->ohci, video->ohci_IsoRcvContextControlClear) & (1 << 10)) ) { + + /* clear the .run bits */ + reg_write(video->ohci, video->ohci_IsoXmitContextControlClear, (1 << 15)); + reg_write(video->ohci, video->ohci_IsoRcvContextControlClear, (1 << 15)); + flush_pci_write(video->ohci); + + video->active_frame = -1; + video->first_run = 1; + + /* wait until DMA really stops */ + i = 0; + while (i < 1000) { + + /* wait 0.1 millisecond */ + udelay(100); + + if ( (reg_read(video->ohci, video->ohci_IsoXmitContextControlClear) & (1 << 10)) || + (reg_read(video->ohci, video->ohci_IsoRcvContextControlClear) & (1 << 10)) ) { + /* still active */ + debug_printk("dv1394: stop_dma: DMA not stopped yet\n" ); + mb(); + } else { + debug_printk("dv1394: stop_dma: DMA stopped safely after %d ms\n", i/10); + break; + } + + i++; + } + + if (i == 1000) { + printk(KERN_ERR "dv1394: stop_dma: DMA still going after %d ms!\n", i/10); + } + } + else + debug_printk("dv1394: stop_dma: already stopped.\n"); + +out: + spin_unlock_irqrestore(&video->spinlock, flags); +} + + + +static void do_dv1394_shutdown(struct video_card *video, int free_dv_buf) +{ + int i; + + debug_printk("dv1394: shutdown...\n"); + + /* stop DMA if in progress */ + stop_dma(video); + + /* release the DMA contexts */ + if (video->ohci_it_ctx != -1) { + video->ohci_IsoXmitContextControlSet = 0; + video->ohci_IsoXmitContextControlClear = 0; + video->ohci_IsoXmitCommandPtr = 0; + + /* disable interrupts for IT context */ + reg_write(video->ohci, OHCI1394_IsoXmitIntMaskClear, (1 << video->ohci_it_ctx)); + + /* remove tasklet */ + ohci1394_unregister_iso_tasklet(video->ohci, &video->it_tasklet); + debug_printk("dv1394: IT context %d released\n", video->ohci_it_ctx); + video->ohci_it_ctx = -1; + } + + if (video->ohci_ir_ctx != -1) { + video->ohci_IsoRcvContextControlSet = 0; + video->ohci_IsoRcvContextControlClear = 0; + video->ohci_IsoRcvCommandPtr = 0; + video->ohci_IsoRcvContextMatch = 0; + + /* disable interrupts for IR context */ + reg_write(video->ohci, OHCI1394_IsoRecvIntMaskClear, (1 << video->ohci_ir_ctx)); + + /* remove tasklet */ + ohci1394_unregister_iso_tasklet(video->ohci, &video->ir_tasklet); + debug_printk("dv1394: IR context %d released\n", video->ohci_ir_ctx); + video->ohci_ir_ctx = -1; + } + + /* release the ISO channel */ + if (video->channel != -1) { + u64 chan_mask; + unsigned long flags; + + chan_mask = (u64)1 << video->channel; + + spin_lock_irqsave(&video->ohci->IR_channel_lock, flags); + video->ohci->ISO_channel_usage &= ~(chan_mask); + spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags); + + video->channel = -1; + } + + /* free the frame structs */ + for (i = 0; i < DV1394_MAX_FRAMES; i++) { + if (video->frames[i]) + frame_delete(video->frames[i]); + video->frames[i] = NULL; + } + + video->n_frames = 0; + + /* we can't free the DMA buffer unless it is guaranteed that + no more user-space mappings exist */ + + if (free_dv_buf) { + dma_region_free(&video->dv_buf); + video->dv_buf_size = 0; + } + + /* free packet buffer */ + dma_region_free(&video->packet_buf); + video->packet_buf_size = 0; + + debug_printk("dv1394: shutdown OK\n"); +} + +/* + ********************************** + *** MMAP() THEORY OF OPERATION *** + ********************************** + + The ringbuffer cannot be re-allocated or freed while + a user program maintains a mapping of it. (note that a mapping + can persist even after the device fd is closed!) + + So, only let the user process allocate the DMA buffer once. + To resize or deallocate it, you must close the device file + and open it again. + + Previously Dan M. hacked out a scheme that allowed the DMA + buffer to change by forcefully unmapping it from the user's + address space. It was prone to error because it's very hard to + track all the places the buffer could have been mapped (we + would have had to walk the vma list of every process in the + system to be sure we found all the mappings!). Instead, we + force the user to choose one buffer size and stick with + it. This small sacrifice is worth the huge reduction in + error-prone code in dv1394. +*/ + +static int dv1394_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_card *video = file_to_video_card(file); + int retval = -EINVAL; + + /* + * We cannot use the blocking variant mutex_lock here because .mmap + * is called with mmap_sem held, while .ioctl, .read, .write acquire + * video->mtx and subsequently call copy_to/from_user which will + * grab mmap_sem in case of a page fault. + */ + if (!mutex_trylock(&video->mtx)) + return -EAGAIN; + + if ( ! video_card_initialized(video) ) { + retval = do_dv1394_init_default(video); + if (retval) + goto out; + } + + retval = dma_region_mmap(&video->dv_buf, file, vma); +out: + mutex_unlock(&video->mtx); + return retval; +} + +/*** DEVICE FILE INTERFACE *************************************************/ + +/* no need to serialize, multiple threads OK */ +static unsigned int dv1394_poll(struct file *file, struct poll_table_struct *wait) +{ + struct video_card *video = file_to_video_card(file); + unsigned int mask = 0; + unsigned long flags; + + poll_wait(file, &video->waitq, wait); + + spin_lock_irqsave(&video->spinlock, flags); + if ( video->n_frames == 0 ) { + + } else if ( video->active_frame == -1 ) { + /* nothing going on */ + mask |= POLLOUT; + } else { + /* any clear/ready buffers? */ + if (video->n_clear_frames >0) + mask |= POLLOUT | POLLIN; + } + spin_unlock_irqrestore(&video->spinlock, flags); + + return mask; +} + +static int dv1394_fasync(int fd, struct file *file, int on) +{ + /* I just copied this code verbatim from Alan Cox's mouse driver example + (Documentation/DocBook/) */ + + struct video_card *video = file_to_video_card(file); + + return fasync_helper(fd, file, on, &video->fasync); +} + +static ssize_t dv1394_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) +{ + struct video_card *video = file_to_video_card(file); + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + size_t cnt; + unsigned long flags; + int target_frame; + + /* serialize this to prevent multi-threaded mayhem */ + if (file->f_flags & O_NONBLOCK) { + if (!mutex_trylock(&video->mtx)) + return -EAGAIN; + } else { + if (mutex_lock_interruptible(&video->mtx)) + return -ERESTARTSYS; + } + + if ( !video_card_initialized(video) ) { + ret = do_dv1394_init_default(video); + if (ret) { + mutex_unlock(&video->mtx); + return ret; + } + } + + ret = 0; + add_wait_queue(&video->waitq, &wait); + + while (count > 0) { + + /* must set TASK_INTERRUPTIBLE *before* checking for free + buffers; otherwise we could miss a wakeup if the interrupt + fires between the check and the schedule() */ + + set_current_state(TASK_INTERRUPTIBLE); + + spin_lock_irqsave(&video->spinlock, flags); + + target_frame = video->first_clear_frame; + + spin_unlock_irqrestore(&video->spinlock, flags); + + if (video->frames[target_frame]->state == FRAME_CLEAR) { + + /* how much room is left in the target frame buffer */ + cnt = video->frame_size - (video->write_off - target_frame * video->frame_size); + + } else { + /* buffer is already used */ + cnt = 0; + } + + if (cnt > count) + cnt = count; + + if (cnt <= 0) { + /* no room left, gotta wait */ + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + + schedule(); + + continue; /* start over from 'while(count > 0)...' */ + } + + if (copy_from_user(video->dv_buf.kvirt + video->write_off, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + + video->write_off = (video->write_off + cnt) % (video->n_frames * video->frame_size); + + count -= cnt; + buffer += cnt; + ret += cnt; + + if (video->write_off == video->frame_size * ((target_frame + 1) % video->n_frames)) + frame_prepare(video, target_frame); + } + + remove_wait_queue(&video->waitq, &wait); + set_current_state(TASK_RUNNING); + mutex_unlock(&video->mtx); + return ret; +} + + +static ssize_t dv1394_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) +{ + struct video_card *video = file_to_video_card(file); + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + size_t cnt; + unsigned long flags; + int target_frame; + + /* serialize this to prevent multi-threaded mayhem */ + if (file->f_flags & O_NONBLOCK) { + if (!mutex_trylock(&video->mtx)) + return -EAGAIN; + } else { + if (mutex_lock_interruptible(&video->mtx)) + return -ERESTARTSYS; + } + + if ( !video_card_initialized(video) ) { + ret = do_dv1394_init_default(video); + if (ret) { + mutex_unlock(&video->mtx); + return ret; + } + video->continuity_counter = -1; + + receive_packets(video); + + start_dma_receive(video); + } + + ret = 0; + add_wait_queue(&video->waitq, &wait); + + while (count > 0) { + + /* must set TASK_INTERRUPTIBLE *before* checking for free + buffers; otherwise we could miss a wakeup if the interrupt + fires between the check and the schedule() */ + + set_current_state(TASK_INTERRUPTIBLE); + + spin_lock_irqsave(&video->spinlock, flags); + + target_frame = video->first_clear_frame; + + spin_unlock_irqrestore(&video->spinlock, flags); + + if (target_frame >= 0 && + video->n_clear_frames > 0 && + video->frames[target_frame]->state == FRAME_CLEAR) { + + /* how much room is left in the target frame buffer */ + cnt = video->frame_size - (video->write_off - target_frame * video->frame_size); + + } else { + /* buffer is already used */ + cnt = 0; + } + + if (cnt > count) + cnt = count; + + if (cnt <= 0) { + /* no room left, gotta wait */ + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + + schedule(); + + continue; /* start over from 'while(count > 0)...' */ + } + + if (copy_to_user(buffer, video->dv_buf.kvirt + video->write_off, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + + video->write_off = (video->write_off + cnt) % (video->n_frames * video->frame_size); + + count -= cnt; + buffer += cnt; + ret += cnt; + + if (video->write_off == video->frame_size * ((target_frame + 1) % video->n_frames)) { + spin_lock_irqsave(&video->spinlock, flags); + video->n_clear_frames--; + video->first_clear_frame = (video->first_clear_frame + 1) % video->n_frames; + spin_unlock_irqrestore(&video->spinlock, flags); + } + } + + remove_wait_queue(&video->waitq, &wait); + set_current_state(TASK_RUNNING); + mutex_unlock(&video->mtx); + return ret; +} + + +/*** DEVICE IOCTL INTERFACE ************************************************/ + +static long dv1394_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct video_card *video = file_to_video_card(file); + unsigned long flags; + int ret = -EINVAL; + void __user *argp = (void __user *)arg; + + DECLARE_WAITQUEUE(wait, current); + + /* serialize this to prevent multi-threaded mayhem */ + if (file->f_flags & O_NONBLOCK) { + if (!mutex_trylock(&video->mtx)) + return -EAGAIN; + } else { + if (mutex_lock_interruptible(&video->mtx)) + return -ERESTARTSYS; + } + + switch(cmd) + { + case DV1394_IOC_SUBMIT_FRAMES: { + unsigned int n_submit; + + if ( !video_card_initialized(video) ) { + ret = do_dv1394_init_default(video); + if (ret) + goto out; + } + + n_submit = (unsigned int) arg; + + if (n_submit > video->n_frames) { + ret = -EINVAL; + goto out; + } + + while (n_submit > 0) { + + add_wait_queue(&video->waitq, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + spin_lock_irqsave(&video->spinlock, flags); + + /* wait until video->first_clear_frame is really CLEAR */ + while (video->frames[video->first_clear_frame]->state != FRAME_CLEAR) { + + spin_unlock_irqrestore(&video->spinlock, flags); + + if (signal_pending(current)) { + remove_wait_queue(&video->waitq, &wait); + set_current_state(TASK_RUNNING); + ret = -EINTR; + goto out; + } + + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + + spin_lock_irqsave(&video->spinlock, flags); + } + spin_unlock_irqrestore(&video->spinlock, flags); + + remove_wait_queue(&video->waitq, &wait); + set_current_state(TASK_RUNNING); + + frame_prepare(video, video->first_clear_frame); + + n_submit--; + } + + ret = 0; + break; + } + + case DV1394_IOC_WAIT_FRAMES: { + unsigned int n_wait; + + if ( !video_card_initialized(video) ) { + ret = -EINVAL; + goto out; + } + + n_wait = (unsigned int) arg; + + /* since we re-run the last frame on underflow, we will + never actually have n_frames clear frames; at most only + n_frames - 1 */ + + if (n_wait > (video->n_frames-1) ) { + ret = -EINVAL; + goto out; + } + + add_wait_queue(&video->waitq, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + spin_lock_irqsave(&video->spinlock, flags); + + while (video->n_clear_frames < n_wait) { + + spin_unlock_irqrestore(&video->spinlock, flags); + + if (signal_pending(current)) { + remove_wait_queue(&video->waitq, &wait); + set_current_state(TASK_RUNNING); + ret = -EINTR; + goto out; + } + + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + + spin_lock_irqsave(&video->spinlock, flags); + } + + spin_unlock_irqrestore(&video->spinlock, flags); + + remove_wait_queue(&video->waitq, &wait); + set_current_state(TASK_RUNNING); + ret = 0; + break; + } + + case DV1394_IOC_RECEIVE_FRAMES: { + unsigned int n_recv; + + if ( !video_card_initialized(video) ) { + ret = -EINVAL; + goto out; + } + + n_recv = (unsigned int) arg; + + /* at least one frame must be active */ + if (n_recv > (video->n_frames-1) ) { + ret = -EINVAL; + goto out; + } + + spin_lock_irqsave(&video->spinlock, flags); + + /* release the clear frames */ + video->n_clear_frames -= n_recv; + + /* advance the clear frame cursor */ + video->first_clear_frame = (video->first_clear_frame + n_recv) % video->n_frames; + + /* reset dropped_frames */ + video->dropped_frames = 0; + + spin_unlock_irqrestore(&video->spinlock, flags); + + ret = 0; + break; + } + + case DV1394_IOC_START_RECEIVE: { + if ( !video_card_initialized(video) ) { + ret = do_dv1394_init_default(video); + if (ret) + goto out; + } + + video->continuity_counter = -1; + + receive_packets(video); + + start_dma_receive(video); + + ret = 0; + break; + } + + case DV1394_IOC_INIT: { + struct dv1394_init init; + if (!argp) { + ret = do_dv1394_init_default(video); + } else { + if (copy_from_user(&init, argp, sizeof(init))) { + ret = -EFAULT; + goto out; + } + ret = do_dv1394_init(video, &init); + } + break; + } + + case DV1394_IOC_SHUTDOWN: + do_dv1394_shutdown(video, 0); + ret = 0; + break; + + + case DV1394_IOC_GET_STATUS: { + struct dv1394_status status; + + if ( !video_card_initialized(video) ) { + ret = -EINVAL; + goto out; + } + + status.init.api_version = DV1394_API_VERSION; + status.init.channel = video->channel; + status.init.n_frames = video->n_frames; + status.init.format = video->pal_or_ntsc; + status.init.cip_n = video->cip_n; + status.init.cip_d = video->cip_d; + status.init.syt_offset = video->syt_offset; + + status.first_clear_frame = video->first_clear_frame; + + /* the rest of the fields need to be locked against the interrupt */ + spin_lock_irqsave(&video->spinlock, flags); + + status.active_frame = video->active_frame; + status.n_clear_frames = video->n_clear_frames; + + status.dropped_frames = video->dropped_frames; + + /* reset dropped_frames */ + video->dropped_frames = 0; + + spin_unlock_irqrestore(&video->spinlock, flags); + + if (copy_to_user(argp, &status, sizeof(status))) { + ret = -EFAULT; + goto out; + } + + ret = 0; + break; + } + + default: + break; + } + + out: + mutex_unlock(&video->mtx); + return ret; +} + +/*** DEVICE FILE INTERFACE CONTINUED ***************************************/ + +static int dv1394_open(struct inode *inode, struct file *file) +{ + struct video_card *video = NULL; + + if (file->private_data) { + video = file->private_data; + + } else { + /* look up the card by ID */ + unsigned long flags; + int idx = ieee1394_file_to_instance(file); + + spin_lock_irqsave(&dv1394_cards_lock, flags); + if (!list_empty(&dv1394_cards)) { + struct video_card *p; + list_for_each_entry(p, &dv1394_cards, list) { + if ((p->id) == idx) { + video = p; + break; + } + } + } + spin_unlock_irqrestore(&dv1394_cards_lock, flags); + + if (!video) { + debug_printk("dv1394: OHCI card %d not found", idx); + return -ENODEV; + } + + file->private_data = (void*) video; + } + +#ifndef DV1394_ALLOW_MORE_THAN_ONE_OPEN + + if ( test_and_set_bit(0, &video->open) ) { + /* video is already open by someone else */ + return -EBUSY; + } + +#endif + + printk(KERN_INFO "%s: NOTE, the dv1394 interface is unsupported " + "and will not be available in the new firewire driver stack. " + "Try libraw1394 based programs instead.\n", current->comm); + + return nonseekable_open(inode, file); +} + + +static int dv1394_release(struct inode *inode, struct file *file) +{ + struct video_card *video = file_to_video_card(file); + + /* OK to free the DMA buffer, no more mappings can exist */ + do_dv1394_shutdown(video, 1); + + /* give someone else a turn */ + clear_bit(0, &video->open); + + return 0; +} + + +/*** DEVICE DRIVER HANDLERS ************************************************/ + +static void it_tasklet_func(unsigned long data) +{ + int wake = 0; + struct video_card *video = (struct video_card*) data; + + spin_lock(&video->spinlock); + + if (!video->dma_running) + goto out; + + irq_printk("ContextControl = %08x, CommandPtr = %08x\n", + reg_read(video->ohci, video->ohci_IsoXmitContextControlSet), + reg_read(video->ohci, video->ohci_IsoXmitCommandPtr) + ); + + + if ( (video->ohci_it_ctx != -1) && + (reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & (1 << 10)) ) { + + struct frame *f; + unsigned int frame, i; + + + if (video->active_frame == -1) + frame = 0; + else + frame = video->active_frame; + + /* check all the DMA-able frames */ + for (i = 0; i < video->n_frames; i++, frame = (frame+1) % video->n_frames) { + + irq_printk("IRQ checking frame %d...", frame); + f = video->frames[frame]; + if (f->state != FRAME_READY) { + irq_printk("clear, skipping\n"); + /* we don't own this frame */ + continue; + } + + irq_printk("DMA\n"); + + /* check the frame begin semaphore to see if we can free the previous frame */ + if ( *(f->frame_begin_timestamp) ) { + int prev_frame; + struct frame *prev_f; + + + + /* don't reset, need this later *(f->frame_begin_timestamp) = 0; */ + irq_printk(" BEGIN\n"); + + prev_frame = frame - 1; + if (prev_frame == -1) + prev_frame += video->n_frames; + prev_f = video->frames[prev_frame]; + + /* make sure we can actually garbage collect + this frame */ + if ( (prev_f->state == FRAME_READY) && + prev_f->done && (!f->done) ) + { + frame_reset(prev_f); + video->n_clear_frames++; + wake = 1; + video->active_frame = frame; + + irq_printk(" BEGIN - freeing previous frame %d, new active frame is %d\n", prev_frame, frame); + } else { + irq_printk(" BEGIN - can't free yet\n"); + } + + f->done = 1; + } + + + /* see if we need to set the timestamp for the next frame */ + if ( *(f->mid_frame_timestamp) ) { + struct frame *next_frame; + u32 begin_ts, ts_cyc, ts_off; + + *(f->mid_frame_timestamp) = 0; + + begin_ts = le32_to_cpu(*(f->frame_begin_timestamp)); + + irq_printk(" MIDDLE - first packet was sent at cycle %4u (%2u), assigned timestamp was (%2u) %4u\n", + begin_ts & 0x1FFF, begin_ts & 0xF, + f->assigned_timestamp >> 12, f->assigned_timestamp & 0xFFF); + + /* prepare next frame and assign timestamp */ + next_frame = video->frames[ (frame+1) % video->n_frames ]; + + if (next_frame->state == FRAME_READY) { + irq_printk(" MIDDLE - next frame is ready, good\n"); + } else { + debug_printk("dv1394: Underflow! At least one frame has been dropped.\n"); + next_frame = f; + } + + /* set the timestamp to the timestamp of the last frame sent, + plus the length of the last frame sent, plus the syt latency */ + ts_cyc = begin_ts & 0xF; + /* advance one frame, plus syt latency (typically 2-3) */ + ts_cyc += f->n_packets + video->syt_offset ; + + ts_off = 0; + + ts_cyc += ts_off/3072; + ts_off %= 3072; + + next_frame->assigned_timestamp = ((ts_cyc&0xF) << 12) + ts_off; + if (next_frame->cip_syt1) { + next_frame->cip_syt1->b[6] = next_frame->assigned_timestamp >> 8; + next_frame->cip_syt1->b[7] = next_frame->assigned_timestamp & 0xFF; + } + if (next_frame->cip_syt2) { + next_frame->cip_syt2->b[6] = next_frame->assigned_timestamp >> 8; + next_frame->cip_syt2->b[7] = next_frame->assigned_timestamp & 0xFF; + } + + } + + /* see if the frame looped */ + if ( *(f->frame_end_timestamp) ) { + + *(f->frame_end_timestamp) = 0; + + debug_printk(" END - the frame looped at least once\n"); + + video->dropped_frames++; + } + + } /* for (each frame) */ + } + + if (wake) { + kill_fasync(&video->fasync, SIGIO, POLL_OUT); + + /* wake readers/writers/ioctl'ers */ + wake_up_interruptible(&video->waitq); + } + +out: + spin_unlock(&video->spinlock); +} + +static void ir_tasklet_func(unsigned long data) +{ + int wake = 0; + struct video_card *video = (struct video_card*) data; + + spin_lock(&video->spinlock); + + if (!video->dma_running) + goto out; + + if ( (video->ohci_ir_ctx != -1) && + (reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 10)) ) { + + int sof=0; /* start-of-frame flag */ + struct frame *f; + u16 packet_length; + int i, dbc=0; + struct DMA_descriptor_block *block = NULL; + u16 xferstatus; + + int next_i, prev_i; + struct DMA_descriptor_block *next = NULL; + dma_addr_t next_dma = 0; + struct DMA_descriptor_block *prev = NULL; + + /* loop over all descriptors in all frames */ + for (i = 0; i < video->n_frames*MAX_PACKETS; i++) { + struct packet *p = dma_region_i(&video->packet_buf, struct packet, video->current_packet); + + /* make sure we are seeing the latest changes to p */ + dma_region_sync_for_cpu(&video->packet_buf, + (unsigned long) p - (unsigned long) video->packet_buf.kvirt, + sizeof(struct packet)); + + packet_length = le16_to_cpu(p->data_length); + + /* get the descriptor based on packet_buffer cursor */ + f = video->frames[video->current_packet / MAX_PACKETS]; + block = &(f->descriptor_pool[video->current_packet % MAX_PACKETS]); + xferstatus = le32_to_cpu(block->u.in.il.q[3]) >> 16; + xferstatus &= 0x1F; + irq_printk("ir_tasklet_func: xferStatus/resCount [%d] = 0x%08x\n", i, le32_to_cpu(block->u.in.il.q[3]) ); + + /* get the current frame */ + f = video->frames[video->active_frame]; + + /* exclude empty packet */ + if (packet_length > 8 && xferstatus == 0x11) { + /* check for start of frame */ + /* DRD> Changed to check section type ([0]>>5==0) + and dif sequence ([1]>>4==0) */ + sof = ( (p->data[0] >> 5) == 0 && (p->data[1] >> 4) == 0); + + dbc = (int) (p->cip_h1 >> 24); + if ( video->continuity_counter != -1 && dbc > ((video->continuity_counter + 1) % 256) ) + { + printk(KERN_WARNING "dv1394: discontinuity detected, dropping all frames\n" ); + video->dropped_frames += video->n_clear_frames + 1; + video->first_frame = 0; + video->n_clear_frames = 0; + video->first_clear_frame = -1; + } + video->continuity_counter = dbc; + + if (!video->first_frame) { + if (sof) { + video->first_frame = 1; + } + + } else if (sof) { + /* close current frame */ + frame_reset(f); /* f->state = STATE_CLEAR */ + video->n_clear_frames++; + if (video->n_clear_frames > video->n_frames) { + video->dropped_frames++; + printk(KERN_WARNING "dv1394: dropped a frame during reception\n" ); + video->n_clear_frames = video->n_frames-1; + video->first_clear_frame = (video->first_clear_frame + 1) % video->n_frames; + } + if (video->first_clear_frame == -1) + video->first_clear_frame = video->active_frame; + + /* get the next frame */ + video->active_frame = (video->active_frame + 1) % video->n_frames; + f = video->frames[video->active_frame]; + irq_printk(" frame received, active_frame = %d, n_clear_frames = %d, first_clear_frame = %d\n", + video->active_frame, video->n_clear_frames, video->first_clear_frame); + } + if (video->first_frame) { + if (sof) { + /* open next frame */ + f->state = FRAME_READY; + } + + /* copy to buffer */ + if (f->n_packets > (video->frame_size / 480)) { + printk(KERN_ERR "frame buffer overflow during receive\n"); + } + + frame_put_packet(f, p); + + } /* first_frame */ + } + + /* stop, end of ready packets */ + else if (xferstatus == 0) { + break; + } + + /* reset xferStatus & resCount */ + block->u.in.il.q[3] = cpu_to_le32(512); + + /* terminate dma chain at this (next) packet */ + next_i = video->current_packet; + f = video->frames[next_i / MAX_PACKETS]; + next = &(f->descriptor_pool[next_i % MAX_PACKETS]); + next_dma = ((unsigned long) block - (unsigned long) f->descriptor_pool) + f->descriptor_pool_dma; + next->u.in.il.q[0] |= cpu_to_le32(3 << 20); /* enable interrupt */ + next->u.in.il.q[2] = cpu_to_le32(0); /* disable branch */ + + /* link previous to next */ + prev_i = (next_i == 0) ? (MAX_PACKETS * video->n_frames - 1) : (next_i - 1); + f = video->frames[prev_i / MAX_PACKETS]; + prev = &(f->descriptor_pool[prev_i % MAX_PACKETS]); + if (prev_i % (MAX_PACKETS/2)) { + prev->u.in.il.q[0] &= ~cpu_to_le32(3 << 20); /* no interrupt */ + } else { + prev->u.in.il.q[0] |= cpu_to_le32(3 << 20); /* enable interrupt */ + } + prev->u.in.il.q[2] = cpu_to_le32(next_dma | 1); /* set Z=1 */ + wmb(); + + /* wake up DMA in case it fell asleep */ + reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, (1 << 12)); + + /* advance packet_buffer cursor */ + video->current_packet = (video->current_packet + 1) % (MAX_PACKETS * video->n_frames); + + } /* for all packets */ + + wake = 1; /* why the hell not? */ + + } /* receive interrupt */ + + if (wake) { + kill_fasync(&video->fasync, SIGIO, POLL_IN); + + /* wake readers/writers/ioctl'ers */ + wake_up_interruptible(&video->waitq); + } + +out: + spin_unlock(&video->spinlock); +} + +static struct cdev dv1394_cdev; +static const struct file_operations dv1394_fops= +{ + .owner = THIS_MODULE, + .poll = dv1394_poll, + .unlocked_ioctl = dv1394_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = dv1394_compat_ioctl, +#endif + .mmap = dv1394_mmap, + .open = dv1394_open, + .write = dv1394_write, + .read = dv1394_read, + .release = dv1394_release, + .fasync = dv1394_fasync, + .llseek = no_llseek, +}; + + +/*** HOTPLUG STUFF **********************************************************/ +/* + * Export information about protocols/devices supported by this driver. + */ +#ifdef MODULE +static const struct ieee1394_device_id dv1394_id_table[] = { + { + .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff, + .version = AVC_SW_VERSION_ENTRY & 0xffffff + }, + { } +}; + +MODULE_DEVICE_TABLE(ieee1394, dv1394_id_table); +#endif /* MODULE */ + +static struct hpsb_protocol_driver dv1394_driver = { + .name = "dv1394", +}; + + +/*** IEEE1394 HPSB CALLBACKS ***********************************************/ + +static int dv1394_init(struct ti_ohci *ohci, enum pal_or_ntsc format, enum modes mode) +{ + struct video_card *video; + unsigned long flags; + int i; + + video = kzalloc(sizeof(*video), GFP_KERNEL); + if (!video) { + printk(KERN_ERR "dv1394: cannot allocate video_card\n"); + return -1; + } + + video->ohci = ohci; + /* lower 2 bits of id indicate which of four "plugs" + per host */ + video->id = ohci->host->id << 2; + if (format == DV1394_NTSC) + video->id |= mode; + else + video->id |= 2 + mode; + + video->ohci_it_ctx = -1; + video->ohci_ir_ctx = -1; + + video->ohci_IsoXmitContextControlSet = 0; + video->ohci_IsoXmitContextControlClear = 0; + video->ohci_IsoXmitCommandPtr = 0; + + video->ohci_IsoRcvContextControlSet = 0; + video->ohci_IsoRcvContextControlClear = 0; + video->ohci_IsoRcvCommandPtr = 0; + video->ohci_IsoRcvContextMatch = 0; + + video->n_frames = 0; /* flag that video is not initialized */ + video->channel = 63; /* default to broadcast channel */ + video->active_frame = -1; + + /* initialize the following */ + video->pal_or_ntsc = format; + video->cip_n = 0; /* 0 = use builtin default */ + video->cip_d = 0; + video->syt_offset = 0; + video->mode = mode; + + for (i = 0; i < DV1394_MAX_FRAMES; i++) + video->frames[i] = NULL; + + dma_region_init(&video->dv_buf); + video->dv_buf_size = 0; + dma_region_init(&video->packet_buf); + video->packet_buf_size = 0; + + clear_bit(0, &video->open); + spin_lock_init(&video->spinlock); + video->dma_running = 0; + mutex_init(&video->mtx); + init_waitqueue_head(&video->waitq); + video->fasync = NULL; + + spin_lock_irqsave(&dv1394_cards_lock, flags); + INIT_LIST_HEAD(&video->list); + list_add_tail(&video->list, &dv1394_cards); + spin_unlock_irqrestore(&dv1394_cards_lock, flags); + + debug_printk("dv1394: dv1394_init() OK on ID %d\n", video->id); + return 0; +} + +static void dv1394_remove_host(struct hpsb_host *host) +{ + struct video_card *video, *tmp_video; + unsigned long flags; + int found_ohci_card = 0; + + do { + video = NULL; + spin_lock_irqsave(&dv1394_cards_lock, flags); + list_for_each_entry(tmp_video, &dv1394_cards, list) { + if ((tmp_video->id >> 2) == host->id) { + list_del(&tmp_video->list); + video = tmp_video; + found_ohci_card = 1; + break; + } + } + spin_unlock_irqrestore(&dv1394_cards_lock, flags); + + if (video) { + do_dv1394_shutdown(video, 1); + kfree(video); + } + } while (video); + + if (found_ohci_card) + device_destroy(hpsb_protocol_class, MKDEV(IEEE1394_MAJOR, + IEEE1394_MINOR_BLOCK_DV1394 * 16 + (host->id << 2))); +} + +static void dv1394_add_host(struct hpsb_host *host) +{ + struct ti_ohci *ohci; + int id = host->id; + + /* We only work with the OHCI-1394 driver */ + if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME)) + return; + + ohci = (struct ti_ohci *)host->hostdata; + + device_create(hpsb_protocol_class, NULL, + MKDEV(IEEE1394_MAJOR, + IEEE1394_MINOR_BLOCK_DV1394 * 16 + (id<<2)), + NULL, "dv1394-%d", id); + + dv1394_init(ohci, DV1394_NTSC, MODE_RECEIVE); + dv1394_init(ohci, DV1394_NTSC, MODE_TRANSMIT); + dv1394_init(ohci, DV1394_PAL, MODE_RECEIVE); + dv1394_init(ohci, DV1394_PAL, MODE_TRANSMIT); +} + + +/* Bus reset handler. In the event of a bus reset, we may need to + re-start the DMA contexts - otherwise the user program would + end up waiting forever. +*/ + +static void dv1394_host_reset(struct hpsb_host *host) +{ + struct video_card *video = NULL, *tmp_vid; + unsigned long flags; + + /* We only work with the OHCI-1394 driver */ + if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME)) + return; + + /* find the corresponding video_cards */ + spin_lock_irqsave(&dv1394_cards_lock, flags); + list_for_each_entry(tmp_vid, &dv1394_cards, list) { + if ((tmp_vid->id >> 2) == host->id) { + video = tmp_vid; + break; + } + } + spin_unlock_irqrestore(&dv1394_cards_lock, flags); + + if (!video) + return; + + + spin_lock_irqsave(&video->spinlock, flags); + + if (!video->dma_running) + goto out; + + /* check IT context */ + if (video->ohci_it_ctx != -1) { + u32 ctx; + + ctx = reg_read(video->ohci, video->ohci_IsoXmitContextControlSet); + + /* if (RUN but not ACTIVE) */ + if ( (ctx & (1<<15)) && + !(ctx & (1<<10)) ) { + + debug_printk("dv1394: IT context stopped due to bus reset; waking it up\n"); + + /* to be safe, assume a frame has been dropped. User-space programs + should handle this condition like an underflow. */ + video->dropped_frames++; + + /* for some reason you must clear, then re-set the RUN bit to restart DMA */ + + /* clear RUN */ + reg_write(video->ohci, video->ohci_IsoXmitContextControlClear, (1 << 15)); + flush_pci_write(video->ohci); + + /* set RUN */ + reg_write(video->ohci, video->ohci_IsoXmitContextControlSet, (1 << 15)); + flush_pci_write(video->ohci); + + /* set the WAKE bit (just in case; this isn't strictly necessary) */ + reg_write(video->ohci, video->ohci_IsoXmitContextControlSet, (1 << 12)); + flush_pci_write(video->ohci); + + irq_printk("dv1394: AFTER IT restart ctx 0x%08x ptr 0x%08x\n", + reg_read(video->ohci, video->ohci_IsoXmitContextControlSet), + reg_read(video->ohci, video->ohci_IsoXmitCommandPtr)); + } + } + + /* check IR context */ + if (video->ohci_ir_ctx != -1) { + u32 ctx; + + ctx = reg_read(video->ohci, video->ohci_IsoRcvContextControlSet); + + /* if (RUN but not ACTIVE) */ + if ( (ctx & (1<<15)) && + !(ctx & (1<<10)) ) { + + debug_printk("dv1394: IR context stopped due to bus reset; waking it up\n"); + + /* to be safe, assume a frame has been dropped. User-space programs + should handle this condition like an overflow. */ + video->dropped_frames++; + + /* for some reason you must clear, then re-set the RUN bit to restart DMA */ + /* XXX this doesn't work for me, I can't get IR DMA to restart :[ */ + + /* clear RUN */ + reg_write(video->ohci, video->ohci_IsoRcvContextControlClear, (1 << 15)); + flush_pci_write(video->ohci); + + /* set RUN */ + reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, (1 << 15)); + flush_pci_write(video->ohci); + + /* set the WAKE bit (just in case; this isn't strictly necessary) */ + reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, (1 << 12)); + flush_pci_write(video->ohci); + + irq_printk("dv1394: AFTER IR restart ctx 0x%08x ptr 0x%08x\n", + reg_read(video->ohci, video->ohci_IsoRcvContextControlSet), + reg_read(video->ohci, video->ohci_IsoRcvCommandPtr)); + } + } + +out: + spin_unlock_irqrestore(&video->spinlock, flags); + + /* wake readers/writers/ioctl'ers */ + wake_up_interruptible(&video->waitq); +} + +static struct hpsb_highlevel dv1394_highlevel = { + .name = "dv1394", + .add_host = dv1394_add_host, + .remove_host = dv1394_remove_host, + .host_reset = dv1394_host_reset, +}; + +#ifdef CONFIG_COMPAT + +#define DV1394_IOC32_INIT _IOW('#', 0x06, struct dv1394_init32) +#define DV1394_IOC32_GET_STATUS _IOR('#', 0x0c, struct dv1394_status32) + +struct dv1394_init32 { + u32 api_version; + u32 channel; + u32 n_frames; + u32 format; + u32 cip_n; + u32 cip_d; + u32 syt_offset; +}; + +struct dv1394_status32 { + struct dv1394_init32 init; + s32 active_frame; + u32 first_clear_frame; + u32 n_clear_frames; + u32 dropped_frames; +}; + +/* RED-PEN: this should use compat_alloc_userspace instead */ + +static int handle_dv1394_init(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct dv1394_init32 dv32; + struct dv1394_init dv; + mm_segment_t old_fs; + int ret; + + if (file->f_op->unlocked_ioctl != dv1394_ioctl) + return -EFAULT; + + if (copy_from_user(&dv32, (void __user *)arg, sizeof(dv32))) + return -EFAULT; + + dv.api_version = dv32.api_version; + dv.channel = dv32.channel; + dv.n_frames = dv32.n_frames; + dv.format = dv32.format; + dv.cip_n = (unsigned long)dv32.cip_n; + dv.cip_d = (unsigned long)dv32.cip_d; + dv.syt_offset = dv32.syt_offset; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = dv1394_ioctl(file, DV1394_IOC_INIT, (unsigned long)&dv); + set_fs(old_fs); + + return ret; +} + +static int handle_dv1394_get_status(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct dv1394_status32 dv32; + struct dv1394_status dv; + mm_segment_t old_fs; + int ret; + + if (file->f_op->unlocked_ioctl != dv1394_ioctl) + return -EFAULT; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = dv1394_ioctl(file, DV1394_IOC_GET_STATUS, (unsigned long)&dv); + set_fs(old_fs); + + if (!ret) { + dv32.init.api_version = dv.init.api_version; + dv32.init.channel = dv.init.channel; + dv32.init.n_frames = dv.init.n_frames; + dv32.init.format = dv.init.format; + dv32.init.cip_n = (u32)dv.init.cip_n; + dv32.init.cip_d = (u32)dv.init.cip_d; + dv32.init.syt_offset = dv.init.syt_offset; + dv32.active_frame = dv.active_frame; + dv32.first_clear_frame = dv.first_clear_frame; + dv32.n_clear_frames = dv.n_clear_frames; + dv32.dropped_frames = dv.dropped_frames; + + if (copy_to_user((struct dv1394_status32 __user *)arg, &dv32, sizeof(dv32))) + ret = -EFAULT; + } + + return ret; +} + + + +static long dv1394_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + switch (cmd) { + case DV1394_IOC_SHUTDOWN: + case DV1394_IOC_SUBMIT_FRAMES: + case DV1394_IOC_WAIT_FRAMES: + case DV1394_IOC_RECEIVE_FRAMES: + case DV1394_IOC_START_RECEIVE: + return dv1394_ioctl(file, cmd, arg); + + case DV1394_IOC32_INIT: + return handle_dv1394_init(file, cmd, arg); + case DV1394_IOC32_GET_STATUS: + return handle_dv1394_get_status(file, cmd, arg); + default: + return -ENOIOCTLCMD; + } +} + +#endif /* CONFIG_COMPAT */ + + +/*** KERNEL MODULE HANDLERS ************************************************/ + +MODULE_AUTHOR("Dan Maas , Dan Dennedy "); +MODULE_DESCRIPTION("driver for DV input/output on OHCI board"); +MODULE_SUPPORTED_DEVICE("dv1394"); +MODULE_LICENSE("GPL"); + +static void __exit dv1394_exit_module(void) +{ + hpsb_unregister_protocol(&dv1394_driver); + hpsb_unregister_highlevel(&dv1394_highlevel); + cdev_del(&dv1394_cdev); +} + +static int __init dv1394_init_module(void) +{ + int ret; + + cdev_init(&dv1394_cdev, &dv1394_fops); + dv1394_cdev.owner = THIS_MODULE; + ret = cdev_add(&dv1394_cdev, IEEE1394_DV1394_DEV, 16); + if (ret) { + printk(KERN_ERR "dv1394: unable to register character device\n"); + return ret; + } + + hpsb_register_highlevel(&dv1394_highlevel); + + ret = hpsb_register_protocol(&dv1394_driver); + if (ret) { + printk(KERN_ERR "dv1394: failed to register protocol\n"); + hpsb_unregister_highlevel(&dv1394_highlevel); + cdev_del(&dv1394_cdev); + return ret; + } + + return 0; +} + +module_init(dv1394_init_module); +module_exit(dv1394_exit_module); diff --git a/trunk/drivers/ieee1394/dv1394.h b/trunk/drivers/ieee1394/dv1394.h new file mode 100644 index 000000000000..5807f5289810 --- /dev/null +++ b/trunk/drivers/ieee1394/dv1394.h @@ -0,0 +1,305 @@ +/* + * dv1394.h - DV input/output over IEEE 1394 on OHCI chips + * Copyright (C)2001 Daniel Maas + * receive by Dan Dennedy + * + * based on: + * video1394.h - driver for OHCI 1394 boards + * Copyright (C)1999,2000 Sebastien Rougeaux + * Peter Schlaile + * + * 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. + */ + +#ifndef _DV_1394_H +#define _DV_1394_H + +/* This is the public user-space interface. Try not to break it. */ + +#define DV1394_API_VERSION 0x20011127 + +/* ******************** + ** ** + ** DV1394 API ** + ** ** + ******************** + + There are two methods of operating the DV1394 DV output device. + + 1) + + The simplest is an interface based on write(): simply write + full DV frames of data to the device, and they will be transmitted + as quickly as possible. The FD may be set for non-blocking I/O, + in which case you can use select() or poll() to wait for output + buffer space. + + To set the DV output parameters (e.g. whether you want NTSC or PAL + video), use the DV1394_INIT ioctl, passing in the parameters you + want in a struct dv1394_init. + + Example 1: + To play a raw .DV file: cat foo.DV > /dev/dv1394 + (cat will use write() internally) + + Example 2: + static struct dv1394_init init = { + 0x63, (broadcast channel) + 4, (four-frame ringbuffer) + DV1394_NTSC, (send NTSC video) + 0, 0 (default empty packet rate) + } + + ioctl(fd, DV1394_INIT, &init); + + while (1) { + read( , buf, DV1394_NTSC_FRAME_SIZE ); + write( , buf, DV1394_NTSC_FRAME_SIZE ); + } + + 2) + + For more control over buffering, and to avoid unnecessary copies + of the DV data, you can use the more sophisticated the mmap() interface. + First, call the DV1394_INIT ioctl to specify your parameters, + including the number of frames in the ringbuffer. Then, calling mmap() + on the dv1394 device will give you direct access to the ringbuffer + from which the DV card reads your frame data. + + The ringbuffer is simply one large, contiguous region of memory + containing two or more frames of packed DV data. Each frame of DV data + is 120000 bytes (NTSC) or 144000 bytes (PAL). + + Fill one or more frames in the ringbuffer, then use the DV1394_SUBMIT_FRAMES + ioctl to begin I/O. You can use either the DV1394_WAIT_FRAMES ioctl + or select()/poll() to wait until the frames are transmitted. Next, you'll + need to call the DV1394_GET_STATUS ioctl to determine which ringbuffer + frames are clear (ready to be filled with new DV data). Finally, use + DV1394_SUBMIT_FRAMES again to send the new data to the DV output. + + + Example: here is what a four-frame ringbuffer might look like + during DV transmission: + + + frame 0 frame 1 frame 2 frame 3 + + *--------------------------------------* + | CLEAR | DV data | DV data | CLEAR | + *--------------------------------------* + + + transmission goes in this direction --->>> + + + The DV hardware is currently transmitting the data in frame 1. + Once frame 1 is finished, it will automatically transmit frame 2. + (if frame 2 finishes before frame 3 is submitted, the device + will continue to transmit frame 2, and will increase the dropped_frames + counter each time it repeats the transmission). + + + If you called DV1394_GET_STATUS at this instant, you would + receive the following values: + + n_frames = 4 + active_frame = 1 + first_clear_frame = 3 + n_clear_frames = 2 + + At this point, you should write new DV data into frame 3 and optionally + frame 0. Then call DV1394_SUBMIT_FRAMES to inform the device that + it may transmit the new frames. + + ERROR HANDLING + + An error (buffer underflow/overflow or a break in the DV stream due + to a 1394 bus reset) can be detected by checking the dropped_frames + field of struct dv1394_status (obtained through the + DV1394_GET_STATUS ioctl). + + The best way to recover from such an error is to re-initialize + dv1394, either by using the DV1394_INIT ioctl call, or closing the + file descriptor and opening it again. (note that you must unmap all + ringbuffer mappings when closing the file descriptor, or else + dv1394 will still be considered 'in use'). + + MAIN LOOP + + For maximum efficiency and robustness against bus errors, you are + advised to model the main loop of your application after the + following pseudo-code example: + + (checks of system call return values omitted for brevity; always + check return values in your code!) + + while ( frames left ) { + + struct pollfd *pfd = ...; + + pfd->fd = dv1394_fd; + pfd->revents = 0; + pfd->events = POLLOUT | POLLIN; (OUT for transmit, IN for receive) + + (add other sources of I/O here) + + poll(pfd, 1, -1); (or select(); add a timeout if you want) + + if (pfd->revents) { + struct dv1394_status status; + + ioctl(dv1394_fd, DV1394_GET_STATUS, &status); + + if (status.dropped_frames > 0) { + reset_dv1394(); + } else { + for (int i = 0; i < status.n_clear_frames; i++) { + copy_DV_frame(); + } + } + } + } + + where copy_DV_frame() reads or writes on the dv1394 file descriptor + (read/write mode) or copies data to/from the mmap ringbuffer and + then calls ioctl(DV1394_SUBMIT_FRAMES) to notify dv1394 that new + frames are availble (mmap mode). + + reset_dv1394() is called in the event of a buffer + underflow/overflow or a halt in the DV stream (e.g. due to a 1394 + bus reset). To guarantee recovery from the error, this function + should close the dv1394 file descriptor (and munmap() all + ringbuffer mappings, if you are using them), then re-open the + dv1394 device (and re-map the ringbuffer). + +*/ + + +/* maximum number of frames in the ringbuffer */ +#define DV1394_MAX_FRAMES 32 + +/* number of *full* isochronous packets per DV frame */ +#define DV1394_NTSC_PACKETS_PER_FRAME 250 +#define DV1394_PAL_PACKETS_PER_FRAME 300 + +/* size of one frame's worth of DV data, in bytes */ +#define DV1394_NTSC_FRAME_SIZE (480 * DV1394_NTSC_PACKETS_PER_FRAME) +#define DV1394_PAL_FRAME_SIZE (480 * DV1394_PAL_PACKETS_PER_FRAME) + + +/* ioctl() commands */ +#include "ieee1394-ioctl.h" + + +enum pal_or_ntsc { + DV1394_NTSC = 0, + DV1394_PAL +}; + + + + +/* this is the argument to DV1394_INIT */ +struct dv1394_init { + /* DV1394_API_VERSION */ + unsigned int api_version; + + /* isochronous transmission channel to use */ + unsigned int channel; + + /* number of frames in the ringbuffer. Must be at least 2 + and at most DV1394_MAX_FRAMES. */ + unsigned int n_frames; + + /* send/receive PAL or NTSC video format */ + enum pal_or_ntsc format; + + /* the following are used only for transmission */ + + /* set these to zero unless you want a + non-default empty packet rate (see below) */ + unsigned long cip_n; + unsigned long cip_d; + + /* set this to zero unless you want a + non-default SYT cycle offset (default = 3 cycles) */ + unsigned int syt_offset; +}; + +/* NOTE: you may only allocate the DV frame ringbuffer once each time + you open the dv1394 device. DV1394_INIT will fail if you call it a + second time with different 'n_frames' or 'format' arguments (which + would imply a different size for the ringbuffer). If you need a + different buffer size, simply close and re-open the device, then + initialize it with your new settings. */ + +/* Q: What are cip_n and cip_d? */ + +/* + A: DV video streams do not utilize 100% of the potential bandwidth offered + by IEEE 1394 (FireWire). To achieve the correct rate of data transmission, + DV devices must periodically insert empty packets into the 1394 data stream. + Typically there is one empty packet per 14-16 data-carrying packets. + + Some DV devices will accept a wide range of empty packet rates, while others + require a precise rate. If the dv1394 driver produces empty packets at + a rate that your device does not accept, you may see ugly patterns on the + DV output, or even no output at all. + + The default empty packet insertion rate seems to work for many people; if + your DV output is stable, you can simply ignore this discussion. However, + we have exposed the empty packet rate as a parameter to support devices that + do not work with the default rate. + + The decision to insert an empty packet is made with a numerator/denominator + algorithm. Empty packets are produced at an average rate of CIP_N / CIP_D. + You can alter the empty packet rate by passing non-zero values for cip_n + and cip_d to the INIT ioctl. + + */ + + + +struct dv1394_status { + /* this embedded init struct returns the current dv1394 + parameters in use */ + struct dv1394_init init; + + /* the ringbuffer frame that is currently being + displayed. (-1 if the device is not transmitting anything) */ + int active_frame; + + /* index of the first buffer (ahead of active_frame) that + is ready to be filled with data */ + unsigned int first_clear_frame; + + /* how many buffers, including first_clear_buffer, are + ready to be filled with data */ + unsigned int n_clear_frames; + + /* how many times the DV stream has underflowed, overflowed, + or otherwise encountered an error, since the previous call + to DV1394_GET_STATUS */ + unsigned int dropped_frames; + + /* N.B. The dropped_frames counter is only a lower bound on the actual + number of dropped frames, with the special case that if dropped_frames + is zero, then it is guaranteed that NO frames have been dropped + since the last call to DV1394_GET_STATUS. + */ +}; + + +#endif /* _DV_1394_H */ diff --git a/trunk/drivers/ieee1394/eth1394.c b/trunk/drivers/ieee1394/eth1394.c new file mode 100644 index 000000000000..63403822330e --- /dev/null +++ b/trunk/drivers/ieee1394/eth1394.c @@ -0,0 +1,1720 @@ +/* + * eth1394.c -- IPv4 driver for Linux IEEE-1394 Subsystem + * + * Copyright (C) 2001-2003 Ben Collins + * 2000 Bonin Franck + * 2003 Steve Kinneberg + * + * Mainly based on work by Emanuel Pirker and Andreas E. Bombe + * + * 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 driver intends to support RFC 2734, which describes a method for + * transporting IPv4 datagrams over IEEE-1394 serial busses. + * + * TODO: + * RFC 2734 related: + * - Add MCAP. Limited Multicast exists only to 224.0.0.1 and 224.0.0.2. + * + * Non-RFC 2734 related: + * - Handle fragmented skb's coming from the networking layer. + * - Move generic GASP reception to core 1394 code + * - Convert kmalloc/kfree for link fragments to use kmem_cache_* instead + * - Stability improvements + * - Performance enhancements + * - Consider garbage collecting old partial datagrams after X amount of time + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config_roms.h" +#include "csr1212.h" +#include "eth1394.h" +#include "highlevel.h" +#include "ieee1394.h" +#include "ieee1394_core.h" +#include "ieee1394_hotplug.h" +#include "ieee1394_transactions.h" +#include "ieee1394_types.h" +#include "iso.h" +#include "nodemgr.h" + +#define ETH1394_PRINT_G(level, fmt, args...) \ + printk(level "%s: " fmt, driver_name, ## args) + +#define ETH1394_PRINT(level, dev_name, fmt, args...) \ + printk(level "%s: %s: " fmt, driver_name, dev_name, ## args) + +struct fragment_info { + struct list_head list; + int offset; + int len; +}; + +struct partial_datagram { + struct list_head list; + u16 dgl; + u16 dg_size; + __be16 ether_type; + struct sk_buff *skb; + char *pbuf; + struct list_head frag_info; +}; + +struct pdg_list { + struct list_head list; /* partial datagram list per node */ + unsigned int sz; /* partial datagram list size per node */ + spinlock_t lock; /* partial datagram lock */ +}; + +struct eth1394_host_info { + struct hpsb_host *host; + struct net_device *dev; +}; + +struct eth1394_node_ref { + struct unit_directory *ud; + struct list_head list; +}; + +struct eth1394_node_info { + u16 maxpayload; /* max payload */ + u8 sspd; /* max speed */ + u64 fifo; /* FIFO address */ + struct pdg_list pdg; /* partial RX datagram lists */ + int dgl; /* outgoing datagram label */ +}; + +static const char driver_name[] = "eth1394"; + +static struct kmem_cache *packet_task_cache; + +static struct hpsb_highlevel eth1394_highlevel; + +/* Use common.lf to determine header len */ +static const int hdr_type_len[] = { + sizeof(struct eth1394_uf_hdr), + sizeof(struct eth1394_ff_hdr), + sizeof(struct eth1394_sf_hdr), + sizeof(struct eth1394_sf_hdr) +}; + +static const u16 eth1394_speedto_maxpayload[] = { +/* S100, S200, S400, S800, S1600, S3200 */ + 512, 1024, 2048, 4096, 4096, 4096 +}; + +MODULE_AUTHOR("Ben Collins (bcollins@debian.org)"); +MODULE_DESCRIPTION("IEEE 1394 IPv4 Driver (IPv4-over-1394 as per RFC 2734)"); +MODULE_LICENSE("GPL"); + +/* + * The max_partial_datagrams parameter is the maximum number of fragmented + * datagrams per node that eth1394 will keep in memory. Providing an upper + * bound allows us to limit the amount of memory that partial datagrams + * consume in the event that some partial datagrams are never completed. + */ +static int max_partial_datagrams = 25; +module_param(max_partial_datagrams, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(max_partial_datagrams, + "Maximum number of partially received fragmented datagrams " + "(default = 25)."); + + +static int ether1394_header(struct sk_buff *skb, struct net_device *dev, + unsigned short type, const void *daddr, + const void *saddr, unsigned len); +static int ether1394_rebuild_header(struct sk_buff *skb); +static int ether1394_header_parse(const struct sk_buff *skb, + unsigned char *haddr); +static int ether1394_header_cache(const struct neighbour *neigh, + struct hh_cache *hh); +static void ether1394_header_cache_update(struct hh_cache *hh, + const struct net_device *dev, + const unsigned char *haddr); +static netdev_tx_t ether1394_tx(struct sk_buff *skb, + struct net_device *dev); +static void ether1394_iso(struct hpsb_iso *iso); + +static int ether1394_write(struct hpsb_host *host, int srcid, int destid, + quadlet_t *data, u64 addr, size_t len, u16 flags); +static void ether1394_add_host(struct hpsb_host *host); +static void ether1394_remove_host(struct hpsb_host *host); +static void ether1394_host_reset(struct hpsb_host *host); + +/* Function for incoming 1394 packets */ +static const struct hpsb_address_ops addr_ops = { + .write = ether1394_write, +}; + +/* Ieee1394 highlevel driver functions */ +static struct hpsb_highlevel eth1394_highlevel = { + .name = driver_name, + .add_host = ether1394_add_host, + .remove_host = ether1394_remove_host, + .host_reset = ether1394_host_reset, +}; + +static int ether1394_recv_init(struct eth1394_priv *priv) +{ + unsigned int iso_buf_size; + + /* FIXME: rawiso limits us to PAGE_SIZE */ + iso_buf_size = min((unsigned int)PAGE_SIZE, + 2 * (1U << (priv->host->csr.max_rec + 1))); + + priv->iso = hpsb_iso_recv_init(priv->host, + ETHER1394_GASP_BUFFERS * iso_buf_size, + ETHER1394_GASP_BUFFERS, + priv->broadcast_channel, + HPSB_ISO_DMA_PACKET_PER_BUFFER, + 1, ether1394_iso); + if (priv->iso == NULL) { + ETH1394_PRINT_G(KERN_ERR, "Failed to allocate IR context\n"); + priv->bc_state = ETHER1394_BC_ERROR; + return -EAGAIN; + } + + if (hpsb_iso_recv_start(priv->iso, -1, (1 << 3), -1) < 0) + priv->bc_state = ETHER1394_BC_STOPPED; + else + priv->bc_state = ETHER1394_BC_RUNNING; + return 0; +} + +/* This is called after an "ifup" */ +static int ether1394_open(struct net_device *dev) +{ + struct eth1394_priv *priv = netdev_priv(dev); + int ret; + + if (priv->bc_state == ETHER1394_BC_ERROR) { + ret = ether1394_recv_init(priv); + if (ret) + return ret; + } + netif_start_queue(dev); + return 0; +} + +/* This is called after an "ifdown" */ +static int ether1394_stop(struct net_device *dev) +{ + /* flush priv->wake */ + flush_scheduled_work(); + + netif_stop_queue(dev); + return 0; +} + +/* FIXME: What to do if we timeout? I think a host reset is probably in order, + * so that's what we do. Should we increment the stat counters too? */ +static void ether1394_tx_timeout(struct net_device *dev) +{ + struct hpsb_host *host = + ((struct eth1394_priv *)netdev_priv(dev))->host; + + ETH1394_PRINT(KERN_ERR, dev->name, "Timeout, resetting host\n"); + ether1394_host_reset(host); +} + +static inline int ether1394_max_mtu(struct hpsb_host* host) +{ + return (1 << (host->csr.max_rec + 1)) + - sizeof(union eth1394_hdr) - ETHER1394_GASP_OVERHEAD; +} + +static int ether1394_change_mtu(struct net_device *dev, int new_mtu) +{ + int max_mtu; + + if (new_mtu < 68) + return -EINVAL; + + max_mtu = ether1394_max_mtu( + ((struct eth1394_priv *)netdev_priv(dev))->host); + if (new_mtu > max_mtu) { + ETH1394_PRINT(KERN_INFO, dev->name, + "Local node constrains MTU to %d\n", max_mtu); + return -ERANGE; + } + + dev->mtu = new_mtu; + return 0; +} + +static void purge_partial_datagram(struct list_head *old) +{ + struct partial_datagram *pd; + struct list_head *lh, *n; + struct fragment_info *fi; + + pd = list_entry(old, struct partial_datagram, list); + + list_for_each_safe(lh, n, &pd->frag_info) { + fi = list_entry(lh, struct fragment_info, list); + list_del(lh); + kfree(fi); + } + list_del(old); + kfree_skb(pd->skb); + kfree(pd); +} + +/****************************************** + * 1394 bus activity functions + ******************************************/ + +static struct eth1394_node_ref *eth1394_find_node(struct list_head *inl, + struct unit_directory *ud) +{ + struct eth1394_node_ref *node; + + list_for_each_entry(node, inl, list) + if (node->ud == ud) + return node; + + return NULL; +} + +static struct eth1394_node_ref *eth1394_find_node_guid(struct list_head *inl, + u64 guid) +{ + struct eth1394_node_ref *node; + + list_for_each_entry(node, inl, list) + if (node->ud->ne->guid == guid) + return node; + + return NULL; +} + +static struct eth1394_node_ref *eth1394_find_node_nodeid(struct list_head *inl, + nodeid_t nodeid) +{ + struct eth1394_node_ref *node; + + list_for_each_entry(node, inl, list) + if (node->ud->ne->nodeid == nodeid) + return node; + + return NULL; +} + +static int eth1394_new_node(struct eth1394_host_info *hi, + struct unit_directory *ud) +{ + struct eth1394_priv *priv; + struct eth1394_node_ref *new_node; + struct eth1394_node_info *node_info; + + new_node = kmalloc(sizeof(*new_node), GFP_KERNEL); + if (!new_node) + return -ENOMEM; + + node_info = kmalloc(sizeof(*node_info), GFP_KERNEL); + if (!node_info) { + kfree(new_node); + return -ENOMEM; + } + + spin_lock_init(&node_info->pdg.lock); + INIT_LIST_HEAD(&node_info->pdg.list); + node_info->pdg.sz = 0; + node_info->fifo = CSR1212_INVALID_ADDR_SPACE; + + dev_set_drvdata(&ud->device, node_info); + new_node->ud = ud; + + priv = netdev_priv(hi->dev); + list_add_tail(&new_node->list, &priv->ip_node_list); + return 0; +} + +static int eth1394_probe(struct device *dev) +{ + struct unit_directory *ud; + struct eth1394_host_info *hi; + + ud = container_of(dev, struct unit_directory, device); + hi = hpsb_get_hostinfo(ð1394_highlevel, ud->ne->host); + if (!hi) + return -ENOENT; + + return eth1394_new_node(hi, ud); +} + +static int eth1394_remove(struct device *dev) +{ + struct unit_directory *ud; + struct eth1394_host_info *hi; + struct eth1394_priv *priv; + struct eth1394_node_ref *old_node; + struct eth1394_node_info *node_info; + struct list_head *lh, *n; + unsigned long flags; + + ud = container_of(dev, struct unit_directory, device); + hi = hpsb_get_hostinfo(ð1394_highlevel, ud->ne->host); + if (!hi) + return -ENOENT; + + priv = netdev_priv(hi->dev); + + old_node = eth1394_find_node(&priv->ip_node_list, ud); + if (!old_node) + return 0; + + list_del(&old_node->list); + kfree(old_node); + + node_info = dev_get_drvdata(&ud->device); + + spin_lock_irqsave(&node_info->pdg.lock, flags); + /* The partial datagram list should be empty, but we'll just + * make sure anyway... */ + list_for_each_safe(lh, n, &node_info->pdg.list) + purge_partial_datagram(lh); + spin_unlock_irqrestore(&node_info->pdg.lock, flags); + + kfree(node_info); + dev_set_drvdata(&ud->device, NULL); + return 0; +} + +static int eth1394_update(struct unit_directory *ud) +{ + struct eth1394_host_info *hi; + struct eth1394_priv *priv; + struct eth1394_node_ref *node; + + hi = hpsb_get_hostinfo(ð1394_highlevel, ud->ne->host); + if (!hi) + return -ENOENT; + + priv = netdev_priv(hi->dev); + node = eth1394_find_node(&priv->ip_node_list, ud); + if (node) + return 0; + + return eth1394_new_node(hi, ud); +} + +static const struct ieee1394_device_id eth1394_id_table[] = { + { + .match_flags = (IEEE1394_MATCH_SPECIFIER_ID | + IEEE1394_MATCH_VERSION), + .specifier_id = ETHER1394_GASP_SPECIFIER_ID, + .version = ETHER1394_GASP_VERSION, + }, + {} +}; + +MODULE_DEVICE_TABLE(ieee1394, eth1394_id_table); + +static struct hpsb_protocol_driver eth1394_proto_driver = { + .name = driver_name, + .id_table = eth1394_id_table, + .update = eth1394_update, + .driver = { + .probe = eth1394_probe, + .remove = eth1394_remove, + }, +}; + +static void ether1394_reset_priv(struct net_device *dev, int set_mtu) +{ + unsigned long flags; + int i; + struct eth1394_priv *priv = netdev_priv(dev); + struct hpsb_host *host = priv->host; + u64 guid = get_unaligned((u64 *)&(host->csr.rom->bus_info_data[3])); + int max_speed = IEEE1394_SPEED_MAX; + + spin_lock_irqsave(&priv->lock, flags); + + memset(priv->ud_list, 0, sizeof(priv->ud_list)); + priv->bc_maxpayload = 512; + + /* Determine speed limit */ + /* FIXME: This is broken for nodes with link speed < PHY speed, + * and it is suboptimal for S200B...S800B hardware. + * The result of nodemgr's speed probe should be used somehow. */ + for (i = 0; i < host->node_count; i++) { + /* take care of S100B...S400B PHY ports */ + if (host->speed[i] == SELFID_SPEED_UNKNOWN) { + max_speed = IEEE1394_SPEED_100; + break; + } + if (max_speed > host->speed[i]) + max_speed = host->speed[i]; + } + priv->bc_sspd = max_speed; + + if (set_mtu) { + /* Use the RFC 2734 default 1500 octets or the maximum payload + * as initial MTU */ + dev->mtu = min(1500, ether1394_max_mtu(host)); + + /* Set our hardware address while we're at it */ + memcpy(dev->dev_addr, &guid, sizeof(u64)); + memset(dev->broadcast, 0xff, sizeof(u64)); + } + + spin_unlock_irqrestore(&priv->lock, flags); +} + +static const struct header_ops ether1394_header_ops = { + .create = ether1394_header, + .rebuild = ether1394_rebuild_header, + .cache = ether1394_header_cache, + .cache_update = ether1394_header_cache_update, + .parse = ether1394_header_parse, +}; + +static const struct net_device_ops ether1394_netdev_ops = { + .ndo_open = ether1394_open, + .ndo_stop = ether1394_stop, + .ndo_start_xmit = ether1394_tx, + .ndo_tx_timeout = ether1394_tx_timeout, + .ndo_change_mtu = ether1394_change_mtu, +}; + +static void ether1394_init_dev(struct net_device *dev) +{ + + dev->header_ops = ðer1394_header_ops; + dev->netdev_ops = ðer1394_netdev_ops; + + dev->watchdog_timeo = ETHER1394_TIMEOUT; + dev->flags = IFF_BROADCAST | IFF_MULTICAST; + dev->features = NETIF_F_HIGHDMA; + dev->addr_len = ETH1394_ALEN; + dev->hard_header_len = ETH1394_HLEN; + dev->type = ARPHRD_IEEE1394; + + /* FIXME: This value was copied from ether_setup(). Is it too much? */ + dev->tx_queue_len = 1000; +} + +/* + * Wake the queue up after commonly encountered transmit failure conditions are + * hopefully over. Currently only tlabel exhaustion is accounted for. + */ +static void ether1394_wake_queue(struct work_struct *work) +{ + struct eth1394_priv *priv; + struct hpsb_packet *packet; + + priv = container_of(work, struct eth1394_priv, wake); + packet = hpsb_alloc_packet(0); + + /* This is really bad, but unjam the queue anyway. */ + if (!packet) + goto out; + + packet->host = priv->host; + packet->node_id = priv->wake_node; + /* + * A transaction label is all we really want. If we get one, it almost + * always means we can get a lot more because the ieee1394 core recycled + * a whole batch of tlabels, at last. + */ + if (hpsb_get_tlabel(packet) == 0) + hpsb_free_tlabel(packet); + + hpsb_free_packet(packet); +out: + netif_wake_queue(priv->wake_dev); +} + +/* + * This function is called every time a card is found. It is generally called + * when the module is installed. This is where we add all of our ethernet + * devices. One for each host. + */ +static void ether1394_add_host(struct hpsb_host *host) +{ + struct eth1394_host_info *hi = NULL; + struct net_device *dev = NULL; + struct eth1394_priv *priv; + u64 fifo_addr; + + if (hpsb_config_rom_ip1394_add(host) != 0) { + ETH1394_PRINT_G(KERN_ERR, "Can't add IP-over-1394 ROM entry\n"); + return; + } + + fifo_addr = hpsb_allocate_and_register_addrspace( + ð1394_highlevel, host, &addr_ops, + ETHER1394_REGION_ADDR_LEN, ETHER1394_REGION_ADDR_LEN, + CSR1212_INVALID_ADDR_SPACE, CSR1212_INVALID_ADDR_SPACE); + if (fifo_addr == CSR1212_INVALID_ADDR_SPACE) { + ETH1394_PRINT_G(KERN_ERR, "Cannot register CSR space\n"); + hpsb_config_rom_ip1394_remove(host); + return; + } + + dev = alloc_netdev(sizeof(*priv), "eth%d", ether1394_init_dev); + if (dev == NULL) { + ETH1394_PRINT_G(KERN_ERR, "Out of memory\n"); + goto out; + } + + SET_NETDEV_DEV(dev, &host->device); + + priv = netdev_priv(dev); + INIT_LIST_HEAD(&priv->ip_node_list); + spin_lock_init(&priv->lock); + priv->host = host; + priv->local_fifo = fifo_addr; + INIT_WORK(&priv->wake, ether1394_wake_queue); + priv->wake_dev = dev; + + hi = hpsb_create_hostinfo(ð1394_highlevel, host, sizeof(*hi)); + if (hi == NULL) { + ETH1394_PRINT_G(KERN_ERR, "Out of memory\n"); + goto out; + } + + ether1394_reset_priv(dev, 1); + + if (register_netdev(dev)) { + ETH1394_PRINT_G(KERN_ERR, "Cannot register the driver\n"); + goto out; + } + + ETH1394_PRINT(KERN_INFO, dev->name, "IPv4 over IEEE 1394 (fw-host%d)\n", + host->id); + + hi->host = host; + hi->dev = dev; + + /* Ignore validity in hopes that it will be set in the future. It'll + * be checked when the eth device is opened. */ + priv->broadcast_channel = host->csr.broadcast_channel & 0x3f; + + ether1394_recv_init(priv); + return; +out: + if (dev) + free_netdev(dev); + if (hi) + hpsb_destroy_hostinfo(ð1394_highlevel, host); + hpsb_unregister_addrspace(ð1394_highlevel, host, fifo_addr); + hpsb_config_rom_ip1394_remove(host); +} + +/* Remove a card from our list */ +static void ether1394_remove_host(struct hpsb_host *host) +{ + struct eth1394_host_info *hi; + struct eth1394_priv *priv; + + hi = hpsb_get_hostinfo(ð1394_highlevel, host); + if (!hi) + return; + priv = netdev_priv(hi->dev); + hpsb_unregister_addrspace(ð1394_highlevel, host, priv->local_fifo); + hpsb_config_rom_ip1394_remove(host); + if (priv->iso) + hpsb_iso_shutdown(priv->iso); + unregister_netdev(hi->dev); + free_netdev(hi->dev); +} + +/* A bus reset happened */ +static void ether1394_host_reset(struct hpsb_host *host) +{ + struct eth1394_host_info *hi; + struct eth1394_priv *priv; + struct net_device *dev; + struct list_head *lh, *n; + struct eth1394_node_ref *node; + struct eth1394_node_info *node_info; + unsigned long flags; + + hi = hpsb_get_hostinfo(ð1394_highlevel, host); + + /* This can happen for hosts that we don't use */ + if (!hi) + return; + + dev = hi->dev; + priv = netdev_priv(dev); + + /* Reset our private host data, but not our MTU */ + netif_stop_queue(dev); + ether1394_reset_priv(dev, 0); + + list_for_each_entry(node, &priv->ip_node_list, list) { + node_info = dev_get_drvdata(&node->ud->device); + + spin_lock_irqsave(&node_info->pdg.lock, flags); + + list_for_each_safe(lh, n, &node_info->pdg.list) + purge_partial_datagram(lh); + + INIT_LIST_HEAD(&(node_info->pdg.list)); + node_info->pdg.sz = 0; + + spin_unlock_irqrestore(&node_info->pdg.lock, flags); + } + + netif_wake_queue(dev); +} + +/****************************************** + * HW Header net device functions + ******************************************/ +/* These functions have been adapted from net/ethernet/eth.c */ + +/* Create a fake MAC header for an arbitrary protocol layer. + * saddr=NULL means use device source address + * daddr=NULL means leave destination address (eg unresolved arp). */ +static int ether1394_header(struct sk_buff *skb, struct net_device *dev, + unsigned short type, const void *daddr, + const void *saddr, unsigned len) +{ + struct eth1394hdr *eth = + (struct eth1394hdr *)skb_push(skb, ETH1394_HLEN); + + eth->h_proto = htons(type); + + if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) { + memset(eth->h_dest, 0, dev->addr_len); + return dev->hard_header_len; + } + + if (daddr) { + memcpy(eth->h_dest, daddr, dev->addr_len); + return dev->hard_header_len; + } + + return -dev->hard_header_len; +} + +/* Rebuild the faked MAC header. This is called after an ARP + * (or in future other address resolution) has completed on this + * sk_buff. We now let ARP fill in the other fields. + * + * This routine CANNOT use cached dst->neigh! + * Really, it is used only when dst->neigh is wrong. + */ +static int ether1394_rebuild_header(struct sk_buff *skb) +{ + struct eth1394hdr *eth = (struct eth1394hdr *)skb->data; + + if (eth->h_proto == htons(ETH_P_IP)) + return arp_find((unsigned char *)ð->h_dest, skb); + + ETH1394_PRINT(KERN_DEBUG, skb->dev->name, + "unable to resolve type %04x addresses\n", + ntohs(eth->h_proto)); + return 0; +} + +static int ether1394_header_parse(const struct sk_buff *skb, + unsigned char *haddr) +{ + memcpy(haddr, skb->dev->dev_addr, ETH1394_ALEN); + return ETH1394_ALEN; +} + +static int ether1394_header_cache(const struct neighbour *neigh, + struct hh_cache *hh) +{ + __be16 type = hh->hh_type; + struct net_device *dev = neigh->dev; + struct eth1394hdr *eth = + (struct eth1394hdr *)((u8 *)hh->hh_data + 16 - ETH1394_HLEN); + + if (type == htons(ETH_P_802_3)) + return -1; + + eth->h_proto = type; + memcpy(eth->h_dest, neigh->ha, dev->addr_len); + + hh->hh_len = ETH1394_HLEN; + return 0; +} + +/* Called by Address Resolution module to notify changes in address. */ +static void ether1394_header_cache_update(struct hh_cache *hh, + const struct net_device *dev, + const unsigned char * haddr) +{ + memcpy((u8 *)hh->hh_data + 16 - ETH1394_HLEN, haddr, dev->addr_len); +} + +/****************************************** + * Datagram reception code + ******************************************/ + +/* Copied from net/ethernet/eth.c */ +static __be16 ether1394_type_trans(struct sk_buff *skb, struct net_device *dev) +{ + struct eth1394hdr *eth; + unsigned char *rawp; + + skb_reset_mac_header(skb); + skb_pull(skb, ETH1394_HLEN); + eth = eth1394_hdr(skb); + + if (*eth->h_dest & 1) { + if (memcmp(eth->h_dest, dev->broadcast, dev->addr_len) == 0) + skb->pkt_type = PACKET_BROADCAST; +#if 0 + else + skb->pkt_type = PACKET_MULTICAST; +#endif + } else { + if (memcmp(eth->h_dest, dev->dev_addr, dev->addr_len)) + skb->pkt_type = PACKET_OTHERHOST; + } + + if (ntohs(eth->h_proto) >= 1536) + return eth->h_proto; + + rawp = skb->data; + + if (*(unsigned short *)rawp == 0xFFFF) + return htons(ETH_P_802_3); + + return htons(ETH_P_802_2); +} + +/* Parse an encapsulated IP1394 header into an ethernet frame packet. + * We also perform ARP translation here, if need be. */ +static __be16 ether1394_parse_encap(struct sk_buff *skb, struct net_device *dev, + nodeid_t srcid, nodeid_t destid, + __be16 ether_type) +{ + struct eth1394_priv *priv = netdev_priv(dev); + __be64 dest_hw; + __be16 ret = 0; + + /* Setup our hw addresses. We use these to build the ethernet header. */ + if (destid == (LOCAL_BUS | ALL_NODES)) + dest_hw = ~cpu_to_be64(0); /* broadcast */ + else + dest_hw = cpu_to_be64((u64)priv->host->csr.guid_hi << 32 | + priv->host->csr.guid_lo); + + /* If this is an ARP packet, convert it. First, we want to make + * use of some of the fields, since they tell us a little bit + * about the sending machine. */ + if (ether_type == htons(ETH_P_ARP)) { + struct eth1394_arp *arp1394 = (struct eth1394_arp *)skb->data; + struct arphdr *arp = (struct arphdr *)skb->data; + unsigned char *arp_ptr = (unsigned char *)(arp + 1); + u64 fifo_addr = (u64)ntohs(arp1394->fifo_hi) << 32 | + ntohl(arp1394->fifo_lo); + u8 max_rec = min(priv->host->csr.max_rec, + (u8)(arp1394->max_rec)); + int sspd = arp1394->sspd; + u16 maxpayload; + struct eth1394_node_ref *node; + struct eth1394_node_info *node_info; + __be64 guid; + + /* Sanity check. MacOSX seems to be sending us 131 in this + * field (atleast on my Panther G5). Not sure why. */ + if (sspd > 5 || sspd < 0) + sspd = 0; + + maxpayload = min(eth1394_speedto_maxpayload[sspd], + (u16)(1 << (max_rec + 1))); + + guid = get_unaligned(&arp1394->s_uniq_id); + node = eth1394_find_node_guid(&priv->ip_node_list, + be64_to_cpu(guid)); + if (!node) + return cpu_to_be16(0); + + node_info = dev_get_drvdata(&node->ud->device); + + /* Update our speed/payload/fifo_offset table */ + node_info->maxpayload = maxpayload; + node_info->sspd = sspd; + node_info->fifo = fifo_addr; + + /* Now that we're done with the 1394 specific stuff, we'll + * need to alter some of the data. Believe it or not, all + * that needs to be done is sender_IP_address needs to be + * moved, the destination hardware address get stuffed + * in and the hardware address length set to 8. + * + * IMPORTANT: The code below overwrites 1394 specific data + * needed above so keep the munging of the data for the + * higher level IP stack last. */ + + arp->ar_hln = 8; + arp_ptr += arp->ar_hln; /* skip over sender unique id */ + *(u32 *)arp_ptr = arp1394->sip; /* move sender IP addr */ + arp_ptr += arp->ar_pln; /* skip over sender IP addr */ + + if (arp->ar_op == htons(ARPOP_REQUEST)) + memset(arp_ptr, 0, sizeof(u64)); + else + memcpy(arp_ptr, dev->dev_addr, sizeof(u64)); + } + + /* Now add the ethernet header. */ + if (dev_hard_header(skb, dev, ntohs(ether_type), &dest_hw, NULL, + skb->len) >= 0) + ret = ether1394_type_trans(skb, dev); + + return ret; +} + +static int fragment_overlap(struct list_head *frag_list, int offset, int len) +{ + struct fragment_info *fi; + int end = offset + len; + + list_for_each_entry(fi, frag_list, list) + if (offset < fi->offset + fi->len && end > fi->offset) + return 1; + + return 0; +} + +static struct list_head *find_partial_datagram(struct list_head *pdgl, int dgl) +{ + struct partial_datagram *pd; + + list_for_each_entry(pd, pdgl, list) + if (pd->dgl == dgl) + return &pd->list; + + return NULL; +} + +/* Assumes that new fragment does not overlap any existing fragments */ +static int new_fragment(struct list_head *frag_info, int offset, int len) +{ + struct list_head *lh; + struct fragment_info *fi, *fi2, *new; + + list_for_each(lh, frag_info) { + fi = list_entry(lh, struct fragment_info, list); + if (fi->offset + fi->len == offset) { + /* The new fragment can be tacked on to the end */ + fi->len += len; + /* Did the new fragment plug a hole? */ + fi2 = list_entry(lh->next, struct fragment_info, list); + if (fi->offset + fi->len == fi2->offset) { + /* glue fragments together */ + fi->len += fi2->len; + list_del(lh->next); + kfree(fi2); + } + return 0; + } else if (offset + len == fi->offset) { + /* The new fragment can be tacked on to the beginning */ + fi->offset = offset; + fi->len += len; + /* Did the new fragment plug a hole? */ + fi2 = list_entry(lh->prev, struct fragment_info, list); + if (fi2->offset + fi2->len == fi->offset) { + /* glue fragments together */ + fi2->len += fi->len; + list_del(lh); + kfree(fi); + } + return 0; + } else if (offset > fi->offset + fi->len) { + break; + } else if (offset + len < fi->offset) { + lh = lh->prev; + break; + } + } + + new = kmalloc(sizeof(*new), GFP_ATOMIC); + if (!new) + return -ENOMEM; + + new->offset = offset; + new->len = len; + + list_add(&new->list, lh); + return 0; +} + +static int new_partial_datagram(struct net_device *dev, struct list_head *pdgl, + int dgl, int dg_size, char *frag_buf, + int frag_off, int frag_len) +{ + struct partial_datagram *new; + + new = kmalloc(sizeof(*new), GFP_ATOMIC); + if (!new) + return -ENOMEM; + + INIT_LIST_HEAD(&new->frag_info); + + if (new_fragment(&new->frag_info, frag_off, frag_len) < 0) { + kfree(new); + return -ENOMEM; + } + + new->dgl = dgl; + new->dg_size = dg_size; + + new->skb = dev_alloc_skb(dg_size + dev->hard_header_len + 15); + if (!new->skb) { + struct fragment_info *fi = list_entry(new->frag_info.next, + struct fragment_info, + list); + kfree(fi); + kfree(new); + return -ENOMEM; + } + + skb_reserve(new->skb, (dev->hard_header_len + 15) & ~15); + new->pbuf = skb_put(new->skb, dg_size); + memcpy(new->pbuf + frag_off, frag_buf, frag_len); + + list_add(&new->list, pdgl); + return 0; +} + +static int update_partial_datagram(struct list_head *pdgl, struct list_head *lh, + char *frag_buf, int frag_off, int frag_len) +{ + struct partial_datagram *pd = + list_entry(lh, struct partial_datagram, list); + + if (new_fragment(&pd->frag_info, frag_off, frag_len) < 0) + return -ENOMEM; + + memcpy(pd->pbuf + frag_off, frag_buf, frag_len); + + /* Move list entry to beginnig of list so that oldest partial + * datagrams percolate to the end of the list */ + list_move(lh, pdgl); + return 0; +} + +static int is_datagram_complete(struct list_head *lh, int dg_size) +{ + struct partial_datagram *pd; + struct fragment_info *fi; + + pd = list_entry(lh, struct partial_datagram, list); + fi = list_entry(pd->frag_info.next, struct fragment_info, list); + + return (fi->len == dg_size); +} + +/* Packet reception. We convert the IP1394 encapsulation header to an + * ethernet header, and fill it with some of our other fields. This is + * an incoming packet from the 1394 bus. */ +static int ether1394_data_handler(struct net_device *dev, int srcid, int destid, + char *buf, int len) +{ + struct sk_buff *skb; + unsigned long flags; + struct eth1394_priv *priv = netdev_priv(dev); + union eth1394_hdr *hdr = (union eth1394_hdr *)buf; + __be16 ether_type = cpu_to_be16(0); /* initialized to clear warning */ + int hdr_len; + struct unit_directory *ud = priv->ud_list[NODEID_TO_NODE(srcid)]; + struct eth1394_node_info *node_info; + + if (!ud) { + struct eth1394_node_ref *node; + node = eth1394_find_node_nodeid(&priv->ip_node_list, srcid); + if (unlikely(!node)) { + HPSB_PRINT(KERN_ERR, "ether1394 rx: sender nodeid " + "lookup failure: " NODE_BUS_FMT, + NODE_BUS_ARGS(priv->host, srcid)); + dev->stats.rx_dropped++; + return -1; + } + ud = node->ud; + + priv->ud_list[NODEID_TO_NODE(srcid)] = ud; + } + + node_info = dev_get_drvdata(&ud->device); + + /* First, did we receive a fragmented or unfragmented datagram? */ + hdr->words.word1 = ntohs(hdr->words.word1); + + hdr_len = hdr_type_len[hdr->common.lf]; + + if (hdr->common.lf == ETH1394_HDR_LF_UF) { + /* An unfragmented datagram has been received by the ieee1394 + * bus. Build an skbuff around it so we can pass it to the + * high level network layer. */ + + skb = dev_alloc_skb(len + dev->hard_header_len + 15); + if (unlikely(!skb)) { + ETH1394_PRINT_G(KERN_ERR, "Out of memory\n"); + dev->stats.rx_dropped++; + return -1; + } + skb_reserve(skb, (dev->hard_header_len + 15) & ~15); + memcpy(skb_put(skb, len - hdr_len), buf + hdr_len, + len - hdr_len); + ether_type = hdr->uf.ether_type; + } else { + /* A datagram fragment has been received, now the fun begins. */ + + struct list_head *pdgl, *lh; + struct partial_datagram *pd; + int fg_off; + int fg_len = len - hdr_len; + int dg_size; + int dgl; + int retval; + struct pdg_list *pdg = &(node_info->pdg); + + hdr->words.word3 = ntohs(hdr->words.word3); + /* The 4th header word is reserved so no need to do ntohs() */ + + if (hdr->common.lf == ETH1394_HDR_LF_FF) { + ether_type = hdr->ff.ether_type; + dgl = hdr->ff.dgl; + dg_size = hdr->ff.dg_size + 1; + fg_off = 0; + } else { + hdr->words.word2 = ntohs(hdr->words.word2); + dgl = hdr->sf.dgl; + dg_size = hdr->sf.dg_size + 1; + fg_off = hdr->sf.fg_off; + } + spin_lock_irqsave(&pdg->lock, flags); + + pdgl = &(pdg->list); + lh = find_partial_datagram(pdgl, dgl); + + if (lh == NULL) { + while (pdg->sz >= max_partial_datagrams) { + /* remove the oldest */ + purge_partial_datagram(pdgl->prev); + pdg->sz--; + } + + retval = new_partial_datagram(dev, pdgl, dgl, dg_size, + buf + hdr_len, fg_off, + fg_len); + if (retval < 0) { + spin_unlock_irqrestore(&pdg->lock, flags); + goto bad_proto; + } + pdg->sz++; + lh = find_partial_datagram(pdgl, dgl); + } else { + pd = list_entry(lh, struct partial_datagram, list); + + if (fragment_overlap(&pd->frag_info, fg_off, fg_len)) { + /* Overlapping fragments, obliterate old + * datagram and start new one. */ + purge_partial_datagram(lh); + retval = new_partial_datagram(dev, pdgl, dgl, + dg_size, + buf + hdr_len, + fg_off, fg_len); + if (retval < 0) { + pdg->sz--; + spin_unlock_irqrestore(&pdg->lock, flags); + goto bad_proto; + } + } else { + retval = update_partial_datagram(pdgl, lh, + buf + hdr_len, + fg_off, fg_len); + if (retval < 0) { + /* Couldn't save off fragment anyway + * so might as well obliterate the + * datagram now. */ + purge_partial_datagram(lh); + pdg->sz--; + spin_unlock_irqrestore(&pdg->lock, flags); + goto bad_proto; + } + } /* fragment overlap */ + } /* new datagram or add to existing one */ + + pd = list_entry(lh, struct partial_datagram, list); + + if (hdr->common.lf == ETH1394_HDR_LF_FF) + pd->ether_type = ether_type; + + if (is_datagram_complete(lh, dg_size)) { + ether_type = pd->ether_type; + pdg->sz--; + skb = skb_get(pd->skb); + purge_partial_datagram(lh); + spin_unlock_irqrestore(&pdg->lock, flags); + } else { + /* Datagram is not complete, we're done for the + * moment. */ + spin_unlock_irqrestore(&pdg->lock, flags); + return 0; + } + } /* unframgented datagram or fragmented one */ + + /* Write metadata, and then pass to the receive level */ + skb->dev = dev; + skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */ + + /* Parse the encapsulation header. This actually does the job of + * converting to an ethernet frame header, aswell as arp + * conversion if needed. ARP conversion is easier in this + * direction, since we are using ethernet as our backend. */ + skb->protocol = ether1394_parse_encap(skb, dev, srcid, destid, + ether_type); + + spin_lock_irqsave(&priv->lock, flags); + + if (!skb->protocol) { + dev->stats.rx_errors++; + dev->stats.rx_dropped++; + dev_kfree_skb_any(skb); + } else if (netif_rx(skb) == NET_RX_DROP) { + dev->stats.rx_errors++; + dev->stats.rx_dropped++; + } else { + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; + } + + spin_unlock_irqrestore(&priv->lock, flags); + +bad_proto: + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + + return 0; +} + +static int ether1394_write(struct hpsb_host *host, int srcid, int destid, + quadlet_t *data, u64 addr, size_t len, u16 flags) +{ + struct eth1394_host_info *hi; + + hi = hpsb_get_hostinfo(ð1394_highlevel, host); + if (unlikely(!hi)) { + ETH1394_PRINT_G(KERN_ERR, "No net device at fw-host%d\n", + host->id); + return RCODE_ADDRESS_ERROR; + } + + if (ether1394_data_handler(hi->dev, srcid, destid, (char*)data, len)) + return RCODE_ADDRESS_ERROR; + else + return RCODE_COMPLETE; +} + +static void ether1394_iso(struct hpsb_iso *iso) +{ + __be32 *data; + char *buf; + struct eth1394_host_info *hi; + struct net_device *dev; + unsigned int len; + u32 specifier_id; + u16 source_id; + int i; + int nready; + + hi = hpsb_get_hostinfo(ð1394_highlevel, iso->host); + if (unlikely(!hi)) { + ETH1394_PRINT_G(KERN_ERR, "No net device at fw-host%d\n", + iso->host->id); + return; + } + + dev = hi->dev; + + nready = hpsb_iso_n_ready(iso); + for (i = 0; i < nready; i++) { + struct hpsb_iso_packet_info *info = + &iso->infos[(iso->first_packet + i) % iso->buf_packets]; + data = (__be32 *)(iso->data_buf.kvirt + info->offset); + + /* skip over GASP header */ + buf = (char *)data + 8; + len = info->len - 8; + + specifier_id = (be32_to_cpu(data[0]) & 0xffff) << 8 | + (be32_to_cpu(data[1]) & 0xff000000) >> 24; + source_id = be32_to_cpu(data[0]) >> 16; + + if (info->channel != (iso->host->csr.broadcast_channel & 0x3f) + || specifier_id != ETHER1394_GASP_SPECIFIER_ID) { + /* This packet is not for us */ + continue; + } + ether1394_data_handler(dev, source_id, LOCAL_BUS | ALL_NODES, + buf, len); + } + + hpsb_iso_recv_release_packets(iso, i); + +} + +/****************************************** + * Datagram transmission code + ******************************************/ + +/* Convert a standard ARP packet to 1394 ARP. The first 8 bytes (the entire + * arphdr) is the same format as the ip1394 header, so they overlap. The rest + * needs to be munged a bit. The remainder of the arphdr is formatted based + * on hwaddr len and ipaddr len. We know what they'll be, so it's easy to + * judge. + * + * Now that the EUI is used for the hardware address all we need to do to make + * this work for 1394 is to insert 2 quadlets that contain max_rec size, + * speed, and unicast FIFO address information between the sender_unique_id + * and the IP addresses. + */ +static void ether1394_arp_to_1394arp(struct sk_buff *skb, + struct net_device *dev) +{ + struct eth1394_priv *priv = netdev_priv(dev); + struct arphdr *arp = (struct arphdr *)skb->data; + unsigned char *arp_ptr = (unsigned char *)(arp + 1); + struct eth1394_arp *arp1394 = (struct eth1394_arp *)skb->data; + + arp1394->hw_addr_len = 16; + arp1394->sip = *(u32*)(arp_ptr + ETH1394_ALEN); + arp1394->max_rec = priv->host->csr.max_rec; + arp1394->sspd = priv->host->csr.lnk_spd; + arp1394->fifo_hi = htons(priv->local_fifo >> 32); + arp1394->fifo_lo = htonl(priv->local_fifo & ~0x0); +} + +/* We need to encapsulate the standard header with our own. We use the + * ethernet header's proto for our own. */ +static unsigned int ether1394_encapsulate_prep(unsigned int max_payload, + __be16 proto, + union eth1394_hdr *hdr, + u16 dg_size, u16 dgl) +{ + unsigned int adj_max_payload = + max_payload - hdr_type_len[ETH1394_HDR_LF_UF]; + + /* Does it all fit in one packet? */ + if (dg_size <= adj_max_payload) { + hdr->uf.lf = ETH1394_HDR_LF_UF; + hdr->uf.ether_type = proto; + } else { + hdr->ff.lf = ETH1394_HDR_LF_FF; + hdr->ff.ether_type = proto; + hdr->ff.dg_size = dg_size - 1; + hdr->ff.dgl = dgl; + adj_max_payload = max_payload - hdr_type_len[ETH1394_HDR_LF_FF]; + } + return DIV_ROUND_UP(dg_size, adj_max_payload); +} + +static unsigned int ether1394_encapsulate(struct sk_buff *skb, + unsigned int max_payload, + union eth1394_hdr *hdr) +{ + union eth1394_hdr *bufhdr; + int ftype = hdr->common.lf; + int hdrsz = hdr_type_len[ftype]; + unsigned int adj_max_payload = max_payload - hdrsz; + + switch (ftype) { + case ETH1394_HDR_LF_UF: + bufhdr = (union eth1394_hdr *)skb_push(skb, hdrsz); + bufhdr->words.word1 = htons(hdr->words.word1); + bufhdr->words.word2 = hdr->words.word2; + break; + + case ETH1394_HDR_LF_FF: + bufhdr = (union eth1394_hdr *)skb_push(skb, hdrsz); + bufhdr->words.word1 = htons(hdr->words.word1); + bufhdr->words.word2 = hdr->words.word2; + bufhdr->words.word3 = htons(hdr->words.word3); + bufhdr->words.word4 = 0; + + /* Set frag type here for future interior fragments */ + hdr->common.lf = ETH1394_HDR_LF_IF; + hdr->sf.fg_off = 0; + break; + + default: + hdr->sf.fg_off += adj_max_payload; + bufhdr = (union eth1394_hdr *)skb_pull(skb, adj_max_payload); + if (max_payload >= skb->len) + hdr->common.lf = ETH1394_HDR_LF_LF; + bufhdr->words.word1 = htons(hdr->words.word1); + bufhdr->words.word2 = htons(hdr->words.word2); + bufhdr->words.word3 = htons(hdr->words.word3); + bufhdr->words.word4 = 0; + } + return min(max_payload, skb->len); +} + +static struct hpsb_packet *ether1394_alloc_common_packet(struct hpsb_host *host) +{ + struct hpsb_packet *p; + + p = hpsb_alloc_packet(0); + if (p) { + p->host = host; + p->generation = get_hpsb_generation(host); + p->type = hpsb_async; + } + return p; +} + +static int ether1394_prep_write_packet(struct hpsb_packet *p, + struct hpsb_host *host, nodeid_t node, + u64 addr, void *data, int tx_len) +{ + p->node_id = node; + + if (hpsb_get_tlabel(p)) + return -EAGAIN; + + p->tcode = TCODE_WRITEB; + p->header_size = 16; + p->expect_response = 1; + p->header[0] = + p->node_id << 16 | p->tlabel << 10 | 1 << 8 | TCODE_WRITEB << 4; + p->header[1] = host->node_id << 16 | addr >> 32; + p->header[2] = addr & 0xffffffff; + p->header[3] = tx_len << 16; + p->data_size = (tx_len + 3) & ~3; + p->data = data; + + return 0; +} + +static void ether1394_prep_gasp_packet(struct hpsb_packet *p, + struct eth1394_priv *priv, + struct sk_buff *skb, int length) +{ + p->header_size = 4; + p->tcode = TCODE_STREAM_DATA; + + p->header[0] = length << 16 | 3 << 14 | priv->broadcast_channel << 8 | + TCODE_STREAM_DATA << 4; + p->data_size = length; + p->data = (quadlet_t *)skb->data - 2; + p->data[0] = cpu_to_be32(priv->host->node_id << 16 | + ETHER1394_GASP_SPECIFIER_ID_HI); + p->data[1] = cpu_to_be32(ETHER1394_GASP_SPECIFIER_ID_LO << 24 | + ETHER1394_GASP_VERSION); + + p->speed_code = priv->bc_sspd; + + /* prevent hpsb_send_packet() from overriding our speed code */ + p->node_id = LOCAL_BUS | ALL_NODES; +} + +static void ether1394_free_packet(struct hpsb_packet *packet) +{ + if (packet->tcode != TCODE_STREAM_DATA) + hpsb_free_tlabel(packet); + hpsb_free_packet(packet); +} + +static void ether1394_complete_cb(void *__ptask); + +static int ether1394_send_packet(struct packet_task *ptask, unsigned int tx_len) +{ + struct eth1394_priv *priv = ptask->priv; + struct hpsb_packet *packet = NULL; + + packet = ether1394_alloc_common_packet(priv->host); + if (!packet) + return -ENOMEM; + + if (ptask->tx_type == ETH1394_GASP) { + int length = tx_len + 2 * sizeof(quadlet_t); + + ether1394_prep_gasp_packet(packet, priv, ptask->skb, length); + } else if (ether1394_prep_write_packet(packet, priv->host, + ptask->dest_node, + ptask->addr, ptask->skb->data, + tx_len)) { + hpsb_free_packet(packet); + return -EAGAIN; + } + + ptask->packet = packet; + hpsb_set_packet_complete_task(ptask->packet, ether1394_complete_cb, + ptask); + + if (hpsb_send_packet(packet) < 0) { + ether1394_free_packet(packet); + return -EIO; + } + + return 0; +} + +/* Task function to be run when a datagram transmission is completed */ +static void ether1394_dg_complete(struct packet_task *ptask, int fail) +{ + struct sk_buff *skb = ptask->skb; + struct net_device *dev = skb->dev; + struct eth1394_priv *priv = netdev_priv(dev); + unsigned long flags; + + /* Statistics */ + spin_lock_irqsave(&priv->lock, flags); + if (fail) { + dev->stats.tx_dropped++; + dev->stats.tx_errors++; + } else { + dev->stats.tx_bytes += skb->len; + dev->stats.tx_packets++; + } + spin_unlock_irqrestore(&priv->lock, flags); + + dev_kfree_skb_any(skb); + kmem_cache_free(packet_task_cache, ptask); +} + +/* Callback for when a packet has been sent and the status of that packet is + * known */ +static void ether1394_complete_cb(void *__ptask) +{ + struct packet_task *ptask = (struct packet_task *)__ptask; + struct hpsb_packet *packet = ptask->packet; + int fail = 0; + + if (packet->tcode != TCODE_STREAM_DATA) + fail = hpsb_packet_success(packet); + + ether1394_free_packet(packet); + + ptask->outstanding_pkts--; + if (ptask->outstanding_pkts > 0 && !fail) { + int tx_len, err; + + /* Add the encapsulation header to the fragment */ + tx_len = ether1394_encapsulate(ptask->skb, ptask->max_payload, + &ptask->hdr); + err = ether1394_send_packet(ptask, tx_len); + if (err) { + if (err == -EAGAIN) + ETH1394_PRINT_G(KERN_ERR, "Out of tlabels\n"); + + ether1394_dg_complete(ptask, 1); + } + } else { + ether1394_dg_complete(ptask, fail); + } +} + +/* Transmit a packet (called by kernel) */ +static netdev_tx_t ether1394_tx(struct sk_buff *skb, + struct net_device *dev) +{ + struct eth1394hdr hdr_buf; + struct eth1394_priv *priv = netdev_priv(dev); + __be16 proto; + unsigned long flags; + nodeid_t dest_node; + eth1394_tx_type tx_type; + unsigned int tx_len; + unsigned int max_payload; + u16 dg_size; + u16 dgl; + struct packet_task *ptask; + struct eth1394_node_ref *node; + struct eth1394_node_info *node_info = NULL; + + ptask = kmem_cache_alloc(packet_task_cache, GFP_ATOMIC); + if (ptask == NULL) + goto fail; + + /* XXX Ignore this for now. Noticed that when MacOSX is the IRM, + * it does not set our validity bit. We need to compensate for + * that somewhere else, but not in eth1394. */ +#if 0 + if ((priv->host->csr.broadcast_channel & 0xc0000000) != 0xc0000000) + goto fail; +#endif + + skb = skb_share_check(skb, GFP_ATOMIC); + if (!skb) + goto fail; + + /* Get rid of the fake eth1394 header, but first make a copy. + * We might need to rebuild the header on tx failure. */ + memcpy(&hdr_buf, skb->data, sizeof(hdr_buf)); + skb_pull(skb, ETH1394_HLEN); + + proto = hdr_buf.h_proto; + dg_size = skb->len; + + /* Set the transmission type for the packet. ARP packets and IP + * broadcast packets are sent via GASP. */ + if (memcmp(hdr_buf.h_dest, dev->broadcast, ETH1394_ALEN) == 0 || + proto == htons(ETH_P_ARP) || + (proto == htons(ETH_P_IP) && + IN_MULTICAST(ntohl(ip_hdr(skb)->daddr)))) { + tx_type = ETH1394_GASP; + dest_node = LOCAL_BUS | ALL_NODES; + max_payload = priv->bc_maxpayload - ETHER1394_GASP_OVERHEAD; + BUG_ON(max_payload < 512 - ETHER1394_GASP_OVERHEAD); + dgl = priv->bc_dgl; + if (max_payload < dg_size + hdr_type_len[ETH1394_HDR_LF_UF]) + priv->bc_dgl++; + } else { + __be64 guid = get_unaligned((__be64 *)hdr_buf.h_dest); + + node = eth1394_find_node_guid(&priv->ip_node_list, + be64_to_cpu(guid)); + if (!node) + goto fail; + + node_info = dev_get_drvdata(&node->ud->device); + if (node_info->fifo == CSR1212_INVALID_ADDR_SPACE) + goto fail; + + dest_node = node->ud->ne->nodeid; + max_payload = node_info->maxpayload; + BUG_ON(max_payload < 512 - ETHER1394_GASP_OVERHEAD); + + dgl = node_info->dgl; + if (max_payload < dg_size + hdr_type_len[ETH1394_HDR_LF_UF]) + node_info->dgl++; + tx_type = ETH1394_WRREQ; + } + + /* If this is an ARP packet, convert it */ + if (proto == htons(ETH_P_ARP)) + ether1394_arp_to_1394arp(skb, dev); + + ptask->hdr.words.word1 = 0; + ptask->hdr.words.word2 = 0; + ptask->hdr.words.word3 = 0; + ptask->hdr.words.word4 = 0; + ptask->skb = skb; + ptask->priv = priv; + ptask->tx_type = tx_type; + + if (tx_type != ETH1394_GASP) { + u64 addr; + + spin_lock_irqsave(&priv->lock, flags); + addr = node_info->fifo; + spin_unlock_irqrestore(&priv->lock, flags); + + ptask->addr = addr; + ptask->dest_node = dest_node; + } + + ptask->tx_type = tx_type; + ptask->max_payload = max_payload; + ptask->outstanding_pkts = ether1394_encapsulate_prep(max_payload, + proto, &ptask->hdr, dg_size, dgl); + + /* Add the encapsulation header to the fragment */ + tx_len = ether1394_encapsulate(skb, max_payload, &ptask->hdr); + dev->trans_start = jiffies; + if (ether1394_send_packet(ptask, tx_len)) { + if (dest_node == (LOCAL_BUS | ALL_NODES)) + goto fail; + + /* At this point we want to restore the packet. When we return + * here with NETDEV_TX_BUSY we will get another entrance in this + * routine with the same skb and we need it to look the same. + * So we pull 4 more bytes, then build the header again. */ + skb_pull(skb, 4); + ether1394_header(skb, dev, ntohs(hdr_buf.h_proto), + hdr_buf.h_dest, NULL, 0); + + /* Most failures of ether1394_send_packet are recoverable. */ + netif_stop_queue(dev); + priv->wake_node = dest_node; + schedule_work(&priv->wake); + kmem_cache_free(packet_task_cache, ptask); + return NETDEV_TX_BUSY; + } + + return NETDEV_TX_OK; +fail: + if (ptask) + kmem_cache_free(packet_task_cache, ptask); + + if (skb != NULL) + dev_kfree_skb(skb); + + spin_lock_irqsave(&priv->lock, flags); + dev->stats.tx_dropped++; + dev->stats.tx_errors++; + spin_unlock_irqrestore(&priv->lock, flags); + + return NETDEV_TX_OK; +} + +static int __init ether1394_init_module(void) +{ + int err; + + packet_task_cache = kmem_cache_create("packet_task", + sizeof(struct packet_task), + 0, 0, NULL); + if (!packet_task_cache) + return -ENOMEM; + + hpsb_register_highlevel(ð1394_highlevel); + err = hpsb_register_protocol(ð1394_proto_driver); + if (err) { + hpsb_unregister_highlevel(ð1394_highlevel); + kmem_cache_destroy(packet_task_cache); + } + return err; +} + +static void __exit ether1394_exit_module(void) +{ + hpsb_unregister_protocol(ð1394_proto_driver); + hpsb_unregister_highlevel(ð1394_highlevel); + kmem_cache_destroy(packet_task_cache); +} + +module_init(ether1394_init_module); +module_exit(ether1394_exit_module); diff --git a/trunk/drivers/ieee1394/eth1394.h b/trunk/drivers/ieee1394/eth1394.h new file mode 100644 index 000000000000..d53bac47b86f --- /dev/null +++ b/trunk/drivers/ieee1394/eth1394.h @@ -0,0 +1,234 @@ +/* + * eth1394.h -- Ethernet driver for Linux IEEE-1394 Subsystem + * + * Copyright (C) 2000 Bonin Franck + * (C) 2001 Ben Collins + * + * Mainly based on work by Emanuel Pirker and Andreas E. Bombe + * + * 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. + */ + +#ifndef __ETH1394_H +#define __ETH1394_H + +#include +#include +#include + +#include "ieee1394.h" +#include "ieee1394_types.h" + +/* Register for incoming packets. This is 4096 bytes, which supports up to + * S3200 (per Table 16-3 of IEEE 1394b-2002). */ +#define ETHER1394_REGION_ADDR_LEN 4096 + +/* GASP identifier numbers for IPv4 over IEEE 1394 */ +#define ETHER1394_GASP_SPECIFIER_ID 0x00005E +#define ETHER1394_GASP_SPECIFIER_ID_HI ((0x00005E >> 8) & 0xffff) +#define ETHER1394_GASP_SPECIFIER_ID_LO (0x00005E & 0xff) +#define ETHER1394_GASP_VERSION 1 + +#define ETHER1394_GASP_OVERHEAD (2 * sizeof(quadlet_t)) /* for GASP header */ + +#define ETHER1394_GASP_BUFFERS 16 + +#define NODE_SET (ALL_NODES + 1) /* Node set == 64 */ + +enum eth1394_bc_states { ETHER1394_BC_ERROR, + ETHER1394_BC_RUNNING, + ETHER1394_BC_STOPPED }; + + +/* Private structure for our ethernet driver */ +struct eth1394_priv { + struct hpsb_host *host; /* The card for this dev */ + u16 bc_maxpayload; /* Max broadcast payload */ + u8 bc_sspd; /* Max broadcast speed */ + u64 local_fifo; /* Local FIFO Address */ + spinlock_t lock; /* Private lock */ + int broadcast_channel; /* Async stream Broadcast Channel */ + enum eth1394_bc_states bc_state; /* broadcast channel state */ + struct hpsb_iso *iso; /* Async stream recv handle */ + int bc_dgl; /* Outgoing broadcast datagram label */ + struct list_head ip_node_list; /* List of IP capable nodes */ + struct unit_directory *ud_list[ALL_NODES]; /* Cached unit dir list */ + + struct work_struct wake; /* Wake up after xmit failure */ + struct net_device *wake_dev; /* Stupid backlink for .wake */ + nodeid_t wake_node; /* Destination of failed xmit */ +}; + + +/* Define a fake hardware header format for the networking core. Note that + * header size cannot exceed 16 bytes as that is the size of the header cache. + * Also, we do not need the source address in the header so we omit it and + * keep the header to under 16 bytes */ +#define ETH1394_ALEN (8) +#define ETH1394_HLEN (10) + +struct eth1394hdr { + unsigned char h_dest[ETH1394_ALEN]; /* destination eth1394 addr */ + __be16 h_proto; /* packet type ID field */ +} __attribute__((packed)); + +static inline struct eth1394hdr *eth1394_hdr(const struct sk_buff *skb) +{ + return (struct eth1394hdr *)skb_mac_header(skb); +} + +typedef enum {ETH1394_GASP, ETH1394_WRREQ} eth1394_tx_type; + +/* IP1394 headers */ + +/* Unfragmented */ +#if defined __BIG_ENDIAN_BITFIELD +struct eth1394_uf_hdr { + u16 lf:2; + u16 res:14; + __be16 ether_type; /* Ethernet packet type */ +} __attribute__((packed)); +#elif defined __LITTLE_ENDIAN_BITFIELD +struct eth1394_uf_hdr { + u16 res:14; + u16 lf:2; + __be16 ether_type; +} __attribute__((packed)); +#else +#error Unknown bit field type +#endif + +/* First fragment */ +#if defined __BIG_ENDIAN_BITFIELD +struct eth1394_ff_hdr { + u16 lf:2; + u16 res1:2; + u16 dg_size:12; /* Datagram size */ + __be16 ether_type; /* Ethernet packet type */ + u16 dgl; /* Datagram label */ + u16 res2; +} __attribute__((packed)); +#elif defined __LITTLE_ENDIAN_BITFIELD +struct eth1394_ff_hdr { + u16 dg_size:12; + u16 res1:2; + u16 lf:2; + __be16 ether_type; + u16 dgl; + u16 res2; +} __attribute__((packed)); +#else +#error Unknown bit field type +#endif + +/* XXX: Subsequent fragments, including last */ +#if defined __BIG_ENDIAN_BITFIELD +struct eth1394_sf_hdr { + u16 lf:2; + u16 res1:2; + u16 dg_size:12; /* Datagram size */ + u16 res2:4; + u16 fg_off:12; /* Fragment offset */ + u16 dgl; /* Datagram label */ + u16 res3; +} __attribute__((packed)); +#elif defined __LITTLE_ENDIAN_BITFIELD +struct eth1394_sf_hdr { + u16 dg_size:12; + u16 res1:2; + u16 lf:2; + u16 fg_off:12; + u16 res2:4; + u16 dgl; + u16 res3; +} __attribute__((packed)); +#else +#error Unknown bit field type +#endif + +#if defined __BIG_ENDIAN_BITFIELD +struct eth1394_common_hdr { + u16 lf:2; + u16 pad1:14; +} __attribute__((packed)); +#elif defined __LITTLE_ENDIAN_BITFIELD +struct eth1394_common_hdr { + u16 pad1:14; + u16 lf:2; +} __attribute__((packed)); +#else +#error Unknown bit field type +#endif + +struct eth1394_hdr_words { + u16 word1; + u16 word2; + u16 word3; + u16 word4; +}; + +union eth1394_hdr { + struct eth1394_common_hdr common; + struct eth1394_uf_hdr uf; + struct eth1394_ff_hdr ff; + struct eth1394_sf_hdr sf; + struct eth1394_hdr_words words; +}; + +/* End of IP1394 headers */ + +/* Fragment types */ +#define ETH1394_HDR_LF_UF 0 /* unfragmented */ +#define ETH1394_HDR_LF_FF 1 /* first fragment */ +#define ETH1394_HDR_LF_LF 2 /* last fragment */ +#define ETH1394_HDR_LF_IF 3 /* interior fragment */ + +#define IP1394_HW_ADDR_LEN 16 /* As per RFC */ + +/* Our arp packet (ARPHRD_IEEE1394) */ +struct eth1394_arp { + u16 hw_type; /* 0x0018 */ + u16 proto_type; /* 0x0806 */ + u8 hw_addr_len; /* 16 */ + u8 ip_addr_len; /* 4 */ + u16 opcode; /* ARP Opcode */ + /* Above is exactly the same format as struct arphdr */ + + __be64 s_uniq_id; /* Sender's 64bit EUI */ + u8 max_rec; /* Sender's max packet size */ + u8 sspd; /* Sender's max speed */ + __be16 fifo_hi; /* hi 16bits of sender's FIFO addr */ + __be32 fifo_lo; /* lo 32bits of sender's FIFO addr */ + u32 sip; /* Sender's IP Address */ + u32 tip; /* IP Address of requested hw addr */ +}; + +/* Network timeout */ +#define ETHER1394_TIMEOUT 100000 + +/* This is our task struct. It's used for the packet complete callback. */ +struct packet_task { + struct sk_buff *skb; + int outstanding_pkts; + eth1394_tx_type tx_type; + int max_payload; + struct hpsb_packet *packet; + struct eth1394_priv *priv; + union eth1394_hdr hdr; + u64 addr; + u16 dest_node; +}; + +#endif /* __ETH1394_H */ diff --git a/trunk/drivers/ieee1394/highlevel.c b/trunk/drivers/ieee1394/highlevel.c new file mode 100644 index 000000000000..4bc443546e04 --- /dev/null +++ b/trunk/drivers/ieee1394/highlevel.c @@ -0,0 +1,691 @@ +/* + * IEEE 1394 for Linux + * + * Copyright (C) 1999 Andreas E. Bombe + * + * This code is licensed under the GPL. See the file COPYING in the root + * directory of the kernel sources for details. + * + * + * Contributions: + * + * Christian Toegel + * unregister address space + * + * Manfred Weihs + * unregister address space + * + */ + +#include +#include +#include + +#include "ieee1394.h" +#include "ieee1394_types.h" +#include "hosts.h" +#include "ieee1394_core.h" +#include "highlevel.h" +#include "nodemgr.h" + + +struct hl_host_info { + struct list_head list; + struct hpsb_host *host; + size_t size; + unsigned long key; + void *data; +}; + + +static LIST_HEAD(hl_drivers); +static DECLARE_RWSEM(hl_drivers_sem); + +static LIST_HEAD(hl_irqs); +static DEFINE_RWLOCK(hl_irqs_lock); + +static DEFINE_RWLOCK(addr_space_lock); + + +static struct hl_host_info *hl_get_hostinfo(struct hpsb_highlevel *hl, + struct hpsb_host *host) +{ + struct hl_host_info *hi = NULL; + + if (!hl || !host) + return NULL; + + read_lock(&hl->host_info_lock); + list_for_each_entry(hi, &hl->host_info_list, list) { + if (hi->host == host) { + read_unlock(&hl->host_info_lock); + return hi; + } + } + read_unlock(&hl->host_info_lock); + return NULL; +} + +/** + * hpsb_get_hostinfo - retrieve a hostinfo pointer bound to this driver/host + * + * Returns a per @host and @hl driver data structure that was previously stored + * by hpsb_create_hostinfo. + */ +void *hpsb_get_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host) +{ + struct hl_host_info *hi = hl_get_hostinfo(hl, host); + + return hi ? hi->data : NULL; +} + +/** + * hpsb_create_hostinfo - allocate a hostinfo pointer bound to this driver/host + * + * Allocate a hostinfo pointer backed by memory with @data_size and bind it to + * to this @hl driver and @host. If @data_size is zero, then the return here is + * only valid for error checking. + */ +void *hpsb_create_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, + size_t data_size) +{ + struct hl_host_info *hi; + void *data; + unsigned long flags; + + hi = hl_get_hostinfo(hl, host); + if (hi) { + HPSB_ERR("%s called hpsb_create_hostinfo when hostinfo already" + " exists", hl->name); + return NULL; + } + + hi = kzalloc(sizeof(*hi) + data_size, GFP_ATOMIC); + if (!hi) + return NULL; + + if (data_size) { + data = hi->data = hi + 1; + hi->size = data_size; + } else + data = hi; + + hi->host = host; + + write_lock_irqsave(&hl->host_info_lock, flags); + list_add_tail(&hi->list, &hl->host_info_list); + write_unlock_irqrestore(&hl->host_info_lock, flags); + + return data; +} + +/** + * hpsb_set_hostinfo - set the hostinfo pointer to something useful + * + * Usually follows a call to hpsb_create_hostinfo, where the size is 0. + */ +int hpsb_set_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, + void *data) +{ + struct hl_host_info *hi; + + hi = hl_get_hostinfo(hl, host); + if (hi) { + if (!hi->size && !hi->data) { + hi->data = data; + return 0; + } else + HPSB_ERR("%s called hpsb_set_hostinfo when hostinfo " + "already has data", hl->name); + } else + HPSB_ERR("%s called hpsb_set_hostinfo when no hostinfo exists", + hl->name); + return -EINVAL; +} + +/** + * hpsb_destroy_hostinfo - free and remove a hostinfo pointer + * + * Free and remove the hostinfo pointer bound to this @hl driver and @host. + */ +void hpsb_destroy_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host) +{ + struct hl_host_info *hi; + + hi = hl_get_hostinfo(hl, host); + if (hi) { + unsigned long flags; + write_lock_irqsave(&hl->host_info_lock, flags); + list_del(&hi->list); + write_unlock_irqrestore(&hl->host_info_lock, flags); + kfree(hi); + } + return; +} + +/** + * hpsb_set_hostinfo_key - set an alternate lookup key for an hostinfo + * + * Sets an alternate lookup key for the hostinfo bound to this @hl driver and + * @host. + */ +void hpsb_set_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host, + unsigned long key) +{ + struct hl_host_info *hi; + + hi = hl_get_hostinfo(hl, host); + if (hi) + hi->key = key; + return; +} + +/** + * hpsb_get_hostinfo_bykey - retrieve a hostinfo pointer by its alternate key + */ +void *hpsb_get_hostinfo_bykey(struct hpsb_highlevel *hl, unsigned long key) +{ + struct hl_host_info *hi; + void *data = NULL; + + if (!hl) + return NULL; + + read_lock(&hl->host_info_lock); + list_for_each_entry(hi, &hl->host_info_list, list) { + if (hi->key == key) { + data = hi->data; + break; + } + } + read_unlock(&hl->host_info_lock); + return data; +} + +static int highlevel_for_each_host_reg(struct hpsb_host *host, void *__data) +{ + struct hpsb_highlevel *hl = __data; + + hl->add_host(host); + + if (host->update_config_rom && hpsb_update_config_rom_image(host) < 0) + HPSB_ERR("Failed to generate Configuration ROM image for host " + "%s-%d", hl->name, host->id); + return 0; +} + +/** + * hpsb_register_highlevel - register highlevel driver + * + * The name pointer in @hl has to stay valid at all times because the string is + * not copied. + */ +void hpsb_register_highlevel(struct hpsb_highlevel *hl) +{ + unsigned long flags; + + hpsb_init_highlevel(hl); + INIT_LIST_HEAD(&hl->addr_list); + + down_write(&hl_drivers_sem); + list_add_tail(&hl->hl_list, &hl_drivers); + up_write(&hl_drivers_sem); + + write_lock_irqsave(&hl_irqs_lock, flags); + list_add_tail(&hl->irq_list, &hl_irqs); + write_unlock_irqrestore(&hl_irqs_lock, flags); + + if (hl->add_host) + nodemgr_for_each_host(hl, highlevel_for_each_host_reg); + return; +} + +static void __delete_addr(struct hpsb_address_serve *as) +{ + list_del(&as->host_list); + list_del(&as->hl_list); + kfree(as); +} + +static void __unregister_host(struct hpsb_highlevel *hl, struct hpsb_host *host, + int update_cr) +{ + unsigned long flags; + struct list_head *lh, *next; + struct hpsb_address_serve *as; + + /* First, let the highlevel driver unreg */ + if (hl->remove_host) + hl->remove_host(host); + + /* Remove any addresses that are matched for this highlevel driver + * and this particular host. */ + write_lock_irqsave(&addr_space_lock, flags); + list_for_each_safe (lh, next, &hl->addr_list) { + as = list_entry(lh, struct hpsb_address_serve, hl_list); + if (as->host == host) + __delete_addr(as); + } + write_unlock_irqrestore(&addr_space_lock, flags); + + /* Now update the config-rom to reflect anything removed by the + * highlevel driver. */ + if (update_cr && host->update_config_rom && + hpsb_update_config_rom_image(host) < 0) + HPSB_ERR("Failed to generate Configuration ROM image for host " + "%s-%d", hl->name, host->id); + + /* Finally remove all the host info associated between these two. */ + hpsb_destroy_hostinfo(hl, host); +} + +static int highlevel_for_each_host_unreg(struct hpsb_host *host, void *__data) +{ + struct hpsb_highlevel *hl = __data; + + __unregister_host(hl, host, 1); + return 0; +} + +/** + * hpsb_unregister_highlevel - unregister highlevel driver + */ +void hpsb_unregister_highlevel(struct hpsb_highlevel *hl) +{ + unsigned long flags; + + write_lock_irqsave(&hl_irqs_lock, flags); + list_del(&hl->irq_list); + write_unlock_irqrestore(&hl_irqs_lock, flags); + + down_write(&hl_drivers_sem); + list_del(&hl->hl_list); + up_write(&hl_drivers_sem); + + nodemgr_for_each_host(hl, highlevel_for_each_host_unreg); +} + +/** + * hpsb_allocate_and_register_addrspace - alloc' and reg' a host address space + * + * @start and @end are 48 bit pointers and have to be quadlet aligned. + * @end points to the first address behind the handled addresses. This + * function can be called multiple times for a single hpsb_highlevel @hl to + * implement sparse register sets. The requested region must not overlap any + * previously allocated region, otherwise registering will fail. + * + * It returns true for successful allocation. Address spaces can be + * unregistered with hpsb_unregister_addrspace. All remaining address spaces + * are automatically deallocated together with the hpsb_highlevel @hl. + */ +u64 hpsb_allocate_and_register_addrspace(struct hpsb_highlevel *hl, + struct hpsb_host *host, + const struct hpsb_address_ops *ops, + u64 size, u64 alignment, + u64 start, u64 end) +{ + struct hpsb_address_serve *as, *a1, *a2; + struct list_head *entry; + u64 retval = CSR1212_INVALID_ADDR_SPACE; + unsigned long flags; + u64 align_mask = ~(alignment - 1); + + if ((alignment & 3) || (alignment > 0x800000000000ULL) || + (hweight64(alignment) != 1)) { + HPSB_ERR("%s called with invalid alignment: 0x%048llx", + __func__, (unsigned long long)alignment); + return retval; + } + + /* default range, + * avoids controller's posted write area (see OHCI 1.1 clause 1.5) */ + if (start == CSR1212_INVALID_ADDR_SPACE && + end == CSR1212_INVALID_ADDR_SPACE) { + start = host->middle_addr_space; + end = CSR1212_ALL_SPACE_END; + } + + if (((start|end) & ~align_mask) || (start >= end) || + (end > CSR1212_ALL_SPACE_END)) { + HPSB_ERR("%s called with invalid addresses " + "(start = %012Lx end = %012Lx)", __func__, + (unsigned long long)start,(unsigned long long)end); + return retval; + } + + as = kmalloc(sizeof(*as), GFP_KERNEL); + if (!as) + return retval; + + INIT_LIST_HEAD(&as->host_list); + INIT_LIST_HEAD(&as->hl_list); + as->op = ops; + as->host = host; + + write_lock_irqsave(&addr_space_lock, flags); + list_for_each(entry, &host->addr_space) { + u64 a1sa, a1ea; + u64 a2sa, a2ea; + + a1 = list_entry(entry, struct hpsb_address_serve, host_list); + a2 = list_entry(entry->next, struct hpsb_address_serve, + host_list); + + a1sa = a1->start & align_mask; + a1ea = (a1->end + alignment -1) & align_mask; + a2sa = a2->start & align_mask; + a2ea = (a2->end + alignment -1) & align_mask; + + if ((a2sa - a1ea >= size) && (a2sa - start >= size) && + (a2sa > start)) { + as->start = max(start, a1ea); + as->end = as->start + size; + list_add(&as->host_list, entry); + list_add_tail(&as->hl_list, &hl->addr_list); + retval = as->start; + break; + } + } + write_unlock_irqrestore(&addr_space_lock, flags); + + if (retval == CSR1212_INVALID_ADDR_SPACE) + kfree(as); + return retval; +} + +/** + * hpsb_register_addrspace - register a host address space + * + * @start and @end are 48 bit pointers and have to be quadlet aligned. + * @end points to the first address behind the handled addresses. This + * function can be called multiple times for a single hpsb_highlevel @hl to + * implement sparse register sets. The requested region must not overlap any + * previously allocated region, otherwise registering will fail. + * + * It returns true for successful allocation. Address spaces can be + * unregistered with hpsb_unregister_addrspace. All remaining address spaces + * are automatically deallocated together with the hpsb_highlevel @hl. + */ +int hpsb_register_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host, + const struct hpsb_address_ops *ops, + u64 start, u64 end) +{ + struct hpsb_address_serve *as; + struct list_head *lh; + int retval = 0; + unsigned long flags; + + if (((start|end) & 3) || (start >= end) || + (end > CSR1212_ALL_SPACE_END)) { + HPSB_ERR("%s called with invalid addresses", __func__); + return 0; + } + + as = kmalloc(sizeof(*as), GFP_KERNEL); + if (!as) + return 0; + + INIT_LIST_HEAD(&as->host_list); + INIT_LIST_HEAD(&as->hl_list); + as->op = ops; + as->start = start; + as->end = end; + as->host = host; + + write_lock_irqsave(&addr_space_lock, flags); + list_for_each(lh, &host->addr_space) { + struct hpsb_address_serve *as_this = + list_entry(lh, struct hpsb_address_serve, host_list); + struct hpsb_address_serve *as_next = + list_entry(lh->next, struct hpsb_address_serve, + host_list); + + if (as_this->end > as->start) + break; + + if (as_next->start >= as->end) { + list_add(&as->host_list, lh); + list_add_tail(&as->hl_list, &hl->addr_list); + retval = 1; + break; + } + } + write_unlock_irqrestore(&addr_space_lock, flags); + + if (retval == 0) + kfree(as); + return retval; +} + +int hpsb_unregister_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host, + u64 start) +{ + int retval = 0; + struct hpsb_address_serve *as; + struct list_head *lh, *next; + unsigned long flags; + + write_lock_irqsave(&addr_space_lock, flags); + list_for_each_safe (lh, next, &hl->addr_list) { + as = list_entry(lh, struct hpsb_address_serve, hl_list); + if (as->start == start && as->host == host) { + __delete_addr(as); + retval = 1; + break; + } + } + write_unlock_irqrestore(&addr_space_lock, flags); + return retval; +} + +static const struct hpsb_address_ops dummy_ops; + +/* dummy address spaces as lower and upper bounds of the host's a.s. list */ +static void init_hpsb_highlevel(struct hpsb_host *host) +{ + INIT_LIST_HEAD(&host->dummy_zero_addr.host_list); + INIT_LIST_HEAD(&host->dummy_zero_addr.hl_list); + INIT_LIST_HEAD(&host->dummy_max_addr.host_list); + INIT_LIST_HEAD(&host->dummy_max_addr.hl_list); + + host->dummy_zero_addr.op = host->dummy_max_addr.op = &dummy_ops; + + host->dummy_zero_addr.start = host->dummy_zero_addr.end = 0; + host->dummy_max_addr.start = host->dummy_max_addr.end = ((u64) 1) << 48; + + list_add_tail(&host->dummy_zero_addr.host_list, &host->addr_space); + list_add_tail(&host->dummy_max_addr.host_list, &host->addr_space); +} + +void highlevel_add_host(struct hpsb_host *host) +{ + struct hpsb_highlevel *hl; + + init_hpsb_highlevel(host); + + down_read(&hl_drivers_sem); + list_for_each_entry(hl, &hl_drivers, hl_list) { + if (hl->add_host) + hl->add_host(host); + } + up_read(&hl_drivers_sem); + if (host->update_config_rom && hpsb_update_config_rom_image(host) < 0) + HPSB_ERR("Failed to generate Configuration ROM image for host " + "%s-%d", hl->name, host->id); +} + +void highlevel_remove_host(struct hpsb_host *host) +{ + struct hpsb_highlevel *hl; + + down_read(&hl_drivers_sem); + list_for_each_entry(hl, &hl_drivers, hl_list) + __unregister_host(hl, host, 0); + up_read(&hl_drivers_sem); +} + +void highlevel_host_reset(struct hpsb_host *host) +{ + unsigned long flags; + struct hpsb_highlevel *hl; + + read_lock_irqsave(&hl_irqs_lock, flags); + list_for_each_entry(hl, &hl_irqs, irq_list) { + if (hl->host_reset) + hl->host_reset(host); + } + read_unlock_irqrestore(&hl_irqs_lock, flags); +} + +void highlevel_fcp_request(struct hpsb_host *host, int nodeid, int direction, + void *data, size_t length) +{ + unsigned long flags; + struct hpsb_highlevel *hl; + int cts = ((quadlet_t *)data)[0] >> 4; + + read_lock_irqsave(&hl_irqs_lock, flags); + list_for_each_entry(hl, &hl_irqs, irq_list) { + if (hl->fcp_request) + hl->fcp_request(host, nodeid, direction, cts, data, + length); + } + read_unlock_irqrestore(&hl_irqs_lock, flags); +} + +/* + * highlevel_read, highlevel_write, highlevel_lock, highlevel_lock64: + * + * These functions are called to handle transactions. They are called when a + * packet arrives. The flags argument contains the second word of the first + * header quadlet of the incoming packet (containing transaction label, retry + * code, transaction code and priority). These functions either return a + * response code or a negative number. In the first case a response will be + * generated. In the latter case, no response will be sent and the driver which + * handled the request will send the response itself. + */ +int highlevel_read(struct hpsb_host *host, int nodeid, void *data, u64 addr, + unsigned int length, u16 flags) +{ + struct hpsb_address_serve *as; + unsigned int partlength; + int rcode = RCODE_ADDRESS_ERROR; + + read_lock(&addr_space_lock); + list_for_each_entry(as, &host->addr_space, host_list) { + if (as->start > addr) + break; + + if (as->end > addr) { + partlength = min(as->end - addr, (u64) length); + + if (as->op->read) + rcode = as->op->read(host, nodeid, data, + addr, partlength, flags); + else + rcode = RCODE_TYPE_ERROR; + + data += partlength; + length -= partlength; + addr += partlength; + + if ((rcode != RCODE_COMPLETE) || !length) + break; + } + } + read_unlock(&addr_space_lock); + + if (length && (rcode == RCODE_COMPLETE)) + rcode = RCODE_ADDRESS_ERROR; + return rcode; +} + +int highlevel_write(struct hpsb_host *host, int nodeid, int destid, void *data, + u64 addr, unsigned int length, u16 flags) +{ + struct hpsb_address_serve *as; + unsigned int partlength; + int rcode = RCODE_ADDRESS_ERROR; + + read_lock(&addr_space_lock); + list_for_each_entry(as, &host->addr_space, host_list) { + if (as->start > addr) + break; + + if (as->end > addr) { + partlength = min(as->end - addr, (u64) length); + + if (as->op->write) + rcode = as->op->write(host, nodeid, destid, + data, addr, partlength, + flags); + else + rcode = RCODE_TYPE_ERROR; + + data += partlength; + length -= partlength; + addr += partlength; + + if ((rcode != RCODE_COMPLETE) || !length) + break; + } + } + read_unlock(&addr_space_lock); + + if (length && (rcode == RCODE_COMPLETE)) + rcode = RCODE_ADDRESS_ERROR; + return rcode; +} + +int highlevel_lock(struct hpsb_host *host, int nodeid, quadlet_t *store, + u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode, + u16 flags) +{ + struct hpsb_address_serve *as; + int rcode = RCODE_ADDRESS_ERROR; + + read_lock(&addr_space_lock); + list_for_each_entry(as, &host->addr_space, host_list) { + if (as->start > addr) + break; + + if (as->end > addr) { + if (as->op->lock) + rcode = as->op->lock(host, nodeid, store, addr, + data, arg, ext_tcode, + flags); + else + rcode = RCODE_TYPE_ERROR; + break; + } + } + read_unlock(&addr_space_lock); + return rcode; +} + +int highlevel_lock64(struct hpsb_host *host, int nodeid, octlet_t *store, + u64 addr, octlet_t data, octlet_t arg, int ext_tcode, + u16 flags) +{ + struct hpsb_address_serve *as; + int rcode = RCODE_ADDRESS_ERROR; + + read_lock(&addr_space_lock); + + list_for_each_entry(as, &host->addr_space, host_list) { + if (as->start > addr) + break; + + if (as->end > addr) { + if (as->op->lock64) + rcode = as->op->lock64(host, nodeid, store, + addr, data, arg, + ext_tcode, flags); + else + rcode = RCODE_TYPE_ERROR; + break; + } + } + read_unlock(&addr_space_lock); + return rcode; +} diff --git a/trunk/drivers/ieee1394/highlevel.h b/trunk/drivers/ieee1394/highlevel.h new file mode 100644 index 000000000000..9dba89fc60ad --- /dev/null +++ b/trunk/drivers/ieee1394/highlevel.h @@ -0,0 +1,141 @@ +#ifndef IEEE1394_HIGHLEVEL_H +#define IEEE1394_HIGHLEVEL_H + +#include +#include +#include + +struct module; + +#include "ieee1394_types.h" + +struct hpsb_host; + +/* internal to ieee1394 core */ +struct hpsb_address_serve { + struct list_head host_list; /* per host list */ + struct list_head hl_list; /* hpsb_highlevel list */ + const struct hpsb_address_ops *op; + struct hpsb_host *host; + u64 start; /* first address handled, quadlet aligned */ + u64 end; /* first address behind, quadlet aligned */ +}; + +/* Only the following structures are of interest to actual highlevel drivers. */ + +struct hpsb_highlevel { + const char *name; + + /* Any of the following pointers can legally be NULL. */ + + /* New host initialized. Will also be called during + * hpsb_register_highlevel for all hosts already installed. */ + void (*add_host)(struct hpsb_host *host); + + /* Host about to be removed. Will also be called during + * hpsb_unregister_highlevel once for each host. */ + void (*remove_host)(struct hpsb_host *host); + + /* Host experienced bus reset with possible configuration changes. + * Note that this one may occur during interrupt/bottom half handling. + * You can not expect to be able to do stock hpsb_reads. */ + void (*host_reset)(struct hpsb_host *host); + + /* A write request was received on either the FCP_COMMAND (direction = + * 0) or the FCP_RESPONSE (direction = 1) register. The cts arg + * contains the cts field (first byte of data). */ + void (*fcp_request)(struct hpsb_host *host, int nodeid, int direction, + int cts, u8 *data, size_t length); + + /* These are initialized by the subsystem when the + * hpsb_higlevel is registered. */ + struct list_head hl_list; + struct list_head irq_list; + struct list_head addr_list; + + struct list_head host_info_list; + rwlock_t host_info_lock; +}; + +struct hpsb_address_ops { + /* + * Null function pointers will make the respective operation complete + * with RCODE_TYPE_ERROR. Makes for easy to implement read-only + * registers (just leave everything but read NULL). + * + * All functions shall return appropriate IEEE 1394 rcodes. + */ + + /* These functions have to implement block reads for themselves. + * + * These functions either return a response code or a negative number. + * In the first case a response will be generated. In the latter case, + * no response will be sent and the driver which handled the request + * will send the response itself. */ + int (*read)(struct hpsb_host *host, int nodeid, quadlet_t *buffer, + u64 addr, size_t length, u16 flags); + int (*write)(struct hpsb_host *host, int nodeid, int destid, + quadlet_t *data, u64 addr, size_t length, u16 flags); + + /* Lock transactions: write results of ext_tcode operation into + * *store. */ + int (*lock)(struct hpsb_host *host, int nodeid, quadlet_t *store, + u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode, + u16 flags); + int (*lock64)(struct hpsb_host *host, int nodeid, octlet_t *store, + u64 addr, octlet_t data, octlet_t arg, int ext_tcode, + u16 flags); +}; + +void highlevel_add_host(struct hpsb_host *host); +void highlevel_remove_host(struct hpsb_host *host); +void highlevel_host_reset(struct hpsb_host *host); +int highlevel_read(struct hpsb_host *host, int nodeid, void *data, u64 addr, + unsigned int length, u16 flags); +int highlevel_write(struct hpsb_host *host, int nodeid, int destid, void *data, + u64 addr, unsigned int length, u16 flags); +int highlevel_lock(struct hpsb_host *host, int nodeid, quadlet_t *store, + u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode, + u16 flags); +int highlevel_lock64(struct hpsb_host *host, int nodeid, octlet_t *store, + u64 addr, octlet_t data, octlet_t arg, int ext_tcode, + u16 flags); +void highlevel_fcp_request(struct hpsb_host *host, int nodeid, int direction, + void *data, size_t length); + +/** + * hpsb_init_highlevel - initialize a struct hpsb_highlevel + * + * This is only necessary if hpsb_get_hostinfo_bykey can be called + * before hpsb_register_highlevel. + */ +static inline void hpsb_init_highlevel(struct hpsb_highlevel *hl) +{ + rwlock_init(&hl->host_info_lock); + INIT_LIST_HEAD(&hl->host_info_list); +} +void hpsb_register_highlevel(struct hpsb_highlevel *hl); +void hpsb_unregister_highlevel(struct hpsb_highlevel *hl); + +u64 hpsb_allocate_and_register_addrspace(struct hpsb_highlevel *hl, + struct hpsb_host *host, + const struct hpsb_address_ops *ops, + u64 size, u64 alignment, + u64 start, u64 end); +int hpsb_register_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host, + const struct hpsb_address_ops *ops, + u64 start, u64 end); +int hpsb_unregister_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host, + u64 start); + +void *hpsb_get_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host); +void *hpsb_create_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, + size_t data_size); +void hpsb_destroy_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host); +void hpsb_set_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host, + unsigned long key); +void *hpsb_get_hostinfo_bykey(struct hpsb_highlevel *hl, unsigned long key); +int hpsb_set_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, + void *data); + +#endif /* IEEE1394_HIGHLEVEL_H */ diff --git a/trunk/drivers/ieee1394/hosts.c b/trunk/drivers/ieee1394/hosts.c new file mode 100644 index 000000000000..e947d8ffac85 --- /dev/null +++ b/trunk/drivers/ieee1394/hosts.c @@ -0,0 +1,249 @@ +/* + * IEEE 1394 for Linux + * + * Low level (host adapter) management. + * + * Copyright (C) 1999 Andreas E. Bombe + * Copyright (C) 1999 Emanuel Pirker + * + * This code is licensed under the GPL. See the file COPYING in the root + * directory of the kernel sources for details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "csr1212.h" +#include "ieee1394.h" +#include "ieee1394_types.h" +#include "hosts.h" +#include "ieee1394_core.h" +#include "highlevel.h" +#include "nodemgr.h" +#include "csr.h" +#include "config_roms.h" + + +static void delayed_reset_bus(struct work_struct *work) +{ + struct hpsb_host *host = + container_of(work, struct hpsb_host, delayed_reset.work); + u8 generation = host->csr.generation + 1; + + /* The generation field rolls over to 2 rather than 0 per IEEE + * 1394a-2000. */ + if (generation > 0xf || generation < 2) + generation = 2; + + csr_set_bus_info_generation(host->csr.rom, generation); + if (csr1212_generate_csr_image(host->csr.rom) != CSR1212_SUCCESS) { + /* CSR image creation failed. + * Reset generation field and do not issue a bus reset. */ + csr_set_bus_info_generation(host->csr.rom, + host->csr.generation); + return; + } + + host->csr.generation = generation; + + host->update_config_rom = 0; + if (host->driver->set_hw_config_rom) + host->driver->set_hw_config_rom(host, + host->csr.rom->bus_info_data); + + host->csr.gen_timestamp[host->csr.generation] = jiffies; + hpsb_reset_bus(host, SHORT_RESET); +} + +static int dummy_transmit_packet(struct hpsb_host *h, struct hpsb_packet *p) +{ + return 0; +} + +static int dummy_devctl(struct hpsb_host *h, enum devctl_cmd c, int arg) +{ + return -1; +} + +static int dummy_isoctl(struct hpsb_iso *iso, enum isoctl_cmd command, + unsigned long arg) +{ + return -1; +} + +static struct hpsb_host_driver dummy_driver = { + .transmit_packet = dummy_transmit_packet, + .devctl = dummy_devctl, + .isoctl = dummy_isoctl +}; + +static int alloc_hostnum_cb(struct hpsb_host *host, void *__data) +{ + int *hostnum = __data; + + if (host->id == *hostnum) + return 1; + + return 0; +} + +static DEFINE_MUTEX(host_num_alloc); + +/** + * hpsb_alloc_host - allocate a new host controller. + * @drv: the driver that will manage the host controller + * @extra: number of extra bytes to allocate for the driver + * + * Allocate a &hpsb_host and initialize the general subsystem specific + * fields. If the driver needs to store per host data, as drivers + * usually do, the amount of memory required can be specified by the + * @extra parameter. Once allocated, the driver should initialize the + * driver specific parts, enable the controller and make it available + * to the general subsystem using hpsb_add_host(). + * + * Return Value: a pointer to the &hpsb_host if successful, %NULL if + * no memory was available. + */ +struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra, + struct device *dev) +{ + struct hpsb_host *h; + int i; + int hostnum = 0; + + h = kzalloc(sizeof(*h) + extra, GFP_KERNEL); + if (!h) + return NULL; + + h->csr.rom = csr1212_create_csr(&csr_bus_ops, CSR_BUS_INFO_SIZE, h); + if (!h->csr.rom) + goto fail; + + h->hostdata = h + 1; + h->driver = drv; + + INIT_LIST_HEAD(&h->pending_packets); + INIT_LIST_HEAD(&h->addr_space); + + for (i = 2; i < 16; i++) + h->csr.gen_timestamp[i] = jiffies - 60 * HZ; + + atomic_set(&h->generation, 0); + + INIT_DELAYED_WORK(&h->delayed_reset, delayed_reset_bus); + + init_timer(&h->timeout); + h->timeout.data = (unsigned long) h; + h->timeout.function = abort_timedouts; + h->timeout_interval = HZ / 20; /* 50ms, half of minimum SPLIT_TIMEOUT */ + + h->topology_map = h->csr.topology_map + 3; + h->speed_map = (u8 *)(h->csr.speed_map + 2); + + mutex_lock(&host_num_alloc); + while (nodemgr_for_each_host(&hostnum, alloc_hostnum_cb)) + hostnum++; + mutex_unlock(&host_num_alloc); + h->id = hostnum; + + memcpy(&h->device, &nodemgr_dev_template_host, sizeof(h->device)); + h->device.parent = dev; + set_dev_node(&h->device, dev_to_node(dev)); + dev_set_name(&h->device, "fw-host%d", h->id); + + h->host_dev.parent = &h->device; + h->host_dev.class = &hpsb_host_class; + dev_set_name(&h->host_dev, "fw-host%d", h->id); + + if (device_register(&h->device)) + goto fail; + if (device_register(&h->host_dev)) { + device_unregister(&h->device); + goto fail; + } + get_device(&h->device); + + return h; + +fail: + kfree(h); + return NULL; +} + +int hpsb_add_host(struct hpsb_host *host) +{ + if (hpsb_default_host_entry(host)) + return -ENOMEM; + + highlevel_add_host(host); + return 0; +} + +void hpsb_resume_host(struct hpsb_host *host) +{ + if (host->driver->set_hw_config_rom) + host->driver->set_hw_config_rom(host, + host->csr.rom->bus_info_data); + host->driver->devctl(host, RESET_BUS, SHORT_RESET); +} + +void hpsb_remove_host(struct hpsb_host *host) +{ + host->is_shutdown = 1; + + cancel_delayed_work(&host->delayed_reset); + flush_scheduled_work(); + + host->driver = &dummy_driver; + highlevel_remove_host(host); + + device_unregister(&host->host_dev); + device_unregister(&host->device); +} + +/** + * hpsb_update_config_rom_image - updates configuration ROM image of a host + * + * Updates the configuration ROM image of a host. rom_version must be the + * current version, otherwise it will fail with return value -1. If this + * host does not support config-rom-update, it will return -%EINVAL. + * Return value 0 indicates success. + */ +int hpsb_update_config_rom_image(struct hpsb_host *host) +{ + unsigned long reset_delay; + int next_gen = host->csr.generation + 1; + + if (!host->update_config_rom) + return -EINVAL; + + if (next_gen > 0xf) + next_gen = 2; + + /* Stop the delayed interrupt, we're about to change the config rom and + * it would be a waste to do a bus reset twice. */ + cancel_delayed_work(&host->delayed_reset); + + /* IEEE 1394a-2000 prohibits using the same generation number + * twice in a 60 second period. */ + if (time_before(jiffies, host->csr.gen_timestamp[next_gen] + 60 * HZ)) + /* Wait 60 seconds from the last time this generation number was + * used. */ + reset_delay = + (60 * HZ) + host->csr.gen_timestamp[next_gen] - jiffies; + else + /* Wait 1 second in case some other code wants to change the + * Config ROM in the near future. */ + reset_delay = HZ; + + PREPARE_DELAYED_WORK(&host->delayed_reset, delayed_reset_bus); + schedule_delayed_work(&host->delayed_reset, reset_delay); + + return 0; +} diff --git a/trunk/drivers/ieee1394/hosts.h b/trunk/drivers/ieee1394/hosts.h new file mode 100644 index 000000000000..49c359022c54 --- /dev/null +++ b/trunk/drivers/ieee1394/hosts.h @@ -0,0 +1,201 @@ +#ifndef _IEEE1394_HOSTS_H +#define _IEEE1394_HOSTS_H + +#include +#include +#include +#include +#include +#include + +struct pci_dev; +struct module; + +#include "ieee1394_types.h" +#include "csr.h" +#include "highlevel.h" + +struct hpsb_packet; +struct hpsb_iso; + +struct hpsb_host { + struct list_head host_list; + + void *hostdata; + + atomic_t generation; + + struct list_head pending_packets; + struct timer_list timeout; + unsigned long timeout_interval; + + int node_count; /* number of identified nodes on this bus */ + int selfid_count; /* total number of SelfIDs received */ + int nodes_active; /* number of nodes with active link layer */ + + nodeid_t node_id; /* node ID of this host */ + nodeid_t irm_id; /* ID of this bus' isochronous resource manager */ + nodeid_t busmgr_id; /* ID of this bus' bus manager */ + + /* this nodes state */ + unsigned in_bus_reset:1; + unsigned is_shutdown:1; + unsigned resume_packet_sent:1; + + /* this nodes' duties on the bus */ + unsigned is_root:1; + unsigned is_cycmst:1; + unsigned is_irm:1; + unsigned is_busmgr:1; + + int reset_retries; + quadlet_t *topology_map; + u8 *speed_map; + + int id; + struct hpsb_host_driver *driver; + struct pci_dev *pdev; + struct device device; + struct device host_dev; + + struct delayed_work delayed_reset; + unsigned config_roms:31; + unsigned update_config_rom:1; + + struct list_head addr_space; + u64 low_addr_space; /* upper bound of physical DMA area */ + u64 middle_addr_space; /* upper bound of posted write area */ + + u8 speed[ALL_NODES]; /* speed between each node and local node */ + + /* per node tlabel allocation */ + u8 next_tl[ALL_NODES]; + struct { DECLARE_BITMAP(map, 64); } tl_pool[ALL_NODES]; + + struct csr_control csr; + + struct hpsb_address_serve dummy_zero_addr; + struct hpsb_address_serve dummy_max_addr; +}; + +enum devctl_cmd { + /* Host is requested to reset its bus and cancel all outstanding async + * requests. If arg == 1, it shall also attempt to become root on the + * bus. Return void. */ + RESET_BUS, + + /* Arg is void, return value is the hardware cycle counter value. */ + GET_CYCLE_COUNTER, + + /* Set the hardware cycle counter to the value in arg, return void. + * FIXME - setting is probably not required. */ + SET_CYCLE_COUNTER, + + /* Configure hardware for new bus ID in arg, return void. */ + SET_BUS_ID, + + /* If arg true, start sending cycle start packets, stop if arg == 0. + * Return void. */ + ACT_CYCLE_MASTER, + + /* Cancel all outstanding async requests without resetting the bus. + * Return void. */ + CANCEL_REQUESTS, +}; + +enum isoctl_cmd { + /* rawiso API - see iso.h for the meanings of these commands + * (they correspond exactly to the hpsb_iso_* API functions) + * INIT = allocate resources + * START = begin transmission/reception + * STOP = halt transmission/reception + * QUEUE/RELEASE = produce/consume packets + * SHUTDOWN = deallocate resources + */ + + XMIT_INIT, + XMIT_START, + XMIT_STOP, + XMIT_QUEUE, + XMIT_SHUTDOWN, + + RECV_INIT, + RECV_LISTEN_CHANNEL, /* multi-channel only */ + RECV_UNLISTEN_CHANNEL, /* multi-channel only */ + RECV_SET_CHANNEL_MASK, /* multi-channel only; arg is a *u64 */ + RECV_START, + RECV_STOP, + RECV_RELEASE, + RECV_SHUTDOWN, + RECV_FLUSH +}; + +enum reset_types { + /* 166 microsecond reset -- only type of reset available on + non-1394a capable controllers */ + LONG_RESET, + + /* Short (arbitrated) reset -- only available on 1394a capable + controllers */ + SHORT_RESET, + + /* Variants that set force_root before issueing the bus reset */ + LONG_RESET_FORCE_ROOT, SHORT_RESET_FORCE_ROOT, + + /* Variants that clear force_root before issueing the bus reset */ + LONG_RESET_NO_FORCE_ROOT, SHORT_RESET_NO_FORCE_ROOT +}; + +struct hpsb_host_driver { + struct module *owner; + const char *name; + + /* The hardware driver may optionally support a function that is used + * to set the hardware ConfigROM if the hardware supports handling + * reads to the ConfigROM on its own. */ + void (*set_hw_config_rom)(struct hpsb_host *host, + __be32 *config_rom); + + /* This function shall implement packet transmission based on + * packet->type. It shall CRC both parts of the packet (unless + * packet->type == raw) and do byte-swapping as necessary or instruct + * the hardware to do so. It can return immediately after the packet + * was queued for sending. After sending, hpsb_sent_packet() has to be + * called. Return 0 on success, negative errno on failure. + * NOTE: The function must be callable in interrupt context. + */ + int (*transmit_packet)(struct hpsb_host *host, + struct hpsb_packet *packet); + + /* This function requests miscellanous services from the driver, see + * above for command codes and expected actions. Return -1 for unknown + * command, though that should never happen. + */ + int (*devctl)(struct hpsb_host *host, enum devctl_cmd command, int arg); + + /* ISO transmission/reception functions. Return 0 on success, -1 + * (or -EXXX errno code) on failure. If the low-level driver does not + * support the new ISO API, set isoctl to NULL. + */ + int (*isoctl)(struct hpsb_iso *iso, enum isoctl_cmd command, + unsigned long arg); + + /* This function is mainly to redirect local CSR reads/locks to the iso + * management registers (bus manager id, bandwidth available, channels + * available) to the hardware registers in OHCI. reg is 0,1,2,3 for bus + * mgr, bwdth avail, ch avail hi, ch avail lo respectively (the same ids + * as OHCI uses). data and compare are the new data and expected data + * respectively, return value is the old value. + */ + quadlet_t (*hw_csr_reg) (struct hpsb_host *host, int reg, + quadlet_t data, quadlet_t compare); +}; + +struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra, + struct device *dev); +int hpsb_add_host(struct hpsb_host *host); +void hpsb_resume_host(struct hpsb_host *host); +void hpsb_remove_host(struct hpsb_host *host); +int hpsb_update_config_rom_image(struct hpsb_host *host); + +#endif /* _IEEE1394_HOSTS_H */ diff --git a/trunk/drivers/ieee1394/ieee1394-ioctl.h b/trunk/drivers/ieee1394/ieee1394-ioctl.h new file mode 100644 index 000000000000..46878fef136c --- /dev/null +++ b/trunk/drivers/ieee1394/ieee1394-ioctl.h @@ -0,0 +1,106 @@ +/* + * Base file for all ieee1394 ioctl's. + * Linux-1394 has allocated base '#' with a range of 0x00-0x3f. + */ + +#ifndef __IEEE1394_IOCTL_H +#define __IEEE1394_IOCTL_H + +#include +#include + +/* DV1394 Gets 10 */ + +/* Get the driver ready to transmit video. pass a struct dv1394_init* as + * the parameter (see below), or NULL to get default parameters */ +#define DV1394_IOC_INIT _IOW('#', 0x06, struct dv1394_init) + +/* Stop transmitting video and free the ringbuffer */ +#define DV1394_IOC_SHUTDOWN _IO ('#', 0x07) + +/* Submit N new frames to be transmitted, where the index of the first new + * frame is first_clear_buffer, and the index of the last new frame is + * (first_clear_buffer + N) % n_frames */ +#define DV1394_IOC_SUBMIT_FRAMES _IO ('#', 0x08) + +/* Block until N buffers are clear (pass N as the parameter) Because we + * re-transmit the last frame on underrun, there will at most be n_frames + * - 1 clear frames at any time */ +#define DV1394_IOC_WAIT_FRAMES _IO ('#', 0x09) + +/* Capture new frames that have been received, where the index of the + * first new frame is first_clear_buffer, and the index of the last new + * frame is (first_clear_buffer + N) % n_frames */ +#define DV1394_IOC_RECEIVE_FRAMES _IO ('#', 0x0a) + +/* Tell card to start receiving DMA */ +#define DV1394_IOC_START_RECEIVE _IO ('#', 0x0b) + +/* Pass a struct dv1394_status* as the parameter */ +#define DV1394_IOC_GET_STATUS _IOR('#', 0x0c, struct dv1394_status) + + +/* Video1394 Gets 10 */ + +#define VIDEO1394_IOC_LISTEN_CHANNEL \ + _IOWR('#', 0x10, struct video1394_mmap) +#define VIDEO1394_IOC_UNLISTEN_CHANNEL \ + _IOW ('#', 0x11, int) +#define VIDEO1394_IOC_LISTEN_QUEUE_BUFFER \ + _IOW ('#', 0x12, struct video1394_wait) +#define VIDEO1394_IOC_LISTEN_WAIT_BUFFER \ + _IOWR('#', 0x13, struct video1394_wait) +#define VIDEO1394_IOC_TALK_CHANNEL \ + _IOWR('#', 0x14, struct video1394_mmap) +#define VIDEO1394_IOC_UNTALK_CHANNEL \ + _IOW ('#', 0x15, int) +/* + * This one is broken: it really wanted + * "sizeof (struct video1394_wait) + sizeof (struct video1394_queue_variable)" + * but got just a "size_t" + */ +#define VIDEO1394_IOC_TALK_QUEUE_BUFFER \ + _IOW ('#', 0x16, size_t) +#define VIDEO1394_IOC_TALK_WAIT_BUFFER \ + _IOW ('#', 0x17, struct video1394_wait) +#define VIDEO1394_IOC_LISTEN_POLL_BUFFER \ + _IOWR('#', 0x18, struct video1394_wait) + + +/* Raw1394's ISO interface */ +#define RAW1394_IOC_ISO_XMIT_INIT \ + _IOW ('#', 0x1a, struct raw1394_iso_status) +#define RAW1394_IOC_ISO_RECV_INIT \ + _IOWR('#', 0x1b, struct raw1394_iso_status) +#define RAW1394_IOC_ISO_RECV_START \ + _IOC (_IOC_WRITE, '#', 0x1c, sizeof(int) * 3) +#define RAW1394_IOC_ISO_XMIT_START \ + _IOC (_IOC_WRITE, '#', 0x1d, sizeof(int) * 2) +#define RAW1394_IOC_ISO_XMIT_RECV_STOP \ + _IO ('#', 0x1e) +#define RAW1394_IOC_ISO_GET_STATUS \ + _IOR ('#', 0x1f, struct raw1394_iso_status) +#define RAW1394_IOC_ISO_SHUTDOWN \ + _IO ('#', 0x20) +#define RAW1394_IOC_ISO_QUEUE_ACTIVITY \ + _IO ('#', 0x21) +#define RAW1394_IOC_ISO_RECV_LISTEN_CHANNEL \ + _IOW ('#', 0x22, unsigned char) +#define RAW1394_IOC_ISO_RECV_UNLISTEN_CHANNEL \ + _IOW ('#', 0x23, unsigned char) +#define RAW1394_IOC_ISO_RECV_SET_CHANNEL_MASK \ + _IOW ('#', 0x24, __u64) +#define RAW1394_IOC_ISO_RECV_PACKETS \ + _IOW ('#', 0x25, struct raw1394_iso_packets) +#define RAW1394_IOC_ISO_RECV_RELEASE_PACKETS \ + _IOW ('#', 0x26, unsigned int) +#define RAW1394_IOC_ISO_XMIT_PACKETS \ + _IOW ('#', 0x27, struct raw1394_iso_packets) +#define RAW1394_IOC_ISO_XMIT_SYNC \ + _IO ('#', 0x28) +#define RAW1394_IOC_ISO_RECV_FLUSH \ + _IO ('#', 0x29) +#define RAW1394_IOC_GET_CYCLE_TIMER \ + _IOR ('#', 0x30, struct raw1394_cycle_timer) + +#endif /* __IEEE1394_IOCTL_H */ diff --git a/trunk/drivers/ieee1394/ieee1394.h b/trunk/drivers/ieee1394/ieee1394.h new file mode 100644 index 000000000000..af320e2c5079 --- /dev/null +++ b/trunk/drivers/ieee1394/ieee1394.h @@ -0,0 +1,220 @@ +/* + * Generic IEEE 1394 definitions + */ + +#ifndef _IEEE1394_IEEE1394_H +#define _IEEE1394_IEEE1394_H + +#define TCODE_WRITEQ 0x0 +#define TCODE_WRITEB 0x1 +#define TCODE_WRITE_RESPONSE 0x2 +#define TCODE_READQ 0x4 +#define TCODE_READB 0x5 +#define TCODE_READQ_RESPONSE 0x6 +#define TCODE_READB_RESPONSE 0x7 +#define TCODE_CYCLE_START 0x8 +#define TCODE_LOCK_REQUEST 0x9 +#define TCODE_ISO_DATA 0xa +#define TCODE_STREAM_DATA 0xa +#define TCODE_LOCK_RESPONSE 0xb + +#define RCODE_COMPLETE 0x0 +#define RCODE_CONFLICT_ERROR 0x4 +#define RCODE_DATA_ERROR 0x5 +#define RCODE_TYPE_ERROR 0x6 +#define RCODE_ADDRESS_ERROR 0x7 + +#define EXTCODE_MASK_SWAP 0x1 +#define EXTCODE_COMPARE_SWAP 0x2 +#define EXTCODE_FETCH_ADD 0x3 +#define EXTCODE_LITTLE_ADD 0x4 +#define EXTCODE_BOUNDED_ADD 0x5 +#define EXTCODE_WRAP_ADD 0x6 + +#define ACK_COMPLETE 0x1 +#define ACK_PENDING 0x2 +#define ACK_BUSY_X 0x4 +#define ACK_BUSY_A 0x5 +#define ACK_BUSY_B 0x6 +#define ACK_TARDY 0xb +#define ACK_CONFLICT_ERROR 0xc +#define ACK_DATA_ERROR 0xd +#define ACK_TYPE_ERROR 0xe +#define ACK_ADDRESS_ERROR 0xf + +/* Non-standard "ACK codes" for internal use */ +#define ACKX_NONE (-1) +#define ACKX_SEND_ERROR (-2) +#define ACKX_ABORTED (-3) +#define ACKX_TIMEOUT (-4) + +#define IEEE1394_SPEED_100 0x00 +#define IEEE1394_SPEED_200 0x01 +#define IEEE1394_SPEED_400 0x02 +#define IEEE1394_SPEED_800 0x03 +#define IEEE1394_SPEED_1600 0x04 +#define IEEE1394_SPEED_3200 0x05 +#define IEEE1394_SPEED_MAX IEEE1394_SPEED_3200 + +/* Maps speed values above to a string representation */ +extern const char *hpsb_speedto_str[]; + +/* 1394a cable PHY packets */ +#define SELFID_PWRCL_NO_POWER 0x0 +#define SELFID_PWRCL_PROVIDE_15W 0x1 +#define SELFID_PWRCL_PROVIDE_30W 0x2 +#define SELFID_PWRCL_PROVIDE_45W 0x3 +#define SELFID_PWRCL_USE_1W 0x4 +#define SELFID_PWRCL_USE_3W 0x5 +#define SELFID_PWRCL_USE_6W 0x6 +#define SELFID_PWRCL_USE_10W 0x7 + +#define SELFID_PORT_CHILD 0x3 +#define SELFID_PORT_PARENT 0x2 +#define SELFID_PORT_NCONN 0x1 +#define SELFID_PORT_NONE 0x0 + +#define SELFID_SPEED_UNKNOWN 0x3 /* 1394b PHY */ + +#define PHYPACKET_LINKON 0x40000000 +#define PHYPACKET_PHYCONFIG_R 0x00800000 +#define PHYPACKET_PHYCONFIG_T 0x00400000 +#define EXTPHYPACKET_TYPE_PING 0x00000000 +#define EXTPHYPACKET_TYPE_REMOTEACCESS_BASE 0x00040000 +#define EXTPHYPACKET_TYPE_REMOTEACCESS_PAGED 0x00140000 +#define EXTPHYPACKET_TYPE_REMOTEREPLY_BASE 0x000C0000 +#define EXTPHYPACKET_TYPE_REMOTEREPLY_PAGED 0x001C0000 +#define EXTPHYPACKET_TYPE_REMOTECOMMAND 0x00200000 +#define EXTPHYPACKET_TYPE_REMOTECONFIRMATION 0x00280000 +#define EXTPHYPACKET_TYPE_RESUME 0x003C0000 + +#define EXTPHYPACKET_TYPEMASK 0xC0FC0000 + +#define PHYPACKET_PORT_SHIFT 24 +#define PHYPACKET_GAPCOUNT_SHIFT 16 + +/* 1394a PHY register map bitmasks */ +#define PHY_00_PHYSICAL_ID 0xFC +#define PHY_00_R 0x02 /* Root */ +#define PHY_00_PS 0x01 /* Power Status*/ +#define PHY_01_RHB 0x80 /* Root Hold-Off */ +#define PHY_01_IBR 0x80 /* Initiate Bus Reset */ +#define PHY_01_GAP_COUNT 0x3F +#define PHY_02_EXTENDED 0xE0 /* 0x7 for 1394a-compliant PHY */ +#define PHY_02_TOTAL_PORTS 0x1F +#define PHY_03_MAX_SPEED 0xE0 +#define PHY_03_DELAY 0x0F +#define PHY_04_LCTRL 0x80 /* Link Active Report Control */ +#define PHY_04_CONTENDER 0x40 +#define PHY_04_JITTER 0x38 +#define PHY_04_PWR_CLASS 0x07 /* Power Class */ +#define PHY_05_WATCHDOG 0x80 +#define PHY_05_ISBR 0x40 /* Initiate Short Bus Reset */ +#define PHY_05_LOOP 0x20 /* Loop Detect */ +#define PHY_05_PWR_FAIL 0x10 /* Cable Power Failure Detect */ +#define PHY_05_TIMEOUT 0x08 /* Arbitration State Machine Timeout */ +#define PHY_05_PORT_EVENT 0x04 /* Port Event Detect */ +#define PHY_05_ENAB_ACCEL 0x02 /* Enable Arbitration Acceleration */ +#define PHY_05_ENAB_MULTI 0x01 /* Ena. Multispeed Packet Concatenation */ + +#include + +/* '1' '3' '9' '4' in ASCII */ +#define IEEE1394_BUSID_MAGIC cpu_to_be32(0x31333934) + +#ifdef __BIG_ENDIAN_BITFIELD + +struct selfid { + u32 packet_identifier:2; /* always binary 10 */ + u32 phy_id:6; + /* byte */ + u32 extended:1; /* if true is struct ext_selfid */ + u32 link_active:1; + u32 gap_count:6; + /* byte */ + u32 speed:2; + u32 phy_delay:2; + u32 contender:1; + u32 power_class:3; + /* byte */ + u32 port0:2; + u32 port1:2; + u32 port2:2; + u32 initiated_reset:1; + u32 more_packets:1; +} __attribute__((packed)); + +struct ext_selfid { + u32 packet_identifier:2; /* always binary 10 */ + u32 phy_id:6; + /* byte */ + u32 extended:1; /* if false is struct selfid */ + u32 seq_nr:3; + u32 reserved:2; + u32 porta:2; + /* byte */ + u32 portb:2; + u32 portc:2; + u32 portd:2; + u32 porte:2; + /* byte */ + u32 portf:2; + u32 portg:2; + u32 porth:2; + u32 reserved2:1; + u32 more_packets:1; +} __attribute__((packed)); + +#elif defined __LITTLE_ENDIAN_BITFIELD /* __BIG_ENDIAN_BITFIELD */ + +/* + * Note: these mean to be bit fields of a big endian SelfID as seen on a little + * endian machine. Without swapping. + */ + +struct selfid { + u32 phy_id:6; + u32 packet_identifier:2; /* always binary 10 */ + /* byte */ + u32 gap_count:6; + u32 link_active:1; + u32 extended:1; /* if true is struct ext_selfid */ + /* byte */ + u32 power_class:3; + u32 contender:1; + u32 phy_delay:2; + u32 speed:2; + /* byte */ + u32 more_packets:1; + u32 initiated_reset:1; + u32 port2:2; + u32 port1:2; + u32 port0:2; +} __attribute__((packed)); + +struct ext_selfid { + u32 phy_id:6; + u32 packet_identifier:2; /* always binary 10 */ + /* byte */ + u32 porta:2; + u32 reserved:2; + u32 seq_nr:3; + u32 extended:1; /* if false is struct selfid */ + /* byte */ + u32 porte:2; + u32 portd:2; + u32 portc:2; + u32 portb:2; + /* byte */ + u32 more_packets:1; + u32 reserved2:1; + u32 porth:2; + u32 portg:2; + u32 portf:2; +} __attribute__((packed)); + +#else +#error What? PDP endian? +#endif /* __BIG_ENDIAN_BITFIELD */ + +#endif /* _IEEE1394_IEEE1394_H */ diff --git a/trunk/drivers/ieee1394/ieee1394_core.c b/trunk/drivers/ieee1394/ieee1394_core.c new file mode 100644 index 000000000000..872338003721 --- /dev/null +++ b/trunk/drivers/ieee1394/ieee1394_core.c @@ -0,0 +1,1380 @@ +/* + * IEEE 1394 for Linux + * + * Core support: hpsb_packet management, packet handling and forwarding to + * highlevel or lowlevel code + * + * Copyright (C) 1999, 2000 Andreas E. Bombe + * 2002 Manfred Weihs + * + * This code is licensed under the GPL. See the file COPYING in the root + * directory of the kernel sources for details. + * + * + * Contributions: + * + * Manfred Weihs + * loopback functionality in hpsb_send_packet + * allow highlevel drivers to disable automatic response generation + * and to generate responses themselves (deferred) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ieee1394_types.h" +#include "ieee1394.h" +#include "hosts.h" +#include "ieee1394_core.h" +#include "highlevel.h" +#include "ieee1394_transactions.h" +#include "csr.h" +#include "nodemgr.h" +#include "dma.h" +#include "iso.h" +#include "config_roms.h" + +/* + * Disable the nodemgr detection and config rom reading functionality. + */ +static int disable_nodemgr; +module_param(disable_nodemgr, int, 0444); +MODULE_PARM_DESC(disable_nodemgr, "Disable nodemgr functionality."); + +/* Disable Isochronous Resource Manager functionality */ +int hpsb_disable_irm = 0; +module_param_named(disable_irm, hpsb_disable_irm, bool, 0444); +MODULE_PARM_DESC(disable_irm, + "Disable Isochronous Resource Manager functionality."); + +/* We are GPL, so treat us special */ +MODULE_LICENSE("GPL"); + +/* Some globals used */ +const char *hpsb_speedto_str[] = { "S100", "S200", "S400", "S800", "S1600", "S3200" }; +struct class *hpsb_protocol_class; + +#ifdef CONFIG_IEEE1394_VERBOSEDEBUG +static void dump_packet(const char *text, quadlet_t *data, int size, int speed) +{ + int i; + + size /= 4; + size = (size > 4 ? 4 : size); + + printk(KERN_DEBUG "ieee1394: %s", text); + if (speed > -1 && speed < 6) + printk(" at %s", hpsb_speedto_str[speed]); + printk(":"); + for (i = 0; i < size; i++) + printk(" %08x", data[i]); + printk("\n"); +} +#else +#define dump_packet(a,b,c,d) do {} while (0) +#endif + +static void abort_requests(struct hpsb_host *host); +static void queue_packet_complete(struct hpsb_packet *packet); + + +/** + * hpsb_set_packet_complete_task - set task that runs when a packet completes + * @packet: the packet whose completion we want the task added to + * @routine: function to call + * @data: data (if any) to pass to the above function + * + * Set the task that runs when a packet completes. You cannot call this more + * than once on a single packet before it is sent. + * + * Typically, the complete @routine is responsible to call hpsb_free_packet(). + */ +void hpsb_set_packet_complete_task(struct hpsb_packet *packet, + void (*routine)(void *), void *data) +{ + WARN_ON(packet->complete_routine != NULL); + packet->complete_routine = routine; + packet->complete_data = data; + return; +} + +/** + * hpsb_alloc_packet - allocate new packet structure + * @data_size: size of the data block to be allocated, in bytes + * + * This function allocates, initializes and returns a new &struct hpsb_packet. + * It can be used in interrupt context. A header block is always included and + * initialized with zeros. Its size is big enough to contain all possible 1394 + * headers. The data block is only allocated if @data_size is not zero. + * + * For packets for which responses will be received the @data_size has to be big + * enough to contain the response's data block since no further allocation + * occurs at response matching time. + * + * The packet's generation value will be set to the current generation number + * for ease of use. Remember to overwrite it with your own recorded generation + * number if you can not be sure that your code will not race with a bus reset. + * + * Return value: A pointer to a &struct hpsb_packet or NULL on allocation + * failure. + */ +struct hpsb_packet *hpsb_alloc_packet(size_t data_size) +{ + struct hpsb_packet *packet; + + data_size = ((data_size + 3) & ~3); + + packet = kzalloc(sizeof(*packet) + data_size, GFP_ATOMIC); + if (!packet) + return NULL; + + packet->state = hpsb_unused; + packet->generation = -1; + INIT_LIST_HEAD(&packet->driver_list); + INIT_LIST_HEAD(&packet->queue); + atomic_set(&packet->refcnt, 1); + + if (data_size) { + packet->data = packet->embedded_data; + packet->allocated_data_size = data_size; + } + return packet; +} + +/** + * hpsb_free_packet - free packet and data associated with it + * @packet: packet to free (is NULL safe) + * + * Frees @packet->data only if it was allocated through hpsb_alloc_packet(). + */ +void hpsb_free_packet(struct hpsb_packet *packet) +{ + if (packet && atomic_dec_and_test(&packet->refcnt)) { + BUG_ON(!list_empty(&packet->driver_list) || + !list_empty(&packet->queue)); + kfree(packet); + } +} + +/** + * hpsb_reset_bus - initiate bus reset on the given host + * @host: host controller whose bus to reset + * @type: one of enum reset_types + * + * Returns 1 if bus reset already in progress, 0 otherwise. + */ +int hpsb_reset_bus(struct hpsb_host *host, int type) +{ + if (!host->in_bus_reset) { + host->driver->devctl(host, RESET_BUS, type); + return 0; + } else { + return 1; + } +} + +/** + * hpsb_read_cycle_timer - read cycle timer register and system time + * @host: host whose isochronous cycle timer register is read + * @cycle_timer: address of bitfield to return the register contents + * @local_time: address to return the system time + * + * The format of * @cycle_timer, is described in OHCI 1.1 clause 5.13. This + * format is also read from non-OHCI controllers. * @local_time contains the + * system time in microseconds since the Epoch, read at the moment when the + * cycle timer was read. + * + * Return value: 0 for success or error number otherwise. + */ +int hpsb_read_cycle_timer(struct hpsb_host *host, u32 *cycle_timer, + u64 *local_time) +{ + int ctr; + struct timeval tv; + unsigned long flags; + + if (!host || !cycle_timer || !local_time) + return -EINVAL; + + preempt_disable(); + local_irq_save(flags); + + ctr = host->driver->devctl(host, GET_CYCLE_COUNTER, 0); + if (ctr) + do_gettimeofday(&tv); + + local_irq_restore(flags); + preempt_enable(); + + if (!ctr) + return -EIO; + *cycle_timer = ctr; + *local_time = tv.tv_sec * 1000000ULL + tv.tv_usec; + return 0; +} + +/** + * hpsb_bus_reset - notify a bus reset to the core + * + * For host driver module usage. Safe to use in interrupt context, although + * quite complex; so you may want to run it in the bottom rather than top half. + * + * Returns 1 if bus reset already in progress, 0 otherwise. + */ +int hpsb_bus_reset(struct hpsb_host *host) +{ + if (host->in_bus_reset) { + HPSB_NOTICE("%s called while bus reset already in progress", + __func__); + return 1; + } + + abort_requests(host); + host->in_bus_reset = 1; + host->irm_id = -1; + host->is_irm = 0; + host->busmgr_id = -1; + host->is_busmgr = 0; + host->is_cycmst = 0; + host->node_count = 0; + host->selfid_count = 0; + + return 0; +} + + +/* + * Verify num_of_selfids SelfIDs and return number of nodes. Return zero in + * case verification failed. + */ +static int check_selfids(struct hpsb_host *host) +{ + int nodeid = -1; + int rest_of_selfids = host->selfid_count; + struct selfid *sid = (struct selfid *)host->topology_map; + struct ext_selfid *esid; + int esid_seq = 23; + + host->nodes_active = 0; + + while (rest_of_selfids--) { + if (!sid->extended) { + nodeid++; + esid_seq = 0; + + if (sid->phy_id != nodeid) { + HPSB_INFO("SelfIDs failed monotony check with " + "%d", sid->phy_id); + return 0; + } + + if (sid->link_active) { + host->nodes_active++; + if (sid->contender) + host->irm_id = LOCAL_BUS | sid->phy_id; + } + } else { + esid = (struct ext_selfid *)sid; + + if ((esid->phy_id != nodeid) + || (esid->seq_nr != esid_seq)) { + HPSB_INFO("SelfIDs failed monotony check with " + "%d/%d", esid->phy_id, esid->seq_nr); + return 0; + } + esid_seq++; + } + sid++; + } + + esid = (struct ext_selfid *)(sid - 1); + while (esid->extended) { + if ((esid->porta == SELFID_PORT_PARENT) || + (esid->portb == SELFID_PORT_PARENT) || + (esid->portc == SELFID_PORT_PARENT) || + (esid->portd == SELFID_PORT_PARENT) || + (esid->porte == SELFID_PORT_PARENT) || + (esid->portf == SELFID_PORT_PARENT) || + (esid->portg == SELFID_PORT_PARENT) || + (esid->porth == SELFID_PORT_PARENT)) { + HPSB_INFO("SelfIDs failed root check on " + "extended SelfID"); + return 0; + } + esid--; + } + + sid = (struct selfid *)esid; + if ((sid->port0 == SELFID_PORT_PARENT) || + (sid->port1 == SELFID_PORT_PARENT) || + (sid->port2 == SELFID_PORT_PARENT)) { + HPSB_INFO("SelfIDs failed root check"); + return 0; + } + + host->node_count = nodeid + 1; + return 1; +} + +static void build_speed_map(struct hpsb_host *host, int nodecount) +{ + u8 cldcnt[nodecount]; + u8 *map = host->speed_map; + u8 *speedcap = host->speed; + u8 local_link_speed = host->csr.lnk_spd; + struct selfid *sid; + struct ext_selfid *esid; + int i, j, n; + + for (i = 0; i < (nodecount * 64); i += 64) { + for (j = 0; j < nodecount; j++) { + map[i+j] = IEEE1394_SPEED_MAX; + } + } + + for (i = 0; i < nodecount; i++) { + cldcnt[i] = 0; + } + + /* find direct children count and speed */ + for (sid = (struct selfid *)&host->topology_map[host->selfid_count-1], + n = nodecount - 1; + (void *)sid >= (void *)host->topology_map; sid--) { + if (sid->extended) { + esid = (struct ext_selfid *)sid; + + if (esid->porta == SELFID_PORT_CHILD) cldcnt[n]++; + if (esid->portb == SELFID_PORT_CHILD) cldcnt[n]++; + if (esid->portc == SELFID_PORT_CHILD) cldcnt[n]++; + if (esid->portd == SELFID_PORT_CHILD) cldcnt[n]++; + if (esid->porte == SELFID_PORT_CHILD) cldcnt[n]++; + if (esid->portf == SELFID_PORT_CHILD) cldcnt[n]++; + if (esid->portg == SELFID_PORT_CHILD) cldcnt[n]++; + if (esid->porth == SELFID_PORT_CHILD) cldcnt[n]++; + } else { + if (sid->port0 == SELFID_PORT_CHILD) cldcnt[n]++; + if (sid->port1 == SELFID_PORT_CHILD) cldcnt[n]++; + if (sid->port2 == SELFID_PORT_CHILD) cldcnt[n]++; + + speedcap[n] = sid->speed; + if (speedcap[n] > local_link_speed) + speedcap[n] = local_link_speed; + n--; + } + } + + /* set self mapping */ + for (i = 0; i < nodecount; i++) { + map[64*i + i] = speedcap[i]; + } + + /* fix up direct children count to total children count; + * also fix up speedcaps for sibling and parent communication */ + for (i = 1; i < nodecount; i++) { + for (j = cldcnt[i], n = i - 1; j > 0; j--) { + cldcnt[i] += cldcnt[n]; + speedcap[n] = min(speedcap[n], speedcap[i]); + n -= cldcnt[n] + 1; + } + } + + for (n = 0; n < nodecount; n++) { + for (i = n - cldcnt[n]; i <= n; i++) { + for (j = 0; j < (n - cldcnt[n]); j++) { + map[j*64 + i] = map[i*64 + j] = + min(map[i*64 + j], speedcap[n]); + } + for (j = n + 1; j < nodecount; j++) { + map[j*64 + i] = map[i*64 + j] = + min(map[i*64 + j], speedcap[n]); + } + } + } + + /* assume a maximum speed for 1394b PHYs, nodemgr will correct it */ + if (local_link_speed > SELFID_SPEED_UNKNOWN) + for (i = 0; i < nodecount; i++) + if (speedcap[i] == SELFID_SPEED_UNKNOWN) + speedcap[i] = local_link_speed; +} + + +/** + * hpsb_selfid_received - hand over received selfid packet to the core + * + * For host driver module usage. Safe to use in interrupt context. + * + * The host driver should have done a successful complement check (second + * quadlet is complement of first) beforehand. + */ +void hpsb_selfid_received(struct hpsb_host *host, quadlet_t sid) +{ + if (host->in_bus_reset) { + HPSB_VERBOSE("Including SelfID 0x%x", sid); + host->topology_map[host->selfid_count++] = sid; + } else { + HPSB_NOTICE("Spurious SelfID packet (0x%08x) received from bus %d", + sid, NODEID_TO_BUS(host->node_id)); + } +} + +/** + * hpsb_selfid_complete - notify completion of SelfID stage to the core + * + * For host driver module usage. Safe to use in interrupt context, although + * quite complex; so you may want to run it in the bottom rather than top half. + * + * Notify completion of SelfID stage to the core and report new physical ID + * and whether host is root now. + */ +void hpsb_selfid_complete(struct hpsb_host *host, int phyid, int isroot) +{ + if (!host->in_bus_reset) + HPSB_NOTICE("SelfID completion called outside of bus reset!"); + + host->node_id = LOCAL_BUS | phyid; + host->is_root = isroot; + + if (!check_selfids(host)) { + if (host->reset_retries++ < 20) { + /* selfid stage did not complete without error */ + HPSB_NOTICE("Error in SelfID stage, resetting"); + host->in_bus_reset = 0; + /* this should work from ohci1394 now... */ + hpsb_reset_bus(host, LONG_RESET); + return; + } else { + HPSB_NOTICE("Stopping out-of-control reset loop"); + HPSB_NOTICE("Warning - topology map and speed map will not be valid"); + host->reset_retries = 0; + } + } else { + host->reset_retries = 0; + build_speed_map(host, host->node_count); + } + + HPSB_VERBOSE("selfid_complete called with successful SelfID stage " + "... irm_id: 0x%X node_id: 0x%X",host->irm_id,host->node_id); + + /* irm_id is kept up to date by check_selfids() */ + if (host->irm_id == host->node_id) { + host->is_irm = 1; + } else { + host->is_busmgr = 0; + host->is_irm = 0; + } + + if (isroot) { + host->driver->devctl(host, ACT_CYCLE_MASTER, 1); + host->is_cycmst = 1; + } + atomic_inc(&host->generation); + host->in_bus_reset = 0; + highlevel_host_reset(host); +} + +static DEFINE_SPINLOCK(pending_packets_lock); + +/** + * hpsb_packet_sent - notify core of sending a packet + * + * For host driver module usage. Safe to call from within a transmit packet + * routine. + * + * Notify core of sending a packet. Ackcode is the ack code returned for async + * transmits or ACKX_SEND_ERROR if the transmission failed completely; ACKX_NONE + * for other cases (internal errors that don't justify a panic). + */ +void hpsb_packet_sent(struct hpsb_host *host, struct hpsb_packet *packet, + int ackcode) +{ + unsigned long flags; + + spin_lock_irqsave(&pending_packets_lock, flags); + + packet->ack_code = ackcode; + + if (packet->no_waiter || packet->state == hpsb_complete) { + /* if packet->no_waiter, must not have a tlabel allocated */ + spin_unlock_irqrestore(&pending_packets_lock, flags); + hpsb_free_packet(packet); + return; + } + + atomic_dec(&packet->refcnt); /* drop HC's reference */ + /* here the packet must be on the host->pending_packets queue */ + + if (ackcode != ACK_PENDING || !packet->expect_response) { + packet->state = hpsb_complete; + list_del_init(&packet->queue); + spin_unlock_irqrestore(&pending_packets_lock, flags); + queue_packet_complete(packet); + return; + } + + packet->state = hpsb_pending; + packet->sendtime = jiffies; + + spin_unlock_irqrestore(&pending_packets_lock, flags); + + mod_timer(&host->timeout, jiffies + host->timeout_interval); +} + +/** + * hpsb_send_phy_config - transmit a PHY configuration packet on the bus + * @host: host that PHY config packet gets sent through + * @rootid: root whose force_root bit should get set (-1 = don't set force_root) + * @gapcnt: gap count value to set (-1 = don't set gap count) + * + * This function sends a PHY config packet on the bus through the specified + * host. + * + * Return value: 0 for success or negative error number otherwise. + */ +int hpsb_send_phy_config(struct hpsb_host *host, int rootid, int gapcnt) +{ + struct hpsb_packet *packet; + quadlet_t d = 0; + int retval = 0; + + if (rootid >= ALL_NODES || rootid < -1 || gapcnt > 0x3f || gapcnt < -1 || + (rootid == -1 && gapcnt == -1)) { + HPSB_DEBUG("Invalid Parameter: rootid = %d gapcnt = %d", + rootid, gapcnt); + return -EINVAL; + } + + if (rootid != -1) + d |= PHYPACKET_PHYCONFIG_R | rootid << PHYPACKET_PORT_SHIFT; + if (gapcnt != -1) + d |= PHYPACKET_PHYCONFIG_T | gapcnt << PHYPACKET_GAPCOUNT_SHIFT; + + packet = hpsb_make_phypacket(host, d); + if (!packet) + return -ENOMEM; + + packet->generation = get_hpsb_generation(host); + retval = hpsb_send_packet_and_wait(packet); + hpsb_free_packet(packet); + + return retval; +} + +/** + * hpsb_send_packet - transmit a packet on the bus + * @packet: packet to send + * + * The packet is sent through the host specified in the packet->host field. + * Before sending, the packet's transmit speed is automatically determined + * using the local speed map when it is an async, non-broadcast packet. + * + * Possibilities for failure are that host is either not initialized, in bus + * reset, the packet's generation number doesn't match the current generation + * number or the host reports a transmit error. + * + * Return value: 0 on success, negative errno on failure. + */ +int hpsb_send_packet(struct hpsb_packet *packet) +{ + struct hpsb_host *host = packet->host; + + if (host->is_shutdown) + return -EINVAL; + if (host->in_bus_reset || + (packet->generation != get_hpsb_generation(host))) + return -EAGAIN; + + packet->state = hpsb_queued; + + /* This just seems silly to me */ + WARN_ON(packet->no_waiter && packet->expect_response); + + if (!packet->no_waiter || packet->expect_response) { + unsigned long flags; + + atomic_inc(&packet->refcnt); + /* Set the initial "sendtime" to 10 seconds from now, to + prevent premature expiry. If a packet takes more than + 10 seconds to hit the wire, we have bigger problems :) */ + packet->sendtime = jiffies + 10 * HZ; + spin_lock_irqsave(&pending_packets_lock, flags); + list_add_tail(&packet->queue, &host->pending_packets); + spin_unlock_irqrestore(&pending_packets_lock, flags); + } + + if (packet->node_id == host->node_id) { + /* it is a local request, so handle it locally */ + + quadlet_t *data; + size_t size = packet->data_size + packet->header_size; + + data = kmalloc(size, GFP_ATOMIC); + if (!data) { + HPSB_ERR("unable to allocate memory for concatenating header and data"); + return -ENOMEM; + } + + memcpy(data, packet->header, packet->header_size); + + if (packet->data_size) + memcpy(((u8*)data) + packet->header_size, packet->data, packet->data_size); + + dump_packet("send packet local", packet->header, packet->header_size, -1); + + hpsb_packet_sent(host, packet, packet->expect_response ? ACK_PENDING : ACK_COMPLETE); + hpsb_packet_received(host, data, size, 0); + + kfree(data); + + return 0; + } + + if (packet->type == hpsb_async && + NODEID_TO_NODE(packet->node_id) != ALL_NODES) + packet->speed_code = + host->speed[NODEID_TO_NODE(packet->node_id)]; + + dump_packet("send packet", packet->header, packet->header_size, packet->speed_code); + + return host->driver->transmit_packet(host, packet); +} + +/* We could just use complete() directly as the packet complete + * callback, but this is more typesafe, in the sense that we get a + * compiler error if the prototype for complete() changes. */ + +static void complete_packet(void *data) +{ + complete((struct completion *) data); +} + +/** + * hpsb_send_packet_and_wait - enqueue packet, block until transaction completes + * @packet: packet to send + * + * Return value: 0 on success, negative errno on failure. + */ +int hpsb_send_packet_and_wait(struct hpsb_packet *packet) +{ + struct completion done; + int retval; + + init_completion(&done); + hpsb_set_packet_complete_task(packet, complete_packet, &done); + retval = hpsb_send_packet(packet); + if (retval == 0) + wait_for_completion(&done); + + return retval; +} + +static void send_packet_nocare(struct hpsb_packet *packet) +{ + if (hpsb_send_packet(packet) < 0) { + hpsb_free_packet(packet); + } +} + +static size_t packet_size_to_data_size(size_t packet_size, size_t header_size, + size_t buffer_size, int tcode) +{ + size_t ret = packet_size <= header_size ? 0 : packet_size - header_size; + + if (unlikely(ret > buffer_size)) + ret = buffer_size; + + if (unlikely(ret + header_size != packet_size)) + HPSB_ERR("unexpected packet size %zd (tcode %d), bug?", + packet_size, tcode); + return ret; +} + +static void handle_packet_response(struct hpsb_host *host, int tcode, + quadlet_t *data, size_t size) +{ + struct hpsb_packet *packet; + int tlabel = (data[0] >> 10) & 0x3f; + size_t header_size; + unsigned long flags; + + spin_lock_irqsave(&pending_packets_lock, flags); + + list_for_each_entry(packet, &host->pending_packets, queue) + if (packet->tlabel == tlabel && + packet->node_id == (data[1] >> 16)) + goto found; + + spin_unlock_irqrestore(&pending_packets_lock, flags); + HPSB_DEBUG("unsolicited response packet received - %s", + "no tlabel match"); + dump_packet("contents", data, 16, -1); + return; + +found: + switch (packet->tcode) { + case TCODE_WRITEQ: + case TCODE_WRITEB: + if (unlikely(tcode != TCODE_WRITE_RESPONSE)) + break; + header_size = 12; + size = 0; + goto dequeue; + + case TCODE_READQ: + if (unlikely(tcode != TCODE_READQ_RESPONSE)) + break; + header_size = 16; + size = 0; + goto dequeue; + + case TCODE_READB: + if (unlikely(tcode != TCODE_READB_RESPONSE)) + break; + header_size = 16; + size = packet_size_to_data_size(size, header_size, + packet->allocated_data_size, + tcode); + goto dequeue; + + case TCODE_LOCK_REQUEST: + if (unlikely(tcode != TCODE_LOCK_RESPONSE)) + break; + header_size = 16; + size = packet_size_to_data_size(min(size, (size_t)(16 + 8)), + header_size, + packet->allocated_data_size, + tcode); + goto dequeue; + } + + spin_unlock_irqrestore(&pending_packets_lock, flags); + HPSB_DEBUG("unsolicited response packet received - %s", + "tcode mismatch"); + dump_packet("contents", data, 16, -1); + return; + +dequeue: + list_del_init(&packet->queue); + spin_unlock_irqrestore(&pending_packets_lock, flags); + + if (packet->state == hpsb_queued) { + packet->sendtime = jiffies; + packet->ack_code = ACK_PENDING; + } + packet->state = hpsb_complete; + + memcpy(packet->header, data, header_size); + if (size) + memcpy(packet->data, data + 4, size); + + queue_packet_complete(packet); +} + + +static struct hpsb_packet *create_reply_packet(struct hpsb_host *host, + quadlet_t *data, size_t dsize) +{ + struct hpsb_packet *p; + + p = hpsb_alloc_packet(dsize); + if (unlikely(p == NULL)) { + /* FIXME - send data_error response */ + HPSB_ERR("out of memory, cannot send response packet"); + return NULL; + } + + p->type = hpsb_async; + p->state = hpsb_unused; + p->host = host; + p->node_id = data[1] >> 16; + p->tlabel = (data[0] >> 10) & 0x3f; + p->no_waiter = 1; + + p->generation = get_hpsb_generation(host); + + if (dsize % 4) + p->data[dsize / 4] = 0; + + return p; +} + +#define PREP_ASYNC_HEAD_RCODE(tc) \ + packet->tcode = tc; \ + packet->header[0] = (packet->node_id << 16) | (packet->tlabel << 10) \ + | (1 << 8) | (tc << 4); \ + packet->header[1] = (packet->host->node_id << 16) | (rcode << 12); \ + packet->header[2] = 0 + +static void fill_async_readquad_resp(struct hpsb_packet *packet, int rcode, + quadlet_t data) +{ + PREP_ASYNC_HEAD_RCODE(TCODE_READQ_RESPONSE); + packet->header[3] = data; + packet->header_size = 16; + packet->data_size = 0; +} + +static void fill_async_readblock_resp(struct hpsb_packet *packet, int rcode, + int length) +{ + if (rcode != RCODE_COMPLETE) + length = 0; + + PREP_ASYNC_HEAD_RCODE(TCODE_READB_RESPONSE); + packet->header[3] = length << 16; + packet->header_size = 16; + packet->data_size = length + (length % 4 ? 4 - (length % 4) : 0); +} + +static void fill_async_write_resp(struct hpsb_packet *packet, int rcode) +{ + PREP_ASYNC_HEAD_RCODE(TCODE_WRITE_RESPONSE); + packet->header_size = 12; + packet->data_size = 0; +} + +static void fill_async_lock_resp(struct hpsb_packet *packet, int rcode, int extcode, + int length) +{ + if (rcode != RCODE_COMPLETE) + length = 0; + + PREP_ASYNC_HEAD_RCODE(TCODE_LOCK_RESPONSE); + packet->header[3] = (length << 16) | extcode; + packet->header_size = 16; + packet->data_size = length; +} + +static void handle_incoming_packet(struct hpsb_host *host, int tcode, + quadlet_t *data, size_t size, + int write_acked) +{ + struct hpsb_packet *packet; + int length, rcode, extcode; + quadlet_t buffer; + nodeid_t source = data[1] >> 16; + nodeid_t dest = data[0] >> 16; + u16 flags = (u16) data[0]; + u64 addr; + + /* FIXME? + * Out-of-bounds lengths are left for highlevel_read|write to cap. */ + + switch (tcode) { + case TCODE_WRITEQ: + addr = (((u64)(data[1] & 0xffff)) << 32) | data[2]; + rcode = highlevel_write(host, source, dest, data + 3, + addr, 4, flags); + goto handle_write_request; + + case TCODE_WRITEB: + addr = (((u64)(data[1] & 0xffff)) << 32) | data[2]; + rcode = highlevel_write(host, source, dest, data + 4, + addr, data[3] >> 16, flags); +handle_write_request: + if (rcode < 0 || write_acked || + NODEID_TO_NODE(data[0] >> 16) == NODE_MASK) + return; + /* not a broadcast write, reply */ + packet = create_reply_packet(host, data, 0); + if (packet) { + fill_async_write_resp(packet, rcode); + send_packet_nocare(packet); + } + return; + + case TCODE_READQ: + addr = (((u64)(data[1] & 0xffff)) << 32) | data[2]; + rcode = highlevel_read(host, source, &buffer, addr, 4, flags); + if (rcode < 0) + return; + + packet = create_reply_packet(host, data, 0); + if (packet) { + fill_async_readquad_resp(packet, rcode, buffer); + send_packet_nocare(packet); + } + return; + + case TCODE_READB: + length = data[3] >> 16; + packet = create_reply_packet(host, data, length); + if (!packet) + return; + + addr = (((u64)(data[1] & 0xffff)) << 32) | data[2]; + rcode = highlevel_read(host, source, packet->data, addr, + length, flags); + if (rcode < 0) { + hpsb_free_packet(packet); + return; + } + fill_async_readblock_resp(packet, rcode, length); + send_packet_nocare(packet); + return; + + case TCODE_LOCK_REQUEST: + length = data[3] >> 16; + extcode = data[3] & 0xffff; + addr = (((u64)(data[1] & 0xffff)) << 32) | data[2]; + + packet = create_reply_packet(host, data, 8); + if (!packet) + return; + + if (extcode == 0 || extcode >= 7) { + /* let switch default handle error */ + length = 0; + } + + switch (length) { + case 4: + rcode = highlevel_lock(host, source, packet->data, addr, + data[4], 0, extcode, flags); + fill_async_lock_resp(packet, rcode, extcode, 4); + break; + case 8: + if (extcode != EXTCODE_FETCH_ADD && + extcode != EXTCODE_LITTLE_ADD) { + rcode = highlevel_lock(host, source, + packet->data, addr, + data[5], data[4], + extcode, flags); + fill_async_lock_resp(packet, rcode, extcode, 4); + } else { + rcode = highlevel_lock64(host, source, + (octlet_t *)packet->data, addr, + *(octlet_t *)(data + 4), 0ULL, + extcode, flags); + fill_async_lock_resp(packet, rcode, extcode, 8); + } + break; + case 16: + rcode = highlevel_lock64(host, source, + (octlet_t *)packet->data, addr, + *(octlet_t *)(data + 6), + *(octlet_t *)(data + 4), + extcode, flags); + fill_async_lock_resp(packet, rcode, extcode, 8); + break; + default: + rcode = RCODE_TYPE_ERROR; + fill_async_lock_resp(packet, rcode, extcode, 0); + } + + if (rcode < 0) + hpsb_free_packet(packet); + else + send_packet_nocare(packet); + return; + } +} + +/** + * hpsb_packet_received - hand over received packet to the core + * + * For host driver module usage. + * + * The contents of data are expected to be the full packet but with the CRCs + * left out (data block follows header immediately), with the header (i.e. the + * first four quadlets) in machine byte order and the data block in big endian. + * *@data can be safely overwritten after this call. + * + * If the packet is a write request, @write_acked is to be set to true if it was + * ack_complete'd already, false otherwise. This argument is ignored for any + * other packet type. + */ +void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size, + int write_acked) +{ + int tcode; + + if (unlikely(host->in_bus_reset)) { + HPSB_DEBUG("received packet during reset; ignoring"); + return; + } + + dump_packet("received packet", data, size, -1); + + tcode = (data[0] >> 4) & 0xf; + + switch (tcode) { + case TCODE_WRITE_RESPONSE: + case TCODE_READQ_RESPONSE: + case TCODE_READB_RESPONSE: + case TCODE_LOCK_RESPONSE: + handle_packet_response(host, tcode, data, size); + break; + + case TCODE_WRITEQ: + case TCODE_WRITEB: + case TCODE_READQ: + case TCODE_READB: + case TCODE_LOCK_REQUEST: + handle_incoming_packet(host, tcode, data, size, write_acked); + break; + + case TCODE_CYCLE_START: + /* simply ignore this packet if it is passed on */ + break; + + default: + HPSB_DEBUG("received packet with bogus transaction code %d", + tcode); + break; + } +} + +static void abort_requests(struct hpsb_host *host) +{ + struct hpsb_packet *packet, *p; + struct list_head tmp; + unsigned long flags; + + host->driver->devctl(host, CANCEL_REQUESTS, 0); + + INIT_LIST_HEAD(&tmp); + spin_lock_irqsave(&pending_packets_lock, flags); + list_splice_init(&host->pending_packets, &tmp); + spin_unlock_irqrestore(&pending_packets_lock, flags); + + list_for_each_entry_safe(packet, p, &tmp, queue) { + list_del_init(&packet->queue); + packet->state = hpsb_complete; + packet->ack_code = ACKX_ABORTED; + queue_packet_complete(packet); + } +} + +void abort_timedouts(unsigned long __opaque) +{ + struct hpsb_host *host = (struct hpsb_host *)__opaque; + struct hpsb_packet *packet, *p; + struct list_head tmp; + unsigned long flags, expire, j; + + spin_lock_irqsave(&host->csr.lock, flags); + expire = host->csr.expire; + spin_unlock_irqrestore(&host->csr.lock, flags); + + j = jiffies; + INIT_LIST_HEAD(&tmp); + spin_lock_irqsave(&pending_packets_lock, flags); + + list_for_each_entry_safe(packet, p, &host->pending_packets, queue) { + if (time_before(packet->sendtime + expire, j)) + list_move_tail(&packet->queue, &tmp); + else + /* Since packets are added to the tail, the oldest + * ones are first, always. When we get to one that + * isn't timed out, the rest aren't either. */ + break; + } + if (!list_empty(&host->pending_packets)) + mod_timer(&host->timeout, j + host->timeout_interval); + + spin_unlock_irqrestore(&pending_packets_lock, flags); + + list_for_each_entry_safe(packet, p, &tmp, queue) { + list_del_init(&packet->queue); + packet->state = hpsb_complete; + packet->ack_code = ACKX_TIMEOUT; + queue_packet_complete(packet); + } +} + +static struct task_struct *khpsbpkt_thread; +static LIST_HEAD(hpsbpkt_queue); + +static void queue_packet_complete(struct hpsb_packet *packet) +{ + unsigned long flags; + + if (packet->no_waiter) { + hpsb_free_packet(packet); + return; + } + if (packet->complete_routine != NULL) { + spin_lock_irqsave(&pending_packets_lock, flags); + list_add_tail(&packet->queue, &hpsbpkt_queue); + spin_unlock_irqrestore(&pending_packets_lock, flags); + wake_up_process(khpsbpkt_thread); + } + return; +} + +/* + * Kernel thread which handles packets that are completed. This way the + * packet's "complete" function is asynchronously run in process context. + * Only packets which have a "complete" function may be sent here. + */ +static int hpsbpkt_thread(void *__hi) +{ + struct hpsb_packet *packet, *p; + struct list_head tmp; + int may_schedule; + + while (!kthread_should_stop()) { + + INIT_LIST_HEAD(&tmp); + spin_lock_irq(&pending_packets_lock); + list_splice_init(&hpsbpkt_queue, &tmp); + spin_unlock_irq(&pending_packets_lock); + + list_for_each_entry_safe(packet, p, &tmp, queue) { + list_del_init(&packet->queue); + packet->complete_routine(packet->complete_data); + } + + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irq(&pending_packets_lock); + may_schedule = list_empty(&hpsbpkt_queue); + spin_unlock_irq(&pending_packets_lock); + if (may_schedule) + schedule(); + __set_current_state(TASK_RUNNING); + } + return 0; +} + +static int __init ieee1394_init(void) +{ + int i, ret; + + /* non-fatal error */ + if (hpsb_init_config_roms()) { + HPSB_ERR("Failed to initialize some config rom entries.\n"); + HPSB_ERR("Some features may not be available\n"); + } + + khpsbpkt_thread = kthread_run(hpsbpkt_thread, NULL, "khpsbpkt"); + if (IS_ERR(khpsbpkt_thread)) { + HPSB_ERR("Failed to start hpsbpkt thread!\n"); + ret = PTR_ERR(khpsbpkt_thread); + goto exit_cleanup_config_roms; + } + + if (register_chrdev_region(IEEE1394_CORE_DEV, 256, "ieee1394")) { + HPSB_ERR("unable to register character device major %d!\n", IEEE1394_MAJOR); + ret = -ENODEV; + goto exit_release_kernel_thread; + } + + ret = bus_register(&ieee1394_bus_type); + if (ret < 0) { + HPSB_INFO("bus register failed"); + goto release_chrdev; + } + + for (i = 0; fw_bus_attrs[i]; i++) { + ret = bus_create_file(&ieee1394_bus_type, fw_bus_attrs[i]); + if (ret < 0) { + while (i >= 0) { + bus_remove_file(&ieee1394_bus_type, + fw_bus_attrs[i--]); + } + bus_unregister(&ieee1394_bus_type); + goto release_chrdev; + } + } + + ret = class_register(&hpsb_host_class); + if (ret < 0) + goto release_all_bus; + + hpsb_protocol_class = class_create(THIS_MODULE, "ieee1394_protocol"); + if (IS_ERR(hpsb_protocol_class)) { + ret = PTR_ERR(hpsb_protocol_class); + goto release_class_host; + } + + ret = init_csr(); + if (ret) { + HPSB_INFO("init csr failed"); + ret = -ENOMEM; + goto release_class_protocol; + } + + if (disable_nodemgr) { + HPSB_INFO("nodemgr and IRM functionality disabled"); + /* We shouldn't contend for IRM with nodemgr disabled, since + nodemgr implements functionality required of ieee1394a-2000 + IRMs */ + hpsb_disable_irm = 1; + + return 0; + } + + if (hpsb_disable_irm) { + HPSB_INFO("IRM functionality disabled"); + } + + ret = init_ieee1394_nodemgr(); + if (ret < 0) { + HPSB_INFO("init nodemgr failed"); + goto cleanup_csr; + } + + return 0; + +cleanup_csr: + cleanup_csr(); +release_class_protocol: + class_destroy(hpsb_protocol_class); +release_class_host: + class_unregister(&hpsb_host_class); +release_all_bus: + for (i = 0; fw_bus_attrs[i]; i++) + bus_remove_file(&ieee1394_bus_type, fw_bus_attrs[i]); + bus_unregister(&ieee1394_bus_type); +release_chrdev: + unregister_chrdev_region(IEEE1394_CORE_DEV, 256); +exit_release_kernel_thread: + kthread_stop(khpsbpkt_thread); +exit_cleanup_config_roms: + hpsb_cleanup_config_roms(); + return ret; +} + +static void __exit ieee1394_cleanup(void) +{ + int i; + + if (!disable_nodemgr) + cleanup_ieee1394_nodemgr(); + + cleanup_csr(); + + class_destroy(hpsb_protocol_class); + class_unregister(&hpsb_host_class); + for (i = 0; fw_bus_attrs[i]; i++) + bus_remove_file(&ieee1394_bus_type, fw_bus_attrs[i]); + bus_unregister(&ieee1394_bus_type); + + kthread_stop(khpsbpkt_thread); + + hpsb_cleanup_config_roms(); + + unregister_chrdev_region(IEEE1394_CORE_DEV, 256); +} + +fs_initcall(ieee1394_init); +module_exit(ieee1394_cleanup); + +/* Exported symbols */ + +/** hosts.c **/ +EXPORT_SYMBOL(hpsb_alloc_host); +EXPORT_SYMBOL(hpsb_add_host); +EXPORT_SYMBOL(hpsb_resume_host); +EXPORT_SYMBOL(hpsb_remove_host); +EXPORT_SYMBOL(hpsb_update_config_rom_image); + +/** ieee1394_core.c **/ +EXPORT_SYMBOL(hpsb_speedto_str); +EXPORT_SYMBOL(hpsb_protocol_class); +EXPORT_SYMBOL(hpsb_set_packet_complete_task); +EXPORT_SYMBOL(hpsb_alloc_packet); +EXPORT_SYMBOL(hpsb_free_packet); +EXPORT_SYMBOL(hpsb_send_packet); +EXPORT_SYMBOL(hpsb_reset_bus); +EXPORT_SYMBOL(hpsb_read_cycle_timer); +EXPORT_SYMBOL(hpsb_bus_reset); +EXPORT_SYMBOL(hpsb_selfid_received); +EXPORT_SYMBOL(hpsb_selfid_complete); +EXPORT_SYMBOL(hpsb_packet_sent); +EXPORT_SYMBOL(hpsb_packet_received); +EXPORT_SYMBOL_GPL(hpsb_disable_irm); + +/** ieee1394_transactions.c **/ +EXPORT_SYMBOL(hpsb_get_tlabel); +EXPORT_SYMBOL(hpsb_free_tlabel); +EXPORT_SYMBOL(hpsb_make_readpacket); +EXPORT_SYMBOL(hpsb_make_writepacket); +EXPORT_SYMBOL(hpsb_make_streampacket); +EXPORT_SYMBOL(hpsb_make_lockpacket); +EXPORT_SYMBOL(hpsb_make_lock64packet); +EXPORT_SYMBOL(hpsb_make_phypacket); +EXPORT_SYMBOL(hpsb_read); +EXPORT_SYMBOL(hpsb_write); +EXPORT_SYMBOL(hpsb_lock); +EXPORT_SYMBOL(hpsb_packet_success); + +/** highlevel.c **/ +EXPORT_SYMBOL(hpsb_register_highlevel); +EXPORT_SYMBOL(hpsb_unregister_highlevel); +EXPORT_SYMBOL(hpsb_register_addrspace); +EXPORT_SYMBOL(hpsb_unregister_addrspace); +EXPORT_SYMBOL(hpsb_allocate_and_register_addrspace); +EXPORT_SYMBOL(hpsb_get_hostinfo); +EXPORT_SYMBOL(hpsb_create_hostinfo); +EXPORT_SYMBOL(hpsb_destroy_hostinfo); +EXPORT_SYMBOL(hpsb_set_hostinfo_key); +EXPORT_SYMBOL(hpsb_get_hostinfo_bykey); +EXPORT_SYMBOL(hpsb_set_hostinfo); + +/** nodemgr.c **/ +EXPORT_SYMBOL(hpsb_node_fill_packet); +EXPORT_SYMBOL(hpsb_node_write); +EXPORT_SYMBOL(__hpsb_register_protocol); +EXPORT_SYMBOL(hpsb_unregister_protocol); + +/** csr.c **/ +EXPORT_SYMBOL(hpsb_update_config_rom); + +/** dma.c **/ +EXPORT_SYMBOL(dma_prog_region_init); +EXPORT_SYMBOL(dma_prog_region_alloc); +EXPORT_SYMBOL(dma_prog_region_free); +EXPORT_SYMBOL(dma_region_init); +EXPORT_SYMBOL(dma_region_alloc); +EXPORT_SYMBOL(dma_region_free); +EXPORT_SYMBOL(dma_region_sync_for_cpu); +EXPORT_SYMBOL(dma_region_sync_for_device); +EXPORT_SYMBOL(dma_region_mmap); +EXPORT_SYMBOL(dma_region_offset_to_bus); + +/** iso.c **/ +EXPORT_SYMBOL(hpsb_iso_xmit_init); +EXPORT_SYMBOL(hpsb_iso_recv_init); +EXPORT_SYMBOL(hpsb_iso_xmit_start); +EXPORT_SYMBOL(hpsb_iso_recv_start); +EXPORT_SYMBOL(hpsb_iso_recv_listen_channel); +EXPORT_SYMBOL(hpsb_iso_recv_unlisten_channel); +EXPORT_SYMBOL(hpsb_iso_recv_set_channel_mask); +EXPORT_SYMBOL(hpsb_iso_stop); +EXPORT_SYMBOL(hpsb_iso_shutdown); +EXPORT_SYMBOL(hpsb_iso_xmit_queue_packet); +EXPORT_SYMBOL(hpsb_iso_xmit_sync); +EXPORT_SYMBOL(hpsb_iso_recv_release_packets); +EXPORT_SYMBOL(hpsb_iso_n_ready); +EXPORT_SYMBOL(hpsb_iso_packet_sent); +EXPORT_SYMBOL(hpsb_iso_packet_received); +EXPORT_SYMBOL(hpsb_iso_wake); +EXPORT_SYMBOL(hpsb_iso_recv_flush); + +/** csr1212.c **/ +EXPORT_SYMBOL(csr1212_attach_keyval_to_directory); +EXPORT_SYMBOL(csr1212_detach_keyval_from_directory); +EXPORT_SYMBOL(csr1212_get_keyval); +EXPORT_SYMBOL(csr1212_new_directory); +EXPORT_SYMBOL(csr1212_parse_keyval); +EXPORT_SYMBOL(csr1212_read); +EXPORT_SYMBOL(csr1212_release_keyval); diff --git a/trunk/drivers/ieee1394/ieee1394_core.h b/trunk/drivers/ieee1394/ieee1394_core.h new file mode 100644 index 000000000000..28b9f58bafd2 --- /dev/null +++ b/trunk/drivers/ieee1394/ieee1394_core.h @@ -0,0 +1,172 @@ +#ifndef _IEEE1394_CORE_H +#define _IEEE1394_CORE_H + +#include +#include +#include +#include +#include +#include + +#include "hosts.h" +#include "ieee1394_types.h" + +struct hpsb_packet { + /* This struct is basically read-only for hosts with the exception of + * the data buffer contents and driver_list. */ + + /* This can be used for host driver internal linking. + * + * NOTE: This must be left in init state when the driver is done + * with it (e.g. by using list_del_init()), since the core does + * some sanity checks to make sure the packet is not on a + * driver_list when free'ing it. */ + struct list_head driver_list; + + nodeid_t node_id; + + /* hpsb_raw = send as-is, do not CRC (but still byte-swap it) */ + enum { hpsb_async, hpsb_raw } __attribute__((packed)) type; + + /* Okay, this is core internal and a no care for hosts. + * queued = queued for sending + * pending = sent, waiting for response + * complete = processing completed, successful or not + */ + enum { + hpsb_unused, hpsb_queued, hpsb_pending, hpsb_complete + } __attribute__((packed)) state; + + /* These are core-internal. */ + signed char tlabel; + signed char ack_code; + unsigned char tcode; + + unsigned expect_response:1; + unsigned no_waiter:1; + + /* Speed to transmit with: 0 = 100Mbps, 1 = 200Mbps, 2 = 400Mbps */ + unsigned speed_code:2; + + struct hpsb_host *host; + unsigned int generation; + + atomic_t refcnt; + struct list_head queue; + + /* Function (and possible data to pass to it) to call when this + * packet is completed. */ + void (*complete_routine)(void *); + void *complete_data; + + /* Store jiffies for implementing bus timeouts. */ + unsigned long sendtime; + + /* Core-internal. */ + size_t allocated_data_size; /* as allocated */ + + /* Sizes are in bytes. To be set by caller of hpsb_alloc_packet. */ + size_t data_size; /* as filled in */ + size_t header_size; /* as filled in, not counting the CRC */ + + /* Buffers */ + quadlet_t *data; /* can be DMA-mapped */ + quadlet_t header[5]; + quadlet_t embedded_data[0]; /* keep as last member */ +}; + +void hpsb_set_packet_complete_task(struct hpsb_packet *packet, + void (*routine)(void *), void *data); +static inline struct hpsb_packet *driver_packet(struct list_head *l) +{ + return list_entry(l, struct hpsb_packet, driver_list); +} +void abort_timedouts(unsigned long __opaque); +struct hpsb_packet *hpsb_alloc_packet(size_t data_size); +void hpsb_free_packet(struct hpsb_packet *packet); + +/** + * get_hpsb_generation - generation counter for the complete 1394 subsystem + * + * Generation gets incremented on every change in the subsystem (notably on bus + * resets). Use the functions, not the variable. + */ +static inline unsigned int get_hpsb_generation(struct hpsb_host *host) +{ + return atomic_read(&host->generation); +} + +int hpsb_send_phy_config(struct hpsb_host *host, int rootid, int gapcnt); +int hpsb_send_packet(struct hpsb_packet *packet); +int hpsb_send_packet_and_wait(struct hpsb_packet *packet); +int hpsb_reset_bus(struct hpsb_host *host, int type); +int hpsb_read_cycle_timer(struct hpsb_host *host, u32 *cycle_timer, + u64 *local_time); + +int hpsb_bus_reset(struct hpsb_host *host); +void hpsb_selfid_received(struct hpsb_host *host, quadlet_t sid); +void hpsb_selfid_complete(struct hpsb_host *host, int phyid, int isroot); +void hpsb_packet_sent(struct hpsb_host *host, struct hpsb_packet *packet, + int ackcode); +void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size, + int write_acked); + +/* + * CHARACTER DEVICE DISPATCHING + * + * All ieee1394 character device drivers share the same major number + * (major 171). The 256 minor numbers are allocated to the various + * task-specific interfaces (raw1394, video1394, dv1394, etc) in + * blocks of 16. + * + * The core ieee1394.o module allocates the device number region + * 171:0-255, the various drivers must then cdev_add() their cdev + * objects to handle their respective sub-regions. + * + * Minor device number block allocations: + * + * Block 0 ( 0- 15) raw1394 + * Block 1 ( 16- 31) video1394 + * Block 2 ( 32- 47) dv1394 + * + * Blocks 3-14 free for future allocation + * + * Block 15 (240-255) reserved for drivers under development, etc. + */ + +#define IEEE1394_MAJOR 171 + +#define IEEE1394_MINOR_BLOCK_RAW1394 0 +#define IEEE1394_MINOR_BLOCK_VIDEO1394 1 +#define IEEE1394_MINOR_BLOCK_DV1394 2 +#define IEEE1394_MINOR_BLOCK_EXPERIMENTAL 15 + +#define IEEE1394_CORE_DEV MKDEV(IEEE1394_MAJOR, 0) +#define IEEE1394_RAW1394_DEV MKDEV(IEEE1394_MAJOR, \ + IEEE1394_MINOR_BLOCK_RAW1394 * 16) +#define IEEE1394_VIDEO1394_DEV MKDEV(IEEE1394_MAJOR, \ + IEEE1394_MINOR_BLOCK_VIDEO1394 * 16) +#define IEEE1394_DV1394_DEV MKDEV(IEEE1394_MAJOR, \ + IEEE1394_MINOR_BLOCK_DV1394 * 16) +#define IEEE1394_EXPERIMENTAL_DEV MKDEV(IEEE1394_MAJOR, \ + IEEE1394_MINOR_BLOCK_EXPERIMENTAL * 16) + +/** + * ieee1394_file_to_instance - get the index within a minor number block + */ +static inline unsigned char ieee1394_file_to_instance(struct file *file) +{ + int idx = cdev_index(file->f_path.dentry->d_inode); + if (idx < 0) + idx = 0; + return idx; +} + +extern int hpsb_disable_irm; + +/* Our sysfs bus entry */ +extern struct bus_type ieee1394_bus_type; +extern struct class hpsb_host_class; +extern struct class *hpsb_protocol_class; + +#endif /* _IEEE1394_CORE_H */ diff --git a/trunk/drivers/ieee1394/ieee1394_hotplug.h b/trunk/drivers/ieee1394/ieee1394_hotplug.h new file mode 100644 index 000000000000..dd5500ed8322 --- /dev/null +++ b/trunk/drivers/ieee1394/ieee1394_hotplug.h @@ -0,0 +1,19 @@ +#ifndef _IEEE1394_HOTPLUG_H +#define _IEEE1394_HOTPLUG_H + +/* Unit spec id and sw version entry for some protocols */ +#define AVC_UNIT_SPEC_ID_ENTRY 0x0000A02D +#define AVC_SW_VERSION_ENTRY 0x00010001 +#define CAMERA_UNIT_SPEC_ID_ENTRY 0x0000A02D +#define CAMERA_SW_VERSION_ENTRY 0x00000100 + +/* /include/linux/mod_devicetable.h defines: + * IEEE1394_MATCH_VENDOR_ID + * IEEE1394_MATCH_MODEL_ID + * IEEE1394_MATCH_SPECIFIER_ID + * IEEE1394_MATCH_VERSION + * struct ieee1394_device_id + */ +#include + +#endif /* _IEEE1394_HOTPLUG_H */ diff --git a/trunk/drivers/ieee1394/ieee1394_transactions.c b/trunk/drivers/ieee1394/ieee1394_transactions.c new file mode 100644 index 000000000000..675b3135d5f1 --- /dev/null +++ b/trunk/drivers/ieee1394/ieee1394_transactions.c @@ -0,0 +1,595 @@ +/* + * IEEE 1394 for Linux + * + * Transaction support. + * + * Copyright (C) 1999 Andreas E. Bombe + * + * This code is licensed under the GPL. See the file COPYING in the root + * directory of the kernel sources for details. + */ + +#include +#include +#include +#include +#include +#include /* because linux/wait.h is broken if CONFIG_SMP=n */ +#include + +#include +#include +#include + +#include "ieee1394.h" +#include "ieee1394_types.h" +#include "hosts.h" +#include "ieee1394_core.h" +#include "ieee1394_transactions.h" + +#define PREP_ASYNC_HEAD_ADDRESS(tc) \ + packet->tcode = tc; \ + packet->header[0] = (packet->node_id << 16) | (packet->tlabel << 10) \ + | (1 << 8) | (tc << 4); \ + packet->header[1] = (packet->host->node_id << 16) | (addr >> 32); \ + packet->header[2] = addr & 0xffffffff + +#ifndef HPSB_DEBUG_TLABELS +static +#endif +DEFINE_SPINLOCK(hpsb_tlabel_lock); + +static DECLARE_WAIT_QUEUE_HEAD(tlabel_wq); + +static void fill_async_readquad(struct hpsb_packet *packet, u64 addr) +{ + PREP_ASYNC_HEAD_ADDRESS(TCODE_READQ); + packet->header_size = 12; + packet->data_size = 0; + packet->expect_response = 1; +} + +static void fill_async_readblock(struct hpsb_packet *packet, u64 addr, + int length) +{ + PREP_ASYNC_HEAD_ADDRESS(TCODE_READB); + packet->header[3] = length << 16; + packet->header_size = 16; + packet->data_size = 0; + packet->expect_response = 1; +} + +static void fill_async_writequad(struct hpsb_packet *packet, u64 addr, + quadlet_t data) +{ + PREP_ASYNC_HEAD_ADDRESS(TCODE_WRITEQ); + packet->header[3] = data; + packet->header_size = 16; + packet->data_size = 0; + packet->expect_response = 1; +} + +static void fill_async_writeblock(struct hpsb_packet *packet, u64 addr, + int length) +{ + PREP_ASYNC_HEAD_ADDRESS(TCODE_WRITEB); + packet->header[3] = length << 16; + packet->header_size = 16; + packet->expect_response = 1; + packet->data_size = length + (length % 4 ? 4 - (length % 4) : 0); +} + +static void fill_async_lock(struct hpsb_packet *packet, u64 addr, int extcode, + int length) +{ + PREP_ASYNC_HEAD_ADDRESS(TCODE_LOCK_REQUEST); + packet->header[3] = (length << 16) | extcode; + packet->header_size = 16; + packet->data_size = length; + packet->expect_response = 1; +} + +static void fill_phy_packet(struct hpsb_packet *packet, quadlet_t data) +{ + packet->header[0] = data; + packet->header[1] = ~data; + packet->header_size = 8; + packet->data_size = 0; + packet->expect_response = 0; + packet->type = hpsb_raw; /* No CRC added */ + packet->speed_code = IEEE1394_SPEED_100; /* Force speed to be 100Mbps */ +} + +static void fill_async_stream_packet(struct hpsb_packet *packet, int length, + int channel, int tag, int sync) +{ + packet->header[0] = (length << 16) | (tag << 14) | (channel << 8) + | (TCODE_STREAM_DATA << 4) | sync; + + packet->header_size = 4; + packet->data_size = length; + packet->type = hpsb_async; + packet->tcode = TCODE_ISO_DATA; +} + +/* same as hpsb_get_tlabel, except that it returns immediately */ +static int hpsb_get_tlabel_atomic(struct hpsb_packet *packet) +{ + unsigned long flags, *tp; + u8 *next; + int tlabel, n = NODEID_TO_NODE(packet->node_id); + + /* Broadcast transactions are complete once the request has been sent. + * Use the same transaction label for all broadcast transactions. */ + if (unlikely(n == ALL_NODES)) { + packet->tlabel = 0; + return 0; + } + tp = packet->host->tl_pool[n].map; + next = &packet->host->next_tl[n]; + + spin_lock_irqsave(&hpsb_tlabel_lock, flags); + tlabel = find_next_zero_bit(tp, 64, *next); + if (tlabel > 63) + tlabel = find_first_zero_bit(tp, 64); + if (tlabel > 63) { + spin_unlock_irqrestore(&hpsb_tlabel_lock, flags); + return -EAGAIN; + } + __set_bit(tlabel, tp); + *next = (tlabel + 1) & 63; + spin_unlock_irqrestore(&hpsb_tlabel_lock, flags); + + packet->tlabel = tlabel; + return 0; +} + +/** + * hpsb_get_tlabel - allocate a transaction label + * @packet: the packet whose tlabel and tl_pool we set + * + * Every asynchronous transaction on the 1394 bus needs a transaction + * label to match the response to the request. This label has to be + * different from any other transaction label in an outstanding request to + * the same node to make matching possible without ambiguity. + * + * There are 64 different tlabels, so an allocated tlabel has to be freed + * with hpsb_free_tlabel() after the transaction is complete (unless it's + * reused again for the same target node). + * + * Return value: Zero on success, otherwise non-zero. A non-zero return + * generally means there are no available tlabels. If this is called out + * of interrupt or atomic context, then it will sleep until can return a + * tlabel or a signal is received. + */ +int hpsb_get_tlabel(struct hpsb_packet *packet) +{ + if (irqs_disabled() || in_atomic()) + return hpsb_get_tlabel_atomic(packet); + + /* NB: The macro wait_event_interruptible() is called with a condition + * argument with side effect. This is only possible because the side + * effect does not occur until the condition became true, and + * wait_event_interruptible() won't evaluate the condition again after + * that. */ + return wait_event_interruptible(tlabel_wq, + !hpsb_get_tlabel_atomic(packet)); +} + +/** + * hpsb_free_tlabel - free an allocated transaction label + * @packet: packet whose tlabel and tl_pool needs to be cleared + * + * Frees the transaction label allocated with hpsb_get_tlabel(). The + * tlabel has to be freed after the transaction is complete (i.e. response + * was received for a split transaction or packet was sent for a unified + * transaction). + * + * A tlabel must not be freed twice. + */ +void hpsb_free_tlabel(struct hpsb_packet *packet) +{ + unsigned long flags, *tp; + int tlabel, n = NODEID_TO_NODE(packet->node_id); + + if (unlikely(n == ALL_NODES)) + return; + tp = packet->host->tl_pool[n].map; + tlabel = packet->tlabel; + BUG_ON(tlabel > 63 || tlabel < 0); + + spin_lock_irqsave(&hpsb_tlabel_lock, flags); + BUG_ON(!__test_and_clear_bit(tlabel, tp)); + spin_unlock_irqrestore(&hpsb_tlabel_lock, flags); + + wake_up_interruptible(&tlabel_wq); +} + +/** + * hpsb_packet_success - Make sense of the ack and reply codes + * + * Make sense of the ack and reply codes and return more convenient error codes: + * 0 = success. -%EBUSY = node is busy, try again. -%EAGAIN = error which can + * probably resolved by retry. -%EREMOTEIO = node suffers from an internal + * error. -%EACCES = this transaction is not allowed on requested address. + * -%EINVAL = invalid address at node. + */ +int hpsb_packet_success(struct hpsb_packet *packet) +{ + switch (packet->ack_code) { + case ACK_PENDING: + switch ((packet->header[1] >> 12) & 0xf) { + case RCODE_COMPLETE: + return 0; + case RCODE_CONFLICT_ERROR: + return -EAGAIN; + case RCODE_DATA_ERROR: + return -EREMOTEIO; + case RCODE_TYPE_ERROR: + return -EACCES; + case RCODE_ADDRESS_ERROR: + return -EINVAL; + default: + HPSB_ERR("received reserved rcode %d from node %d", + (packet->header[1] >> 12) & 0xf, + packet->node_id); + return -EAGAIN; + } + + case ACK_BUSY_X: + case ACK_BUSY_A: + case ACK_BUSY_B: + return -EBUSY; + + case ACK_TYPE_ERROR: + return -EACCES; + + case ACK_COMPLETE: + if (packet->tcode == TCODE_WRITEQ + || packet->tcode == TCODE_WRITEB) { + return 0; + } else { + HPSB_ERR("impossible ack_complete from node %d " + "(tcode %d)", packet->node_id, packet->tcode); + return -EAGAIN; + } + + case ACK_DATA_ERROR: + if (packet->tcode == TCODE_WRITEB + || packet->tcode == TCODE_LOCK_REQUEST) { + return -EAGAIN; + } else { + HPSB_ERR("impossible ack_data_error from node %d " + "(tcode %d)", packet->node_id, packet->tcode); + return -EAGAIN; + } + + case ACK_ADDRESS_ERROR: + return -EINVAL; + + case ACK_TARDY: + case ACK_CONFLICT_ERROR: + case ACKX_NONE: + case ACKX_SEND_ERROR: + case ACKX_ABORTED: + case ACKX_TIMEOUT: + /* error while sending */ + return -EAGAIN; + + default: + HPSB_ERR("got invalid ack %d from node %d (tcode %d)", + packet->ack_code, packet->node_id, packet->tcode); + return -EAGAIN; + } +} + +struct hpsb_packet *hpsb_make_readpacket(struct hpsb_host *host, nodeid_t node, + u64 addr, size_t length) +{ + struct hpsb_packet *packet; + + if (length == 0) + return NULL; + + packet = hpsb_alloc_packet(length); + if (!packet) + return NULL; + + packet->host = host; + packet->node_id = node; + + if (hpsb_get_tlabel(packet)) { + hpsb_free_packet(packet); + return NULL; + } + + if (length == 4) + fill_async_readquad(packet, addr); + else + fill_async_readblock(packet, addr, length); + + return packet; +} + +struct hpsb_packet *hpsb_make_writepacket(struct hpsb_host *host, nodeid_t node, + u64 addr, quadlet_t * buffer, + size_t length) +{ + struct hpsb_packet *packet; + + if (length == 0) + return NULL; + + packet = hpsb_alloc_packet(length); + if (!packet) + return NULL; + + if (length % 4) { /* zero padding bytes */ + packet->data[length >> 2] = 0; + } + packet->host = host; + packet->node_id = node; + + if (hpsb_get_tlabel(packet)) { + hpsb_free_packet(packet); + return NULL; + } + + if (length == 4) { + fill_async_writequad(packet, addr, buffer ? *buffer : 0); + } else { + fill_async_writeblock(packet, addr, length); + if (buffer) + memcpy(packet->data, buffer, length); + } + + return packet; +} + +struct hpsb_packet *hpsb_make_streampacket(struct hpsb_host *host, u8 * buffer, + int length, int channel, int tag, + int sync) +{ + struct hpsb_packet *packet; + + if (length == 0) + return NULL; + + packet = hpsb_alloc_packet(length); + if (!packet) + return NULL; + + if (length % 4) { /* zero padding bytes */ + packet->data[length >> 2] = 0; + } + packet->host = host; + + /* Because it is too difficult to determine all PHY speeds and link + * speeds here, we use S100... */ + packet->speed_code = IEEE1394_SPEED_100; + + /* ...and prevent hpsb_send_packet() from overriding it. */ + packet->node_id = LOCAL_BUS | ALL_NODES; + + if (hpsb_get_tlabel(packet)) { + hpsb_free_packet(packet); + return NULL; + } + + fill_async_stream_packet(packet, length, channel, tag, sync); + if (buffer) + memcpy(packet->data, buffer, length); + + return packet; +} + +struct hpsb_packet *hpsb_make_lockpacket(struct hpsb_host *host, nodeid_t node, + u64 addr, int extcode, + quadlet_t * data, quadlet_t arg) +{ + struct hpsb_packet *p; + u32 length; + + p = hpsb_alloc_packet(8); + if (!p) + return NULL; + + p->host = host; + p->node_id = node; + if (hpsb_get_tlabel(p)) { + hpsb_free_packet(p); + return NULL; + } + + switch (extcode) { + case EXTCODE_FETCH_ADD: + case EXTCODE_LITTLE_ADD: + length = 4; + if (data) + p->data[0] = *data; + break; + default: + length = 8; + if (data) { + p->data[0] = arg; + p->data[1] = *data; + } + break; + } + fill_async_lock(p, addr, extcode, length); + + return p; +} + +struct hpsb_packet *hpsb_make_lock64packet(struct hpsb_host *host, + nodeid_t node, u64 addr, int extcode, + octlet_t * data, octlet_t arg) +{ + struct hpsb_packet *p; + u32 length; + + p = hpsb_alloc_packet(16); + if (!p) + return NULL; + + p->host = host; + p->node_id = node; + if (hpsb_get_tlabel(p)) { + hpsb_free_packet(p); + return NULL; + } + + switch (extcode) { + case EXTCODE_FETCH_ADD: + case EXTCODE_LITTLE_ADD: + length = 8; + if (data) { + p->data[0] = *data >> 32; + p->data[1] = *data & 0xffffffff; + } + break; + default: + length = 16; + if (data) { + p->data[0] = arg >> 32; + p->data[1] = arg & 0xffffffff; + p->data[2] = *data >> 32; + p->data[3] = *data & 0xffffffff; + } + break; + } + fill_async_lock(p, addr, extcode, length); + + return p; +} + +struct hpsb_packet *hpsb_make_phypacket(struct hpsb_host *host, quadlet_t data) +{ + struct hpsb_packet *p; + + p = hpsb_alloc_packet(0); + if (!p) + return NULL; + + p->host = host; + fill_phy_packet(p, data); + + return p; +} + +/* + * FIXME - these functions should probably read from / write to user space to + * avoid in kernel buffers for user space callers + */ + +/** + * hpsb_read - generic read function + * + * Recognizes the local node ID and act accordingly. Automatically uses a + * quadlet read request if @length == 4 and and a block read request otherwise. + * It does not yet support lengths that are not a multiple of 4. + * + * You must explicitly specifiy the @generation for which the node ID is valid, + * to avoid sending packets to the wrong nodes when we race with a bus reset. + */ +int hpsb_read(struct hpsb_host *host, nodeid_t node, unsigned int generation, + u64 addr, quadlet_t * buffer, size_t length) +{ + struct hpsb_packet *packet; + int retval = 0; + + if (length == 0) + return -EINVAL; + + packet = hpsb_make_readpacket(host, node, addr, length); + + if (!packet) { + return -ENOMEM; + } + + packet->generation = generation; + retval = hpsb_send_packet_and_wait(packet); + if (retval < 0) + goto hpsb_read_fail; + + retval = hpsb_packet_success(packet); + + if (retval == 0) { + if (length == 4) { + *buffer = packet->header[3]; + } else { + memcpy(buffer, packet->data, length); + } + } + + hpsb_read_fail: + hpsb_free_tlabel(packet); + hpsb_free_packet(packet); + + return retval; +} + +/** + * hpsb_write - generic write function + * + * Recognizes the local node ID and act accordingly. Automatically uses a + * quadlet write request if @length == 4 and and a block write request + * otherwise. It does not yet support lengths that are not a multiple of 4. + * + * You must explicitly specifiy the @generation for which the node ID is valid, + * to avoid sending packets to the wrong nodes when we race with a bus reset. + */ +int hpsb_write(struct hpsb_host *host, nodeid_t node, unsigned int generation, + u64 addr, quadlet_t * buffer, size_t length) +{ + struct hpsb_packet *packet; + int retval; + + if (length == 0) + return -EINVAL; + + packet = hpsb_make_writepacket(host, node, addr, buffer, length); + + if (!packet) + return -ENOMEM; + + packet->generation = generation; + retval = hpsb_send_packet_and_wait(packet); + if (retval < 0) + goto hpsb_write_fail; + + retval = hpsb_packet_success(packet); + + hpsb_write_fail: + hpsb_free_tlabel(packet); + hpsb_free_packet(packet); + + return retval; +} + +int hpsb_lock(struct hpsb_host *host, nodeid_t node, unsigned int generation, + u64 addr, int extcode, quadlet_t *data, quadlet_t arg) +{ + struct hpsb_packet *packet; + int retval = 0; + + packet = hpsb_make_lockpacket(host, node, addr, extcode, data, arg); + if (!packet) + return -ENOMEM; + + packet->generation = generation; + retval = hpsb_send_packet_and_wait(packet); + if (retval < 0) + goto hpsb_lock_fail; + + retval = hpsb_packet_success(packet); + + if (retval == 0) + *data = packet->data[0]; + +hpsb_lock_fail: + hpsb_free_tlabel(packet); + hpsb_free_packet(packet); + + return retval; +} diff --git a/trunk/drivers/ieee1394/ieee1394_transactions.h b/trunk/drivers/ieee1394/ieee1394_transactions.h new file mode 100644 index 000000000000..20b693be14b2 --- /dev/null +++ b/trunk/drivers/ieee1394/ieee1394_transactions.h @@ -0,0 +1,40 @@ +#ifndef _IEEE1394_TRANSACTIONS_H +#define _IEEE1394_TRANSACTIONS_H + +#include + +#include "ieee1394_types.h" + +struct hpsb_packet; +struct hpsb_host; + +int hpsb_get_tlabel(struct hpsb_packet *packet); +void hpsb_free_tlabel(struct hpsb_packet *packet); +struct hpsb_packet *hpsb_make_readpacket(struct hpsb_host *host, nodeid_t node, + u64 addr, size_t length); +struct hpsb_packet *hpsb_make_lockpacket(struct hpsb_host *host, nodeid_t node, + u64 addr, int extcode, quadlet_t *data, + quadlet_t arg); +struct hpsb_packet *hpsb_make_lock64packet(struct hpsb_host *host, + nodeid_t node, u64 addr, int extcode, + octlet_t *data, octlet_t arg); +struct hpsb_packet *hpsb_make_phypacket(struct hpsb_host *host, quadlet_t data); +struct hpsb_packet *hpsb_make_writepacket(struct hpsb_host *host, + nodeid_t node, u64 addr, + quadlet_t *buffer, size_t length); +struct hpsb_packet *hpsb_make_streampacket(struct hpsb_host *host, u8 *buffer, + int length, int channel, int tag, + int sync); +int hpsb_packet_success(struct hpsb_packet *packet); +int hpsb_read(struct hpsb_host *host, nodeid_t node, unsigned int generation, + u64 addr, quadlet_t *buffer, size_t length); +int hpsb_write(struct hpsb_host *host, nodeid_t node, unsigned int generation, + u64 addr, quadlet_t *buffer, size_t length); +int hpsb_lock(struct hpsb_host *host, nodeid_t node, unsigned int generation, + u64 addr, int extcode, quadlet_t *data, quadlet_t arg); + +#ifdef HPSB_DEBUG_TLABELS +extern spinlock_t hpsb_tlabel_lock; +#endif + +#endif /* _IEEE1394_TRANSACTIONS_H */ diff --git a/trunk/drivers/ieee1394/ieee1394_types.h b/trunk/drivers/ieee1394/ieee1394_types.h new file mode 100644 index 000000000000..9803aaa15be0 --- /dev/null +++ b/trunk/drivers/ieee1394/ieee1394_types.h @@ -0,0 +1,69 @@ +#ifndef _IEEE1394_TYPES_H +#define _IEEE1394_TYPES_H + +#include +#include +#include +#include + +typedef u32 quadlet_t; +typedef u64 octlet_t; +typedef u16 nodeid_t; + +typedef u8 byte_t; +typedef u64 nodeaddr_t; +typedef u16 arm_length_t; + +#define BUS_MASK 0xffc0 +#define BUS_SHIFT 6 +#define NODE_MASK 0x003f +#define LOCAL_BUS 0xffc0 +#define ALL_NODES 0x003f + +#define NODEID_TO_BUS(nodeid) ((nodeid & BUS_MASK) >> BUS_SHIFT) +#define NODEID_TO_NODE(nodeid) (nodeid & NODE_MASK) + +/* Can be used to consistently print a node/bus ID. */ +#define NODE_BUS_FMT "%d-%02d:%04d" +#define NODE_BUS_ARGS(__host, __nodeid) \ + __host->id, NODEID_TO_NODE(__nodeid), NODEID_TO_BUS(__nodeid) + +#define HPSB_PRINT(level, fmt, args...) \ + printk(level "ieee1394: " fmt "\n" , ## args) + +#define HPSB_DEBUG(fmt, args...) HPSB_PRINT(KERN_DEBUG, fmt , ## args) +#define HPSB_INFO(fmt, args...) HPSB_PRINT(KERN_INFO, fmt , ## args) +#define HPSB_NOTICE(fmt, args...) HPSB_PRINT(KERN_NOTICE, fmt , ## args) +#define HPSB_WARN(fmt, args...) HPSB_PRINT(KERN_WARNING, fmt , ## args) +#define HPSB_ERR(fmt, args...) HPSB_PRINT(KERN_ERR, fmt , ## args) + +#ifdef CONFIG_IEEE1394_VERBOSEDEBUG +#define HPSB_VERBOSE(fmt, args...) HPSB_PRINT(KERN_DEBUG, fmt , ## args) +#define HPSB_DEBUG_TLABELS +#else +#define HPSB_VERBOSE(fmt, args...) do {} while (0) +#endif + +#ifdef __BIG_ENDIAN + +static inline void *memcpy_le32(u32 *dest, const u32 *__src, size_t count) +{ + void *tmp = dest; + u32 *src = (u32 *)__src; + + count /= 4; + while (count--) + *dest++ = swab32p(src++); + return tmp; +} + +#else + +static __inline__ void *memcpy_le32(u32 *dest, const u32 *src, size_t count) +{ + return memcpy(dest, src, count); +} + +#endif /* __BIG_ENDIAN */ + +#endif /* _IEEE1394_TYPES_H */ diff --git a/trunk/drivers/firewire/init_ohci1394_dma.c b/trunk/drivers/ieee1394/init_ohci1394_dma.c similarity index 84% rename from trunk/drivers/firewire/init_ohci1394_dma.c rename to trunk/drivers/ieee1394/init_ohci1394_dma.c index a9a347adb353..ddaab6eb8ace 100644 --- a/trunk/drivers/firewire/init_ohci1394_dma.c +++ b/trunk/drivers/ieee1394/init_ohci1394_dma.c @@ -32,41 +32,23 @@ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include /* for ohci1394.h */ #include -#include -#include #include /* for PCI defines */ -#include - +#include #include /* for direct PCI config space access */ #include -#include -#include "ohci.h" +#include "ieee1394_types.h" +#include "ohci1394.h" int __initdata init_ohci1394_dma_early; -struct ohci { - void __iomem *registers; -}; - -static inline void reg_write(const struct ohci *ohci, int offset, u32 data) -{ - writel(data, ohci->registers + offset); -} - -static inline u32 reg_read(const struct ohci *ohci, int offset) -{ - return readl(ohci->registers + offset); -} - -#define OHCI_LOOP_COUNT 100 /* Number of loops for reg read waits */ - /* Reads a PHY register of an OHCI-1394 controller */ -static inline u8 __init get_phy_reg(struct ohci *ohci, u8 addr) +static inline u8 __init get_phy_reg(struct ti_ohci *ohci, u8 addr) { int i; - u32 r; + quadlet_t r; reg_write(ohci, OHCI1394_PhyControl, (addr << 8) | 0x00008000); @@ -81,22 +63,22 @@ static inline u8 __init get_phy_reg(struct ohci *ohci, u8 addr) } /* Writes to a PHY register of an OHCI-1394 controller */ -static inline void __init set_phy_reg(struct ohci *ohci, u8 addr, u8 data) +static inline void __init set_phy_reg(struct ti_ohci *ohci, u8 addr, u8 data) { int i; reg_write(ohci, OHCI1394_PhyControl, (addr << 8) | data | 0x00004000); for (i = 0; i < OHCI_LOOP_COUNT; i++) { - if (!(reg_read(ohci, OHCI1394_PhyControl) & 0x00004000)) + u32 r = reg_read(ohci, OHCI1394_PhyControl); + if (!(r & 0x00004000)) break; mdelay(1); } } /* Resets an OHCI-1394 controller (for sane state before initialization) */ -static inline void __init init_ohci1394_soft_reset(struct ohci *ohci) -{ +static inline void __init init_ohci1394_soft_reset(struct ti_ohci *ohci) { int i; reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_softReset); @@ -109,14 +91,10 @@ static inline void __init init_ohci1394_soft_reset(struct ohci *ohci) } } -#define OHCI1394_MAX_AT_REQ_RETRIES 0xf -#define OHCI1394_MAX_AT_RESP_RETRIES 0x2 -#define OHCI1394_MAX_PHYS_RESP_RETRIES 0x8 - /* Basic OHCI-1394 register and port inititalization */ -static inline void __init init_ohci1394_initialize(struct ohci *ohci) +static inline void __init init_ohci1394_initialize(struct ti_ohci *ohci) { - u32 bus_options; + quadlet_t bus_options; int num_ports, i; /* Put some defaults to these undefined bus options */ @@ -138,7 +116,7 @@ static inline void __init init_ohci1394_initialize(struct ohci *ohci) /* enable phys */ reg_write(ohci, OHCI1394_LinkControlSet, - OHCI1394_LinkControl_rcvPhyPkt); + OHCI1394_LinkControl_RcvPhyPkt); /* Don't accept phy packets into AR request context */ reg_write(ohci, OHCI1394_LinkControlClear, 0x00000400); @@ -150,7 +128,7 @@ static inline void __init init_ohci1394_initialize(struct ohci *ohci) reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 0xffffffff); /* Accept asyncronous transfer requests from all nodes for now */ - reg_write(ohci, OHCI1394_AsReqFilterHiSet, 0x80000000); + reg_write(ohci,OHCI1394_AsReqFilterHiSet, 0x80000000); /* Specify asyncronous transfer retries */ reg_write(ohci, OHCI1394_ATRetries, @@ -159,8 +137,7 @@ static inline void __init init_ohci1394_initialize(struct ohci *ohci) (OHCI1394_MAX_PHYS_RESP_RETRIES<<8)); /* We don't want hardware swapping */ - reg_write(ohci, OHCI1394_HCControlClear, - OHCI1394_HCControl_noByteSwapData); + reg_write(ohci, OHCI1394_HCControlClear, OHCI1394_HCControl_noByteSwap); /* Enable link */ reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_linkEnable); @@ -187,11 +164,11 @@ static inline void __init init_ohci1394_initialize(struct ohci *ohci) * has to be enabled after each bus reset when needed. We resort * to polling here because on early boot, we have no interrupts. */ -static inline void __init init_ohci1394_wait_for_busresets(struct ohci *ohci) +static inline void __init init_ohci1394_wait_for_busresets(struct ti_ohci *ohci) { int i, events; - for (i = 0; i < 9; i++) { + for (i=0; i < 9; i++) { mdelay(200); events = reg_read(ohci, OHCI1394_IntEventSet); if (events & OHCI1394_busReset) @@ -205,18 +182,18 @@ static inline void __init init_ohci1394_wait_for_busresets(struct ohci *ohci) * This enables remote DMA access over IEEE1394 from every host for the low * 4GB of address space. DMA accesses above 4GB are not available currently. */ -static inline void __init init_ohci1394_enable_physical_dma(struct ohci *ohci) +static inline void __init init_ohci1394_enable_physical_dma(struct ti_ohci *hci) { - reg_write(ohci, OHCI1394_PhyReqFilterHiSet, 0xffffffff); - reg_write(ohci, OHCI1394_PhyReqFilterLoSet, 0xffffffff); - reg_write(ohci, OHCI1394_PhyUpperBound, 0xffff0000); + reg_write(hci, OHCI1394_PhyReqFilterHiSet, 0xffffffff); + reg_write(hci, OHCI1394_PhyReqFilterLoSet, 0xffffffff); + reg_write(hci, OHCI1394_PhyUpperBound, 0xffff0000); } /** * init_ohci1394_reset_and_init_dma - init controller and enable DMA * This initializes the given controller and enables physical DMA engine in it. */ -static inline void __init init_ohci1394_reset_and_init_dma(struct ohci *ohci) +static inline void __init init_ohci1394_reset_and_init_dma(struct ti_ohci *ohci) { /* Start off with a soft reset, clears everything to a sane state. */ init_ohci1394_soft_reset(ohci); @@ -248,7 +225,7 @@ static inline void __init init_ohci1394_reset_and_init_dma(struct ohci *ohci) static inline void __init init_ohci1394_controller(int num, int slot, int func) { unsigned long ohci_base; - struct ohci ohci; + struct ti_ohci ohci; printk(KERN_INFO "init_ohci1394_dma: initializing OHCI-1394" " at %02x:%02x.%x\n", num, slot, func); @@ -258,7 +235,7 @@ static inline void __init init_ohci1394_controller(int num, int slot, int func) set_fixmap_nocache(FIX_OHCI1394_BASE, ohci_base); - ohci.registers = (void __iomem *)fix_to_virt(FIX_OHCI1394_BASE); + ohci.registers = (void *)fix_to_virt(FIX_OHCI1394_BASE); init_ohci1394_reset_and_init_dma(&ohci); } @@ -270,7 +247,6 @@ static inline void __init init_ohci1394_controller(int num, int slot, int func) void __init init_ohci1394_dma_on_all_controllers(void) { int num, slot, func; - u32 class; if (!early_pci_allowed()) return; @@ -279,9 +255,9 @@ void __init init_ohci1394_dma_on_all_controllers(void) for (num = 0; num < 32; num++) { for (slot = 0; slot < 32; slot++) { for (func = 0; func < 8; func++) { - class = read_pci_config(num, slot, func, + u32 class = read_pci_config(num,slot,func, PCI_CLASS_REVISION); - if (class == 0xffffffff) + if ((class == 0xffffffff)) continue; /* No device at this func */ if (class>>8 != PCI_CLASS_SERIAL_FIREWIRE_OHCI) diff --git a/trunk/drivers/ieee1394/iso.c b/trunk/drivers/ieee1394/iso.c new file mode 100644 index 000000000000..1cf6487b65ba --- /dev/null +++ b/trunk/drivers/ieee1394/iso.c @@ -0,0 +1,568 @@ +/* + * IEEE 1394 for Linux + * + * kernel ISO transmission/reception + * + * Copyright (C) 2002 Maas Digital LLC + * + * This code is licensed under the GPL. See the file COPYING in the root + * directory of the kernel sources for details. + */ + +#include +#include +#include +#include + +#include "hosts.h" +#include "iso.h" + +/** + * hpsb_iso_stop - stop DMA + */ +void hpsb_iso_stop(struct hpsb_iso *iso) +{ + if (!(iso->flags & HPSB_ISO_DRIVER_STARTED)) + return; + + iso->host->driver->isoctl(iso, iso->type == HPSB_ISO_XMIT ? + XMIT_STOP : RECV_STOP, 0); + iso->flags &= ~HPSB_ISO_DRIVER_STARTED; +} + +/** + * hpsb_iso_shutdown - deallocate buffer and DMA context + */ +void hpsb_iso_shutdown(struct hpsb_iso *iso) +{ + if (iso->flags & HPSB_ISO_DRIVER_INIT) { + hpsb_iso_stop(iso); + iso->host->driver->isoctl(iso, iso->type == HPSB_ISO_XMIT ? + XMIT_SHUTDOWN : RECV_SHUTDOWN, 0); + iso->flags &= ~HPSB_ISO_DRIVER_INIT; + } + + dma_region_free(&iso->data_buf); + kfree(iso); +} + +static struct hpsb_iso *hpsb_iso_common_init(struct hpsb_host *host, + enum hpsb_iso_type type, + unsigned int data_buf_size, + unsigned int buf_packets, + int channel, int dma_mode, + int irq_interval, + void (*callback) (struct hpsb_iso + *)) +{ + struct hpsb_iso *iso; + int dma_direction; + + /* make sure driver supports the ISO API */ + if (!host->driver->isoctl) { + printk(KERN_INFO + "ieee1394: host driver '%s' does not support the rawiso API\n", + host->driver->name); + return NULL; + } + + /* sanitize parameters */ + + if (buf_packets < 2) + buf_packets = 2; + + if ((dma_mode < HPSB_ISO_DMA_DEFAULT) + || (dma_mode > HPSB_ISO_DMA_PACKET_PER_BUFFER)) + dma_mode = HPSB_ISO_DMA_DEFAULT; + + if ((irq_interval < 0) || (irq_interval > buf_packets / 4)) + irq_interval = buf_packets / 4; + if (irq_interval == 0) /* really interrupt for each packet */ + irq_interval = 1; + + if (channel < -1 || channel >= 64) + return NULL; + + /* channel = -1 is OK for multi-channel recv but not for xmit */ + if (type == HPSB_ISO_XMIT && channel < 0) + return NULL; + + /* allocate and write the struct hpsb_iso */ + + iso = + kmalloc(sizeof(*iso) + + buf_packets * sizeof(struct hpsb_iso_packet_info), + GFP_KERNEL); + if (!iso) + return NULL; + + iso->infos = (struct hpsb_iso_packet_info *)(iso + 1); + + iso->type = type; + iso->host = host; + iso->hostdata = NULL; + iso->callback = callback; + init_waitqueue_head(&iso->waitq); + iso->channel = channel; + iso->irq_interval = irq_interval; + iso->dma_mode = dma_mode; + dma_region_init(&iso->data_buf); + iso->buf_size = PAGE_ALIGN(data_buf_size); + iso->buf_packets = buf_packets; + iso->pkt_dma = 0; + iso->first_packet = 0; + spin_lock_init(&iso->lock); + + if (iso->type == HPSB_ISO_XMIT) { + iso->n_ready_packets = iso->buf_packets; + dma_direction = PCI_DMA_TODEVICE; + } else { + iso->n_ready_packets = 0; + dma_direction = PCI_DMA_FROMDEVICE; + } + + atomic_set(&iso->overflows, 0); + iso->bytes_discarded = 0; + iso->flags = 0; + iso->prebuffer = 0; + + /* allocate the packet buffer */ + if (dma_region_alloc + (&iso->data_buf, iso->buf_size, host->pdev, dma_direction)) + goto err; + + return iso; + + err: + hpsb_iso_shutdown(iso); + return NULL; +} + +/** + * hpsb_iso_n_ready - returns number of packets ready to send or receive + */ +int hpsb_iso_n_ready(struct hpsb_iso *iso) +{ + unsigned long flags; + int val; + + spin_lock_irqsave(&iso->lock, flags); + val = iso->n_ready_packets; + spin_unlock_irqrestore(&iso->lock, flags); + + return val; +} + +/** + * hpsb_iso_xmit_init - allocate the buffer and DMA context + */ +struct hpsb_iso *hpsb_iso_xmit_init(struct hpsb_host *host, + unsigned int data_buf_size, + unsigned int buf_packets, + int channel, + int speed, + int irq_interval, + void (*callback) (struct hpsb_iso *)) +{ + struct hpsb_iso *iso = hpsb_iso_common_init(host, HPSB_ISO_XMIT, + data_buf_size, buf_packets, + channel, + HPSB_ISO_DMA_DEFAULT, + irq_interval, callback); + if (!iso) + return NULL; + + iso->speed = speed; + + /* tell the driver to start working */ + if (host->driver->isoctl(iso, XMIT_INIT, 0)) + goto err; + + iso->flags |= HPSB_ISO_DRIVER_INIT; + return iso; + + err: + hpsb_iso_shutdown(iso); + return NULL; +} + +/** + * hpsb_iso_recv_init - allocate the buffer and DMA context + * + * Note, if channel = -1, multi-channel receive is enabled. + */ +struct hpsb_iso *hpsb_iso_recv_init(struct hpsb_host *host, + unsigned int data_buf_size, + unsigned int buf_packets, + int channel, + int dma_mode, + int irq_interval, + void (*callback) (struct hpsb_iso *)) +{ + struct hpsb_iso *iso = hpsb_iso_common_init(host, HPSB_ISO_RECV, + data_buf_size, buf_packets, + channel, dma_mode, + irq_interval, callback); + if (!iso) + return NULL; + + /* tell the driver to start working */ + if (host->driver->isoctl(iso, RECV_INIT, 0)) + goto err; + + iso->flags |= HPSB_ISO_DRIVER_INIT; + return iso; + + err: + hpsb_iso_shutdown(iso); + return NULL; +} + +/** + * hpsb_iso_recv_listen_channel + * + * multi-channel only + */ +int hpsb_iso_recv_listen_channel(struct hpsb_iso *iso, unsigned char channel) +{ + if (iso->type != HPSB_ISO_RECV || iso->channel != -1 || channel >= 64) + return -EINVAL; + return iso->host->driver->isoctl(iso, RECV_LISTEN_CHANNEL, channel); +} + +/** + * hpsb_iso_recv_unlisten_channel + * + * multi-channel only + */ +int hpsb_iso_recv_unlisten_channel(struct hpsb_iso *iso, unsigned char channel) +{ + if (iso->type != HPSB_ISO_RECV || iso->channel != -1 || channel >= 64) + return -EINVAL; + return iso->host->driver->isoctl(iso, RECV_UNLISTEN_CHANNEL, channel); +} + +/** + * hpsb_iso_recv_set_channel_mask + * + * multi-channel only + */ +int hpsb_iso_recv_set_channel_mask(struct hpsb_iso *iso, u64 mask) +{ + if (iso->type != HPSB_ISO_RECV || iso->channel != -1) + return -EINVAL; + return iso->host->driver->isoctl(iso, RECV_SET_CHANNEL_MASK, + (unsigned long)&mask); +} + +/** + * hpsb_iso_recv_flush - check for arrival of new packets + * + * check for arrival of new packets immediately (even if irq_interval + * has not yet been reached) + */ +int hpsb_iso_recv_flush(struct hpsb_iso *iso) +{ + if (iso->type != HPSB_ISO_RECV) + return -EINVAL; + return iso->host->driver->isoctl(iso, RECV_FLUSH, 0); +} + +static int do_iso_xmit_start(struct hpsb_iso *iso, int cycle) +{ + int retval = iso->host->driver->isoctl(iso, XMIT_START, cycle); + if (retval) + return retval; + + iso->flags |= HPSB_ISO_DRIVER_STARTED; + return retval; +} + +/** + * hpsb_iso_xmit_start - start DMA + */ +int hpsb_iso_xmit_start(struct hpsb_iso *iso, int cycle, int prebuffer) +{ + if (iso->type != HPSB_ISO_XMIT) + return -1; + + if (iso->flags & HPSB_ISO_DRIVER_STARTED) + return 0; + + if (cycle < -1) + cycle = -1; + else if (cycle >= 8000) + cycle %= 8000; + + iso->xmit_cycle = cycle; + + if (prebuffer < 0) + prebuffer = iso->buf_packets - 1; + else if (prebuffer == 0) + prebuffer = 1; + + if (prebuffer >= iso->buf_packets) + prebuffer = iso->buf_packets - 1; + + iso->prebuffer = prebuffer; + + /* remember the starting cycle; DMA will commence from xmit_queue_packets() + once enough packets have been buffered */ + iso->start_cycle = cycle; + + return 0; +} + +/** + * hpsb_iso_recv_start - start DMA + */ +int hpsb_iso_recv_start(struct hpsb_iso *iso, int cycle, int tag_mask, int sync) +{ + int retval = 0; + int isoctl_args[3]; + + if (iso->type != HPSB_ISO_RECV) + return -1; + + if (iso->flags & HPSB_ISO_DRIVER_STARTED) + return 0; + + if (cycle < -1) + cycle = -1; + else if (cycle >= 8000) + cycle %= 8000; + + isoctl_args[0] = cycle; + + if (tag_mask < 0) + /* match all tags */ + tag_mask = 0xF; + isoctl_args[1] = tag_mask; + + isoctl_args[2] = sync; + + retval = + iso->host->driver->isoctl(iso, RECV_START, + (unsigned long)&isoctl_args[0]); + if (retval) + return retval; + + iso->flags |= HPSB_ISO_DRIVER_STARTED; + return retval; +} + +/* check to make sure the user has not supplied bogus values of offset/len + * that would cause the kernel to access memory outside the buffer */ +static int hpsb_iso_check_offset_len(struct hpsb_iso *iso, + unsigned int offset, unsigned short len, + unsigned int *out_offset, + unsigned short *out_len) +{ + if (offset >= iso->buf_size) + return -EFAULT; + + /* make sure the packet does not go beyond the end of the buffer */ + if (offset + len > iso->buf_size) + return -EFAULT; + + /* check for wrap-around */ + if (offset + len < offset) + return -EFAULT; + + /* now we can trust 'offset' and 'length' */ + *out_offset = offset; + *out_len = len; + + return 0; +} + +/** + * hpsb_iso_xmit_queue_packet - queue a packet for transmission. + * + * @offset is relative to the beginning of the DMA buffer, where the packet's + * data payload should already have been placed. + */ +int hpsb_iso_xmit_queue_packet(struct hpsb_iso *iso, u32 offset, u16 len, + u8 tag, u8 sy) +{ + struct hpsb_iso_packet_info *info; + unsigned long flags; + int rv; + + if (iso->type != HPSB_ISO_XMIT) + return -EINVAL; + + /* is there space in the buffer? */ + if (iso->n_ready_packets <= 0) { + return -EBUSY; + } + + info = &iso->infos[iso->first_packet]; + + /* check for bogus offset/length */ + if (hpsb_iso_check_offset_len + (iso, offset, len, &info->offset, &info->len)) + return -EFAULT; + + info->tag = tag; + info->sy = sy; + + spin_lock_irqsave(&iso->lock, flags); + + rv = iso->host->driver->isoctl(iso, XMIT_QUEUE, (unsigned long)info); + if (rv) + goto out; + + /* increment cursors */ + iso->first_packet = (iso->first_packet + 1) % iso->buf_packets; + iso->xmit_cycle = (iso->xmit_cycle + 1) % 8000; + iso->n_ready_packets--; + + if (iso->prebuffer != 0) { + iso->prebuffer--; + if (iso->prebuffer <= 0) { + iso->prebuffer = 0; + rv = do_iso_xmit_start(iso, iso->start_cycle); + } + } + + out: + spin_unlock_irqrestore(&iso->lock, flags); + return rv; +} + +/** + * hpsb_iso_xmit_sync - wait until all queued packets have been transmitted + */ +int hpsb_iso_xmit_sync(struct hpsb_iso *iso) +{ + if (iso->type != HPSB_ISO_XMIT) + return -EINVAL; + + return wait_event_interruptible(iso->waitq, + hpsb_iso_n_ready(iso) == + iso->buf_packets); +} + +/** + * hpsb_iso_packet_sent + * + * Available to low-level drivers. + * + * Call after a packet has been transmitted to the bus (interrupt context is + * OK). @cycle is the _exact_ cycle the packet was sent on. @error should be + * non-zero if some sort of error occurred when sending the packet. + */ +void hpsb_iso_packet_sent(struct hpsb_iso *iso, int cycle, int error) +{ + unsigned long flags; + spin_lock_irqsave(&iso->lock, flags); + + /* predict the cycle of the next packet to be queued */ + + /* jump ahead by the number of packets that are already buffered */ + cycle += iso->buf_packets - iso->n_ready_packets; + cycle %= 8000; + + iso->xmit_cycle = cycle; + iso->n_ready_packets++; + iso->pkt_dma = (iso->pkt_dma + 1) % iso->buf_packets; + + if (iso->n_ready_packets == iso->buf_packets || error != 0) { + /* the buffer has run empty! */ + atomic_inc(&iso->overflows); + } + + spin_unlock_irqrestore(&iso->lock, flags); +} + +/** + * hpsb_iso_packet_received + * + * Available to low-level drivers. + * + * Call after a packet has been received (interrupt context is OK). + */ +void hpsb_iso_packet_received(struct hpsb_iso *iso, u32 offset, u16 len, + u16 total_len, u16 cycle, u8 channel, u8 tag, + u8 sy) +{ + unsigned long flags; + spin_lock_irqsave(&iso->lock, flags); + + if (iso->n_ready_packets == iso->buf_packets) { + /* overflow! */ + atomic_inc(&iso->overflows); + /* Record size of this discarded packet */ + iso->bytes_discarded += total_len; + } else { + struct hpsb_iso_packet_info *info = &iso->infos[iso->pkt_dma]; + info->offset = offset; + info->len = len; + info->total_len = total_len; + info->cycle = cycle; + info->channel = channel; + info->tag = tag; + info->sy = sy; + + iso->pkt_dma = (iso->pkt_dma + 1) % iso->buf_packets; + iso->n_ready_packets++; + } + + spin_unlock_irqrestore(&iso->lock, flags); +} + +/** + * hpsb_iso_recv_release_packets - release packets, reuse buffer + * + * @n_packets have been read out of the buffer, re-use the buffer space + */ +int hpsb_iso_recv_release_packets(struct hpsb_iso *iso, unsigned int n_packets) +{ + unsigned long flags; + unsigned int i; + int rv = 0; + + if (iso->type != HPSB_ISO_RECV) + return -1; + + spin_lock_irqsave(&iso->lock, flags); + for (i = 0; i < n_packets; i++) { + rv = iso->host->driver->isoctl(iso, RECV_RELEASE, + (unsigned long)&iso->infos[iso-> + first_packet]); + if (rv) + break; + + iso->first_packet = (iso->first_packet + 1) % iso->buf_packets; + iso->n_ready_packets--; + + /* release memory from packets discarded when queue was full */ + if (iso->n_ready_packets == 0) { /* Release only after all prior packets handled */ + if (iso->bytes_discarded != 0) { + struct hpsb_iso_packet_info inf; + inf.total_len = iso->bytes_discarded; + iso->host->driver->isoctl(iso, RECV_RELEASE, + (unsigned long)&inf); + iso->bytes_discarded = 0; + } + } + } + spin_unlock_irqrestore(&iso->lock, flags); + return rv; +} + +/** + * hpsb_iso_wake + * + * Available to low-level drivers. + * + * Call to wake waiting processes after buffer space has opened up. + */ +void hpsb_iso_wake(struct hpsb_iso *iso) +{ + wake_up_interruptible(&iso->waitq); + + if (iso->callback) + iso->callback(iso); +} diff --git a/trunk/drivers/ieee1394/iso.h b/trunk/drivers/ieee1394/iso.h new file mode 100644 index 000000000000..c2089c093aa7 --- /dev/null +++ b/trunk/drivers/ieee1394/iso.h @@ -0,0 +1,195 @@ +/* + * IEEE 1394 for Linux + * + * kernel ISO transmission/reception + * + * Copyright (C) 2002 Maas Digital LLC + * + * This code is licensed under the GPL. See the file COPYING in the root + * directory of the kernel sources for details. + */ + +#ifndef IEEE1394_ISO_H +#define IEEE1394_ISO_H + +#include +#include +#include +#include + +#include "dma.h" + +struct hpsb_host; + +/* high-level ISO interface */ + +/* + * This API sends and receives isochronous packets on a large, + * virtually-contiguous kernel memory buffer. The buffer may be mapped + * into a user-space process for zero-copy transmission and reception. + * + * There are no explicit boundaries between packets in the buffer. A + * packet may be transmitted or received at any location. However, + * low-level drivers may impose certain restrictions on alignment or + * size of packets. (e.g. in OHCI no packet may cross a page boundary, + * and packets should be quadlet-aligned) + */ + +/* Packet descriptor - the API maintains a ring buffer of these packet + * descriptors in kernel memory (hpsb_iso.infos[]). */ +struct hpsb_iso_packet_info { + /* offset of data payload relative to the first byte of the buffer */ + __u32 offset; + + /* length of the data payload, in bytes (not including the isochronous + * header) */ + __u16 len; + + /* (recv only) the cycle number (mod 8000) on which the packet was + * received */ + __u16 cycle; + + /* (recv only) channel on which the packet was received */ + __u8 channel; + + /* 2-bit 'tag' and 4-bit 'sy' fields of the isochronous header */ + __u8 tag; + __u8 sy; + + /* length in bytes of the packet including header/trailer. + * MUST be at structure end, since the first part of this structure is + * also defined in raw1394.h (i.e. struct raw1394_iso_packet_info), is + * copied to userspace and is accessed there through libraw1394. */ + __u16 total_len; +}; + +enum hpsb_iso_type { HPSB_ISO_RECV = 0, HPSB_ISO_XMIT = 1 }; + +/* The mode of the dma when receiving iso data. Must be supported by chip */ +enum raw1394_iso_dma_recv_mode { + HPSB_ISO_DMA_DEFAULT = -1, + HPSB_ISO_DMA_OLD_ABI = 0, + HPSB_ISO_DMA_BUFFERFILL = 1, + HPSB_ISO_DMA_PACKET_PER_BUFFER = 2 +}; + +struct hpsb_iso { + enum hpsb_iso_type type; + + /* pointer to low-level driver and its private data */ + struct hpsb_host *host; + void *hostdata; + + /* a function to be called (from interrupt context) after + * outgoing packets have been sent, or incoming packets have + * arrived */ + void (*callback)(struct hpsb_iso*); + + /* wait for buffer space */ + wait_queue_head_t waitq; + + int speed; /* IEEE1394_SPEED_100, 200, or 400 */ + int channel; /* -1 if multichannel */ + int dma_mode; /* dma receive mode */ + + + /* greatest # of packets between interrupts - controls + * the maximum latency of the buffer */ + int irq_interval; + + /* the buffer for packet data payloads */ + struct dma_region data_buf; + + /* size of data_buf, in bytes (always a multiple of PAGE_SIZE) */ + unsigned int buf_size; + + /* # of packets in the ringbuffer */ + unsigned int buf_packets; + + /* protects packet cursors */ + spinlock_t lock; + + /* the index of the next packet that will be produced + or consumed by the user */ + int first_packet; + + /* the index of the next packet that will be transmitted + or received by the 1394 hardware */ + int pkt_dma; + + /* how many packets, starting at first_packet: + * (transmit) are ready to be filled with data + * (receive) contain received data */ + int n_ready_packets; + + /* how many times the buffer has overflowed or underflowed */ + atomic_t overflows; + /* how many cycles were skipped for a given context */ + atomic_t skips; + + /* Current number of bytes lost in discarded packets */ + int bytes_discarded; + + /* private flags to track initialization progress */ +#define HPSB_ISO_DRIVER_INIT (1<<0) +#define HPSB_ISO_DRIVER_STARTED (1<<1) + unsigned int flags; + + /* # of packets left to prebuffer (xmit only) */ + int prebuffer; + + /* starting cycle for DMA (xmit only) */ + int start_cycle; + + /* cycle at which next packet will be transmitted, + * -1 if not known */ + int xmit_cycle; + + /* ringbuffer of packet descriptors in regular kernel memory + * XXX Keep this last, since we use over-allocated memory from + * this entry to fill this field. */ + struct hpsb_iso_packet_info *infos; +}; + +/* functions available to high-level drivers (e.g. raw1394) */ + +struct hpsb_iso* hpsb_iso_xmit_init(struct hpsb_host *host, + unsigned int data_buf_size, + unsigned int buf_packets, + int channel, + int speed, + int irq_interval, + void (*callback)(struct hpsb_iso*)); +struct hpsb_iso* hpsb_iso_recv_init(struct hpsb_host *host, + unsigned int data_buf_size, + unsigned int buf_packets, + int channel, + int dma_mode, + int irq_interval, + void (*callback)(struct hpsb_iso*)); +int hpsb_iso_recv_listen_channel(struct hpsb_iso *iso, unsigned char channel); +int hpsb_iso_recv_unlisten_channel(struct hpsb_iso *iso, unsigned char channel); +int hpsb_iso_recv_set_channel_mask(struct hpsb_iso *iso, u64 mask); +int hpsb_iso_xmit_start(struct hpsb_iso *iso, int start_on_cycle, + int prebuffer); +int hpsb_iso_recv_start(struct hpsb_iso *iso, int start_on_cycle, + int tag_mask, int sync); +void hpsb_iso_stop(struct hpsb_iso *iso); +void hpsb_iso_shutdown(struct hpsb_iso *iso); +int hpsb_iso_xmit_queue_packet(struct hpsb_iso *iso, u32 offset, u16 len, + u8 tag, u8 sy); +int hpsb_iso_xmit_sync(struct hpsb_iso *iso); +int hpsb_iso_recv_release_packets(struct hpsb_iso *recv, + unsigned int n_packets); +int hpsb_iso_recv_flush(struct hpsb_iso *iso); +int hpsb_iso_n_ready(struct hpsb_iso *iso); + +/* the following are callbacks available to low-level drivers */ + +void hpsb_iso_packet_sent(struct hpsb_iso *iso, int cycle, int error); +void hpsb_iso_packet_received(struct hpsb_iso *iso, u32 offset, u16 len, + u16 total_len, u16 cycle, u8 channel, u8 tag, + u8 sy); +void hpsb_iso_wake(struct hpsb_iso *iso); + +#endif /* IEEE1394_ISO_H */ diff --git a/trunk/drivers/ieee1394/nodemgr.c b/trunk/drivers/ieee1394/nodemgr.c new file mode 100644 index 000000000000..18350213479e --- /dev/null +++ b/trunk/drivers/ieee1394/nodemgr.c @@ -0,0 +1,1901 @@ +/* + * Node information (ConfigROM) collection and management. + * + * Copyright (C) 2000 Andreas E. Bombe + * 2001-2003 Ben Collins + * + * This code is licensed under the GPL. See the file COPYING in the root + * directory of the kernel sources for details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "csr.h" +#include "highlevel.h" +#include "hosts.h" +#include "ieee1394.h" +#include "ieee1394_core.h" +#include "ieee1394_hotplug.h" +#include "ieee1394_types.h" +#include "ieee1394_transactions.h" +#include "nodemgr.h" + +static int ignore_drivers; +module_param(ignore_drivers, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(ignore_drivers, "Disable automatic probing for drivers."); + +struct nodemgr_csr_info { + struct hpsb_host *host; + nodeid_t nodeid; + unsigned int generation; + + kmemcheck_bitfield_begin(flags); + unsigned int speed_unverified:1; + kmemcheck_bitfield_end(flags); +}; + + +/* + * Correct the speed map entry. This is necessary + * - for nodes with link speed < phy speed, + * - for 1394b nodes with negotiated phy port speed < IEEE1394_SPEED_MAX. + * A possible speed is determined by trial and error, using quadlet reads. + */ +static int nodemgr_check_speed(struct nodemgr_csr_info *ci, u64 addr, + quadlet_t *buffer) +{ + quadlet_t q; + u8 i, *speed, old_speed, good_speed; + int error; + + speed = &(ci->host->speed[NODEID_TO_NODE(ci->nodeid)]); + old_speed = *speed; + good_speed = IEEE1394_SPEED_MAX + 1; + + /* Try every speed from S100 to old_speed. + * If we did it the other way around, a too low speed could be caught + * if the retry succeeded for some other reason, e.g. because the link + * just finished its initialization. */ + for (i = IEEE1394_SPEED_100; i <= old_speed; i++) { + *speed = i; + error = hpsb_read(ci->host, ci->nodeid, ci->generation, addr, + &q, 4); + if (error) + break; + *buffer = q; + good_speed = i; + } + if (good_speed <= IEEE1394_SPEED_MAX) { + HPSB_DEBUG("Speed probe of node " NODE_BUS_FMT " yields %s", + NODE_BUS_ARGS(ci->host, ci->nodeid), + hpsb_speedto_str[good_speed]); + *speed = good_speed; + ci->speed_unverified = 0; + return 0; + } + *speed = old_speed; + return error; +} + +static int nodemgr_bus_read(struct csr1212_csr *csr, u64 addr, + void *buffer, void *__ci) +{ + struct nodemgr_csr_info *ci = (struct nodemgr_csr_info*)__ci; + int i, error; + + for (i = 1; ; i++) { + error = hpsb_read(ci->host, ci->nodeid, ci->generation, addr, + buffer, 4); + if (!error) { + ci->speed_unverified = 0; + break; + } + /* Give up after 3rd failure. */ + if (i == 3) + break; + + /* The ieee1394_core guessed the node's speed capability from + * the self ID. Check whether a lower speed works. */ + if (ci->speed_unverified) { + error = nodemgr_check_speed(ci, addr, buffer); + if (!error) + break; + } + if (msleep_interruptible(334)) + return -EINTR; + } + return error; +} + +static struct csr1212_bus_ops nodemgr_csr_ops = { + .bus_read = nodemgr_bus_read, +}; + + +/* + * Basically what we do here is start off retrieving the bus_info block. + * From there will fill in some info about the node, verify it is of IEEE + * 1394 type, and that the crc checks out ok. After that we start off with + * the root directory, and subdirectories. To do this, we retrieve the + * quadlet header for a directory, find out the length, and retrieve the + * complete directory entry (be it a leaf or a directory). We then process + * it and add the info to our structure for that particular node. + * + * We verify CRC's along the way for each directory/block/leaf. The entire + * node structure is generic, and simply stores the information in a way + * that's easy to parse by the protocol interface. + */ + +/* + * The nodemgr relies heavily on the Driver Model for device callbacks and + * driver/device mappings. The old nodemgr used to handle all this itself, + * but now we are much simpler because of the LDM. + */ + +struct host_info { + struct hpsb_host *host; + struct list_head list; + struct task_struct *thread; +}; + +static int nodemgr_bus_match(struct device * dev, struct device_driver * drv); +static int nodemgr_uevent(struct device *dev, struct kobj_uevent_env *env); + +struct bus_type ieee1394_bus_type = { + .name = "ieee1394", + .match = nodemgr_bus_match, +}; + +static void host_cls_release(struct device *dev) +{ + put_device(&container_of((dev), struct hpsb_host, host_dev)->device); +} + +struct class hpsb_host_class = { + .name = "ieee1394_host", + .dev_release = host_cls_release, +}; + +static void ne_cls_release(struct device *dev) +{ + put_device(&container_of((dev), struct node_entry, node_dev)->device); +} + +static struct class nodemgr_ne_class = { + .name = "ieee1394_node", + .dev_release = ne_cls_release, +}; + +static void ud_cls_release(struct device *dev) +{ + put_device(&container_of((dev), struct unit_directory, unit_dev)->device); +} + +/* The name here is only so that unit directory hotplug works with old + * style hotplug, which only ever did unit directories anyway. + */ +static struct class nodemgr_ud_class = { + .name = "ieee1394", + .dev_release = ud_cls_release, + .dev_uevent = nodemgr_uevent, +}; + +static struct hpsb_highlevel nodemgr_highlevel; + + +static void nodemgr_release_ud(struct device *dev) +{ + struct unit_directory *ud = container_of(dev, struct unit_directory, device); + + if (ud->vendor_name_kv) + csr1212_release_keyval(ud->vendor_name_kv); + if (ud->model_name_kv) + csr1212_release_keyval(ud->model_name_kv); + + kfree(ud); +} + +static void nodemgr_release_ne(struct device *dev) +{ + struct node_entry *ne = container_of(dev, struct node_entry, device); + + if (ne->vendor_name_kv) + csr1212_release_keyval(ne->vendor_name_kv); + + kfree(ne); +} + + +static void nodemgr_release_host(struct device *dev) +{ + struct hpsb_host *host = container_of(dev, struct hpsb_host, device); + + csr1212_destroy_csr(host->csr.rom); + + kfree(host); +} + +static int nodemgr_ud_platform_data; + +static struct device nodemgr_dev_template_ud = { + .bus = &ieee1394_bus_type, + .release = nodemgr_release_ud, + .platform_data = &nodemgr_ud_platform_data, +}; + +static struct device nodemgr_dev_template_ne = { + .bus = &ieee1394_bus_type, + .release = nodemgr_release_ne, +}; + +/* This dummy driver prevents the host devices from being scanned. We have no + * useful drivers for them yet, and there would be a deadlock possible if the + * driver core scans the host device while the host's low-level driver (i.e. + * the host's parent device) is being removed. */ +static struct device_driver nodemgr_mid_layer_driver = { + .bus = &ieee1394_bus_type, + .name = "nodemgr", + .owner = THIS_MODULE, +}; + +struct device nodemgr_dev_template_host = { + .bus = &ieee1394_bus_type, + .release = nodemgr_release_host, +}; + + +#define fw_attr(class, class_type, field, type, format_string) \ +static ssize_t fw_show_##class##_##field (struct device *dev, struct device_attribute *attr, char *buf)\ +{ \ + class_type *class; \ + class = container_of(dev, class_type, device); \ + return sprintf(buf, format_string, (type)class->field); \ +} \ +static struct device_attribute dev_attr_##class##_##field = { \ + .attr = {.name = __stringify(field), .mode = S_IRUGO }, \ + .show = fw_show_##class##_##field, \ +}; + +#define fw_attr_td(class, class_type, td_kv) \ +static ssize_t fw_show_##class##_##td_kv (struct device *dev, struct device_attribute *attr, char *buf)\ +{ \ + int len; \ + class_type *class = container_of(dev, class_type, device); \ + len = (class->td_kv->value.leaf.len - 2) * sizeof(quadlet_t); \ + memcpy(buf, \ + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(class->td_kv), \ + len); \ + while (buf[len - 1] == '\0') \ + len--; \ + buf[len++] = '\n'; \ + buf[len] = '\0'; \ + return len; \ +} \ +static struct device_attribute dev_attr_##class##_##td_kv = { \ + .attr = {.name = __stringify(td_kv), .mode = S_IRUGO }, \ + .show = fw_show_##class##_##td_kv, \ +}; + + +#define fw_drv_attr(field, type, format_string) \ +static ssize_t fw_drv_show_##field (struct device_driver *drv, char *buf) \ +{ \ + struct hpsb_protocol_driver *driver; \ + driver = container_of(drv, struct hpsb_protocol_driver, driver); \ + return sprintf(buf, format_string, (type)driver->field);\ +} \ +static struct driver_attribute driver_attr_drv_##field = { \ + .attr = {.name = __stringify(field), .mode = S_IRUGO }, \ + .show = fw_drv_show_##field, \ +}; + + +static ssize_t fw_show_ne_bus_options(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct node_entry *ne = container_of(dev, struct node_entry, device); + + return sprintf(buf, "IRMC(%d) CMC(%d) ISC(%d) BMC(%d) PMC(%d) GEN(%d) " + "LSPD(%d) MAX_REC(%d) MAX_ROM(%d) CYC_CLK_ACC(%d)\n", + ne->busopt.irmc, + ne->busopt.cmc, ne->busopt.isc, ne->busopt.bmc, + ne->busopt.pmc, ne->busopt.generation, ne->busopt.lnkspd, + ne->busopt.max_rec, + ne->busopt.max_rom, + ne->busopt.cyc_clk_acc); +} +static DEVICE_ATTR(bus_options,S_IRUGO,fw_show_ne_bus_options,NULL); + + +#ifdef HPSB_DEBUG_TLABELS +static ssize_t fw_show_ne_tlabels_free(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct node_entry *ne = container_of(dev, struct node_entry, device); + unsigned long flags; + unsigned long *tp = ne->host->tl_pool[NODEID_TO_NODE(ne->nodeid)].map; + int tf; + + spin_lock_irqsave(&hpsb_tlabel_lock, flags); + tf = 64 - bitmap_weight(tp, 64); + spin_unlock_irqrestore(&hpsb_tlabel_lock, flags); + + return sprintf(buf, "%d\n", tf); +} +static DEVICE_ATTR(tlabels_free,S_IRUGO,fw_show_ne_tlabels_free,NULL); + + +static ssize_t fw_show_ne_tlabels_mask(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct node_entry *ne = container_of(dev, struct node_entry, device); + unsigned long flags; + unsigned long *tp = ne->host->tl_pool[NODEID_TO_NODE(ne->nodeid)].map; + u64 tm; + + spin_lock_irqsave(&hpsb_tlabel_lock, flags); +#if (BITS_PER_LONG <= 32) + tm = ((u64)tp[0] << 32) + tp[1]; +#else + tm = tp[0]; +#endif + spin_unlock_irqrestore(&hpsb_tlabel_lock, flags); + + return sprintf(buf, "0x%016llx\n", (unsigned long long)tm); +} +static DEVICE_ATTR(tlabels_mask, S_IRUGO, fw_show_ne_tlabels_mask, NULL); +#endif /* HPSB_DEBUG_TLABELS */ + + +static ssize_t fw_set_ignore_driver(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct unit_directory *ud = container_of(dev, struct unit_directory, device); + int state = simple_strtoul(buf, NULL, 10); + + if (state == 1) { + ud->ignore_driver = 1; + device_release_driver(dev); + } else if (state == 0) + ud->ignore_driver = 0; + + return count; +} +static ssize_t fw_get_ignore_driver(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct unit_directory *ud = container_of(dev, struct unit_directory, device); + + return sprintf(buf, "%d\n", ud->ignore_driver); +} +static DEVICE_ATTR(ignore_driver, S_IWUSR | S_IRUGO, fw_get_ignore_driver, fw_set_ignore_driver); + + +static ssize_t fw_set_rescan(struct bus_type *bus, const char *buf, + size_t count) +{ + int error = 0; + + if (simple_strtoul(buf, NULL, 10) == 1) + error = bus_rescan_devices(&ieee1394_bus_type); + return error ? error : count; +} +static ssize_t fw_get_rescan(struct bus_type *bus, char *buf) +{ + return sprintf(buf, "You can force a rescan of the bus for " + "drivers by writing a 1 to this file\n"); +} +static BUS_ATTR(rescan, S_IWUSR | S_IRUGO, fw_get_rescan, fw_set_rescan); + + +static ssize_t fw_set_ignore_drivers(struct bus_type *bus, const char *buf, size_t count) +{ + int state = simple_strtoul(buf, NULL, 10); + + if (state == 1) + ignore_drivers = 1; + else if (state == 0) + ignore_drivers = 0; + + return count; +} +static ssize_t fw_get_ignore_drivers(struct bus_type *bus, char *buf) +{ + return sprintf(buf, "%d\n", ignore_drivers); +} +static BUS_ATTR(ignore_drivers, S_IWUSR | S_IRUGO, fw_get_ignore_drivers, fw_set_ignore_drivers); + + +struct bus_attribute *const fw_bus_attrs[] = { + &bus_attr_rescan, + &bus_attr_ignore_drivers, + NULL +}; + + +fw_attr(ne, struct node_entry, capabilities, unsigned int, "0x%06x\n") +fw_attr(ne, struct node_entry, nodeid, unsigned int, "0x%04x\n") + +fw_attr(ne, struct node_entry, vendor_id, unsigned int, "0x%06x\n") +fw_attr_td(ne, struct node_entry, vendor_name_kv) + +fw_attr(ne, struct node_entry, guid, unsigned long long, "0x%016Lx\n") +fw_attr(ne, struct node_entry, guid_vendor_id, unsigned int, "0x%06x\n") +fw_attr(ne, struct node_entry, in_limbo, int, "%d\n"); + +static struct device_attribute *const fw_ne_attrs[] = { + &dev_attr_ne_guid, + &dev_attr_ne_guid_vendor_id, + &dev_attr_ne_capabilities, + &dev_attr_ne_vendor_id, + &dev_attr_ne_nodeid, + &dev_attr_bus_options, +#ifdef HPSB_DEBUG_TLABELS + &dev_attr_tlabels_free, + &dev_attr_tlabels_mask, +#endif +}; + + + +fw_attr(ud, struct unit_directory, address, unsigned long long, "0x%016Lx\n") +fw_attr(ud, struct unit_directory, length, int, "%d\n") +/* These are all dependent on the value being provided */ +fw_attr(ud, struct unit_directory, vendor_id, unsigned int, "0x%06x\n") +fw_attr(ud, struct unit_directory, model_id, unsigned int, "0x%06x\n") +fw_attr(ud, struct unit_directory, specifier_id, unsigned int, "0x%06x\n") +fw_attr(ud, struct unit_directory, version, unsigned int, "0x%06x\n") +fw_attr_td(ud, struct unit_directory, vendor_name_kv) +fw_attr_td(ud, struct unit_directory, model_name_kv) + +static struct device_attribute *const fw_ud_attrs[] = { + &dev_attr_ud_address, + &dev_attr_ud_length, + &dev_attr_ignore_driver, +}; + + +fw_attr(host, struct hpsb_host, node_count, int, "%d\n") +fw_attr(host, struct hpsb_host, selfid_count, int, "%d\n") +fw_attr(host, struct hpsb_host, nodes_active, int, "%d\n") +fw_attr(host, struct hpsb_host, in_bus_reset, int, "%d\n") +fw_attr(host, struct hpsb_host, is_root, int, "%d\n") +fw_attr(host, struct hpsb_host, is_cycmst, int, "%d\n") +fw_attr(host, struct hpsb_host, is_irm, int, "%d\n") +fw_attr(host, struct hpsb_host, is_busmgr, int, "%d\n") + +static struct device_attribute *const fw_host_attrs[] = { + &dev_attr_host_node_count, + &dev_attr_host_selfid_count, + &dev_attr_host_nodes_active, + &dev_attr_host_in_bus_reset, + &dev_attr_host_is_root, + &dev_attr_host_is_cycmst, + &dev_attr_host_is_irm, + &dev_attr_host_is_busmgr, +}; + + +static ssize_t fw_show_drv_device_ids(struct device_driver *drv, char *buf) +{ + struct hpsb_protocol_driver *driver; + const struct ieee1394_device_id *id; + int length = 0; + char *scratch = buf; + + driver = container_of(drv, struct hpsb_protocol_driver, driver); + id = driver->id_table; + if (!id) + return 0; + + for (; id->match_flags != 0; id++) { + int need_coma = 0; + + if (id->match_flags & IEEE1394_MATCH_VENDOR_ID) { + length += sprintf(scratch, "vendor_id=0x%06x", id->vendor_id); + scratch = buf + length; + need_coma++; + } + + if (id->match_flags & IEEE1394_MATCH_MODEL_ID) { + length += sprintf(scratch, "%smodel_id=0x%06x", + need_coma++ ? "," : "", + id->model_id); + scratch = buf + length; + } + + if (id->match_flags & IEEE1394_MATCH_SPECIFIER_ID) { + length += sprintf(scratch, "%sspecifier_id=0x%06x", + need_coma++ ? "," : "", + id->specifier_id); + scratch = buf + length; + } + + if (id->match_flags & IEEE1394_MATCH_VERSION) { + length += sprintf(scratch, "%sversion=0x%06x", + need_coma++ ? "," : "", + id->version); + scratch = buf + length; + } + + if (need_coma) { + *scratch++ = '\n'; + length++; + } + } + + return length; +} +static DRIVER_ATTR(device_ids,S_IRUGO,fw_show_drv_device_ids,NULL); + + +fw_drv_attr(name, const char *, "%s\n") + +static struct driver_attribute *const fw_drv_attrs[] = { + &driver_attr_drv_name, + &driver_attr_device_ids, +}; + + +static void nodemgr_create_drv_files(struct hpsb_protocol_driver *driver) +{ + struct device_driver *drv = &driver->driver; + int i; + + for (i = 0; i < ARRAY_SIZE(fw_drv_attrs); i++) + if (driver_create_file(drv, fw_drv_attrs[i])) + goto fail; + return; +fail: + HPSB_ERR("Failed to add sysfs attribute"); +} + + +static void nodemgr_remove_drv_files(struct hpsb_protocol_driver *driver) +{ + struct device_driver *drv = &driver->driver; + int i; + + for (i = 0; i < ARRAY_SIZE(fw_drv_attrs); i++) + driver_remove_file(drv, fw_drv_attrs[i]); +} + + +static void nodemgr_create_ne_dev_files(struct node_entry *ne) +{ + struct device *dev = &ne->device; + int i; + + for (i = 0; i < ARRAY_SIZE(fw_ne_attrs); i++) + if (device_create_file(dev, fw_ne_attrs[i])) + goto fail; + return; +fail: + HPSB_ERR("Failed to add sysfs attribute"); +} + + +static void nodemgr_create_host_dev_files(struct hpsb_host *host) +{ + struct device *dev = &host->device; + int i; + + for (i = 0; i < ARRAY_SIZE(fw_host_attrs); i++) + if (device_create_file(dev, fw_host_attrs[i])) + goto fail; + return; +fail: + HPSB_ERR("Failed to add sysfs attribute"); +} + + +static struct node_entry *find_entry_by_nodeid(struct hpsb_host *host, + nodeid_t nodeid); + +static void nodemgr_update_host_dev_links(struct hpsb_host *host) +{ + struct device *dev = &host->device; + struct node_entry *ne; + + sysfs_remove_link(&dev->kobj, "irm_id"); + sysfs_remove_link(&dev->kobj, "busmgr_id"); + sysfs_remove_link(&dev->kobj, "host_id"); + + if ((ne = find_entry_by_nodeid(host, host->irm_id)) && + sysfs_create_link(&dev->kobj, &ne->device.kobj, "irm_id")) + goto fail; + if ((ne = find_entry_by_nodeid(host, host->busmgr_id)) && + sysfs_create_link(&dev->kobj, &ne->device.kobj, "busmgr_id")) + goto fail; + if ((ne = find_entry_by_nodeid(host, host->node_id)) && + sysfs_create_link(&dev->kobj, &ne->device.kobj, "host_id")) + goto fail; + return; +fail: + HPSB_ERR("Failed to update sysfs attributes for host %d", host->id); +} + +static void nodemgr_create_ud_dev_files(struct unit_directory *ud) +{ + struct device *dev = &ud->device; + int i; + + for (i = 0; i < ARRAY_SIZE(fw_ud_attrs); i++) + if (device_create_file(dev, fw_ud_attrs[i])) + goto fail; + if (ud->flags & UNIT_DIRECTORY_SPECIFIER_ID) + if (device_create_file(dev, &dev_attr_ud_specifier_id)) + goto fail; + if (ud->flags & UNIT_DIRECTORY_VERSION) + if (device_create_file(dev, &dev_attr_ud_version)) + goto fail; + if (ud->flags & UNIT_DIRECTORY_VENDOR_ID) { + if (device_create_file(dev, &dev_attr_ud_vendor_id)) + goto fail; + if (ud->vendor_name_kv && + device_create_file(dev, &dev_attr_ud_vendor_name_kv)) + goto fail; + } + if (ud->flags & UNIT_DIRECTORY_MODEL_ID) { + if (device_create_file(dev, &dev_attr_ud_model_id)) + goto fail; + if (ud->model_name_kv && + device_create_file(dev, &dev_attr_ud_model_name_kv)) + goto fail; + } + return; +fail: + HPSB_ERR("Failed to add sysfs attribute"); +} + + +static int nodemgr_bus_match(struct device * dev, struct device_driver * drv) +{ + struct hpsb_protocol_driver *driver; + struct unit_directory *ud; + const struct ieee1394_device_id *id; + + /* We only match unit directories */ + if (dev->platform_data != &nodemgr_ud_platform_data) + return 0; + + ud = container_of(dev, struct unit_directory, device); + if (ud->ne->in_limbo || ud->ignore_driver) + return 0; + + /* We only match drivers of type hpsb_protocol_driver */ + if (drv == &nodemgr_mid_layer_driver) + return 0; + + driver = container_of(drv, struct hpsb_protocol_driver, driver); + id = driver->id_table; + if (!id) + return 0; + + for (; id->match_flags != 0; id++) { + if ((id->match_flags & IEEE1394_MATCH_VENDOR_ID) && + id->vendor_id != ud->vendor_id) + continue; + + if ((id->match_flags & IEEE1394_MATCH_MODEL_ID) && + id->model_id != ud->model_id) + continue; + + if ((id->match_flags & IEEE1394_MATCH_SPECIFIER_ID) && + id->specifier_id != ud->specifier_id) + continue; + + if ((id->match_flags & IEEE1394_MATCH_VERSION) && + id->version != ud->version) + continue; + + return 1; + } + + return 0; +} + + +static DEFINE_MUTEX(nodemgr_serialize_remove_uds); + +static int match_ne(struct device *dev, void *data) +{ + struct unit_directory *ud; + struct node_entry *ne = data; + + ud = container_of(dev, struct unit_directory, unit_dev); + return ud->ne == ne; +} + +static void nodemgr_remove_uds(struct node_entry *ne) +{ + struct device *dev; + struct unit_directory *ud; + + /* Use class_find device to iterate the devices. Since this code + * may be called from other contexts besides the knodemgrds, + * protect it by nodemgr_serialize_remove_uds. + */ + mutex_lock(&nodemgr_serialize_remove_uds); + for (;;) { + dev = class_find_device(&nodemgr_ud_class, NULL, ne, match_ne); + if (!dev) + break; + ud = container_of(dev, struct unit_directory, unit_dev); + put_device(dev); + device_unregister(&ud->unit_dev); + device_unregister(&ud->device); + } + mutex_unlock(&nodemgr_serialize_remove_uds); +} + + +static void nodemgr_remove_ne(struct node_entry *ne) +{ + struct device *dev; + + dev = get_device(&ne->device); + if (!dev) + return; + + HPSB_DEBUG("Node removed: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]", + NODE_BUS_ARGS(ne->host, ne->nodeid), (unsigned long long)ne->guid); + nodemgr_remove_uds(ne); + + device_unregister(&ne->node_dev); + device_unregister(dev); + + put_device(dev); +} + +static int remove_host_dev(struct device *dev, void *data) +{ + if (dev->bus == &ieee1394_bus_type) + nodemgr_remove_ne(container_of(dev, struct node_entry, + device)); + return 0; +} + +static void nodemgr_remove_host_dev(struct device *dev) +{ + device_for_each_child(dev, NULL, remove_host_dev); + sysfs_remove_link(&dev->kobj, "irm_id"); + sysfs_remove_link(&dev->kobj, "busmgr_id"); + sysfs_remove_link(&dev->kobj, "host_id"); +} + + +static void nodemgr_update_bus_options(struct node_entry *ne) +{ +#ifdef CONFIG_IEEE1394_VERBOSEDEBUG + static const u16 mr[] = { 4, 64, 1024, 0}; +#endif + quadlet_t busoptions = be32_to_cpu(ne->csr->bus_info_data[2]); + + ne->busopt.irmc = (busoptions >> 31) & 1; + ne->busopt.cmc = (busoptions >> 30) & 1; + ne->busopt.isc = (busoptions >> 29) & 1; + ne->busopt.bmc = (busoptions >> 28) & 1; + ne->busopt.pmc = (busoptions >> 27) & 1; + ne->busopt.cyc_clk_acc = (busoptions >> 16) & 0xff; + ne->busopt.max_rec = 1 << (((busoptions >> 12) & 0xf) + 1); + ne->busopt.max_rom = (busoptions >> 8) & 0x3; + ne->busopt.generation = (busoptions >> 4) & 0xf; + ne->busopt.lnkspd = busoptions & 0x7; + + HPSB_VERBOSE("NodeMgr: raw=0x%08x irmc=%d cmc=%d isc=%d bmc=%d pmc=%d " + "cyc_clk_acc=%d max_rec=%d max_rom=%d gen=%d lspd=%d", + busoptions, ne->busopt.irmc, ne->busopt.cmc, + ne->busopt.isc, ne->busopt.bmc, ne->busopt.pmc, + ne->busopt.cyc_clk_acc, ne->busopt.max_rec, + mr[ne->busopt.max_rom], + ne->busopt.generation, ne->busopt.lnkspd); +} + + +static struct node_entry *nodemgr_create_node(octlet_t guid, + struct csr1212_csr *csr, struct hpsb_host *host, + nodeid_t nodeid, unsigned int generation) +{ + struct node_entry *ne; + + ne = kzalloc(sizeof(*ne), GFP_KERNEL); + if (!ne) + goto fail_alloc; + + ne->host = host; + ne->nodeid = nodeid; + ne->generation = generation; + ne->needs_probe = true; + + ne->guid = guid; + ne->guid_vendor_id = (guid >> 40) & 0xffffff; + ne->csr = csr; + + memcpy(&ne->device, &nodemgr_dev_template_ne, + sizeof(ne->device)); + ne->device.parent = &host->device; + dev_set_name(&ne->device, "%016Lx", (unsigned long long)(ne->guid)); + + ne->node_dev.parent = &ne->device; + ne->node_dev.class = &nodemgr_ne_class; + dev_set_name(&ne->node_dev, "%016Lx", (unsigned long long)(ne->guid)); + + if (device_register(&ne->device)) + goto fail_devreg; + if (device_register(&ne->node_dev)) + goto fail_classdevreg; + get_device(&ne->device); + + nodemgr_create_ne_dev_files(ne); + + nodemgr_update_bus_options(ne); + + HPSB_DEBUG("%s added: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]", + (host->node_id == nodeid) ? "Host" : "Node", + NODE_BUS_ARGS(host, nodeid), (unsigned long long)guid); + + return ne; + +fail_classdevreg: + device_unregister(&ne->device); +fail_devreg: + kfree(ne); +fail_alloc: + HPSB_ERR("Failed to create node ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]", + NODE_BUS_ARGS(host, nodeid), (unsigned long long)guid); + + return NULL; +} + +static int match_ne_guid(struct device *dev, void *data) +{ + struct node_entry *ne; + u64 *guid = data; + + ne = container_of(dev, struct node_entry, node_dev); + return ne->guid == *guid; +} + +static struct node_entry *find_entry_by_guid(u64 guid) +{ + struct device *dev; + struct node_entry *ne; + + dev = class_find_device(&nodemgr_ne_class, NULL, &guid, match_ne_guid); + if (!dev) + return NULL; + ne = container_of(dev, struct node_entry, node_dev); + put_device(dev); + + return ne; +} + +struct match_nodeid_parameter { + struct hpsb_host *host; + nodeid_t nodeid; +}; + +static int match_ne_nodeid(struct device *dev, void *data) +{ + int found = 0; + struct node_entry *ne; + struct match_nodeid_parameter *p = data; + + if (!dev) + goto ret; + ne = container_of(dev, struct node_entry, node_dev); + if (ne->host == p->host && ne->nodeid == p->nodeid) + found = 1; +ret: + return found; +} + +static struct node_entry *find_entry_by_nodeid(struct hpsb_host *host, + nodeid_t nodeid) +{ + struct device *dev; + struct node_entry *ne; + struct match_nodeid_parameter p; + + p.host = host; + p.nodeid = nodeid; + + dev = class_find_device(&nodemgr_ne_class, NULL, &p, match_ne_nodeid); + if (!dev) + return NULL; + ne = container_of(dev, struct node_entry, node_dev); + put_device(dev); + + return ne; +} + + +static void nodemgr_register_device(struct node_entry *ne, + struct unit_directory *ud, struct device *parent) +{ + memcpy(&ud->device, &nodemgr_dev_template_ud, + sizeof(ud->device)); + + ud->device.parent = parent; + + dev_set_name(&ud->device, "%s-%u", dev_name(&ne->device), ud->id); + + ud->unit_dev.parent = &ud->device; + ud->unit_dev.class = &nodemgr_ud_class; + dev_set_name(&ud->unit_dev, "%s-%u", dev_name(&ne->device), ud->id); + + if (device_register(&ud->device)) + goto fail_devreg; + if (device_register(&ud->unit_dev)) + goto fail_classdevreg; + get_device(&ud->device); + + nodemgr_create_ud_dev_files(ud); + + return; + +fail_classdevreg: + device_unregister(&ud->device); +fail_devreg: + HPSB_ERR("Failed to create unit %s", dev_name(&ud->device)); +} + + +/* This implementation currently only scans the config rom and its + * immediate unit directories looking for software_id and + * software_version entries, in order to get driver autoloading working. */ +static struct unit_directory *nodemgr_process_unit_directory + (struct node_entry *ne, struct csr1212_keyval *ud_kv, + unsigned int *id, struct unit_directory *parent) +{ + struct unit_directory *ud; + struct unit_directory *ud_child = NULL; + struct csr1212_dentry *dentry; + struct csr1212_keyval *kv; + u8 last_key_id = 0; + + ud = kzalloc(sizeof(*ud), GFP_KERNEL); + if (!ud) + goto unit_directory_error; + + ud->ne = ne; + ud->ignore_driver = ignore_drivers; + ud->address = ud_kv->offset + CSR1212_REGISTER_SPACE_BASE; + ud->directory_id = ud->address & 0xffffff; + ud->ud_kv = ud_kv; + ud->id = (*id)++; + + /* inherit vendor_id from root directory if none exists in unit dir */ + ud->vendor_id = ne->vendor_id; + + csr1212_for_each_dir_entry(ne->csr, kv, ud_kv, dentry) { + switch (kv->key.id) { + case CSR1212_KV_ID_VENDOR: + if (kv->key.type == CSR1212_KV_TYPE_IMMEDIATE) { + ud->vendor_id = kv->value.immediate; + ud->flags |= UNIT_DIRECTORY_VENDOR_ID; + } + break; + + case CSR1212_KV_ID_MODEL: + ud->model_id = kv->value.immediate; + ud->flags |= UNIT_DIRECTORY_MODEL_ID; + break; + + case CSR1212_KV_ID_SPECIFIER_ID: + ud->specifier_id = kv->value.immediate; + ud->flags |= UNIT_DIRECTORY_SPECIFIER_ID; + break; + + case CSR1212_KV_ID_VERSION: + ud->version = kv->value.immediate; + ud->flags |= UNIT_DIRECTORY_VERSION; + break; + + case CSR1212_KV_ID_DESCRIPTOR: + if (kv->key.type == CSR1212_KV_TYPE_LEAF && + CSR1212_DESCRIPTOR_LEAF_TYPE(kv) == 0 && + CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) == 0 && + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH(kv) == 0 && + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET(kv) == 0 && + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) == 0) { + switch (last_key_id) { + case CSR1212_KV_ID_VENDOR: + csr1212_keep_keyval(kv); + ud->vendor_name_kv = kv; + break; + + case CSR1212_KV_ID_MODEL: + csr1212_keep_keyval(kv); + ud->model_name_kv = kv; + break; + + } + } /* else if (kv->key.type == CSR1212_KV_TYPE_DIRECTORY) ... */ + break; + + case CSR1212_KV_ID_DEPENDENT_INFO: + /* Logical Unit Number */ + if (kv->key.type == CSR1212_KV_TYPE_IMMEDIATE) { + if (ud->flags & UNIT_DIRECTORY_HAS_LUN) { + ud_child = kmemdup(ud, sizeof(*ud_child), GFP_KERNEL); + if (!ud_child) + goto unit_directory_error; + nodemgr_register_device(ne, ud_child, &ne->device); + ud_child = NULL; + + ud->id = (*id)++; + } + ud->lun = kv->value.immediate; + ud->flags |= UNIT_DIRECTORY_HAS_LUN; + + /* Logical Unit Directory */ + } else if (kv->key.type == CSR1212_KV_TYPE_DIRECTORY) { + /* This should really be done in SBP2 as this is + * doing SBP2 specific parsing. + */ + + /* first register the parent unit */ + ud->flags |= UNIT_DIRECTORY_HAS_LUN_DIRECTORY; + if (ud->device.bus != &ieee1394_bus_type) + nodemgr_register_device(ne, ud, &ne->device); + + /* process the child unit */ + ud_child = nodemgr_process_unit_directory(ne, kv, id, ud); + + if (ud_child == NULL) + break; + + /* inherit unspecified values, the driver core picks it up */ + if ((ud->flags & UNIT_DIRECTORY_MODEL_ID) && + !(ud_child->flags & UNIT_DIRECTORY_MODEL_ID)) + { + ud_child->flags |= UNIT_DIRECTORY_MODEL_ID; + ud_child->model_id = ud->model_id; + } + if ((ud->flags & UNIT_DIRECTORY_SPECIFIER_ID) && + !(ud_child->flags & UNIT_DIRECTORY_SPECIFIER_ID)) + { + ud_child->flags |= UNIT_DIRECTORY_SPECIFIER_ID; + ud_child->specifier_id = ud->specifier_id; + } + if ((ud->flags & UNIT_DIRECTORY_VERSION) && + !(ud_child->flags & UNIT_DIRECTORY_VERSION)) + { + ud_child->flags |= UNIT_DIRECTORY_VERSION; + ud_child->version = ud->version; + } + + /* register the child unit */ + ud_child->flags |= UNIT_DIRECTORY_LUN_DIRECTORY; + nodemgr_register_device(ne, ud_child, &ud->device); + } + + break; + + case CSR1212_KV_ID_DIRECTORY_ID: + ud->directory_id = kv->value.immediate; + break; + + default: + break; + } + last_key_id = kv->key.id; + } + + /* do not process child units here and only if not already registered */ + if (!parent && ud->device.bus != &ieee1394_bus_type) + nodemgr_register_device(ne, ud, &ne->device); + + return ud; + +unit_directory_error: + kfree(ud); + return NULL; +} + + +static void nodemgr_process_root_directory(struct node_entry *ne) +{ + unsigned int ud_id = 0; + struct csr1212_dentry *dentry; + struct csr1212_keyval *kv, *vendor_name_kv = NULL; + u8 last_key_id = 0; + + ne->needs_probe = false; + + csr1212_for_each_dir_entry(ne->csr, kv, ne->csr->root_kv, dentry) { + switch (kv->key.id) { + case CSR1212_KV_ID_VENDOR: + ne->vendor_id = kv->value.immediate; + break; + + case CSR1212_KV_ID_NODE_CAPABILITIES: + ne->capabilities = kv->value.immediate; + break; + + case CSR1212_KV_ID_UNIT: + nodemgr_process_unit_directory(ne, kv, &ud_id, NULL); + break; + + case CSR1212_KV_ID_DESCRIPTOR: + if (last_key_id == CSR1212_KV_ID_VENDOR) { + if (kv->key.type == CSR1212_KV_TYPE_LEAF && + CSR1212_DESCRIPTOR_LEAF_TYPE(kv) == 0 && + CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) == 0 && + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH(kv) == 0 && + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET(kv) == 0 && + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) == 0) { + csr1212_keep_keyval(kv); + vendor_name_kv = kv; + } + } + break; + } + last_key_id = kv->key.id; + } + + if (ne->vendor_name_kv) { + kv = ne->vendor_name_kv; + ne->vendor_name_kv = vendor_name_kv; + csr1212_release_keyval(kv); + } else if (vendor_name_kv) { + ne->vendor_name_kv = vendor_name_kv; + if (device_create_file(&ne->device, + &dev_attr_ne_vendor_name_kv) != 0) + HPSB_ERR("Failed to add sysfs attribute"); + } +} + +#ifdef CONFIG_HOTPLUG + +static int nodemgr_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct unit_directory *ud; + int retval = 0; + /* ieee1394:venNmoNspNverN */ + char buf[8 + 1 + 3 + 8 + 2 + 8 + 2 + 8 + 3 + 8 + 1]; + + if (!dev) + return -ENODEV; + + ud = container_of(dev, struct unit_directory, unit_dev); + + if (ud->ne->in_limbo || ud->ignore_driver) + return -ENODEV; + +#define PUT_ENVP(fmt,val) \ +do { \ + retval = add_uevent_var(env, fmt, val); \ + if (retval) \ + return retval; \ +} while (0) + + PUT_ENVP("VENDOR_ID=%06x", ud->vendor_id); + PUT_ENVP("MODEL_ID=%06x", ud->model_id); + PUT_ENVP("GUID=%016Lx", (unsigned long long)ud->ne->guid); + PUT_ENVP("SPECIFIER_ID=%06x", ud->specifier_id); + PUT_ENVP("VERSION=%06x", ud->version); + snprintf(buf, sizeof(buf), "ieee1394:ven%08Xmo%08Xsp%08Xver%08X", + ud->vendor_id, + ud->model_id, + ud->specifier_id, + ud->version); + PUT_ENVP("MODALIAS=%s", buf); + +#undef PUT_ENVP + + return 0; +} + +#else + +static int nodemgr_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + return -ENODEV; +} + +#endif /* CONFIG_HOTPLUG */ + + +int __hpsb_register_protocol(struct hpsb_protocol_driver *drv, + struct module *owner) +{ + int error; + + drv->driver.bus = &ieee1394_bus_type; + drv->driver.owner = owner; + drv->driver.name = drv->name; + + /* This will cause a probe for devices */ + error = driver_register(&drv->driver); + if (!error) + nodemgr_create_drv_files(drv); + return error; +} + +void hpsb_unregister_protocol(struct hpsb_protocol_driver *driver) +{ + nodemgr_remove_drv_files(driver); + /* This will subsequently disconnect all devices that our driver + * is attached to. */ + driver_unregister(&driver->driver); +} + + +/* + * This function updates nodes that were present on the bus before the + * reset and still are after the reset. The nodeid and the config rom + * may have changed, and the drivers managing this device must be + * informed that this device just went through a bus reset, to allow + * the to take whatever actions required. + */ +static void nodemgr_update_node(struct node_entry *ne, struct csr1212_csr *csr, + nodeid_t nodeid, unsigned int generation) +{ + if (ne->nodeid != nodeid) { + HPSB_DEBUG("Node changed: " NODE_BUS_FMT " -> " NODE_BUS_FMT, + NODE_BUS_ARGS(ne->host, ne->nodeid), + NODE_BUS_ARGS(ne->host, nodeid)); + ne->nodeid = nodeid; + } + + if (ne->busopt.generation != ((be32_to_cpu(csr->bus_info_data[2]) >> 4) & 0xf)) { + kfree(ne->csr->private); + csr1212_destroy_csr(ne->csr); + ne->csr = csr; + + /* If the node's configrom generation has changed, we + * unregister all the unit directories. */ + nodemgr_remove_uds(ne); + + nodemgr_update_bus_options(ne); + + /* Mark the node as new, so it gets re-probed */ + ne->needs_probe = true; + } else { + /* old cache is valid, so update its generation */ + struct nodemgr_csr_info *ci = ne->csr->private; + ci->generation = generation; + /* free the partially filled now unneeded new cache */ + kfree(csr->private); + csr1212_destroy_csr(csr); + } + + /* Finally, mark the node current */ + smp_wmb(); + ne->generation = generation; + + if (ne->in_limbo) { + device_remove_file(&ne->device, &dev_attr_ne_in_limbo); + ne->in_limbo = false; + + HPSB_DEBUG("Node reactivated: " + "ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]", + NODE_BUS_ARGS(ne->host, ne->nodeid), + (unsigned long long)ne->guid); + } +} + +static void nodemgr_node_scan_one(struct hpsb_host *host, + nodeid_t nodeid, int generation) +{ + struct node_entry *ne; + octlet_t guid; + struct csr1212_csr *csr; + struct nodemgr_csr_info *ci; + u8 *speed; + + ci = kmalloc(sizeof(*ci), GFP_KERNEL); + kmemcheck_annotate_bitfield(ci, flags); + if (!ci) + return; + + ci->host = host; + ci->nodeid = nodeid; + ci->generation = generation; + + /* Prepare for speed probe which occurs when reading the ROM */ + speed = &(host->speed[NODEID_TO_NODE(nodeid)]); + if (*speed > host->csr.lnk_spd) + *speed = host->csr.lnk_spd; + ci->speed_unverified = *speed > IEEE1394_SPEED_100; + + /* We need to detect when the ConfigROM's generation has changed, + * so we only update the node's info when it needs to be. */ + + csr = csr1212_create_csr(&nodemgr_csr_ops, 5 * sizeof(quadlet_t), ci); + if (!csr || csr1212_parse_csr(csr) != CSR1212_SUCCESS) { + HPSB_ERR("Error parsing configrom for node " NODE_BUS_FMT, + NODE_BUS_ARGS(host, nodeid)); + if (csr) + csr1212_destroy_csr(csr); + kfree(ci); + return; + } + + if (csr->bus_info_data[1] != IEEE1394_BUSID_MAGIC) { + /* This isn't a 1394 device, but we let it slide. There + * was a report of a device with broken firmware which + * reported '2394' instead of '1394', which is obviously a + * mistake. One would hope that a non-1394 device never + * gets connected to Firewire bus. If someone does, we + * shouldn't be held responsible, so we'll allow it with a + * warning. */ + HPSB_WARN("Node " NODE_BUS_FMT " has invalid busID magic [0x%08x]", + NODE_BUS_ARGS(host, nodeid), csr->bus_info_data[1]); + } + + guid = ((u64)be32_to_cpu(csr->bus_info_data[3]) << 32) | be32_to_cpu(csr->bus_info_data[4]); + ne = find_entry_by_guid(guid); + + if (ne && ne->host != host && ne->in_limbo) { + /* Must have moved this device from one host to another */ + nodemgr_remove_ne(ne); + ne = NULL; + } + + if (!ne) + nodemgr_create_node(guid, csr, host, nodeid, generation); + else + nodemgr_update_node(ne, csr, nodeid, generation); +} + + +static void nodemgr_node_scan(struct hpsb_host *host, int generation) +{ + int count; + struct selfid *sid = (struct selfid *)host->topology_map; + nodeid_t nodeid = LOCAL_BUS; + + /* Scan each node on the bus */ + for (count = host->selfid_count; count; count--, sid++) { + if (sid->extended) + continue; + + if (!sid->link_active) { + nodeid++; + continue; + } + nodemgr_node_scan_one(host, nodeid++, generation); + } +} + +static void nodemgr_pause_ne(struct node_entry *ne) +{ + HPSB_DEBUG("Node paused: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]", + NODE_BUS_ARGS(ne->host, ne->nodeid), + (unsigned long long)ne->guid); + + ne->in_limbo = true; + WARN_ON(device_create_file(&ne->device, &dev_attr_ne_in_limbo)); +} + +static int update_pdrv(struct device *dev, void *data) +{ + struct unit_directory *ud; + struct device_driver *drv; + struct hpsb_protocol_driver *pdrv; + struct node_entry *ne = data; + int error; + + ud = container_of(dev, struct unit_directory, unit_dev); + if (ud->ne == ne) { + drv = get_driver(ud->device.driver); + if (drv) { + error = 0; + pdrv = container_of(drv, struct hpsb_protocol_driver, + driver); + if (pdrv->update) { + device_lock(&ud->device); + error = pdrv->update(ud); + device_unlock(&ud->device); + } + if (error) + device_release_driver(&ud->device); + put_driver(drv); + } + } + + return 0; +} + +static void nodemgr_update_pdrv(struct node_entry *ne) +{ + class_for_each_device(&nodemgr_ud_class, NULL, ne, update_pdrv); +} + +/* Write the BROADCAST_CHANNEL as per IEEE1394a 8.3.2.3.11 and 8.4.2.3. This + * seems like an optional service but in the end it is practically mandatory + * as a consequence of these clauses. + * + * Note that we cannot do a broadcast write to all nodes at once because some + * pre-1394a devices would hang. */ +static void nodemgr_irm_write_bc(struct node_entry *ne, int generation) +{ + const u64 bc_addr = (CSR_REGISTER_BASE | CSR_BROADCAST_CHANNEL); + quadlet_t bc_remote, bc_local; + int error; + + if (!ne->host->is_irm || ne->generation != generation || + ne->nodeid == ne->host->node_id) + return; + + bc_local = cpu_to_be32(ne->host->csr.broadcast_channel); + + /* Check if the register is implemented and 1394a compliant. */ + error = hpsb_read(ne->host, ne->nodeid, generation, bc_addr, &bc_remote, + sizeof(bc_remote)); + if (!error && bc_remote & cpu_to_be32(0x80000000) && + bc_remote != bc_local) + hpsb_node_write(ne, bc_addr, &bc_local, sizeof(bc_local)); +} + + +static void nodemgr_probe_ne(struct hpsb_host *host, struct node_entry *ne, + int generation) +{ + struct device *dev; + + if (ne->host != host || ne->in_limbo) + return; + + dev = get_device(&ne->device); + if (!dev) + return; + + nodemgr_irm_write_bc(ne, generation); + + /* If "needs_probe", then this is either a new or changed node we + * rescan totally. If the generation matches for an existing node + * (one that existed prior to the bus reset) we send update calls + * down to the drivers. Otherwise, this is a dead node and we + * suspend it. */ + if (ne->needs_probe) + nodemgr_process_root_directory(ne); + else if (ne->generation == generation) + nodemgr_update_pdrv(ne); + else + nodemgr_pause_ne(ne); + + put_device(dev); +} + +struct node_probe_parameter { + struct hpsb_host *host; + int generation; + bool probe_now; +}; + +static int node_probe(struct device *dev, void *data) +{ + struct node_probe_parameter *p = data; + struct node_entry *ne; + + if (p->generation != get_hpsb_generation(p->host)) + return -EAGAIN; + + ne = container_of(dev, struct node_entry, node_dev); + if (ne->needs_probe == p->probe_now) + nodemgr_probe_ne(p->host, ne, p->generation); + return 0; +} + +static int nodemgr_node_probe(struct hpsb_host *host, int generation) +{ + struct node_probe_parameter p; + + p.host = host; + p.generation = generation; + /* + * Do some processing of the nodes we've probed. This pulls them + * into the sysfs layer if needed, and can result in processing of + * unit-directories, or just updating the node and it's + * unit-directories. + * + * Run updates before probes. Usually, updates are time-critical + * while probes are time-consuming. + * + * Meanwhile, another bus reset may have happened. In this case we + * skip everything here and let the next bus scan handle it. + * Otherwise we may prematurely remove nodes which are still there. + */ + p.probe_now = false; + if (class_for_each_device(&nodemgr_ne_class, NULL, &p, node_probe) != 0) + return 0; + + p.probe_now = true; + if (class_for_each_device(&nodemgr_ne_class, NULL, &p, node_probe) != 0) + return 0; + /* + * Now let's tell the bus to rescan our devices. This may seem + * like overhead, but the driver-model core will only scan a + * device for a driver when either the device is added, or when a + * new driver is added. A bus reset is a good reason to rescan + * devices that were there before. For example, an sbp2 device + * may become available for login, if the host that held it was + * just removed. + */ + if (bus_rescan_devices(&ieee1394_bus_type) != 0) + HPSB_DEBUG("bus_rescan_devices had an error"); + + return 1; +} + +static int remove_nodes_in_limbo(struct device *dev, void *data) +{ + struct node_entry *ne; + + if (dev->bus != &ieee1394_bus_type) + return 0; + + ne = container_of(dev, struct node_entry, device); + if (ne->in_limbo) + nodemgr_remove_ne(ne); + + return 0; +} + +static void nodemgr_remove_nodes_in_limbo(struct hpsb_host *host) +{ + device_for_each_child(&host->device, NULL, remove_nodes_in_limbo); +} + +static int nodemgr_send_resume_packet(struct hpsb_host *host) +{ + struct hpsb_packet *packet; + int error = -ENOMEM; + + packet = hpsb_make_phypacket(host, + EXTPHYPACKET_TYPE_RESUME | + NODEID_TO_NODE(host->node_id) << PHYPACKET_PORT_SHIFT); + if (packet) { + packet->no_waiter = 1; + packet->generation = get_hpsb_generation(host); + error = hpsb_send_packet(packet); + } + if (error) + HPSB_WARN("fw-host%d: Failed to broadcast resume packet", + host->id); + return error; +} + +/* Perform a few high-level IRM responsibilities. */ +static int nodemgr_do_irm_duties(struct hpsb_host *host, int cycles) +{ + quadlet_t bc; + + /* if irm_id == -1 then there is no IRM on this bus */ + if (!host->is_irm || host->irm_id == (nodeid_t)-1) + return 1; + + /* We are a 1394a-2000 compliant IRM. Set the validity bit. */ + host->csr.broadcast_channel |= 0x40000000; + + /* If there is no bus manager then we should set the root node's + * force_root bit to promote bus stability per the 1394 + * spec. (8.4.2.6) */ + if (host->busmgr_id == 0xffff && host->node_count > 1) + { + u16 root_node = host->node_count - 1; + + /* get cycle master capability flag from root node */ + if (host->is_cycmst || + (!hpsb_read(host, LOCAL_BUS | root_node, get_hpsb_generation(host), + (CSR_REGISTER_BASE + CSR_CONFIG_ROM + 2 * sizeof(quadlet_t)), + &bc, sizeof(quadlet_t)) && + be32_to_cpu(bc) & 1 << CSR_CMC_SHIFT)) + hpsb_send_phy_config(host, root_node, -1); + else { + HPSB_DEBUG("The root node is not cycle master capable; " + "selecting a new root node and resetting..."); + + if (cycles >= 5) { + /* Oh screw it! Just leave the bus as it is */ + HPSB_DEBUG("Stopping reset loop for IRM sanity"); + return 1; + } + + hpsb_send_phy_config(host, NODEID_TO_NODE(host->node_id), -1); + hpsb_reset_bus(host, LONG_RESET_FORCE_ROOT); + + return 0; + } + } + + /* Some devices suspend their ports while being connected to an inactive + * host adapter, i.e. if connected before the low-level driver is + * loaded. They become visible either when physically unplugged and + * replugged, or when receiving a resume packet. Send one once. */ + if (!host->resume_packet_sent && !nodemgr_send_resume_packet(host)) + host->resume_packet_sent = 1; + + return 1; +} + +/* We need to ensure that if we are not the IRM, that the IRM node is capable of + * everything we can do, otherwise issue a bus reset and try to become the IRM + * ourselves. */ +static int nodemgr_check_irm_capability(struct hpsb_host *host, int cycles) +{ + quadlet_t bc; + int status; + + if (hpsb_disable_irm || host->is_irm) + return 1; + + status = hpsb_read(host, LOCAL_BUS | (host->irm_id), + get_hpsb_generation(host), + (CSR_REGISTER_BASE | CSR_BROADCAST_CHANNEL), + &bc, sizeof(quadlet_t)); + + if (status < 0 || !(be32_to_cpu(bc) & 0x80000000)) { + /* The current irm node does not have a valid BROADCAST_CHANNEL + * register and we do, so reset the bus with force_root set */ + HPSB_DEBUG("Current remote IRM is not 1394a-2000 compliant, resetting..."); + + if (cycles >= 5) { + /* Oh screw it! Just leave the bus as it is */ + HPSB_DEBUG("Stopping reset loop for IRM sanity"); + return 1; + } + + hpsb_send_phy_config(host, NODEID_TO_NODE(host->node_id), -1); + hpsb_reset_bus(host, LONG_RESET_FORCE_ROOT); + + return 0; + } + + return 1; +} + +static int nodemgr_host_thread(void *data) +{ + struct hpsb_host *host = data; + unsigned int g, generation = 0; + int i, reset_cycles = 0; + + set_freezable(); + /* Setup our device-model entries */ + nodemgr_create_host_dev_files(host); + + for (;;) { + /* Sleep until next bus reset */ + set_current_state(TASK_INTERRUPTIBLE); + if (get_hpsb_generation(host) == generation && + !kthread_should_stop()) + schedule(); + __set_current_state(TASK_RUNNING); + + /* Thread may have been woken up to freeze or to exit */ + if (try_to_freeze()) + continue; + if (kthread_should_stop()) + goto exit; + + /* Pause for 1/4 second in 1/16 second intervals, + * to make sure things settle down. */ + g = get_hpsb_generation(host); + for (i = 0; i < 4 ; i++) { + msleep_interruptible(63); + try_to_freeze(); + if (kthread_should_stop()) + goto exit; + + /* Now get the generation in which the node ID's we collect + * are valid. During the bus scan we will use this generation + * for the read transactions, so that if another reset occurs + * during the scan the transactions will fail instead of + * returning bogus data. */ + generation = get_hpsb_generation(host); + + /* If we get a reset before we are done waiting, then + * start the waiting over again */ + if (generation != g) + g = generation, i = 0; + } + + if (!nodemgr_check_irm_capability(host, reset_cycles) || + !nodemgr_do_irm_duties(host, reset_cycles)) { + reset_cycles++; + continue; + } + reset_cycles = 0; + + /* Scan our nodes to get the bus options and create node + * entries. This does not do the sysfs stuff, since that + * would trigger uevents and such, which is a bad idea at + * this point. */ + nodemgr_node_scan(host, generation); + + /* This actually does the full probe, with sysfs + * registration. */ + if (!nodemgr_node_probe(host, generation)) + continue; + + /* Update some of our sysfs symlinks */ + nodemgr_update_host_dev_links(host); + + /* Sleep 3 seconds */ + for (i = 3000/200; i; i--) { + msleep_interruptible(200); + try_to_freeze(); + if (kthread_should_stop()) + goto exit; + + if (generation != get_hpsb_generation(host)) + break; + } + /* Remove nodes which are gone, unless a bus reset happened */ + if (!i) + nodemgr_remove_nodes_in_limbo(host); + } +exit: + HPSB_VERBOSE("NodeMgr: Exiting thread"); + return 0; +} + +struct per_host_parameter { + void *data; + int (*cb)(struct hpsb_host *, void *); +}; + +static int per_host(struct device *dev, void *data) +{ + struct hpsb_host *host; + struct per_host_parameter *p = data; + + host = container_of(dev, struct hpsb_host, host_dev); + return p->cb(host, p->data); +} + +/** + * nodemgr_for_each_host - call a function for each IEEE 1394 host + * @data: an address to supply to the callback + * @cb: function to call for each host + * + * Iterate the hosts, calling a given function with supplied data for each host. + * If the callback fails on a host, i.e. if it returns a non-zero value, the + * iteration is stopped. + * + * Return value: 0 on success, non-zero on failure (same as returned by last run + * of the callback). + */ +int nodemgr_for_each_host(void *data, int (*cb)(struct hpsb_host *, void *)) +{ + struct per_host_parameter p; + + p.cb = cb; + p.data = data; + return class_for_each_device(&hpsb_host_class, NULL, &p, per_host); +} + +/* The following two convenience functions use a struct node_entry + * for addressing a node on the bus. They are intended for use by any + * process context, not just the nodemgr thread, so we need to be a + * little careful when reading out the node ID and generation. The + * thing that can go wrong is that we get the node ID, then a bus + * reset occurs, and then we read the generation. The node ID is + * possibly invalid, but the generation is current, and we end up + * sending a packet to a the wrong node. + * + * The solution is to make sure we read the generation first, so that + * if a reset occurs in the process, we end up with a stale generation + * and the transactions will fail instead of silently using wrong node + * ID's. + */ + +/** + * hpsb_node_fill_packet - fill some destination information into a packet + * @ne: destination node + * @packet: packet to fill in + * + * This will fill in the given, pre-initialised hpsb_packet with the current + * information from the node entry (host, node ID, bus generation number). + */ +void hpsb_node_fill_packet(struct node_entry *ne, struct hpsb_packet *packet) +{ + packet->host = ne->host; + packet->generation = ne->generation; + smp_rmb(); + packet->node_id = ne->nodeid; +} + +int hpsb_node_write(struct node_entry *ne, u64 addr, + quadlet_t *buffer, size_t length) +{ + unsigned int generation = ne->generation; + + smp_rmb(); + return hpsb_write(ne->host, ne->nodeid, generation, + addr, buffer, length); +} + +static void nodemgr_add_host(struct hpsb_host *host) +{ + struct host_info *hi; + + hi = hpsb_create_hostinfo(&nodemgr_highlevel, host, sizeof(*hi)); + if (!hi) { + HPSB_ERR("NodeMgr: out of memory in add host"); + return; + } + hi->host = host; + hi->thread = kthread_run(nodemgr_host_thread, host, "knodemgrd_%d", + host->id); + if (IS_ERR(hi->thread)) { + HPSB_ERR("NodeMgr: cannot start thread for host %d", host->id); + hpsb_destroy_hostinfo(&nodemgr_highlevel, host); + } +} + +static void nodemgr_host_reset(struct hpsb_host *host) +{ + struct host_info *hi = hpsb_get_hostinfo(&nodemgr_highlevel, host); + + if (hi) { + HPSB_VERBOSE("NodeMgr: Processing reset for host %d", host->id); + wake_up_process(hi->thread); + } +} + +static void nodemgr_remove_host(struct hpsb_host *host) +{ + struct host_info *hi = hpsb_get_hostinfo(&nodemgr_highlevel, host); + + if (hi) { + kthread_stop(hi->thread); + nodemgr_remove_host_dev(&host->device); + } +} + +static struct hpsb_highlevel nodemgr_highlevel = { + .name = "Node manager", + .add_host = nodemgr_add_host, + .host_reset = nodemgr_host_reset, + .remove_host = nodemgr_remove_host, +}; + +int init_ieee1394_nodemgr(void) +{ + int error; + + error = class_register(&nodemgr_ne_class); + if (error) + goto fail_ne; + error = class_register(&nodemgr_ud_class); + if (error) + goto fail_ud; + error = driver_register(&nodemgr_mid_layer_driver); + if (error) + goto fail_ml; + /* This driver is not used if nodemgr is off (disable_nodemgr=1). */ + nodemgr_dev_template_host.driver = &nodemgr_mid_layer_driver; + + hpsb_register_highlevel(&nodemgr_highlevel); + return 0; + +fail_ml: + class_unregister(&nodemgr_ud_class); +fail_ud: + class_unregister(&nodemgr_ne_class); +fail_ne: + return error; +} + +void cleanup_ieee1394_nodemgr(void) +{ + hpsb_unregister_highlevel(&nodemgr_highlevel); + driver_unregister(&nodemgr_mid_layer_driver); + class_unregister(&nodemgr_ud_class); + class_unregister(&nodemgr_ne_class); +} diff --git a/trunk/drivers/ieee1394/nodemgr.h b/trunk/drivers/ieee1394/nodemgr.h new file mode 100644 index 000000000000..749b271d3107 --- /dev/null +++ b/trunk/drivers/ieee1394/nodemgr.h @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2000 Andreas E. Bombe + * 2001 Ben Collins + * + * 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. + */ + +#ifndef _IEEE1394_NODEMGR_H +#define _IEEE1394_NODEMGR_H + +#include +#include +#include + +#include "ieee1394_core.h" +#include "ieee1394_transactions.h" +#include "ieee1394_types.h" + +struct csr1212_csr; +struct csr1212_keyval; +struct hpsb_host; +struct ieee1394_device_id; + +/* This is the start of a Node entry structure. It should be a stable API + * for which to gather info from the Node Manager about devices attached + * to the bus. */ +struct bus_options { + u8 irmc; /* Iso Resource Manager Capable */ + u8 cmc; /* Cycle Master Capable */ + u8 isc; /* Iso Capable */ + u8 bmc; /* Bus Master Capable */ + u8 pmc; /* Power Manager Capable (PNP spec) */ + u8 cyc_clk_acc; /* Cycle clock accuracy */ + u8 max_rom; /* Maximum block read supported in the CSR */ + u8 generation; /* Incremented when configrom changes */ + u8 lnkspd; /* Link speed */ + u16 max_rec; /* Maximum packet size node can receive */ +}; + +#define UNIT_DIRECTORY_VENDOR_ID 0x01 +#define UNIT_DIRECTORY_MODEL_ID 0x02 +#define UNIT_DIRECTORY_SPECIFIER_ID 0x04 +#define UNIT_DIRECTORY_VERSION 0x08 +#define UNIT_DIRECTORY_HAS_LUN_DIRECTORY 0x10 +#define UNIT_DIRECTORY_LUN_DIRECTORY 0x20 +#define UNIT_DIRECTORY_HAS_LUN 0x40 + +/* + * A unit directory corresponds to a protocol supported by the + * node. If a node supports eg. IP/1394 and AV/C, its config rom has a + * unit directory for each of these protocols. + */ +struct unit_directory { + struct node_entry *ne; /* The node which this directory belongs to */ + octlet_t address; /* Address of the unit directory on the node */ + u8 flags; /* Indicates which entries were read */ + + quadlet_t vendor_id; + struct csr1212_keyval *vendor_name_kv; + + quadlet_t model_id; + struct csr1212_keyval *model_name_kv; + quadlet_t specifier_id; + quadlet_t version; + quadlet_t directory_id; + + unsigned int id; + + int ignore_driver; + + int length; /* Number of quadlets */ + + struct device device; + struct device unit_dev; + + struct csr1212_keyval *ud_kv; + u32 lun; /* logical unit number immediate value */ +}; + +struct node_entry { + u64 guid; /* GUID of this node */ + u32 guid_vendor_id; /* Top 24bits of guid */ + + struct hpsb_host *host; /* Host this node is attached to */ + nodeid_t nodeid; /* NodeID */ + struct bus_options busopt; /* Bus Options */ + bool needs_probe; + unsigned int generation; /* Synced with hpsb generation */ + + /* The following is read from the config rom */ + u32 vendor_id; + struct csr1212_keyval *vendor_name_kv; + + u32 capabilities; + + struct device device; + struct device node_dev; + + /* Means this node is not attached anymore */ + bool in_limbo; + + struct csr1212_csr *csr; +}; + +struct hpsb_protocol_driver { + /* The name of the driver, e.g. SBP2 or IP1394 */ + const char *name; + + /* + * The device id table describing the protocols and/or devices + * supported by this driver. This is used by the nodemgr to + * decide if a driver could support a given node, but the + * probe function below can implement further protocol + * dependent or vendor dependent checking. + */ + const struct ieee1394_device_id *id_table; + + /* + * The update function is called when the node has just + * survived a bus reset, i.e. it is still present on the bus. + * However, it may be necessary to reestablish the connection + * or login into the node again, depending on the protocol. If the + * probe fails (returns non-zero), we unbind the driver from this + * device. + */ + int (*update)(struct unit_directory *ud); + + /* Our LDM structure */ + struct device_driver driver; +}; + +int __hpsb_register_protocol(struct hpsb_protocol_driver *, struct module *); +static inline int hpsb_register_protocol(struct hpsb_protocol_driver *driver) +{ + return __hpsb_register_protocol(driver, THIS_MODULE); +} + +void hpsb_unregister_protocol(struct hpsb_protocol_driver *driver); + +static inline int hpsb_node_entry_valid(struct node_entry *ne) +{ + return ne->generation == get_hpsb_generation(ne->host); +} +void hpsb_node_fill_packet(struct node_entry *ne, struct hpsb_packet *packet); +int hpsb_node_write(struct node_entry *ne, u64 addr, + quadlet_t *buffer, size_t length); +static inline int hpsb_node_read(struct node_entry *ne, u64 addr, + quadlet_t *buffer, size_t length) +{ + unsigned int g = ne->generation; + + smp_rmb(); + return hpsb_read(ne->host, ne->nodeid, g, addr, buffer, length); +} +static inline int hpsb_node_lock(struct node_entry *ne, u64 addr, int extcode, + quadlet_t *buffer, quadlet_t arg) +{ + unsigned int g = ne->generation; + + smp_rmb(); + return hpsb_lock(ne->host, ne->nodeid, g, addr, extcode, buffer, arg); +} +int nodemgr_for_each_host(void *data, int (*cb)(struct hpsb_host *, void *)); + +int init_ieee1394_nodemgr(void); +void cleanup_ieee1394_nodemgr(void); + +/* The template for a host device */ +extern struct device nodemgr_dev_template_host; + +/* Bus attributes we export */ +extern struct bus_attribute *const fw_bus_attrs[]; + +#endif /* _IEEE1394_NODEMGR_H */ diff --git a/trunk/drivers/ieee1394/ohci1394.c b/trunk/drivers/ieee1394/ohci1394.c new file mode 100644 index 000000000000..50815022cff1 --- /dev/null +++ b/trunk/drivers/ieee1394/ohci1394.c @@ -0,0 +1,3590 @@ +/* + * ohci1394.c - driver for OHCI 1394 boards + * Copyright (C)1999,2000 Sebastien Rougeaux + * Gord Peters + * 2001 Ben Collins + * + * 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. + */ + +/* + * Things known to be working: + * . Async Request Transmit + * . Async Response Receive + * . Async Request Receive + * . Async Response Transmit + * . Iso Receive + * . DMA mmap for iso receive + * . Config ROM generation + * + * Things implemented, but still in test phase: + * . Iso Transmit + * . Async Stream Packets Transmit (Receive done via Iso interface) + * + * Things not implemented: + * . DMA error recovery + * + * Known bugs: + * . devctl BUS_RESET arg confusion (reset type or root holdoff?) + * added LONG_RESET_ROOT and SHORT_RESET_ROOT for root holdoff --kk + */ + +/* + * Acknowledgments: + * + * Adam J Richter + * . Use of pci_class to find device + * + * Emilie Chung + * . Tip on Async Request Filter + * + * Pascal Drolet + * . Various tips for optimization and functionnalities + * + * Robert Ficklin + * . Loop in irq_handler + * + * James Goodwin + * . Various tips on initialization, self-id reception, etc. + * + * Albrecht Dress + * . Apple PowerBook detection + * + * Daniel Kobras + * . Reset the board properly before leaving + misc cleanups + * + * Leon van Stuivenberg + * . Bug fixes + * + * Ben Collins + * . Working big-endian support + * . Updated to 2.4.x module scheme (PCI aswell) + * . Config ROM generation + * + * Manfred Weihs + * . Reworked code for initiating bus resets + * (long, short, with or without hold-off) + * + * Nandu Santhi + * . Added support for nVidia nForce2 onboard Firewire chipset + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_PPC_PMAC +#include +#include +#include +#include +#endif + +#include "csr1212.h" +#include "ieee1394.h" +#include "ieee1394_types.h" +#include "hosts.h" +#include "dma.h" +#include "iso.h" +#include "ieee1394_core.h" +#include "highlevel.h" +#include "ohci1394.h" + +#ifdef CONFIG_IEEE1394_VERBOSEDEBUG +#define OHCI1394_DEBUG +#endif + +#ifdef DBGMSG +#undef DBGMSG +#endif + +#ifdef OHCI1394_DEBUG +#define DBGMSG(fmt, args...) \ +printk(KERN_INFO "%s: fw-host%d: " fmt "\n" , OHCI1394_DRIVER_NAME, ohci->host->id , ## args) +#else +#define DBGMSG(fmt, args...) do {} while (0) +#endif + +/* print general (card independent) information */ +#define PRINT_G(level, fmt, args...) \ +printk(level "%s: " fmt "\n" , OHCI1394_DRIVER_NAME , ## args) + +/* print card specific information */ +#define PRINT(level, fmt, args...) \ +printk(level "%s: fw-host%d: " fmt "\n" , OHCI1394_DRIVER_NAME, ohci->host->id , ## args) + +/* Module Parameters */ +static int phys_dma = 1; +module_param(phys_dma, int, 0444); +MODULE_PARM_DESC(phys_dma, "Enable physical DMA (default = 1)."); + +static void dma_trm_tasklet(unsigned long data); +static void dma_trm_reset(struct dma_trm_ctx *d); + +static int alloc_dma_rcv_ctx(struct ti_ohci *ohci, struct dma_rcv_ctx *d, + enum context_type type, int ctx, int num_desc, + int buf_size, int split_buf_size, int context_base); +static void free_dma_rcv_ctx(struct dma_rcv_ctx *d); + +static int alloc_dma_trm_ctx(struct ti_ohci *ohci, struct dma_trm_ctx *d, + enum context_type type, int ctx, int num_desc, + int context_base); + +static void ohci1394_pci_remove(struct pci_dev *pdev); + +#ifndef __LITTLE_ENDIAN +static const size_t hdr_sizes[] = { + 3, /* TCODE_WRITEQ */ + 4, /* TCODE_WRITEB */ + 3, /* TCODE_WRITE_RESPONSE */ + 0, /* reserved */ + 3, /* TCODE_READQ */ + 4, /* TCODE_READB */ + 3, /* TCODE_READQ_RESPONSE */ + 4, /* TCODE_READB_RESPONSE */ + 1, /* TCODE_CYCLE_START */ + 4, /* TCODE_LOCK_REQUEST */ + 2, /* TCODE_ISO_DATA */ + 4, /* TCODE_LOCK_RESPONSE */ + /* rest is reserved or link-internal */ +}; + +static inline void header_le32_to_cpu(quadlet_t *data, unsigned char tcode) +{ + size_t size; + + if (unlikely(tcode >= ARRAY_SIZE(hdr_sizes))) + return; + + size = hdr_sizes[tcode]; + while (size--) + data[size] = le32_to_cpu(data[size]); +} +#else +#define header_le32_to_cpu(w,x) do {} while (0) +#endif /* !LITTLE_ENDIAN */ + +/*********************************** + * IEEE-1394 functionality section * + ***********************************/ + +static u8 get_phy_reg(struct ti_ohci *ohci, u8 addr) +{ + int i; + unsigned long flags; + quadlet_t r; + + spin_lock_irqsave (&ohci->phy_reg_lock, flags); + + reg_write(ohci, OHCI1394_PhyControl, (addr << 8) | 0x00008000); + + for (i = 0; i < OHCI_LOOP_COUNT; i++) { + if (reg_read(ohci, OHCI1394_PhyControl) & 0x80000000) + break; + + mdelay(1); + } + + r = reg_read(ohci, OHCI1394_PhyControl); + + if (i >= OHCI_LOOP_COUNT) + PRINT (KERN_ERR, "Get PHY Reg timeout [0x%08x/0x%08x/%d]", + r, r & 0x80000000, i); + + spin_unlock_irqrestore (&ohci->phy_reg_lock, flags); + + return (r & 0x00ff0000) >> 16; +} + +static void set_phy_reg(struct ti_ohci *ohci, u8 addr, u8 data) +{ + int i; + unsigned long flags; + u32 r = 0; + + spin_lock_irqsave (&ohci->phy_reg_lock, flags); + + reg_write(ohci, OHCI1394_PhyControl, (addr << 8) | data | 0x00004000); + + for (i = 0; i < OHCI_LOOP_COUNT; i++) { + r = reg_read(ohci, OHCI1394_PhyControl); + if (!(r & 0x00004000)) + break; + + mdelay(1); + } + + if (i == OHCI_LOOP_COUNT) + PRINT (KERN_ERR, "Set PHY Reg timeout [0x%08x/0x%08x/%d]", + r, r & 0x00004000, i); + + spin_unlock_irqrestore (&ohci->phy_reg_lock, flags); + + return; +} + +/* Or's our value into the current value */ +static void set_phy_reg_mask(struct ti_ohci *ohci, u8 addr, u8 data) +{ + u8 old; + + old = get_phy_reg (ohci, addr); + old |= data; + set_phy_reg (ohci, addr, old); + + return; +} + +static void handle_selfid(struct ti_ohci *ohci, struct hpsb_host *host, + int phyid, int isroot) +{ + quadlet_t *q = ohci->selfid_buf_cpu; + quadlet_t self_id_count=reg_read(ohci, OHCI1394_SelfIDCount); + size_t size; + quadlet_t q0, q1; + + /* Check status of self-id reception */ + + if (ohci->selfid_swap) + q0 = le32_to_cpu(q[0]); + else + q0 = q[0]; + + if ((self_id_count & 0x80000000) || + ((self_id_count & 0x00FF0000) != (q0 & 0x00FF0000))) { + PRINT(KERN_ERR, + "Error in reception of SelfID packets [0x%08x/0x%08x] (count: %d)", + self_id_count, q0, ohci->self_id_errors); + + /* Tip by James Goodwin : + * We had an error, generate another bus reset in response. */ + if (ohci->self_id_errorsself_id_errors++; + } else { + PRINT(KERN_ERR, + "Too many errors on SelfID error reception, giving up!"); + } + return; + } + + /* SelfID Ok, reset error counter. */ + ohci->self_id_errors = 0; + + size = ((self_id_count & 0x00001FFC) >> 2) - 1; + q++; + + while (size > 0) { + if (ohci->selfid_swap) { + q0 = le32_to_cpu(q[0]); + q1 = le32_to_cpu(q[1]); + } else { + q0 = q[0]; + q1 = q[1]; + } + + if (q0 == ~q1) { + DBGMSG ("SelfID packet 0x%x received", q0); + hpsb_selfid_received(host, cpu_to_be32(q0)); + if (((q0 & 0x3f000000) >> 24) == phyid) + DBGMSG ("SelfID for this node is 0x%08x", q0); + } else { + PRINT(KERN_ERR, + "SelfID is inconsistent [0x%08x/0x%08x]", q0, q1); + } + q += 2; + size -= 2; + } + + DBGMSG("SelfID complete"); + + return; +} + +static void ohci_soft_reset(struct ti_ohci *ohci) { + int i; + + reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_softReset); + + for (i = 0; i < OHCI_LOOP_COUNT; i++) { + if (!(reg_read(ohci, OHCI1394_HCControlSet) & OHCI1394_HCControl_softReset)) + break; + mdelay(1); + } + DBGMSG ("Soft reset finished"); +} + + +/* Generate the dma receive prgs and start the context */ +static void initialize_dma_rcv_ctx(struct dma_rcv_ctx *d, int generate_irq) +{ + struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci); + int i; + + ohci1394_stop_context(ohci, d->ctrlClear, NULL); + + for (i=0; inum_desc; i++) { + u32 c; + + c = DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | DMA_CTL_BRANCH; + if (generate_irq) + c |= DMA_CTL_IRQ; + + d->prg_cpu[i]->control = cpu_to_le32(c | d->buf_size); + + /* End of descriptor list? */ + if (i + 1 < d->num_desc) { + d->prg_cpu[i]->branchAddress = + cpu_to_le32((d->prg_bus[i+1] & 0xfffffff0) | 0x1); + } else { + d->prg_cpu[i]->branchAddress = + cpu_to_le32((d->prg_bus[0] & 0xfffffff0)); + } + + d->prg_cpu[i]->address = cpu_to_le32(d->buf_bus[i]); + d->prg_cpu[i]->status = cpu_to_le32(d->buf_size); + } + + d->buf_ind = 0; + d->buf_offset = 0; + + if (d->type == DMA_CTX_ISO) { + /* Clear contextControl */ + reg_write(ohci, d->ctrlClear, 0xffffffff); + + /* Set bufferFill, isochHeader, multichannel for IR context */ + reg_write(ohci, d->ctrlSet, 0xd0000000); + + /* Set the context match register to match on all tags */ + reg_write(ohci, d->ctxtMatch, 0xf0000000); + + /* Clear the multi channel mask high and low registers */ + reg_write(ohci, OHCI1394_IRMultiChanMaskHiClear, 0xffffffff); + reg_write(ohci, OHCI1394_IRMultiChanMaskLoClear, 0xffffffff); + + /* Set up isoRecvIntMask to generate interrupts */ + reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 1 << d->ctx); + } + + /* Tell the controller where the first AR program is */ + reg_write(ohci, d->cmdPtr, d->prg_bus[0] | 0x1); + + /* Run context */ + reg_write(ohci, d->ctrlSet, 0x00008000); + + DBGMSG("Receive DMA ctx=%d initialized", d->ctx); +} + +/* Initialize the dma transmit context */ +static void initialize_dma_trm_ctx(struct dma_trm_ctx *d) +{ + struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci); + + /* Stop the context */ + ohci1394_stop_context(ohci, d->ctrlClear, NULL); + + d->prg_ind = 0; + d->sent_ind = 0; + d->free_prgs = d->num_desc; + d->branchAddrPtr = NULL; + INIT_LIST_HEAD(&d->fifo_list); + INIT_LIST_HEAD(&d->pending_list); + + if (d->type == DMA_CTX_ISO) { + /* enable interrupts */ + reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, 1 << d->ctx); + } + + DBGMSG("Transmit DMA ctx=%d initialized", d->ctx); +} + +/* Count the number of available iso contexts */ +static int get_nb_iso_ctx(struct ti_ohci *ohci, int reg) +{ + u32 tmp; + + reg_write(ohci, reg, 0xffffffff); + tmp = reg_read(ohci, reg); + + DBGMSG("Iso contexts reg: %08x implemented: %08x", reg, tmp); + + /* Count the number of contexts */ + return hweight32(tmp); +} + +/* Global initialization */ +static void ohci_initialize(struct ti_ohci *ohci) +{ + quadlet_t buf; + int num_ports, i; + + spin_lock_init(&ohci->phy_reg_lock); + + /* Put some defaults to these undefined bus options */ + buf = reg_read(ohci, OHCI1394_BusOptions); + buf |= 0x60000000; /* Enable CMC and ISC */ + if (hpsb_disable_irm) + buf &= ~0x80000000; + else + buf |= 0x80000000; /* Enable IRMC */ + buf &= ~0x00ff0000; /* XXX: Set cyc_clk_acc to zero for now */ + buf &= ~0x18000000; /* Disable PMC and BMC */ + reg_write(ohci, OHCI1394_BusOptions, buf); + + /* Set the bus number */ + reg_write(ohci, OHCI1394_NodeID, 0x0000ffc0); + + /* Enable posted writes */ + reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_postedWriteEnable); + + /* Clear link control register */ + reg_write(ohci, OHCI1394_LinkControlClear, 0xffffffff); + + /* Enable cycle timer and cycle master and set the IRM + * contender bit in our self ID packets if appropriate. */ + reg_write(ohci, OHCI1394_LinkControlSet, + OHCI1394_LinkControl_CycleTimerEnable | + OHCI1394_LinkControl_CycleMaster); + i = get_phy_reg(ohci, 4) | PHY_04_LCTRL; + if (hpsb_disable_irm) + i &= ~PHY_04_CONTENDER; + else + i |= PHY_04_CONTENDER; + set_phy_reg(ohci, 4, i); + + /* Set up self-id dma buffer */ + reg_write(ohci, OHCI1394_SelfIDBuffer, ohci->selfid_buf_bus); + + /* enable self-id */ + reg_write(ohci, OHCI1394_LinkControlSet, OHCI1394_LinkControl_RcvSelfID); + + /* Set the Config ROM mapping register */ + reg_write(ohci, OHCI1394_ConfigROMmap, ohci->csr_config_rom_bus); + + /* Now get our max packet size */ + ohci->max_packet_size = + 1<<(((reg_read(ohci, OHCI1394_BusOptions)>>12)&0xf)+1); + + /* Clear the interrupt mask */ + reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 0xffffffff); + reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 0xffffffff); + + /* Clear the interrupt mask */ + reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 0xffffffff); + reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 0xffffffff); + + /* Initialize AR dma */ + initialize_dma_rcv_ctx(&ohci->ar_req_context, 0); + initialize_dma_rcv_ctx(&ohci->ar_resp_context, 0); + + /* Initialize AT dma */ + initialize_dma_trm_ctx(&ohci->at_req_context); + initialize_dma_trm_ctx(&ohci->at_resp_context); + + /* Accept AR requests from all nodes */ + reg_write(ohci, OHCI1394_AsReqFilterHiSet, 0x80000000); + + /* Set the address range of the physical response unit. + * Most controllers do not implement it as a writable register though. + * They will keep a hardwired offset of 0x00010000 and show 0x0 as + * register content. + * To actually enable physical responses is the job of our interrupt + * handler which programs the physical request filter. */ + reg_write(ohci, OHCI1394_PhyUpperBound, + OHCI1394_PHYS_UPPER_BOUND_PROGRAMMED >> 16); + + DBGMSG("physUpperBoundOffset=%08x", + reg_read(ohci, OHCI1394_PhyUpperBound)); + + /* Specify AT retries */ + reg_write(ohci, OHCI1394_ATRetries, + OHCI1394_MAX_AT_REQ_RETRIES | + (OHCI1394_MAX_AT_RESP_RETRIES<<4) | + (OHCI1394_MAX_PHYS_RESP_RETRIES<<8)); + + /* We don't want hardware swapping */ + reg_write(ohci, OHCI1394_HCControlClear, OHCI1394_HCControl_noByteSwap); + + /* Enable interrupts */ + reg_write(ohci, OHCI1394_IntMaskSet, + OHCI1394_unrecoverableError | + OHCI1394_masterIntEnable | + OHCI1394_busReset | + OHCI1394_selfIDComplete | + OHCI1394_RSPkt | + OHCI1394_RQPkt | + OHCI1394_respTxComplete | + OHCI1394_reqTxComplete | + OHCI1394_isochRx | + OHCI1394_isochTx | + OHCI1394_postedWriteErr | + OHCI1394_cycleTooLong | + OHCI1394_cycleInconsistent); + + /* Enable link */ + reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_linkEnable); + + buf = reg_read(ohci, OHCI1394_Version); + PRINT(KERN_INFO, "OHCI-1394 %d.%d (PCI): IRQ=[%d] " + "MMIO=[%llx-%llx] Max Packet=[%d] IR/IT contexts=[%d/%d]", + ((((buf) >> 16) & 0xf) + (((buf) >> 20) & 0xf) * 10), + ((((buf) >> 4) & 0xf) + ((buf) & 0xf) * 10), ohci->dev->irq, + (unsigned long long)pci_resource_start(ohci->dev, 0), + (unsigned long long)pci_resource_start(ohci->dev, 0) + OHCI1394_REGISTER_SIZE - 1, + ohci->max_packet_size, + ohci->nb_iso_rcv_ctx, ohci->nb_iso_xmit_ctx); + + /* Check all of our ports to make sure that if anything is + * connected, we enable that port. */ + num_ports = get_phy_reg(ohci, 2) & 0xf; + for (i = 0; i < num_ports; i++) { + unsigned int status; + + set_phy_reg(ohci, 7, i); + status = get_phy_reg(ohci, 8); + + if (status & 0x20) + set_phy_reg(ohci, 8, status & ~1); + } + + /* Serial EEPROM Sanity check. */ + if ((ohci->max_packet_size < 512) || + (ohci->max_packet_size > 4096)) { + /* Serial EEPROM contents are suspect, set a sane max packet + * size and print the raw contents for bug reports if verbose + * debug is enabled. */ +#ifdef CONFIG_IEEE1394_VERBOSEDEBUG + int i; +#endif + + PRINT(KERN_DEBUG, "Serial EEPROM has suspicious values, " + "attempting to set max_packet_size to 512 bytes"); + reg_write(ohci, OHCI1394_BusOptions, + (reg_read(ohci, OHCI1394_BusOptions) & 0xf007) | 0x8002); + ohci->max_packet_size = 512; +#ifdef CONFIG_IEEE1394_VERBOSEDEBUG + PRINT(KERN_DEBUG, " EEPROM Present: %d", + (reg_read(ohci, OHCI1394_Version) >> 24) & 0x1); + reg_write(ohci, OHCI1394_GUID_ROM, 0x80000000); + + for (i = 0; + ((i < 1000) && + (reg_read(ohci, OHCI1394_GUID_ROM) & 0x80000000)); i++) + udelay(10); + + for (i = 0; i < 0x20; i++) { + reg_write(ohci, OHCI1394_GUID_ROM, 0x02000000); + PRINT(KERN_DEBUG, " EEPROM %02x: %02x", i, + (reg_read(ohci, OHCI1394_GUID_ROM) >> 16) & 0xff); + } +#endif + } +} + +/* + * Insert a packet in the DMA fifo and generate the DMA prg + * FIXME: rewrite the program in order to accept packets crossing + * page boundaries. + * check also that a single dma descriptor doesn't cross a + * page boundary. + */ +static void insert_packet(struct ti_ohci *ohci, + struct dma_trm_ctx *d, struct hpsb_packet *packet) +{ + u32 cycleTimer; + int idx = d->prg_ind; + + DBGMSG("Inserting packet for node " NODE_BUS_FMT + ", tlabel=%d, tcode=0x%x, speed=%d", + NODE_BUS_ARGS(ohci->host, packet->node_id), packet->tlabel, + packet->tcode, packet->speed_code); + + d->prg_cpu[idx]->begin.address = 0; + d->prg_cpu[idx]->begin.branchAddress = 0; + + if (d->type == DMA_CTX_ASYNC_RESP) { + /* + * For response packets, we need to put a timeout value in + * the 16 lower bits of the status... let's try 1 sec timeout + */ + cycleTimer = reg_read(ohci, OHCI1394_IsochronousCycleTimer); + d->prg_cpu[idx]->begin.status = cpu_to_le32( + (((((cycleTimer>>25)&0x7)+1)&0x7)<<13) | + ((cycleTimer&0x01fff000)>>12)); + + DBGMSG("cycleTimer: %08x timeStamp: %08x", + cycleTimer, d->prg_cpu[idx]->begin.status); + } else + d->prg_cpu[idx]->begin.status = 0; + + if ( (packet->type == hpsb_async) || (packet->type == hpsb_raw) ) { + + if (packet->type == hpsb_raw) { + d->prg_cpu[idx]->data[0] = cpu_to_le32(OHCI1394_TCODE_PHY<<4); + d->prg_cpu[idx]->data[1] = cpu_to_le32(packet->header[0]); + d->prg_cpu[idx]->data[2] = cpu_to_le32(packet->header[1]); + } else { + d->prg_cpu[idx]->data[0] = packet->speed_code<<16 | + (packet->header[0] & 0xFFFF); + + if (packet->tcode == TCODE_ISO_DATA) { + /* Sending an async stream packet */ + d->prg_cpu[idx]->data[1] = packet->header[0] & 0xFFFF0000; + } else { + /* Sending a normal async request or response */ + d->prg_cpu[idx]->data[1] = + (packet->header[1] & 0xFFFF) | + (packet->header[0] & 0xFFFF0000); + d->prg_cpu[idx]->data[2] = packet->header[2]; + d->prg_cpu[idx]->data[3] = packet->header[3]; + } + header_le32_to_cpu(d->prg_cpu[idx]->data, packet->tcode); + } + + if (packet->data_size) { /* block transmit */ + if (packet->tcode == TCODE_STREAM_DATA){ + d->prg_cpu[idx]->begin.control = + cpu_to_le32(DMA_CTL_OUTPUT_MORE | + DMA_CTL_IMMEDIATE | 0x8); + } else { + d->prg_cpu[idx]->begin.control = + cpu_to_le32(DMA_CTL_OUTPUT_MORE | + DMA_CTL_IMMEDIATE | 0x10); + } + d->prg_cpu[idx]->end.control = + cpu_to_le32(DMA_CTL_OUTPUT_LAST | + DMA_CTL_IRQ | + DMA_CTL_BRANCH | + packet->data_size); + /* + * Check that the packet data buffer + * does not cross a page boundary. + * + * XXX Fix this some day. eth1394 seems to trigger + * it, but ignoring it doesn't seem to cause a + * problem. + */ +#if 0 + if (cross_bound((unsigned long)packet->data, + packet->data_size)>0) { + /* FIXME: do something about it */ + PRINT(KERN_ERR, + "%s: packet data addr: %p size %Zd bytes " + "cross page boundary", __func__, + packet->data, packet->data_size); + } +#endif + d->prg_cpu[idx]->end.address = cpu_to_le32( + pci_map_single(ohci->dev, packet->data, + packet->data_size, + PCI_DMA_TODEVICE)); + + d->prg_cpu[idx]->end.branchAddress = 0; + d->prg_cpu[idx]->end.status = 0; + if (d->branchAddrPtr) + *(d->branchAddrPtr) = + cpu_to_le32(d->prg_bus[idx] | 0x3); + d->branchAddrPtr = + &(d->prg_cpu[idx]->end.branchAddress); + } else { /* quadlet transmit */ + if (packet->type == hpsb_raw) + d->prg_cpu[idx]->begin.control = + cpu_to_le32(DMA_CTL_OUTPUT_LAST | + DMA_CTL_IMMEDIATE | + DMA_CTL_IRQ | + DMA_CTL_BRANCH | + (packet->header_size + 4)); + else + d->prg_cpu[idx]->begin.control = + cpu_to_le32(DMA_CTL_OUTPUT_LAST | + DMA_CTL_IMMEDIATE | + DMA_CTL_IRQ | + DMA_CTL_BRANCH | + packet->header_size); + + if (d->branchAddrPtr) + *(d->branchAddrPtr) = + cpu_to_le32(d->prg_bus[idx] | 0x2); + d->branchAddrPtr = + &(d->prg_cpu[idx]->begin.branchAddress); + } + + } else { /* iso packet */ + d->prg_cpu[idx]->data[0] = packet->speed_code<<16 | + (packet->header[0] & 0xFFFF); + d->prg_cpu[idx]->data[1] = packet->header[0] & 0xFFFF0000; + header_le32_to_cpu(d->prg_cpu[idx]->data, packet->tcode); + + d->prg_cpu[idx]->begin.control = + cpu_to_le32(DMA_CTL_OUTPUT_MORE | + DMA_CTL_IMMEDIATE | 0x8); + d->prg_cpu[idx]->end.control = + cpu_to_le32(DMA_CTL_OUTPUT_LAST | + DMA_CTL_UPDATE | + DMA_CTL_IRQ | + DMA_CTL_BRANCH | + packet->data_size); + d->prg_cpu[idx]->end.address = cpu_to_le32( + pci_map_single(ohci->dev, packet->data, + packet->data_size, PCI_DMA_TODEVICE)); + + d->prg_cpu[idx]->end.branchAddress = 0; + d->prg_cpu[idx]->end.status = 0; + DBGMSG("Iso xmit context info: header[%08x %08x]\n" + " begin=%08x %08x %08x %08x\n" + " %08x %08x %08x %08x\n" + " end =%08x %08x %08x %08x", + d->prg_cpu[idx]->data[0], d->prg_cpu[idx]->data[1], + d->prg_cpu[idx]->begin.control, + d->prg_cpu[idx]->begin.address, + d->prg_cpu[idx]->begin.branchAddress, + d->prg_cpu[idx]->begin.status, + d->prg_cpu[idx]->data[0], + d->prg_cpu[idx]->data[1], + d->prg_cpu[idx]->data[2], + d->prg_cpu[idx]->data[3], + d->prg_cpu[idx]->end.control, + d->prg_cpu[idx]->end.address, + d->prg_cpu[idx]->end.branchAddress, + d->prg_cpu[idx]->end.status); + if (d->branchAddrPtr) + *(d->branchAddrPtr) = cpu_to_le32(d->prg_bus[idx] | 0x3); + d->branchAddrPtr = &(d->prg_cpu[idx]->end.branchAddress); + } + d->free_prgs--; + + /* queue the packet in the appropriate context queue */ + list_add_tail(&packet->driver_list, &d->fifo_list); + d->prg_ind = (d->prg_ind + 1) % d->num_desc; +} + +/* + * This function fills the FIFO with the (eventual) pending packets + * and runs or wakes up the DMA prg if necessary. + * + * The function MUST be called with the d->lock held. + */ +static void dma_trm_flush(struct ti_ohci *ohci, struct dma_trm_ctx *d) +{ + struct hpsb_packet *packet, *ptmp; + int idx = d->prg_ind; + int z = 0; + + /* insert the packets into the dma fifo */ + list_for_each_entry_safe(packet, ptmp, &d->pending_list, driver_list) { + if (!d->free_prgs) + break; + + /* For the first packet only */ + if (!z) + z = (packet->data_size) ? 3 : 2; + + /* Insert the packet */ + list_del_init(&packet->driver_list); + insert_packet(ohci, d, packet); + } + + /* Nothing must have been done, either no free_prgs or no packets */ + if (z == 0) + return; + + /* Is the context running ? (should be unless it is + the first packet to be sent in this context) */ + if (!(reg_read(ohci, d->ctrlSet) & 0x8000)) { + u32 nodeId = reg_read(ohci, OHCI1394_NodeID); + + DBGMSG("Starting transmit DMA ctx=%d",d->ctx); + reg_write(ohci, d->cmdPtr, d->prg_bus[idx] | z); + + /* Check that the node id is valid, and not 63 */ + if (!(nodeId & 0x80000000) || (nodeId & 0x3f) == 63) + PRINT(KERN_ERR, "Running dma failed because Node ID is not valid"); + else + reg_write(ohci, d->ctrlSet, 0x8000); + } else { + /* Wake up the dma context if necessary */ + if (!(reg_read(ohci, d->ctrlSet) & 0x400)) + DBGMSG("Waking transmit DMA ctx=%d",d->ctx); + + /* do this always, to avoid race condition */ + reg_write(ohci, d->ctrlSet, 0x1000); + } + + return; +} + +/* Transmission of an async or iso packet */ +static int ohci_transmit(struct hpsb_host *host, struct hpsb_packet *packet) +{ + struct ti_ohci *ohci = host->hostdata; + struct dma_trm_ctx *d; + unsigned long flags; + + if (packet->data_size > ohci->max_packet_size) { + PRINT(KERN_ERR, + "Transmit packet size %Zd is too big", + packet->data_size); + return -EOVERFLOW; + } + + if (packet->type == hpsb_raw) + d = &ohci->at_req_context; + else if ((packet->tcode & 0x02) && (packet->tcode != TCODE_ISO_DATA)) + d = &ohci->at_resp_context; + else + d = &ohci->at_req_context; + + spin_lock_irqsave(&d->lock,flags); + + list_add_tail(&packet->driver_list, &d->pending_list); + + dma_trm_flush(ohci, d); + + spin_unlock_irqrestore(&d->lock,flags); + + return 0; +} + +static int ohci_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg) +{ + struct ti_ohci *ohci = host->hostdata; + int retval = 0, phy_reg; + + switch (cmd) { + case RESET_BUS: + switch (arg) { + case SHORT_RESET: + phy_reg = get_phy_reg(ohci, 5); + phy_reg |= 0x40; + set_phy_reg(ohci, 5, phy_reg); /* set ISBR */ + break; + case LONG_RESET: + phy_reg = get_phy_reg(ohci, 1); + phy_reg |= 0x40; + set_phy_reg(ohci, 1, phy_reg); /* set IBR */ + break; + case SHORT_RESET_NO_FORCE_ROOT: + phy_reg = get_phy_reg(ohci, 1); + if (phy_reg & 0x80) { + phy_reg &= ~0x80; + set_phy_reg(ohci, 1, phy_reg); /* clear RHB */ + } + + phy_reg = get_phy_reg(ohci, 5); + phy_reg |= 0x40; + set_phy_reg(ohci, 5, phy_reg); /* set ISBR */ + break; + case LONG_RESET_NO_FORCE_ROOT: + phy_reg = get_phy_reg(ohci, 1); + phy_reg &= ~0x80; + phy_reg |= 0x40; + set_phy_reg(ohci, 1, phy_reg); /* clear RHB, set IBR */ + break; + case SHORT_RESET_FORCE_ROOT: + phy_reg = get_phy_reg(ohci, 1); + if (!(phy_reg & 0x80)) { + phy_reg |= 0x80; + set_phy_reg(ohci, 1, phy_reg); /* set RHB */ + } + + phy_reg = get_phy_reg(ohci, 5); + phy_reg |= 0x40; + set_phy_reg(ohci, 5, phy_reg); /* set ISBR */ + break; + case LONG_RESET_FORCE_ROOT: + phy_reg = get_phy_reg(ohci, 1); + phy_reg |= 0xc0; + set_phy_reg(ohci, 1, phy_reg); /* set RHB and IBR */ + break; + default: + retval = -1; + } + break; + + case GET_CYCLE_COUNTER: + retval = reg_read(ohci, OHCI1394_IsochronousCycleTimer); + break; + + case SET_CYCLE_COUNTER: + reg_write(ohci, OHCI1394_IsochronousCycleTimer, arg); + break; + + case SET_BUS_ID: + PRINT(KERN_ERR, "devctl command SET_BUS_ID err"); + break; + + case ACT_CYCLE_MASTER: + if (arg) { + /* check if we are root and other nodes are present */ + u32 nodeId = reg_read(ohci, OHCI1394_NodeID); + if ((nodeId & (1<<30)) && (nodeId & 0x3f)) { + /* + * enable cycleTimer, cycleMaster + */ + DBGMSG("Cycle master enabled"); + reg_write(ohci, OHCI1394_LinkControlSet, + OHCI1394_LinkControl_CycleTimerEnable | + OHCI1394_LinkControl_CycleMaster); + } + } else { + /* disable cycleTimer, cycleMaster, cycleSource */ + reg_write(ohci, OHCI1394_LinkControlClear, + OHCI1394_LinkControl_CycleTimerEnable | + OHCI1394_LinkControl_CycleMaster | + OHCI1394_LinkControl_CycleSource); + } + break; + + case CANCEL_REQUESTS: + DBGMSG("Cancel request received"); + dma_trm_reset(&ohci->at_req_context); + dma_trm_reset(&ohci->at_resp_context); + break; + + default: + PRINT_G(KERN_ERR, "ohci_devctl cmd %d not implemented yet", + cmd); + break; + } + return retval; +} + +/*********************************** + * rawiso ISO reception * + ***********************************/ + +/* + We use either buffer-fill or packet-per-buffer DMA mode. The DMA + buffer is split into "blocks" (regions described by one DMA + descriptor). Each block must be one page or less in size, and + must not cross a page boundary. + + There is one little wrinkle with buffer-fill mode: a packet that + starts in the final block may wrap around into the first block. But + the user API expects all packets to be contiguous. Our solution is + to keep the very last page of the DMA buffer in reserve - if a + packet spans the gap, we copy its tail into this page. +*/ + +struct ohci_iso_recv { + struct ti_ohci *ohci; + + struct ohci1394_iso_tasklet task; + int task_active; + + enum { BUFFER_FILL_MODE = 0, + PACKET_PER_BUFFER_MODE = 1 } dma_mode; + + /* memory and PCI mapping for the DMA descriptors */ + struct dma_prog_region prog; + struct dma_cmd *block; /* = (struct dma_cmd*) prog.virt */ + + /* how many DMA blocks fit in the buffer */ + unsigned int nblocks; + + /* stride of DMA blocks */ + unsigned int buf_stride; + + /* number of blocks to batch between interrupts */ + int block_irq_interval; + + /* block that DMA will finish next */ + int block_dma; + + /* (buffer-fill only) block that the reader will release next */ + int block_reader; + + /* (buffer-fill only) bytes of buffer the reader has released, + less than one block */ + int released_bytes; + + /* (buffer-fill only) buffer offset at which the next packet will appear */ + int dma_offset; + + /* OHCI DMA context control registers */ + u32 ContextControlSet; + u32 ContextControlClear; + u32 CommandPtr; + u32 ContextMatch; +}; + +static void ohci_iso_recv_task(unsigned long data); +static void ohci_iso_recv_stop(struct hpsb_iso *iso); +static void ohci_iso_recv_shutdown(struct hpsb_iso *iso); +static int ohci_iso_recv_start(struct hpsb_iso *iso, int cycle, int tag_mask, int sync); +static void ohci_iso_recv_program(struct hpsb_iso *iso); + +static int ohci_iso_recv_init(struct hpsb_iso *iso) +{ + struct ti_ohci *ohci = iso->host->hostdata; + struct ohci_iso_recv *recv; + int ctx; + int ret = -ENOMEM; + + recv = kmalloc(sizeof(*recv), GFP_KERNEL); + if (!recv) + return -ENOMEM; + + iso->hostdata = recv; + recv->ohci = ohci; + recv->task_active = 0; + dma_prog_region_init(&recv->prog); + recv->block = NULL; + + /* use buffer-fill mode, unless irq_interval is 1 + (note: multichannel requires buffer-fill) */ + + if (((iso->irq_interval == 1 && iso->dma_mode == HPSB_ISO_DMA_OLD_ABI) || + iso->dma_mode == HPSB_ISO_DMA_PACKET_PER_BUFFER) && iso->channel != -1) { + recv->dma_mode = PACKET_PER_BUFFER_MODE; + } else { + recv->dma_mode = BUFFER_FILL_MODE; + } + + /* set nblocks, buf_stride, block_irq_interval */ + + if (recv->dma_mode == BUFFER_FILL_MODE) { + recv->buf_stride = PAGE_SIZE; + + /* one block per page of data in the DMA buffer, minus the final guard page */ + recv->nblocks = iso->buf_size/PAGE_SIZE - 1; + if (recv->nblocks < 3) { + DBGMSG("ohci_iso_recv_init: DMA buffer too small"); + goto err; + } + + /* iso->irq_interval is in packets - translate that to blocks */ + if (iso->irq_interval == 1) + recv->block_irq_interval = 1; + else + recv->block_irq_interval = iso->irq_interval * + ((recv->nblocks+1)/iso->buf_packets); + if (recv->block_irq_interval*4 > recv->nblocks) + recv->block_irq_interval = recv->nblocks/4; + if (recv->block_irq_interval < 1) + recv->block_irq_interval = 1; + + } else { + int max_packet_size; + + recv->nblocks = iso->buf_packets; + recv->block_irq_interval = iso->irq_interval; + if (recv->block_irq_interval * 4 > iso->buf_packets) + recv->block_irq_interval = iso->buf_packets / 4; + if (recv->block_irq_interval < 1) + recv->block_irq_interval = 1; + + /* choose a buffer stride */ + /* must be a power of 2, and <= PAGE_SIZE */ + + max_packet_size = iso->buf_size / iso->buf_packets; + + for (recv->buf_stride = 8; recv->buf_stride < max_packet_size; + recv->buf_stride *= 2); + + if (recv->buf_stride*iso->buf_packets > iso->buf_size || + recv->buf_stride > PAGE_SIZE) { + /* this shouldn't happen, but anyway... */ + DBGMSG("ohci_iso_recv_init: problem choosing a buffer stride"); + goto err; + } + } + + recv->block_reader = 0; + recv->released_bytes = 0; + recv->block_dma = 0; + recv->dma_offset = 0; + + /* size of DMA program = one descriptor per block */ + if (dma_prog_region_alloc(&recv->prog, + sizeof(struct dma_cmd) * recv->nblocks, + recv->ohci->dev)) + goto err; + + recv->block = (struct dma_cmd*) recv->prog.kvirt; + + ohci1394_init_iso_tasklet(&recv->task, + iso->channel == -1 ? OHCI_ISO_MULTICHANNEL_RECEIVE : + OHCI_ISO_RECEIVE, + ohci_iso_recv_task, (unsigned long) iso); + + if (ohci1394_register_iso_tasklet(recv->ohci, &recv->task) < 0) { + ret = -EBUSY; + goto err; + } + + recv->task_active = 1; + + /* recv context registers are spaced 32 bytes apart */ + ctx = recv->task.context; + recv->ContextControlSet = OHCI1394_IsoRcvContextControlSet + 32 * ctx; + recv->ContextControlClear = OHCI1394_IsoRcvContextControlClear + 32 * ctx; + recv->CommandPtr = OHCI1394_IsoRcvCommandPtr + 32 * ctx; + recv->ContextMatch = OHCI1394_IsoRcvContextMatch + 32 * ctx; + + if (iso->channel == -1) { + /* clear multi-channel selection mask */ + reg_write(recv->ohci, OHCI1394_IRMultiChanMaskHiClear, 0xFFFFFFFF); + reg_write(recv->ohci, OHCI1394_IRMultiChanMaskLoClear, 0xFFFFFFFF); + } + + /* write the DMA program */ + ohci_iso_recv_program(iso); + + DBGMSG("ohci_iso_recv_init: %s mode, DMA buffer is %lu pages" + " (%u bytes), using %u blocks, buf_stride %u, block_irq_interval %d", + recv->dma_mode == BUFFER_FILL_MODE ? + "buffer-fill" : "packet-per-buffer", + iso->buf_size/PAGE_SIZE, iso->buf_size, + recv->nblocks, recv->buf_stride, recv->block_irq_interval); + + return 0; + +err: + ohci_iso_recv_shutdown(iso); + return ret; +} + +static void ohci_iso_recv_stop(struct hpsb_iso *iso) +{ + struct ohci_iso_recv *recv = iso->hostdata; + + /* disable interrupts */ + reg_write(recv->ohci, OHCI1394_IsoRecvIntMaskClear, 1 << recv->task.context); + + /* halt DMA */ + ohci1394_stop_context(recv->ohci, recv->ContextControlClear, NULL); +} + +static void ohci_iso_recv_shutdown(struct hpsb_iso *iso) +{ + struct ohci_iso_recv *recv = iso->hostdata; + + if (recv->task_active) { + ohci_iso_recv_stop(iso); + ohci1394_unregister_iso_tasklet(recv->ohci, &recv->task); + recv->task_active = 0; + } + + dma_prog_region_free(&recv->prog); + kfree(recv); + iso->hostdata = NULL; +} + +/* set up a "gapped" ring buffer DMA program */ +static void ohci_iso_recv_program(struct hpsb_iso *iso) +{ + struct ohci_iso_recv *recv = iso->hostdata; + int blk; + + /* address of 'branch' field in previous DMA descriptor */ + u32 *prev_branch = NULL; + + for (blk = 0; blk < recv->nblocks; blk++) { + u32 control; + + /* the DMA descriptor */ + struct dma_cmd *cmd = &recv->block[blk]; + + /* offset of the DMA descriptor relative to the DMA prog buffer */ + unsigned long prog_offset = blk * sizeof(struct dma_cmd); + + /* offset of this packet's data within the DMA buffer */ + unsigned long buf_offset = blk * recv->buf_stride; + + if (recv->dma_mode == BUFFER_FILL_MODE) { + control = 2 << 28; /* INPUT_MORE */ + } else { + control = 3 << 28; /* INPUT_LAST */ + } + + control |= 8 << 24; /* s = 1, update xferStatus and resCount */ + + /* interrupt on last block, and at intervals */ + if (blk == recv->nblocks-1 || (blk % recv->block_irq_interval) == 0) { + control |= 3 << 20; /* want interrupt */ + } + + control |= 3 << 18; /* enable branch to address */ + control |= recv->buf_stride; + + cmd->control = cpu_to_le32(control); + cmd->address = cpu_to_le32(dma_region_offset_to_bus(&iso->data_buf, buf_offset)); + cmd->branchAddress = 0; /* filled in on next loop */ + cmd->status = cpu_to_le32(recv->buf_stride); + + /* link the previous descriptor to this one */ + if (prev_branch) { + *prev_branch = cpu_to_le32(dma_prog_region_offset_to_bus(&recv->prog, prog_offset) | 1); + } + + prev_branch = &cmd->branchAddress; + } + + /* the final descriptor's branch address and Z should be left at 0 */ +} + +/* listen or unlisten to a specific channel (multi-channel mode only) */ +static void ohci_iso_recv_change_channel(struct hpsb_iso *iso, unsigned char channel, int listen) +{ + struct ohci_iso_recv *recv = iso->hostdata; + int reg, i; + + if (channel < 32) { + reg = listen ? OHCI1394_IRMultiChanMaskLoSet : OHCI1394_IRMultiChanMaskLoClear; + i = channel; + } else { + reg = listen ? OHCI1394_IRMultiChanMaskHiSet : OHCI1394_IRMultiChanMaskHiClear; + i = channel - 32; + } + + reg_write(recv->ohci, reg, (1 << i)); + + /* issue a dummy read to force all PCI writes to be posted immediately */ + mb(); + reg_read(recv->ohci, OHCI1394_IsochronousCycleTimer); +} + +static void ohci_iso_recv_set_channel_mask(struct hpsb_iso *iso, u64 mask) +{ + struct ohci_iso_recv *recv = iso->hostdata; + int i; + + for (i = 0; i < 64; i++) { + if (mask & (1ULL << i)) { + if (i < 32) + reg_write(recv->ohci, OHCI1394_IRMultiChanMaskLoSet, (1 << i)); + else + reg_write(recv->ohci, OHCI1394_IRMultiChanMaskHiSet, (1 << (i-32))); + } else { + if (i < 32) + reg_write(recv->ohci, OHCI1394_IRMultiChanMaskLoClear, (1 << i)); + else + reg_write(recv->ohci, OHCI1394_IRMultiChanMaskHiClear, (1 << (i-32))); + } + } + + /* issue a dummy read to force all PCI writes to be posted immediately */ + mb(); + reg_read(recv->ohci, OHCI1394_IsochronousCycleTimer); +} + +static int ohci_iso_recv_start(struct hpsb_iso *iso, int cycle, int tag_mask, int sync) +{ + struct ohci_iso_recv *recv = iso->hostdata; + struct ti_ohci *ohci = recv->ohci; + u32 command, contextMatch; + + reg_write(recv->ohci, recv->ContextControlClear, 0xFFFFFFFF); + wmb(); + + /* always keep ISO headers */ + command = (1 << 30); + + if (recv->dma_mode == BUFFER_FILL_MODE) + command |= (1 << 31); + + reg_write(recv->ohci, recv->ContextControlSet, command); + + /* match on specified tags */ + contextMatch = tag_mask << 28; + + if (iso->channel == -1) { + /* enable multichannel reception */ + reg_write(recv->ohci, recv->ContextControlSet, (1 << 28)); + } else { + /* listen on channel */ + contextMatch |= iso->channel; + } + + if (cycle != -1) { + u32 seconds; + + /* enable cycleMatch */ + reg_write(recv->ohci, recv->ContextControlSet, (1 << 29)); + + /* set starting cycle */ + cycle &= 0x1FFF; + + /* 'cycle' is only mod 8000, but we also need two 'seconds' bits - + just snarf them from the current time */ + seconds = reg_read(recv->ohci, OHCI1394_IsochronousCycleTimer) >> 25; + + /* advance one second to give some extra time for DMA to start */ + seconds += 1; + + cycle |= (seconds & 3) << 13; + + contextMatch |= cycle << 12; + } + + if (sync != -1) { + /* set sync flag on first DMA descriptor */ + struct dma_cmd *cmd = &recv->block[recv->block_dma]; + cmd->control |= cpu_to_le32(DMA_CTL_WAIT); + + /* match sync field */ + contextMatch |= (sync&0xf)<<8; + } + + reg_write(recv->ohci, recv->ContextMatch, contextMatch); + + /* address of first descriptor block */ + command = dma_prog_region_offset_to_bus(&recv->prog, + recv->block_dma * sizeof(struct dma_cmd)); + command |= 1; /* Z=1 */ + + reg_write(recv->ohci, recv->CommandPtr, command); + + /* enable interrupts */ + reg_write(recv->ohci, OHCI1394_IsoRecvIntMaskSet, 1 << recv->task.context); + + wmb(); + + /* run */ + reg_write(recv->ohci, recv->ContextControlSet, 0x8000); + + /* issue a dummy read of the cycle timer register to force + all PCI writes to be posted immediately */ + mb(); + reg_read(recv->ohci, OHCI1394_IsochronousCycleTimer); + + /* check RUN */ + if (!(reg_read(recv->ohci, recv->ContextControlSet) & 0x8000)) { + PRINT(KERN_ERR, + "Error starting IR DMA (ContextControl 0x%08x)\n", + reg_read(recv->ohci, recv->ContextControlSet)); + return -1; + } + + return 0; +} + +static void ohci_iso_recv_release_block(struct ohci_iso_recv *recv, int block) +{ + /* re-use the DMA descriptor for the block */ + /* by linking the previous descriptor to it */ + + int next_i = block; + int prev_i = (next_i == 0) ? (recv->nblocks - 1) : (next_i - 1); + + struct dma_cmd *next = &recv->block[next_i]; + struct dma_cmd *prev = &recv->block[prev_i]; + + /* ignore out-of-range requests */ + if ((block < 0) || (block > recv->nblocks)) + return; + + /* 'next' becomes the new end of the DMA chain, + so disable branch and enable interrupt */ + next->branchAddress = 0; + next->control |= cpu_to_le32(3 << 20); + next->status = cpu_to_le32(recv->buf_stride); + + /* link prev to next */ + prev->branchAddress = cpu_to_le32(dma_prog_region_offset_to_bus(&recv->prog, + sizeof(struct dma_cmd) * next_i) + | 1); /* Z=1 */ + + /* disable interrupt on previous DMA descriptor, except at intervals */ + if ((prev_i % recv->block_irq_interval) == 0) { + prev->control |= cpu_to_le32(3 << 20); /* enable interrupt */ + } else { + prev->control &= cpu_to_le32(~(3<<20)); /* disable interrupt */ + } + wmb(); + + /* wake up DMA in case it fell asleep */ + reg_write(recv->ohci, recv->ContextControlSet, (1 << 12)); +} + +static void ohci_iso_recv_bufferfill_release(struct ohci_iso_recv *recv, + struct hpsb_iso_packet_info *info) +{ + /* release the memory where the packet was */ + recv->released_bytes += info->total_len; + + /* have we released enough memory for one block? */ + while (recv->released_bytes > recv->buf_stride) { + ohci_iso_recv_release_block(recv, recv->block_reader); + recv->block_reader = (recv->block_reader + 1) % recv->nblocks; + recv->released_bytes -= recv->buf_stride; + } +} + +static inline void ohci_iso_recv_release(struct hpsb_iso *iso, struct hpsb_iso_packet_info *info) +{ + struct ohci_iso_recv *recv = iso->hostdata; + if (recv->dma_mode == BUFFER_FILL_MODE) { + ohci_iso_recv_bufferfill_release(recv, info); + } else { + ohci_iso_recv_release_block(recv, info - iso->infos); + } +} + +/* parse all packets from blocks that have been fully received */ +static void ohci_iso_recv_bufferfill_parse(struct hpsb_iso *iso, struct ohci_iso_recv *recv) +{ + int wake = 0; + int runaway = 0; + struct ti_ohci *ohci = recv->ohci; + + while (1) { + /* we expect the next parsable packet to begin at recv->dma_offset */ + /* note: packet layout is as shown in section 10.6.1.1 of the OHCI spec */ + + unsigned int offset; + unsigned short len, cycle, total_len; + unsigned char channel, tag, sy; + + unsigned char *p = iso->data_buf.kvirt; + + unsigned int this_block = recv->dma_offset/recv->buf_stride; + + /* don't loop indefinitely */ + if (runaway++ > 100000) { + atomic_inc(&iso->overflows); + PRINT(KERN_ERR, + "IR DMA error - Runaway during buffer parsing!\n"); + break; + } + + /* stop parsing once we arrive at block_dma (i.e. don't get ahead of DMA) */ + if (this_block == recv->block_dma) + break; + + wake = 1; + + /* parse data length, tag, channel, and sy */ + + /* note: we keep our own local copies of 'len' and 'offset' + so the user can't mess with them by poking in the mmap area */ + + len = p[recv->dma_offset+2] | (p[recv->dma_offset+3] << 8); + + if (len > 4096) { + PRINT(KERN_ERR, + "IR DMA error - bogus 'len' value %u\n", len); + } + + channel = p[recv->dma_offset+1] & 0x3F; + tag = p[recv->dma_offset+1] >> 6; + sy = p[recv->dma_offset+0] & 0xF; + + /* advance to data payload */ + recv->dma_offset += 4; + + /* check for wrap-around */ + if (recv->dma_offset >= recv->buf_stride*recv->nblocks) { + recv->dma_offset -= recv->buf_stride*recv->nblocks; + } + + /* dma_offset now points to the first byte of the data payload */ + offset = recv->dma_offset; + + /* advance to xferStatus/timeStamp */ + recv->dma_offset += len; + + total_len = len + 8; /* 8 bytes header+trailer in OHCI packet */ + /* payload is padded to 4 bytes */ + if (len % 4) { + recv->dma_offset += 4 - (len%4); + total_len += 4 - (len%4); + } + + /* check for wrap-around */ + if (recv->dma_offset >= recv->buf_stride*recv->nblocks) { + /* uh oh, the packet data wraps from the last + to the first DMA block - make the packet + contiguous by copying its "tail" into the + guard page */ + + int guard_off = recv->buf_stride*recv->nblocks; + int tail_len = len - (guard_off - offset); + + if (tail_len > 0 && tail_len < recv->buf_stride) { + memcpy(iso->data_buf.kvirt + guard_off, + iso->data_buf.kvirt, + tail_len); + } + + recv->dma_offset -= recv->buf_stride*recv->nblocks; + } + + /* parse timestamp */ + cycle = p[recv->dma_offset+0] | (p[recv->dma_offset+1]<<8); + cycle &= 0x1FFF; + + /* advance to next packet */ + recv->dma_offset += 4; + + /* check for wrap-around */ + if (recv->dma_offset >= recv->buf_stride*recv->nblocks) { + recv->dma_offset -= recv->buf_stride*recv->nblocks; + } + + hpsb_iso_packet_received(iso, offset, len, total_len, cycle, channel, tag, sy); + } + + if (wake) + hpsb_iso_wake(iso); +} + +static void ohci_iso_recv_bufferfill_task(struct hpsb_iso *iso, struct ohci_iso_recv *recv) +{ + int loop; + struct ti_ohci *ohci = recv->ohci; + + /* loop over all blocks */ + for (loop = 0; loop < recv->nblocks; loop++) { + + /* check block_dma to see if it's done */ + struct dma_cmd *im = &recv->block[recv->block_dma]; + + /* check the DMA descriptor for new writes to xferStatus */ + u16 xferstatus = le32_to_cpu(im->status) >> 16; + + /* rescount is the number of bytes *remaining to be written* in the block */ + u16 rescount = le32_to_cpu(im->status) & 0xFFFF; + + unsigned char event = xferstatus & 0x1F; + + if (!event) { + /* nothing has happened to this block yet */ + break; + } + + if (event != 0x11) { + atomic_inc(&iso->overflows); + PRINT(KERN_ERR, + "IR DMA error - OHCI error code 0x%02x\n", event); + } + + if (rescount != 0) { + /* the card is still writing to this block; + we can't touch it until it's done */ + break; + } + + /* OK, the block is finished... */ + + /* sync our view of the block */ + dma_region_sync_for_cpu(&iso->data_buf, recv->block_dma*recv->buf_stride, recv->buf_stride); + + /* reset the DMA descriptor */ + im->status = recv->buf_stride; + + /* advance block_dma */ + recv->block_dma = (recv->block_dma + 1) % recv->nblocks; + + if ((recv->block_dma+1) % recv->nblocks == recv->block_reader) { + atomic_inc(&iso->overflows); + DBGMSG("ISO reception overflow - " + "ran out of DMA blocks"); + } + } + + /* parse any packets that have arrived */ + ohci_iso_recv_bufferfill_parse(iso, recv); +} + +static void ohci_iso_recv_packetperbuf_task(struct hpsb_iso *iso, struct ohci_iso_recv *recv) +{ + int count; + int wake = 0; + struct ti_ohci *ohci = recv->ohci; + + /* loop over the entire buffer */ + for (count = 0; count < recv->nblocks; count++) { + u32 packet_len = 0; + + /* pointer to the DMA descriptor */ + struct dma_cmd *il = ((struct dma_cmd*) recv->prog.kvirt) + iso->pkt_dma; + + /* check the DMA descriptor for new writes to xferStatus */ + u16 xferstatus = le32_to_cpu(il->status) >> 16; + u16 rescount = le32_to_cpu(il->status) & 0xFFFF; + + unsigned char event = xferstatus & 0x1F; + + if (!event) { + /* this packet hasn't come in yet; we are done for now */ + goto out; + } + + if (event == 0x11) { + /* packet received successfully! */ + + /* rescount is the number of bytes *remaining* in the packet buffer, + after the packet was written */ + packet_len = recv->buf_stride - rescount; + + } else if (event == 0x02) { + PRINT(KERN_ERR, "IR DMA error - packet too long for buffer\n"); + } else if (event) { + PRINT(KERN_ERR, "IR DMA error - OHCI error code 0x%02x\n", event); + } + + /* sync our view of the buffer */ + dma_region_sync_for_cpu(&iso->data_buf, iso->pkt_dma * recv->buf_stride, recv->buf_stride); + + /* record the per-packet info */ + { + /* iso header is 8 bytes ahead of the data payload */ + unsigned char *hdr; + + unsigned int offset; + unsigned short cycle; + unsigned char channel, tag, sy; + + offset = iso->pkt_dma * recv->buf_stride; + hdr = iso->data_buf.kvirt + offset; + + /* skip iso header */ + offset += 8; + packet_len -= 8; + + cycle = (hdr[0] | (hdr[1] << 8)) & 0x1FFF; + channel = hdr[5] & 0x3F; + tag = hdr[5] >> 6; + sy = hdr[4] & 0xF; + + hpsb_iso_packet_received(iso, offset, packet_len, + recv->buf_stride, cycle, channel, tag, sy); + } + + /* reset the DMA descriptor */ + il->status = recv->buf_stride; + + wake = 1; + recv->block_dma = iso->pkt_dma; + } + +out: + if (wake) + hpsb_iso_wake(iso); +} + +static void ohci_iso_recv_task(unsigned long data) +{ + struct hpsb_iso *iso = (struct hpsb_iso*) data; + struct ohci_iso_recv *recv = iso->hostdata; + + if (recv->dma_mode == BUFFER_FILL_MODE) + ohci_iso_recv_bufferfill_task(iso, recv); + else + ohci_iso_recv_packetperbuf_task(iso, recv); +} + +/*********************************** + * rawiso ISO transmission * + ***********************************/ + +struct ohci_iso_xmit { + struct ti_ohci *ohci; + struct dma_prog_region prog; + struct ohci1394_iso_tasklet task; + int task_active; + int last_cycle; + atomic_t skips; + + u32 ContextControlSet; + u32 ContextControlClear; + u32 CommandPtr; +}; + +/* transmission DMA program: + one OUTPUT_MORE_IMMEDIATE for the IT header + one OUTPUT_LAST for the buffer data */ + +struct iso_xmit_cmd { + struct dma_cmd output_more_immediate; + u8 iso_hdr[8]; + u32 unused[2]; + struct dma_cmd output_last; +}; + +static int ohci_iso_xmit_init(struct hpsb_iso *iso); +static int ohci_iso_xmit_start(struct hpsb_iso *iso, int cycle); +static void ohci_iso_xmit_shutdown(struct hpsb_iso *iso); +static void ohci_iso_xmit_task(unsigned long data); + +static int ohci_iso_xmit_init(struct hpsb_iso *iso) +{ + struct ohci_iso_xmit *xmit; + unsigned int prog_size; + int ctx; + int ret = -ENOMEM; + + xmit = kmalloc(sizeof(*xmit), GFP_KERNEL); + if (!xmit) + return -ENOMEM; + + iso->hostdata = xmit; + xmit->ohci = iso->host->hostdata; + xmit->task_active = 0; + xmit->last_cycle = -1; + atomic_set(&iso->skips, 0); + + dma_prog_region_init(&xmit->prog); + + prog_size = sizeof(struct iso_xmit_cmd) * iso->buf_packets; + + if (dma_prog_region_alloc(&xmit->prog, prog_size, xmit->ohci->dev)) + goto err; + + ohci1394_init_iso_tasklet(&xmit->task, OHCI_ISO_TRANSMIT, + ohci_iso_xmit_task, (unsigned long) iso); + + if (ohci1394_register_iso_tasklet(xmit->ohci, &xmit->task) < 0) { + ret = -EBUSY; + goto err; + } + + xmit->task_active = 1; + + /* xmit context registers are spaced 16 bytes apart */ + ctx = xmit->task.context; + xmit->ContextControlSet = OHCI1394_IsoXmitContextControlSet + 16 * ctx; + xmit->ContextControlClear = OHCI1394_IsoXmitContextControlClear + 16 * ctx; + xmit->CommandPtr = OHCI1394_IsoXmitCommandPtr + 16 * ctx; + + return 0; + +err: + ohci_iso_xmit_shutdown(iso); + return ret; +} + +static void ohci_iso_xmit_stop(struct hpsb_iso *iso) +{ + struct ohci_iso_xmit *xmit = iso->hostdata; + struct ti_ohci *ohci = xmit->ohci; + + /* disable interrupts */ + reg_write(xmit->ohci, OHCI1394_IsoXmitIntMaskClear, 1 << xmit->task.context); + + /* halt DMA */ + if (ohci1394_stop_context(xmit->ohci, xmit->ContextControlClear, NULL)) { + /* XXX the DMA context will lock up if you try to send too much data! */ + PRINT(KERN_ERR, + "you probably exceeded the OHCI card's bandwidth limit - " + "reload the module and reduce xmit bandwidth"); + } +} + +static void ohci_iso_xmit_shutdown(struct hpsb_iso *iso) +{ + struct ohci_iso_xmit *xmit = iso->hostdata; + + if (xmit->task_active) { + ohci_iso_xmit_stop(iso); + ohci1394_unregister_iso_tasklet(xmit->ohci, &xmit->task); + xmit->task_active = 0; + } + + dma_prog_region_free(&xmit->prog); + kfree(xmit); + iso->hostdata = NULL; +} + +static void ohci_iso_xmit_task(unsigned long data) +{ + struct hpsb_iso *iso = (struct hpsb_iso*) data; + struct ohci_iso_xmit *xmit = iso->hostdata; + struct ti_ohci *ohci = xmit->ohci; + int wake = 0; + int count; + + /* check the whole buffer if necessary, starting at pkt_dma */ + for (count = 0; count < iso->buf_packets; count++) { + int cycle; + + /* DMA descriptor */ + struct iso_xmit_cmd *cmd = dma_region_i(&xmit->prog, struct iso_xmit_cmd, iso->pkt_dma); + + /* check for new writes to xferStatus */ + u16 xferstatus = le32_to_cpu(cmd->output_last.status) >> 16; + u8 event = xferstatus & 0x1F; + + if (!event) { + /* packet hasn't been sent yet; we are done for now */ + break; + } + + if (event != 0x11) + PRINT(KERN_ERR, + "IT DMA error - OHCI error code 0x%02x\n", event); + + /* at least one packet went out, so wake up the writer */ + wake = 1; + + /* parse cycle */ + cycle = le32_to_cpu(cmd->output_last.status) & 0x1FFF; + + if (xmit->last_cycle > -1) { + int cycle_diff = cycle - xmit->last_cycle; + int skip; + + /* unwrap */ + if (cycle_diff < 0) { + cycle_diff += 8000; + if (cycle_diff < 0) + PRINT(KERN_ERR, "bogus cycle diff %d\n", + cycle_diff); + } + + skip = cycle_diff - 1; + if (skip > 0) { + DBGMSG("skipped %d cycles without packet loss", skip); + atomic_add(skip, &iso->skips); + } + } + xmit->last_cycle = cycle; + + /* tell the subsystem the packet has gone out */ + hpsb_iso_packet_sent(iso, cycle, event != 0x11); + + /* reset the DMA descriptor for next time */ + cmd->output_last.status = 0; + } + + if (wake) + hpsb_iso_wake(iso); +} + +static int ohci_iso_xmit_queue(struct hpsb_iso *iso, struct hpsb_iso_packet_info *info) +{ + struct ohci_iso_xmit *xmit = iso->hostdata; + struct ti_ohci *ohci = xmit->ohci; + + int next_i, prev_i; + struct iso_xmit_cmd *next, *prev; + + unsigned int offset; + unsigned short len; + unsigned char tag, sy; + + /* check that the packet doesn't cross a page boundary + (we could allow this if we added OUTPUT_MORE descriptor support) */ + if (cross_bound(info->offset, info->len)) { + PRINT(KERN_ERR, + "rawiso xmit: packet %u crosses a page boundary", + iso->first_packet); + return -EINVAL; + } + + offset = info->offset; + len = info->len; + tag = info->tag; + sy = info->sy; + + /* sync up the card's view of the buffer */ + dma_region_sync_for_device(&iso->data_buf, offset, len); + + /* append first_packet to the DMA chain */ + /* by linking the previous descriptor to it */ + /* (next will become the new end of the DMA chain) */ + + next_i = iso->first_packet; + prev_i = (next_i == 0) ? (iso->buf_packets - 1) : (next_i - 1); + + next = dma_region_i(&xmit->prog, struct iso_xmit_cmd, next_i); + prev = dma_region_i(&xmit->prog, struct iso_xmit_cmd, prev_i); + + /* set up the OUTPUT_MORE_IMMEDIATE descriptor */ + memset(next, 0, sizeof(struct iso_xmit_cmd)); + next->output_more_immediate.control = cpu_to_le32(0x02000008); + + /* ISO packet header is embedded in the OUTPUT_MORE_IMMEDIATE */ + + /* tcode = 0xA, and sy */ + next->iso_hdr[0] = 0xA0 | (sy & 0xF); + + /* tag and channel number */ + next->iso_hdr[1] = (tag << 6) | (iso->channel & 0x3F); + + /* transmission speed */ + next->iso_hdr[2] = iso->speed & 0x7; + + /* payload size */ + next->iso_hdr[6] = len & 0xFF; + next->iso_hdr[7] = len >> 8; + + /* set up the OUTPUT_LAST */ + next->output_last.control = cpu_to_le32(1 << 28); + next->output_last.control |= cpu_to_le32(1 << 27); /* update timeStamp */ + next->output_last.control |= cpu_to_le32(3 << 20); /* want interrupt */ + next->output_last.control |= cpu_to_le32(3 << 18); /* enable branch */ + next->output_last.control |= cpu_to_le32(len); + + /* payload bus address */ + next->output_last.address = cpu_to_le32(dma_region_offset_to_bus(&iso->data_buf, offset)); + + /* leave branchAddress at zero for now */ + + /* re-write the previous DMA descriptor to chain to this one */ + + /* set prev branch address to point to next (Z=3) */ + prev->output_last.branchAddress = cpu_to_le32( + dma_prog_region_offset_to_bus(&xmit->prog, sizeof(struct iso_xmit_cmd) * next_i) | 3); + + /* + * Link the skip address to this descriptor itself. This causes a + * context to skip a cycle whenever lost cycles or FIFO overruns occur, + * without dropping the data at that point the application should then + * decide whether this is an error condition or not. Some protocols + * can deal with this by dropping some rate-matching padding packets. + */ + next->output_more_immediate.branchAddress = + prev->output_last.branchAddress; + + /* disable interrupt, unless required by the IRQ interval */ + if (prev_i % iso->irq_interval) { + prev->output_last.control &= cpu_to_le32(~(3 << 20)); /* no interrupt */ + } else { + prev->output_last.control |= cpu_to_le32(3 << 20); /* enable interrupt */ + } + + wmb(); + + /* wake DMA in case it is sleeping */ + reg_write(xmit->ohci, xmit->ContextControlSet, 1 << 12); + + /* issue a dummy read of the cycle timer to force all PCI + writes to be posted immediately */ + mb(); + reg_read(xmit->ohci, OHCI1394_IsochronousCycleTimer); + + return 0; +} + +static int ohci_iso_xmit_start(struct hpsb_iso *iso, int cycle) +{ + struct ohci_iso_xmit *xmit = iso->hostdata; + struct ti_ohci *ohci = xmit->ohci; + + /* clear out the control register */ + reg_write(xmit->ohci, xmit->ContextControlClear, 0xFFFFFFFF); + wmb(); + + /* address and length of first descriptor block (Z=3) */ + reg_write(xmit->ohci, xmit->CommandPtr, + dma_prog_region_offset_to_bus(&xmit->prog, iso->pkt_dma * sizeof(struct iso_xmit_cmd)) | 3); + + /* cycle match */ + if (cycle != -1) { + u32 start = cycle & 0x1FFF; + + /* 'cycle' is only mod 8000, but we also need two 'seconds' bits - + just snarf them from the current time */ + u32 seconds = reg_read(xmit->ohci, OHCI1394_IsochronousCycleTimer) >> 25; + + /* advance one second to give some extra time for DMA to start */ + seconds += 1; + + start |= (seconds & 3) << 13; + + reg_write(xmit->ohci, xmit->ContextControlSet, 0x80000000 | (start << 16)); + } + + /* enable interrupts */ + reg_write(xmit->ohci, OHCI1394_IsoXmitIntMaskSet, 1 << xmit->task.context); + + /* run */ + reg_write(xmit->ohci, xmit->ContextControlSet, 0x8000); + mb(); + + /* wait 100 usec to give the card time to go active */ + udelay(100); + + /* check the RUN bit */ + if (!(reg_read(xmit->ohci, xmit->ContextControlSet) & 0x8000)) { + PRINT(KERN_ERR, "Error starting IT DMA (ContextControl 0x%08x)\n", + reg_read(xmit->ohci, xmit->ContextControlSet)); + return -1; + } + + return 0; +} + +static int ohci_isoctl(struct hpsb_iso *iso, enum isoctl_cmd cmd, unsigned long arg) +{ + + switch(cmd) { + case XMIT_INIT: + return ohci_iso_xmit_init(iso); + case XMIT_START: + return ohci_iso_xmit_start(iso, arg); + case XMIT_STOP: + ohci_iso_xmit_stop(iso); + return 0; + case XMIT_QUEUE: + return ohci_iso_xmit_queue(iso, (struct hpsb_iso_packet_info*) arg); + case XMIT_SHUTDOWN: + ohci_iso_xmit_shutdown(iso); + return 0; + + case RECV_INIT: + return ohci_iso_recv_init(iso); + case RECV_START: { + int *args = (int*) arg; + return ohci_iso_recv_start(iso, args[0], args[1], args[2]); + } + case RECV_STOP: + ohci_iso_recv_stop(iso); + return 0; + case RECV_RELEASE: + ohci_iso_recv_release(iso, (struct hpsb_iso_packet_info*) arg); + return 0; + case RECV_FLUSH: + ohci_iso_recv_task((unsigned long) iso); + return 0; + case RECV_SHUTDOWN: + ohci_iso_recv_shutdown(iso); + return 0; + case RECV_LISTEN_CHANNEL: + ohci_iso_recv_change_channel(iso, arg, 1); + return 0; + case RECV_UNLISTEN_CHANNEL: + ohci_iso_recv_change_channel(iso, arg, 0); + return 0; + case RECV_SET_CHANNEL_MASK: + ohci_iso_recv_set_channel_mask(iso, *((u64*) arg)); + return 0; + + default: + PRINT_G(KERN_ERR, "ohci_isoctl cmd %d not implemented yet", + cmd); + break; + } + return -EINVAL; +} + +/*************************************** + * IEEE-1394 functionality section END * + ***************************************/ + + +/******************************************************** + * Global stuff (interrupt handler, init/shutdown code) * + ********************************************************/ + +static void dma_trm_reset(struct dma_trm_ctx *d) +{ + unsigned long flags; + LIST_HEAD(packet_list); + struct ti_ohci *ohci = d->ohci; + struct hpsb_packet *packet, *ptmp; + + ohci1394_stop_context(ohci, d->ctrlClear, NULL); + + /* Lock the context, reset it and release it. Move the packets + * that were pending in the context to packet_list and free + * them after releasing the lock. */ + + spin_lock_irqsave(&d->lock, flags); + + list_splice_init(&d->fifo_list, &packet_list); + list_splice_init(&d->pending_list, &packet_list); + + d->branchAddrPtr = NULL; + d->sent_ind = d->prg_ind; + d->free_prgs = d->num_desc; + + spin_unlock_irqrestore(&d->lock, flags); + + if (list_empty(&packet_list)) + return; + + PRINT(KERN_INFO, "AT dma reset ctx=%d, aborting transmission", d->ctx); + + /* Now process subsystem callbacks for the packets from this + * context. */ + list_for_each_entry_safe(packet, ptmp, &packet_list, driver_list) { + list_del_init(&packet->driver_list); + hpsb_packet_sent(ohci->host, packet, ACKX_ABORTED); + } +} + +static void ohci_schedule_iso_tasklets(struct ti_ohci *ohci, + quadlet_t rx_event, + quadlet_t tx_event) +{ + struct ohci1394_iso_tasklet *t; + unsigned long mask; + unsigned long flags; + + spin_lock_irqsave(&ohci->iso_tasklet_list_lock, flags); + + list_for_each_entry(t, &ohci->iso_tasklet_list, link) { + mask = 1 << t->context; + + if (t->type == OHCI_ISO_TRANSMIT) { + if (tx_event & mask) + tasklet_schedule(&t->tasklet); + } else { + /* OHCI_ISO_RECEIVE or OHCI_ISO_MULTICHANNEL_RECEIVE */ + if (rx_event & mask) + tasklet_schedule(&t->tasklet); + } + } + + spin_unlock_irqrestore(&ohci->iso_tasklet_list_lock, flags); +} + +static irqreturn_t ohci_irq_handler(int irq, void *dev_id) +{ + quadlet_t event, node_id; + struct ti_ohci *ohci = (struct ti_ohci *)dev_id; + struct hpsb_host *host = ohci->host; + int phyid = -1, isroot = 0; + unsigned long flags; + + /* Read and clear the interrupt event register. Don't clear + * the busReset event, though. This is done when we get the + * selfIDComplete interrupt. */ + spin_lock_irqsave(&ohci->event_lock, flags); + event = reg_read(ohci, OHCI1394_IntEventClear); + reg_write(ohci, OHCI1394_IntEventClear, event & ~OHCI1394_busReset); + spin_unlock_irqrestore(&ohci->event_lock, flags); + + if (!event) + return IRQ_NONE; + + /* If event is ~(u32)0 cardbus card was ejected. In this case + * we just return, and clean up in the ohci1394_pci_remove + * function. */ + if (event == ~(u32) 0) { + DBGMSG("Device removed."); + return IRQ_NONE; + } + + DBGMSG("IntEvent: %08x", event); + + if (event & OHCI1394_unrecoverableError) { + int ctx; + PRINT(KERN_ERR, "Unrecoverable error!"); + + if (reg_read(ohci, OHCI1394_AsReqTrContextControlSet) & 0x800) + PRINT(KERN_ERR, "Async Req Tx Context died: " + "ctrl[%08x] cmdptr[%08x]", + reg_read(ohci, OHCI1394_AsReqTrContextControlSet), + reg_read(ohci, OHCI1394_AsReqTrCommandPtr)); + + if (reg_read(ohci, OHCI1394_AsRspTrContextControlSet) & 0x800) + PRINT(KERN_ERR, "Async Rsp Tx Context died: " + "ctrl[%08x] cmdptr[%08x]", + reg_read(ohci, OHCI1394_AsRspTrContextControlSet), + reg_read(ohci, OHCI1394_AsRspTrCommandPtr)); + + if (reg_read(ohci, OHCI1394_AsReqRcvContextControlSet) & 0x800) + PRINT(KERN_ERR, "Async Req Rcv Context died: " + "ctrl[%08x] cmdptr[%08x]", + reg_read(ohci, OHCI1394_AsReqRcvContextControlSet), + reg_read(ohci, OHCI1394_AsReqRcvCommandPtr)); + + if (reg_read(ohci, OHCI1394_AsRspRcvContextControlSet) & 0x800) + PRINT(KERN_ERR, "Async Rsp Rcv Context died: " + "ctrl[%08x] cmdptr[%08x]", + reg_read(ohci, OHCI1394_AsRspRcvContextControlSet), + reg_read(ohci, OHCI1394_AsRspRcvCommandPtr)); + + for (ctx = 0; ctx < ohci->nb_iso_xmit_ctx; ctx++) { + if (reg_read(ohci, OHCI1394_IsoXmitContextControlSet + (16 * ctx)) & 0x800) + PRINT(KERN_ERR, "Iso Xmit %d Context died: " + "ctrl[%08x] cmdptr[%08x]", ctx, + reg_read(ohci, OHCI1394_IsoXmitContextControlSet + (16 * ctx)), + reg_read(ohci, OHCI1394_IsoXmitCommandPtr + (16 * ctx))); + } + + for (ctx = 0; ctx < ohci->nb_iso_rcv_ctx; ctx++) { + if (reg_read(ohci, OHCI1394_IsoRcvContextControlSet + (32 * ctx)) & 0x800) + PRINT(KERN_ERR, "Iso Recv %d Context died: " + "ctrl[%08x] cmdptr[%08x] match[%08x]", ctx, + reg_read(ohci, OHCI1394_IsoRcvContextControlSet + (32 * ctx)), + reg_read(ohci, OHCI1394_IsoRcvCommandPtr + (32 * ctx)), + reg_read(ohci, OHCI1394_IsoRcvContextMatch + (32 * ctx))); + } + + event &= ~OHCI1394_unrecoverableError; + } + if (event & OHCI1394_postedWriteErr) { + PRINT(KERN_ERR, "physical posted write error"); + /* no recovery strategy yet, had to involve protocol drivers */ + event &= ~OHCI1394_postedWriteErr; + } + if (event & OHCI1394_cycleTooLong) { + if(printk_ratelimit()) + PRINT(KERN_WARNING, "isochronous cycle too long"); + else + DBGMSG("OHCI1394_cycleTooLong"); + reg_write(ohci, OHCI1394_LinkControlSet, + OHCI1394_LinkControl_CycleMaster); + event &= ~OHCI1394_cycleTooLong; + } + if (event & OHCI1394_cycleInconsistent) { + /* We subscribe to the cycleInconsistent event only to + * clear the corresponding event bit... otherwise, + * isochronous cycleMatch DMA won't work. */ + DBGMSG("OHCI1394_cycleInconsistent"); + event &= ~OHCI1394_cycleInconsistent; + } + if (event & OHCI1394_busReset) { + /* The busReset event bit can't be cleared during the + * selfID phase, so we disable busReset interrupts, to + * avoid burying the cpu in interrupt requests. */ + spin_lock_irqsave(&ohci->event_lock, flags); + reg_write(ohci, OHCI1394_IntMaskClear, OHCI1394_busReset); + + if (ohci->check_busreset) { + int loop_count = 0; + + udelay(10); + + while (reg_read(ohci, OHCI1394_IntEventSet) & OHCI1394_busReset) { + reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_busReset); + + spin_unlock_irqrestore(&ohci->event_lock, flags); + udelay(10); + spin_lock_irqsave(&ohci->event_lock, flags); + + /* The loop counter check is to prevent the driver + * from remaining in this state forever. For the + * initial bus reset, the loop continues for ever + * and the system hangs, until some device is plugged-in + * or out manually into a port! The forced reset seems + * to solve this problem. This mainly effects nForce2. */ + if (loop_count > 10000) { + ohci_devctl(host, RESET_BUS, LONG_RESET); + DBGMSG("Detected bus-reset loop. Forced a bus reset!"); + loop_count = 0; + } + + loop_count++; + } + } + spin_unlock_irqrestore(&ohci->event_lock, flags); + if (!host->in_bus_reset) { + DBGMSG("irq_handler: Bus reset requested"); + + /* Subsystem call */ + hpsb_bus_reset(ohci->host); + } + event &= ~OHCI1394_busReset; + } + if (event & OHCI1394_reqTxComplete) { + struct dma_trm_ctx *d = &ohci->at_req_context; + DBGMSG("Got reqTxComplete interrupt " + "status=0x%08X", reg_read(ohci, d->ctrlSet)); + if (reg_read(ohci, d->ctrlSet) & 0x800) + ohci1394_stop_context(ohci, d->ctrlClear, + "reqTxComplete"); + else + dma_trm_tasklet((unsigned long)d); + //tasklet_schedule(&d->task); + event &= ~OHCI1394_reqTxComplete; + } + if (event & OHCI1394_respTxComplete) { + struct dma_trm_ctx *d = &ohci->at_resp_context; + DBGMSG("Got respTxComplete interrupt " + "status=0x%08X", reg_read(ohci, d->ctrlSet)); + if (reg_read(ohci, d->ctrlSet) & 0x800) + ohci1394_stop_context(ohci, d->ctrlClear, + "respTxComplete"); + else + tasklet_schedule(&d->task); + event &= ~OHCI1394_respTxComplete; + } + if (event & OHCI1394_RQPkt) { + struct dma_rcv_ctx *d = &ohci->ar_req_context; + DBGMSG("Got RQPkt interrupt status=0x%08X", + reg_read(ohci, d->ctrlSet)); + if (reg_read(ohci, d->ctrlSet) & 0x800) + ohci1394_stop_context(ohci, d->ctrlClear, "RQPkt"); + else + tasklet_schedule(&d->task); + event &= ~OHCI1394_RQPkt; + } + if (event & OHCI1394_RSPkt) { + struct dma_rcv_ctx *d = &ohci->ar_resp_context; + DBGMSG("Got RSPkt interrupt status=0x%08X", + reg_read(ohci, d->ctrlSet)); + if (reg_read(ohci, d->ctrlSet) & 0x800) + ohci1394_stop_context(ohci, d->ctrlClear, "RSPkt"); + else + tasklet_schedule(&d->task); + event &= ~OHCI1394_RSPkt; + } + if (event & OHCI1394_isochRx) { + quadlet_t rx_event; + + rx_event = reg_read(ohci, OHCI1394_IsoRecvIntEventSet); + reg_write(ohci, OHCI1394_IsoRecvIntEventClear, rx_event); + ohci_schedule_iso_tasklets(ohci, rx_event, 0); + event &= ~OHCI1394_isochRx; + } + if (event & OHCI1394_isochTx) { + quadlet_t tx_event; + + tx_event = reg_read(ohci, OHCI1394_IsoXmitIntEventSet); + reg_write(ohci, OHCI1394_IsoXmitIntEventClear, tx_event); + ohci_schedule_iso_tasklets(ohci, 0, tx_event); + event &= ~OHCI1394_isochTx; + } + if (event & OHCI1394_selfIDComplete) { + if (host->in_bus_reset) { + node_id = reg_read(ohci, OHCI1394_NodeID); + + if (!(node_id & 0x80000000)) { + PRINT(KERN_ERR, + "SelfID received, but NodeID invalid " + "(probably new bus reset occurred): %08X", + node_id); + goto selfid_not_valid; + } + + phyid = node_id & 0x0000003f; + isroot = (node_id & 0x40000000) != 0; + + DBGMSG("SelfID interrupt received " + "(phyid %d, %s)", phyid, + (isroot ? "root" : "not root")); + + handle_selfid(ohci, host, phyid, isroot); + + /* Clear the bus reset event and re-enable the + * busReset interrupt. */ + spin_lock_irqsave(&ohci->event_lock, flags); + reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_busReset); + reg_write(ohci, OHCI1394_IntMaskSet, OHCI1394_busReset); + spin_unlock_irqrestore(&ohci->event_lock, flags); + + /* Turn on phys dma reception. + * + * TODO: Enable some sort of filtering management. + */ + if (phys_dma) { + reg_write(ohci, OHCI1394_PhyReqFilterHiSet, + 0xffffffff); + reg_write(ohci, OHCI1394_PhyReqFilterLoSet, + 0xffffffff); + } + + DBGMSG("PhyReqFilter=%08x%08x", + reg_read(ohci, OHCI1394_PhyReqFilterHiSet), + reg_read(ohci, OHCI1394_PhyReqFilterLoSet)); + + hpsb_selfid_complete(host, phyid, isroot); + } else + PRINT(KERN_ERR, + "SelfID received outside of bus reset sequence"); + +selfid_not_valid: + event &= ~OHCI1394_selfIDComplete; + } + + /* Make sure we handle everything, just in case we accidentally + * enabled an interrupt that we didn't write a handler for. */ + if (event) + PRINT(KERN_ERR, "Unhandled interrupt(s) 0x%08x", + event); + + return IRQ_HANDLED; +} + +/* Put the buffer back into the dma context */ +static void insert_dma_buffer(struct dma_rcv_ctx *d, int idx) +{ + struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci); + DBGMSG("Inserting dma buf ctx=%d idx=%d", d->ctx, idx); + + d->prg_cpu[idx]->status = cpu_to_le32(d->buf_size); + d->prg_cpu[idx]->branchAddress &= le32_to_cpu(0xfffffff0); + idx = (idx + d->num_desc - 1 ) % d->num_desc; + d->prg_cpu[idx]->branchAddress |= le32_to_cpu(0x00000001); + + /* To avoid a race, ensure 1394 interface hardware sees the inserted + * context program descriptors before it sees the wakeup bit set. */ + wmb(); + + /* wake up the dma context if necessary */ + if (!(reg_read(ohci, d->ctrlSet) & 0x400)) { + PRINT(KERN_INFO, + "Waking dma ctx=%d ... processing is probably too slow", + d->ctx); + } + + /* do this always, to avoid race condition */ + reg_write(ohci, d->ctrlSet, 0x1000); +} + +#define cond_le32_to_cpu(data, noswap) \ + (noswap ? data : le32_to_cpu(data)) + +static const int TCODE_SIZE[16] = {20, 0, 16, -1, 16, 20, 20, 0, + -1, 0, -1, 0, -1, -1, 16, -1}; + +/* + * Determine the length of a packet in the buffer + * Optimization suggested by Pascal Drolet + */ +static inline int packet_length(struct dma_rcv_ctx *d, int idx, + quadlet_t *buf_ptr, int offset, + unsigned char tcode, int noswap) +{ + int length = -1; + + if (d->type == DMA_CTX_ASYNC_REQ || d->type == DMA_CTX_ASYNC_RESP) { + length = TCODE_SIZE[tcode]; + if (length == 0) { + if (offset + 12 >= d->buf_size) { + length = (cond_le32_to_cpu(d->buf_cpu[(idx + 1) % d->num_desc] + [3 - ((d->buf_size - offset) >> 2)], noswap) >> 16); + } else { + length = (cond_le32_to_cpu(buf_ptr[3], noswap) >> 16); + } + length += 20; + } + } else if (d->type == DMA_CTX_ISO) { + /* Assumption: buffer fill mode with header/trailer */ + length = (cond_le32_to_cpu(buf_ptr[0], noswap) >> 16) + 8; + } + + if (length > 0 && length % 4) + length += 4 - (length % 4); + + return length; +} + +/* Tasklet that processes dma receive buffers */ +static void dma_rcv_tasklet (unsigned long data) +{ + struct dma_rcv_ctx *d = (struct dma_rcv_ctx*)data; + struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci); + unsigned int split_left, idx, offset, rescount; + unsigned char tcode; + int length, bytes_left, ack; + unsigned long flags; + quadlet_t *buf_ptr; + char *split_ptr; + char msg[256]; + + spin_lock_irqsave(&d->lock, flags); + + idx = d->buf_ind; + offset = d->buf_offset; + buf_ptr = d->buf_cpu[idx] + offset/4; + + rescount = le32_to_cpu(d->prg_cpu[idx]->status) & 0xffff; + bytes_left = d->buf_size - rescount - offset; + + while (bytes_left > 0) { + tcode = (cond_le32_to_cpu(buf_ptr[0], ohci->no_swap_incoming) >> 4) & 0xf; + + /* packet_length() will return < 4 for an error */ + length = packet_length(d, idx, buf_ptr, offset, tcode, ohci->no_swap_incoming); + + if (length < 4) { /* something is wrong */ + sprintf(msg,"Unexpected tcode 0x%x(0x%08x) in AR ctx=%d, length=%d", + tcode, cond_le32_to_cpu(buf_ptr[0], ohci->no_swap_incoming), + d->ctx, length); + ohci1394_stop_context(ohci, d->ctrlClear, msg); + spin_unlock_irqrestore(&d->lock, flags); + return; + } + + /* The first case is where we have a packet that crosses + * over more than one descriptor. The next case is where + * it's all in the first descriptor. */ + if ((offset + length) > d->buf_size) { + DBGMSG("Split packet rcv'd"); + if (length > d->split_buf_size) { + ohci1394_stop_context(ohci, d->ctrlClear, + "Split packet size exceeded"); + d->buf_ind = idx; + d->buf_offset = offset; + spin_unlock_irqrestore(&d->lock, flags); + return; + } + + if (le32_to_cpu(d->prg_cpu[(idx+1)%d->num_desc]->status) + == d->buf_size) { + /* Other part of packet not written yet. + * this should never happen I think + * anyway we'll get it on the next call. */ + PRINT(KERN_INFO, + "Got only half a packet!"); + d->buf_ind = idx; + d->buf_offset = offset; + spin_unlock_irqrestore(&d->lock, flags); + return; + } + + split_left = length; + split_ptr = (char *)d->spb; + memcpy(split_ptr,buf_ptr,d->buf_size-offset); + split_left -= d->buf_size-offset; + split_ptr += d->buf_size-offset; + insert_dma_buffer(d, idx); + idx = (idx+1) % d->num_desc; + buf_ptr = d->buf_cpu[idx]; + offset=0; + + while (split_left >= d->buf_size) { + memcpy(split_ptr,buf_ptr,d->buf_size); + split_ptr += d->buf_size; + split_left -= d->buf_size; + insert_dma_buffer(d, idx); + idx = (idx+1) % d->num_desc; + buf_ptr = d->buf_cpu[idx]; + } + + if (split_left > 0) { + memcpy(split_ptr, buf_ptr, split_left); + offset = split_left; + buf_ptr += offset/4; + } + } else { + DBGMSG("Single packet rcv'd"); + memcpy(d->spb, buf_ptr, length); + offset += length; + buf_ptr += length/4; + if (offset==d->buf_size) { + insert_dma_buffer(d, idx); + idx = (idx+1) % d->num_desc; + buf_ptr = d->buf_cpu[idx]; + offset=0; + } + } + + /* We get one phy packet to the async descriptor for each + * bus reset. We always ignore it. */ + if (tcode != OHCI1394_TCODE_PHY) { + if (!ohci->no_swap_incoming) + header_le32_to_cpu(d->spb, tcode); + DBGMSG("Packet received from node" + " %d ack=0x%02X spd=%d tcode=0x%X" + " length=%d ctx=%d tlabel=%d", + (d->spb[1]>>16)&0x3f, + (cond_le32_to_cpu(d->spb[length/4-1], ohci->no_swap_incoming)>>16)&0x1f, + (cond_le32_to_cpu(d->spb[length/4-1], ohci->no_swap_incoming)>>21)&0x3, + tcode, length, d->ctx, + (d->spb[0]>>10)&0x3f); + + ack = (((cond_le32_to_cpu(d->spb[length/4-1], ohci->no_swap_incoming)>>16)&0x1f) + == 0x11) ? 1 : 0; + + hpsb_packet_received(ohci->host, d->spb, + length-4, ack); + } +#ifdef OHCI1394_DEBUG + else + PRINT (KERN_DEBUG, "Got phy packet ctx=%d ... discarded", + d->ctx); +#endif + + rescount = le32_to_cpu(d->prg_cpu[idx]->status) & 0xffff; + + bytes_left = d->buf_size - rescount - offset; + + } + + d->buf_ind = idx; + d->buf_offset = offset; + + spin_unlock_irqrestore(&d->lock, flags); +} + +/* Bottom half that processes sent packets */ +static void dma_trm_tasklet (unsigned long data) +{ + struct dma_trm_ctx *d = (struct dma_trm_ctx*)data; + struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci); + struct hpsb_packet *packet, *ptmp; + unsigned long flags; + u32 status, ack; + size_t datasize; + + spin_lock_irqsave(&d->lock, flags); + + list_for_each_entry_safe(packet, ptmp, &d->fifo_list, driver_list) { + datasize = packet->data_size; + if (datasize && packet->type != hpsb_raw) + status = le32_to_cpu( + d->prg_cpu[d->sent_ind]->end.status) >> 16; + else + status = le32_to_cpu( + d->prg_cpu[d->sent_ind]->begin.status) >> 16; + + if (status == 0) + /* this packet hasn't been sent yet*/ + break; + +#ifdef OHCI1394_DEBUG + if (datasize) + if (((le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])>>4)&0xf) == 0xa) + DBGMSG("Stream packet sent to channel %d tcode=0x%X " + "ack=0x%X spd=%d dataLength=%d ctx=%d", + (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])>>8)&0x3f, + (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])>>4)&0xf, + status&0x1f, (status>>5)&0x3, + le32_to_cpu(d->prg_cpu[d->sent_ind]->data[1])>>16, + d->ctx); + else + DBGMSG("Packet sent to node %d tcode=0x%X tLabel=" + "%d ack=0x%X spd=%d dataLength=%d ctx=%d", + (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[1])>>16)&0x3f, + (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])>>4)&0xf, + (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])>>10)&0x3f, + status&0x1f, (status>>5)&0x3, + le32_to_cpu(d->prg_cpu[d->sent_ind]->data[3])>>16, + d->ctx); + else + DBGMSG("Packet sent to node %d tcode=0x%X tLabel=" + "%d ack=0x%X spd=%d data=0x%08X ctx=%d", + (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[1]) + >>16)&0x3f, + (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0]) + >>4)&0xf, + (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0]) + >>10)&0x3f, + status&0x1f, (status>>5)&0x3, + le32_to_cpu(d->prg_cpu[d->sent_ind]->data[3]), + d->ctx); +#endif + + if (status & 0x10) { + ack = status & 0xf; + } else { + switch (status & 0x1f) { + case EVT_NO_STATUS: /* that should never happen */ + case EVT_RESERVED_A: /* that should never happen */ + case EVT_LONG_PACKET: /* that should never happen */ + PRINT(KERN_WARNING, "Received OHCI evt_* error 0x%x", status & 0x1f); + ack = ACKX_SEND_ERROR; + break; + case EVT_MISSING_ACK: + ack = ACKX_TIMEOUT; + break; + case EVT_UNDERRUN: + ack = ACKX_SEND_ERROR; + break; + case EVT_OVERRUN: /* that should never happen */ + PRINT(KERN_WARNING, "Received OHCI evt_* error 0x%x", status & 0x1f); + ack = ACKX_SEND_ERROR; + break; + case EVT_DESCRIPTOR_READ: + case EVT_DATA_READ: + case EVT_DATA_WRITE: + ack = ACKX_SEND_ERROR; + break; + case EVT_BUS_RESET: /* that should never happen */ + PRINT(KERN_WARNING, "Received OHCI evt_* error 0x%x", status & 0x1f); + ack = ACKX_SEND_ERROR; + break; + case EVT_TIMEOUT: + ack = ACKX_TIMEOUT; + break; + case EVT_TCODE_ERR: + ack = ACKX_SEND_ERROR; + break; + case EVT_RESERVED_B: /* that should never happen */ + case EVT_RESERVED_C: /* that should never happen */ + PRINT(KERN_WARNING, "Received OHCI evt_* error 0x%x", status & 0x1f); + ack = ACKX_SEND_ERROR; + break; + case EVT_UNKNOWN: + case EVT_FLUSHED: + ack = ACKX_SEND_ERROR; + break; + default: + PRINT(KERN_ERR, "Unhandled OHCI evt_* error 0x%x", status & 0x1f); + ack = ACKX_SEND_ERROR; + BUG(); + } + } + + list_del_init(&packet->driver_list); + hpsb_packet_sent(ohci->host, packet, ack); + + if (datasize) + pci_unmap_single(ohci->dev, + cpu_to_le32(d->prg_cpu[d->sent_ind]->end.address), + datasize, PCI_DMA_TODEVICE); + + d->sent_ind = (d->sent_ind+1)%d->num_desc; + d->free_prgs++; + } + + dma_trm_flush(ohci, d); + + spin_unlock_irqrestore(&d->lock, flags); +} + +static void free_dma_rcv_ctx(struct dma_rcv_ctx *d) +{ + int i; + struct ti_ohci *ohci = d->ohci; + + if (ohci == NULL) + return; + + DBGMSG("Freeing dma_rcv_ctx %d", d->ctx); + + if (d->buf_cpu) { + for (i=0; inum_desc; i++) + if (d->buf_cpu[i] && d->buf_bus[i]) + pci_free_consistent( + ohci->dev, d->buf_size, + d->buf_cpu[i], d->buf_bus[i]); + kfree(d->buf_cpu); + kfree(d->buf_bus); + } + if (d->prg_cpu) { + for (i=0; inum_desc; i++) + if (d->prg_cpu[i] && d->prg_bus[i]) + pci_pool_free(d->prg_pool, d->prg_cpu[i], + d->prg_bus[i]); + pci_pool_destroy(d->prg_pool); + kfree(d->prg_cpu); + kfree(d->prg_bus); + } + kfree(d->spb); + + /* Mark this context as freed. */ + d->ohci = NULL; +} + +static int +alloc_dma_rcv_ctx(struct ti_ohci *ohci, struct dma_rcv_ctx *d, + enum context_type type, int ctx, int num_desc, + int buf_size, int split_buf_size, int context_base) +{ + int i, len; + static int num_allocs; + static char pool_name[20]; + + d->ohci = ohci; + d->type = type; + d->ctx = ctx; + + d->num_desc = num_desc; + d->buf_size = buf_size; + d->split_buf_size = split_buf_size; + + d->ctrlSet = 0; + d->ctrlClear = 0; + d->cmdPtr = 0; + + d->buf_cpu = kzalloc(d->num_desc * sizeof(*d->buf_cpu), GFP_ATOMIC); + d->buf_bus = kzalloc(d->num_desc * sizeof(*d->buf_bus), GFP_ATOMIC); + + if (d->buf_cpu == NULL || d->buf_bus == NULL) { + PRINT(KERN_ERR, "Failed to allocate %s", "DMA buffer"); + free_dma_rcv_ctx(d); + return -ENOMEM; + } + + d->prg_cpu = kzalloc(d->num_desc * sizeof(*d->prg_cpu), GFP_ATOMIC); + d->prg_bus = kzalloc(d->num_desc * sizeof(*d->prg_bus), GFP_ATOMIC); + + if (d->prg_cpu == NULL || d->prg_bus == NULL) { + PRINT(KERN_ERR, "Failed to allocate %s", "DMA prg"); + free_dma_rcv_ctx(d); + return -ENOMEM; + } + + d->spb = kmalloc(d->split_buf_size, GFP_ATOMIC); + + if (d->spb == NULL) { + PRINT(KERN_ERR, "Failed to allocate %s", "split buffer"); + free_dma_rcv_ctx(d); + return -ENOMEM; + } + + len = sprintf(pool_name, "ohci1394_rcv_prg"); + sprintf(pool_name+len, "%d", num_allocs); + d->prg_pool = pci_pool_create(pool_name, ohci->dev, + sizeof(struct dma_cmd), 4, 0); + if(d->prg_pool == NULL) + { + PRINT(KERN_ERR, "pci_pool_create failed for %s", pool_name); + free_dma_rcv_ctx(d); + return -ENOMEM; + } + num_allocs++; + + for (i=0; inum_desc; i++) { + d->buf_cpu[i] = pci_alloc_consistent(ohci->dev, + d->buf_size, + d->buf_bus+i); + + if (d->buf_cpu[i] != NULL) { + memset(d->buf_cpu[i], 0, d->buf_size); + } else { + PRINT(KERN_ERR, + "Failed to allocate %s", "DMA buffer"); + free_dma_rcv_ctx(d); + return -ENOMEM; + } + + d->prg_cpu[i] = pci_pool_alloc(d->prg_pool, GFP_KERNEL, d->prg_bus+i); + + if (d->prg_cpu[i] != NULL) { + memset(d->prg_cpu[i], 0, sizeof(struct dma_cmd)); + } else { + PRINT(KERN_ERR, + "Failed to allocate %s", "DMA prg"); + free_dma_rcv_ctx(d); + return -ENOMEM; + } + } + + spin_lock_init(&d->lock); + + d->ctrlSet = context_base + OHCI1394_ContextControlSet; + d->ctrlClear = context_base + OHCI1394_ContextControlClear; + d->cmdPtr = context_base + OHCI1394_ContextCommandPtr; + + tasklet_init(&d->task, dma_rcv_tasklet, (unsigned long) d); + return 0; +} + +static void free_dma_trm_ctx(struct dma_trm_ctx *d) +{ + int i; + struct ti_ohci *ohci = d->ohci; + + if (ohci == NULL) + return; + + DBGMSG("Freeing dma_trm_ctx %d", d->ctx); + + if (d->prg_cpu) { + for (i=0; inum_desc; i++) + if (d->prg_cpu[i] && d->prg_bus[i]) + pci_pool_free(d->prg_pool, d->prg_cpu[i], + d->prg_bus[i]); + pci_pool_destroy(d->prg_pool); + kfree(d->prg_cpu); + kfree(d->prg_bus); + } + + /* Mark this context as freed. */ + d->ohci = NULL; +} + +static int +alloc_dma_trm_ctx(struct ti_ohci *ohci, struct dma_trm_ctx *d, + enum context_type type, int ctx, int num_desc, + int context_base) +{ + int i, len; + static char pool_name[20]; + static int num_allocs=0; + + d->ohci = ohci; + d->type = type; + d->ctx = ctx; + d->num_desc = num_desc; + d->ctrlSet = 0; + d->ctrlClear = 0; + d->cmdPtr = 0; + + d->prg_cpu = kzalloc(d->num_desc * sizeof(*d->prg_cpu), GFP_KERNEL); + d->prg_bus = kzalloc(d->num_desc * sizeof(*d->prg_bus), GFP_KERNEL); + + if (d->prg_cpu == NULL || d->prg_bus == NULL) { + PRINT(KERN_ERR, "Failed to allocate %s", "AT DMA prg"); + free_dma_trm_ctx(d); + return -ENOMEM; + } + + len = sprintf(pool_name, "ohci1394_trm_prg"); + sprintf(pool_name+len, "%d", num_allocs); + d->prg_pool = pci_pool_create(pool_name, ohci->dev, + sizeof(struct at_dma_prg), 4, 0); + if (d->prg_pool == NULL) { + PRINT(KERN_ERR, "pci_pool_create failed for %s", pool_name); + free_dma_trm_ctx(d); + return -ENOMEM; + } + num_allocs++; + + for (i = 0; i < d->num_desc; i++) { + d->prg_cpu[i] = pci_pool_alloc(d->prg_pool, GFP_KERNEL, d->prg_bus+i); + + if (d->prg_cpu[i] != NULL) { + memset(d->prg_cpu[i], 0, sizeof(struct at_dma_prg)); + } else { + PRINT(KERN_ERR, + "Failed to allocate %s", "AT DMA prg"); + free_dma_trm_ctx(d); + return -ENOMEM; + } + } + + spin_lock_init(&d->lock); + + /* initialize tasklet */ + d->ctrlSet = context_base + OHCI1394_ContextControlSet; + d->ctrlClear = context_base + OHCI1394_ContextControlClear; + d->cmdPtr = context_base + OHCI1394_ContextCommandPtr; + tasklet_init(&d->task, dma_trm_tasklet, (unsigned long)d); + return 0; +} + +static void ohci_set_hw_config_rom(struct hpsb_host *host, __be32 *config_rom) +{ + struct ti_ohci *ohci = host->hostdata; + + reg_write(ohci, OHCI1394_ConfigROMhdr, be32_to_cpu(config_rom[0])); + reg_write(ohci, OHCI1394_BusOptions, be32_to_cpu(config_rom[2])); + + memcpy(ohci->csr_config_rom_cpu, config_rom, OHCI_CONFIG_ROM_LEN); +} + + +static quadlet_t ohci_hw_csr_reg(struct hpsb_host *host, int reg, + quadlet_t data, quadlet_t compare) +{ + struct ti_ohci *ohci = host->hostdata; + int i; + + reg_write(ohci, OHCI1394_CSRData, data); + reg_write(ohci, OHCI1394_CSRCompareData, compare); + reg_write(ohci, OHCI1394_CSRControl, reg & 0x3); + + for (i = 0; i < OHCI_LOOP_COUNT; i++) { + if (reg_read(ohci, OHCI1394_CSRControl) & 0x80000000) + break; + + mdelay(1); + } + + return reg_read(ohci, OHCI1394_CSRData); +} + +static struct hpsb_host_driver ohci1394_driver = { + .owner = THIS_MODULE, + .name = OHCI1394_DRIVER_NAME, + .set_hw_config_rom = ohci_set_hw_config_rom, + .transmit_packet = ohci_transmit, + .devctl = ohci_devctl, + .isoctl = ohci_isoctl, + .hw_csr_reg = ohci_hw_csr_reg, +}; + +/*********************************** + * PCI Driver Interface functions * + ***********************************/ + +#ifdef CONFIG_PPC_PMAC +static void ohci1394_pmac_on(struct pci_dev *dev) +{ + if (machine_is(powermac)) { + struct device_node *ofn = pci_device_to_OF_node(dev); + + if (ofn) { + pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, ofn, 0, 1); + pmac_call_feature(PMAC_FTR_1394_ENABLE, ofn, 0, 1); + } + } +} + +static void ohci1394_pmac_off(struct pci_dev *dev) +{ + if (machine_is(powermac)) { + struct device_node *ofn = pci_device_to_OF_node(dev); + + if (ofn) { + pmac_call_feature(PMAC_FTR_1394_ENABLE, ofn, 0, 0); + pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, ofn, 0, 0); + } + } +} +#else +#define ohci1394_pmac_on(dev) +#define ohci1394_pmac_off(dev) +#endif /* CONFIG_PPC_PMAC */ + +static int __devinit ohci1394_pci_probe(struct pci_dev *dev, + const struct pci_device_id *ent) +{ + struct hpsb_host *host; + struct ti_ohci *ohci; /* shortcut to currently handled device */ + resource_size_t ohci_base; + int err = -ENOMEM; + + ohci1394_pmac_on(dev); + if (pci_enable_device(dev)) { + PRINT_G(KERN_ERR, "Failed to enable OHCI hardware"); + err = -ENXIO; + goto err; + } + pci_set_master(dev); + + host = hpsb_alloc_host(&ohci1394_driver, sizeof(struct ti_ohci), &dev->dev); + if (!host) { + PRINT_G(KERN_ERR, "Failed to allocate %s", "host structure"); + goto err; + } + ohci = host->hostdata; + ohci->dev = dev; + ohci->host = host; + ohci->init_state = OHCI_INIT_ALLOC_HOST; + host->pdev = dev; + pci_set_drvdata(dev, ohci); + + /* We don't want hardware swapping */ + pci_write_config_dword(dev, OHCI1394_PCI_HCI_Control, 0); + + /* Some oddball Apple controllers do not order the selfid + * properly, so we make up for it here. */ +#ifndef __LITTLE_ENDIAN + /* XXX: Need a better way to check this. I'm wondering if we can + * read the values of the OHCI1394_PCI_HCI_Control and the + * noByteSwapData registers to see if they were not cleared to + * zero. Should this work? Obviously it's not defined what these + * registers will read when they aren't supported. Bleh! */ + if (dev->vendor == PCI_VENDOR_ID_APPLE && + dev->device == PCI_DEVICE_ID_APPLE_UNI_N_FW) { + ohci->no_swap_incoming = 1; + ohci->selfid_swap = 0; + } else + ohci->selfid_swap = 1; +#endif + + +#ifndef PCI_DEVICE_ID_NVIDIA_NFORCE2_FW +#define PCI_DEVICE_ID_NVIDIA_NFORCE2_FW 0x006e +#endif + + /* These chipsets require a bit of extra care when checking after + * a busreset. */ + if ((dev->vendor == PCI_VENDOR_ID_APPLE && + dev->device == PCI_DEVICE_ID_APPLE_UNI_N_FW) || + (dev->vendor == PCI_VENDOR_ID_NVIDIA && + dev->device == PCI_DEVICE_ID_NVIDIA_NFORCE2_FW)) + ohci->check_busreset = 1; + + /* We hardwire the MMIO length, since some CardBus adaptors + * fail to report the right length. Anyway, the ohci spec + * clearly says it's 2kb, so this shouldn't be a problem. */ + ohci_base = pci_resource_start(dev, 0); + if (pci_resource_len(dev, 0) < OHCI1394_REGISTER_SIZE) + PRINT(KERN_WARNING, "PCI resource length of 0x%llx too small!", + (unsigned long long)pci_resource_len(dev, 0)); + + if (!request_mem_region(ohci_base, OHCI1394_REGISTER_SIZE, + OHCI1394_DRIVER_NAME)) { + PRINT_G(KERN_ERR, "MMIO resource (0x%llx - 0x%llx) unavailable", + (unsigned long long)ohci_base, + (unsigned long long)ohci_base + OHCI1394_REGISTER_SIZE); + goto err; + } + ohci->init_state = OHCI_INIT_HAVE_MEM_REGION; + + ohci->registers = ioremap(ohci_base, OHCI1394_REGISTER_SIZE); + if (ohci->registers == NULL) { + PRINT_G(KERN_ERR, "Failed to remap registers"); + err = -ENXIO; + goto err; + } + ohci->init_state = OHCI_INIT_HAVE_IOMAPPING; + DBGMSG("Remapped memory spaces reg 0x%p", ohci->registers); + + /* csr_config rom allocation */ + ohci->csr_config_rom_cpu = + pci_alloc_consistent(ohci->dev, OHCI_CONFIG_ROM_LEN, + &ohci->csr_config_rom_bus); + if (ohci->csr_config_rom_cpu == NULL) { + PRINT_G(KERN_ERR, "Failed to allocate %s", "buffer config rom"); + goto err; + } + ohci->init_state = OHCI_INIT_HAVE_CONFIG_ROM_BUFFER; + + /* self-id dma buffer allocation */ + ohci->selfid_buf_cpu = + pci_alloc_consistent(ohci->dev, OHCI1394_SI_DMA_BUF_SIZE, + &ohci->selfid_buf_bus); + if (ohci->selfid_buf_cpu == NULL) { + PRINT_G(KERN_ERR, "Failed to allocate %s", "self-ID buffer"); + goto err; + } + ohci->init_state = OHCI_INIT_HAVE_SELFID_BUFFER; + + if ((unsigned long)ohci->selfid_buf_cpu & 0x1fff) + PRINT(KERN_INFO, "SelfID buffer %p is not aligned on " + "8Kb boundary... may cause problems on some CXD3222 chip", + ohci->selfid_buf_cpu); + + /* No self-id errors at startup */ + ohci->self_id_errors = 0; + + ohci->init_state = OHCI_INIT_HAVE_TXRX_BUFFERS__MAYBE; + /* AR DMA request context allocation */ + if (alloc_dma_rcv_ctx(ohci, &ohci->ar_req_context, + DMA_CTX_ASYNC_REQ, 0, AR_REQ_NUM_DESC, + AR_REQ_BUF_SIZE, AR_REQ_SPLIT_BUF_SIZE, + OHCI1394_AsReqRcvContextBase) < 0) { + PRINT_G(KERN_ERR, "Failed to allocate %s", "AR Req context"); + goto err; + } + /* AR DMA response context allocation */ + if (alloc_dma_rcv_ctx(ohci, &ohci->ar_resp_context, + DMA_CTX_ASYNC_RESP, 0, AR_RESP_NUM_DESC, + AR_RESP_BUF_SIZE, AR_RESP_SPLIT_BUF_SIZE, + OHCI1394_AsRspRcvContextBase) < 0) { + PRINT_G(KERN_ERR, "Failed to allocate %s", "AR Resp context"); + goto err; + } + /* AT DMA request context */ + if (alloc_dma_trm_ctx(ohci, &ohci->at_req_context, + DMA_CTX_ASYNC_REQ, 0, AT_REQ_NUM_DESC, + OHCI1394_AsReqTrContextBase) < 0) { + PRINT_G(KERN_ERR, "Failed to allocate %s", "AT Req context"); + goto err; + } + /* AT DMA response context */ + if (alloc_dma_trm_ctx(ohci, &ohci->at_resp_context, + DMA_CTX_ASYNC_RESP, 1, AT_RESP_NUM_DESC, + OHCI1394_AsRspTrContextBase) < 0) { + PRINT_G(KERN_ERR, "Failed to allocate %s", "AT Resp context"); + goto err; + } + /* Start off with a soft reset, to clear everything to a sane + * state. */ + ohci_soft_reset(ohci); + + /* Now enable LPS, which we need in order to start accessing + * most of the registers. In fact, on some cards (ALI M5251), + * accessing registers in the SClk domain without LPS enabled + * will lock up the machine. */ + reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_LPS); + + /* Disable and clear interrupts */ + reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff); + reg_write(ohci, OHCI1394_IntMaskClear, 0xffffffff); + + /* Flush MMIO writes and wait to make sure we have full link enabled. */ + reg_read(ohci, OHCI1394_Version); + msleep(50); + + /* Determine the number of available IR and IT contexts. */ + ohci->nb_iso_rcv_ctx = + get_nb_iso_ctx(ohci, OHCI1394_IsoRecvIntMaskSet); + ohci->nb_iso_xmit_ctx = + get_nb_iso_ctx(ohci, OHCI1394_IsoXmitIntMaskSet); + + /* Set the usage bits for non-existent contexts so they can't + * be allocated */ + ohci->ir_ctx_usage = ~0 << ohci->nb_iso_rcv_ctx; + ohci->it_ctx_usage = ~0 << ohci->nb_iso_xmit_ctx; + + INIT_LIST_HEAD(&ohci->iso_tasklet_list); + spin_lock_init(&ohci->iso_tasklet_list_lock); + ohci->ISO_channel_usage = 0; + spin_lock_init(&ohci->IR_channel_lock); + + spin_lock_init(&ohci->event_lock); + + /* + * interrupts are disabled, all right, but... due to IRQF_SHARED we + * might get called anyway. We'll see no event, of course, but + * we need to get to that "no event", so enough should be initialized + * by that point. + */ + err = request_irq(dev->irq, ohci_irq_handler, IRQF_SHARED, + OHCI1394_DRIVER_NAME, ohci); + if (err) { + PRINT_G(KERN_ERR, "Failed to allocate interrupt %d", dev->irq); + goto err; + } + ohci->init_state = OHCI_INIT_HAVE_IRQ; + ohci_initialize(ohci); + + /* Set certain csr values */ + host->csr.guid_hi = reg_read(ohci, OHCI1394_GUIDHi); + host->csr.guid_lo = reg_read(ohci, OHCI1394_GUIDLo); + host->csr.cyc_clk_acc = 100; /* how do we determine clk accuracy? */ + host->csr.max_rec = (reg_read(ohci, OHCI1394_BusOptions) >> 12) & 0xf; + host->csr.lnk_spd = reg_read(ohci, OHCI1394_BusOptions) & 0x7; + + if (phys_dma) { + host->low_addr_space = + (u64) reg_read(ohci, OHCI1394_PhyUpperBound) << 16; + if (!host->low_addr_space) + host->low_addr_space = OHCI1394_PHYS_UPPER_BOUND_FIXED; + } + host->middle_addr_space = OHCI1394_MIDDLE_ADDRESS_SPACE; + + /* Tell the highlevel this host is ready */ + if (hpsb_add_host(host)) { + PRINT_G(KERN_ERR, "Failed to register host with highlevel"); + goto err; + } + ohci->init_state = OHCI_INIT_DONE; + + return 0; +err: + ohci1394_pci_remove(dev); + return err; +} + +static void ohci1394_pci_remove(struct pci_dev *dev) +{ + struct ti_ohci *ohci; + struct device *device; + + ohci = pci_get_drvdata(dev); + if (!ohci) + goto out; + + device = get_device(&ohci->host->device); + + switch (ohci->init_state) { + case OHCI_INIT_DONE: + hpsb_remove_host(ohci->host); + + /* Clear out BUS Options */ + reg_write(ohci, OHCI1394_ConfigROMhdr, 0); + reg_write(ohci, OHCI1394_BusOptions, + (reg_read(ohci, OHCI1394_BusOptions) & 0x0000f007) | + 0x00ff0000); + memset(ohci->csr_config_rom_cpu, 0, OHCI_CONFIG_ROM_LEN); + + case OHCI_INIT_HAVE_IRQ: + /* Clear interrupt registers */ + reg_write(ohci, OHCI1394_IntMaskClear, 0xffffffff); + reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff); + reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 0xffffffff); + reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 0xffffffff); + reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 0xffffffff); + reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 0xffffffff); + + /* Disable IRM Contender */ + set_phy_reg(ohci, 4, ~0xc0 & get_phy_reg(ohci, 4)); + + /* Clear link control register */ + reg_write(ohci, OHCI1394_LinkControlClear, 0xffffffff); + + /* Let all other nodes know to ignore us */ + ohci_devctl(ohci->host, RESET_BUS, LONG_RESET_NO_FORCE_ROOT); + + /* Soft reset before we start - this disables + * interrupts and clears linkEnable and LPS. */ + ohci_soft_reset(ohci); + free_irq(dev->irq, ohci); + + case OHCI_INIT_HAVE_TXRX_BUFFERS__MAYBE: + /* The ohci_soft_reset() stops all DMA contexts, so we + * dont need to do this. */ + free_dma_rcv_ctx(&ohci->ar_req_context); + free_dma_rcv_ctx(&ohci->ar_resp_context); + free_dma_trm_ctx(&ohci->at_req_context); + free_dma_trm_ctx(&ohci->at_resp_context); + + case OHCI_INIT_HAVE_SELFID_BUFFER: + pci_free_consistent(dev, OHCI1394_SI_DMA_BUF_SIZE, + ohci->selfid_buf_cpu, + ohci->selfid_buf_bus); + + case OHCI_INIT_HAVE_CONFIG_ROM_BUFFER: + pci_free_consistent(dev, OHCI_CONFIG_ROM_LEN, + ohci->csr_config_rom_cpu, + ohci->csr_config_rom_bus); + + case OHCI_INIT_HAVE_IOMAPPING: + iounmap(ohci->registers); + + case OHCI_INIT_HAVE_MEM_REGION: + release_mem_region(pci_resource_start(dev, 0), + OHCI1394_REGISTER_SIZE); + + case OHCI_INIT_ALLOC_HOST: + pci_set_drvdata(dev, NULL); + } + + if (device) + put_device(device); +out: + ohci1394_pmac_off(dev); +} + +#ifdef CONFIG_PM +static int ohci1394_pci_suspend(struct pci_dev *dev, pm_message_t state) +{ + int err; + struct ti_ohci *ohci = pci_get_drvdata(dev); + + if (!ohci) { + printk(KERN_ERR "%s: tried to suspend nonexisting host\n", + OHCI1394_DRIVER_NAME); + return -ENXIO; + } + DBGMSG("suspend called"); + + /* Clear the async DMA contexts and stop using the controller */ + hpsb_bus_reset(ohci->host); + + /* See ohci1394_pci_remove() for comments on this sequence */ + reg_write(ohci, OHCI1394_ConfigROMhdr, 0); + reg_write(ohci, OHCI1394_BusOptions, + (reg_read(ohci, OHCI1394_BusOptions) & 0x0000f007) | + 0x00ff0000); + reg_write(ohci, OHCI1394_IntMaskClear, 0xffffffff); + reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff); + reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 0xffffffff); + reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 0xffffffff); + reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 0xffffffff); + reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 0xffffffff); + set_phy_reg(ohci, 4, ~0xc0 & get_phy_reg(ohci, 4)); + reg_write(ohci, OHCI1394_LinkControlClear, 0xffffffff); + ohci_devctl(ohci->host, RESET_BUS, LONG_RESET_NO_FORCE_ROOT); + ohci_soft_reset(ohci); + + free_irq(dev->irq, ohci); + err = pci_save_state(dev); + if (err) { + PRINT(KERN_ERR, "pci_save_state failed with %d", err); + return err; + } + err = pci_set_power_state(dev, pci_choose_state(dev, state)); + if (err) + DBGMSG("pci_set_power_state failed with %d", err); + ohci1394_pmac_off(dev); + + return 0; +} + +static int ohci1394_pci_resume(struct pci_dev *dev) +{ + int err; + struct ti_ohci *ohci = pci_get_drvdata(dev); + + if (!ohci) { + printk(KERN_ERR "%s: tried to resume nonexisting host\n", + OHCI1394_DRIVER_NAME); + return -ENXIO; + } + DBGMSG("resume called"); + + ohci1394_pmac_on(dev); + pci_set_power_state(dev, PCI_D0); + pci_restore_state(dev); + err = pci_enable_device(dev); + if (err) { + PRINT(KERN_ERR, "pci_enable_device failed with %d", err); + return err; + } + + /* See ohci1394_pci_probe() for comments on this sequence */ + ohci_soft_reset(ohci); + reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_LPS); + reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff); + reg_write(ohci, OHCI1394_IntMaskClear, 0xffffffff); + reg_read(ohci, OHCI1394_Version); + msleep(50); + + err = request_irq(dev->irq, ohci_irq_handler, IRQF_SHARED, + OHCI1394_DRIVER_NAME, ohci); + if (err) { + PRINT_G(KERN_ERR, "Failed to allocate interrupt %d", dev->irq); + return err; + } + + ohci_initialize(ohci); + + hpsb_resume_host(ohci->host); + return 0; +} +#endif /* CONFIG_PM */ + +static struct pci_device_id ohci1394_pci_tbl[] = { + { + .class = PCI_CLASS_SERIAL_FIREWIRE_OHCI, + .class_mask = PCI_ANY_ID, + .vendor = PCI_ANY_ID, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { 0, }, +}; + +MODULE_DEVICE_TABLE(pci, ohci1394_pci_tbl); + +static struct pci_driver ohci1394_pci_driver = { + .name = OHCI1394_DRIVER_NAME, + .id_table = ohci1394_pci_tbl, + .probe = ohci1394_pci_probe, + .remove = ohci1394_pci_remove, +#ifdef CONFIG_PM + .resume = ohci1394_pci_resume, + .suspend = ohci1394_pci_suspend, +#endif +}; + +/*********************************** + * OHCI1394 Video Interface * + ***********************************/ + +/* essentially the only purpose of this code is to allow another + module to hook into ohci's interrupt handler */ + +/* returns zero if successful, one if DMA context is locked up */ +int ohci1394_stop_context(struct ti_ohci *ohci, int reg, char *msg) +{ + int i=0; + + /* stop the channel program if it's still running */ + reg_write(ohci, reg, 0x8000); + + /* Wait until it effectively stops */ + while (reg_read(ohci, reg) & 0x400) { + i++; + if (i>5000) { + PRINT(KERN_ERR, + "Runaway loop while stopping context: %s...", msg ? msg : ""); + return 1; + } + + mb(); + udelay(10); + } + if (msg) PRINT(KERN_ERR, "%s: dma prg stopped", msg); + return 0; +} + +void ohci1394_init_iso_tasklet(struct ohci1394_iso_tasklet *tasklet, int type, + void (*func)(unsigned long), unsigned long data) +{ + tasklet_init(&tasklet->tasklet, func, data); + tasklet->type = type; + /* We init the tasklet->link field, so we can list_del() it + * without worrying whether it was added to the list or not. */ + INIT_LIST_HEAD(&tasklet->link); +} + +int ohci1394_register_iso_tasklet(struct ti_ohci *ohci, + struct ohci1394_iso_tasklet *tasklet) +{ + unsigned long flags, *usage; + int n, i, r = -EBUSY; + + if (tasklet->type == OHCI_ISO_TRANSMIT) { + n = ohci->nb_iso_xmit_ctx; + usage = &ohci->it_ctx_usage; + } + else { + n = ohci->nb_iso_rcv_ctx; + usage = &ohci->ir_ctx_usage; + + /* only one receive context can be multichannel (OHCI sec 10.4.1) */ + if (tasklet->type == OHCI_ISO_MULTICHANNEL_RECEIVE) { + if (test_and_set_bit(0, &ohci->ir_multichannel_used)) { + return r; + } + } + } + + spin_lock_irqsave(&ohci->iso_tasklet_list_lock, flags); + + for (i = 0; i < n; i++) + if (!test_and_set_bit(i, usage)) { + tasklet->context = i; + list_add_tail(&tasklet->link, &ohci->iso_tasklet_list); + r = 0; + break; + } + + spin_unlock_irqrestore(&ohci->iso_tasklet_list_lock, flags); + + return r; +} + +void ohci1394_unregister_iso_tasklet(struct ti_ohci *ohci, + struct ohci1394_iso_tasklet *tasklet) +{ + unsigned long flags; + + tasklet_kill(&tasklet->tasklet); + + spin_lock_irqsave(&ohci->iso_tasklet_list_lock, flags); + + if (tasklet->type == OHCI_ISO_TRANSMIT) + clear_bit(tasklet->context, &ohci->it_ctx_usage); + else { + clear_bit(tasklet->context, &ohci->ir_ctx_usage); + + if (tasklet->type == OHCI_ISO_MULTICHANNEL_RECEIVE) { + clear_bit(0, &ohci->ir_multichannel_used); + } + } + + list_del(&tasklet->link); + + spin_unlock_irqrestore(&ohci->iso_tasklet_list_lock, flags); +} + +EXPORT_SYMBOL(ohci1394_stop_context); +EXPORT_SYMBOL(ohci1394_init_iso_tasklet); +EXPORT_SYMBOL(ohci1394_register_iso_tasklet); +EXPORT_SYMBOL(ohci1394_unregister_iso_tasklet); + +/*********************************** + * General module initialization * + ***********************************/ + +MODULE_AUTHOR("Sebastien Rougeaux "); +MODULE_DESCRIPTION("Driver for PCI OHCI IEEE-1394 controllers"); +MODULE_LICENSE("GPL"); + +static void __exit ohci1394_cleanup (void) +{ + pci_unregister_driver(&ohci1394_pci_driver); +} + +static int __init ohci1394_init(void) +{ + return pci_register_driver(&ohci1394_pci_driver); +} + +module_init(ohci1394_init); +module_exit(ohci1394_cleanup); diff --git a/trunk/drivers/ieee1394/ohci1394.h b/trunk/drivers/ieee1394/ohci1394.h new file mode 100644 index 000000000000..7fb8ab9780ae --- /dev/null +++ b/trunk/drivers/ieee1394/ohci1394.h @@ -0,0 +1,453 @@ +/* + * ohci1394.h - driver for OHCI 1394 boards + * Copyright (C)1999,2000 Sebastien Rougeaux + * Gord Peters + * + * 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. + */ + +#ifndef _OHCI1394_H +#define _OHCI1394_H + +#include "ieee1394_types.h" +#include + +#define OHCI1394_DRIVER_NAME "ohci1394" + +#define OHCI1394_MAX_AT_REQ_RETRIES 0xf +#define OHCI1394_MAX_AT_RESP_RETRIES 0x2 +#define OHCI1394_MAX_PHYS_RESP_RETRIES 0x8 +#define OHCI1394_MAX_SELF_ID_ERRORS 16 + +#define AR_REQ_NUM_DESC 4 /* number of AR req descriptors */ +#define AR_REQ_BUF_SIZE PAGE_SIZE /* size of AR req buffers */ +#define AR_REQ_SPLIT_BUF_SIZE PAGE_SIZE /* split packet buffer */ + +#define AR_RESP_NUM_DESC 4 /* number of AR resp descriptors */ +#define AR_RESP_BUF_SIZE PAGE_SIZE /* size of AR resp buffers */ +#define AR_RESP_SPLIT_BUF_SIZE PAGE_SIZE /* split packet buffer */ + +#define IR_NUM_DESC 16 /* number of IR descriptors */ +#define IR_BUF_SIZE PAGE_SIZE /* 4096 bytes/buffer */ +#define IR_SPLIT_BUF_SIZE PAGE_SIZE /* split packet buffer */ + +#define IT_NUM_DESC 16 /* number of IT descriptors */ + +#define AT_REQ_NUM_DESC 32 /* number of AT req descriptors */ +#define AT_RESP_NUM_DESC 32 /* number of AT resp descriptors */ + +#define OHCI_LOOP_COUNT 100 /* Number of loops for reg read waits */ + +#define OHCI_CONFIG_ROM_LEN 1024 /* Length of the mapped configrom space */ + +#define OHCI1394_SI_DMA_BUF_SIZE 8192 /* length of the selfid buffer */ + +/* PCI configuration space addresses */ +#define OHCI1394_PCI_HCI_Control 0x40 + +struct dma_cmd { + u32 control; + u32 address; + u32 branchAddress; + u32 status; +}; + +/* + * FIXME: + * It is important that a single at_dma_prg does not cross a page boundary + * The proper way to do it would be to do the check dynamically as the + * programs are inserted into the AT fifo. + */ +struct at_dma_prg { + struct dma_cmd begin; + quadlet_t data[4]; + struct dma_cmd end; + quadlet_t pad[4]; /* FIXME: quick hack for memory alignment */ +}; + +/* identify whether a DMA context is asynchronous or isochronous */ +enum context_type { DMA_CTX_ASYNC_REQ, DMA_CTX_ASYNC_RESP, DMA_CTX_ISO }; + +/* DMA receive context */ +struct dma_rcv_ctx { + struct ti_ohci *ohci; + enum context_type type; + int ctx; + unsigned int num_desc; + + unsigned int buf_size; + unsigned int split_buf_size; + + /* dma block descriptors */ + struct dma_cmd **prg_cpu; + dma_addr_t *prg_bus; + struct pci_pool *prg_pool; + + /* dma buffers */ + quadlet_t **buf_cpu; + dma_addr_t *buf_bus; + + unsigned int buf_ind; + unsigned int buf_offset; + quadlet_t *spb; + spinlock_t lock; + struct tasklet_struct task; + int ctrlClear; + int ctrlSet; + int cmdPtr; + int ctxtMatch; +}; + +/* DMA transmit context */ +struct dma_trm_ctx { + struct ti_ohci *ohci; + enum context_type type; + int ctx; + unsigned int num_desc; + + /* dma block descriptors */ + struct at_dma_prg **prg_cpu; + dma_addr_t *prg_bus; + struct pci_pool *prg_pool; + + unsigned int prg_ind; + unsigned int sent_ind; + int free_prgs; + quadlet_t *branchAddrPtr; + + /* list of packets inserted in the AT FIFO */ + struct list_head fifo_list; + + /* list of pending packets to be inserted in the AT FIFO */ + struct list_head pending_list; + + spinlock_t lock; + struct tasklet_struct task; + int ctrlClear; + int ctrlSet; + int cmdPtr; +}; + +struct ohci1394_iso_tasklet { + struct tasklet_struct tasklet; + struct list_head link; + int context; + enum { OHCI_ISO_TRANSMIT, OHCI_ISO_RECEIVE, + OHCI_ISO_MULTICHANNEL_RECEIVE } type; +}; + +struct ti_ohci { + struct pci_dev *dev; + + enum { + OHCI_INIT_ALLOC_HOST, + OHCI_INIT_HAVE_MEM_REGION, + OHCI_INIT_HAVE_IOMAPPING, + OHCI_INIT_HAVE_CONFIG_ROM_BUFFER, + OHCI_INIT_HAVE_SELFID_BUFFER, + OHCI_INIT_HAVE_TXRX_BUFFERS__MAYBE, + OHCI_INIT_HAVE_IRQ, + OHCI_INIT_DONE, + } init_state; + + /* remapped memory spaces */ + void __iomem *registers; + + /* dma buffer for self-id packets */ + quadlet_t *selfid_buf_cpu; + dma_addr_t selfid_buf_bus; + + /* buffer for csr config rom */ + quadlet_t *csr_config_rom_cpu; + dma_addr_t csr_config_rom_bus; + int csr_config_rom_length; + + unsigned int max_packet_size; + + /* async receive */ + struct dma_rcv_ctx ar_resp_context; + struct dma_rcv_ctx ar_req_context; + + /* async transmit */ + struct dma_trm_ctx at_resp_context; + struct dma_trm_ctx at_req_context; + + /* iso receive */ + int nb_iso_rcv_ctx; + unsigned long ir_ctx_usage; /* use test_and_set_bit() for atomicity */ + unsigned long ir_multichannel_used; /* ditto */ + spinlock_t IR_channel_lock; + + /* iso transmit */ + int nb_iso_xmit_ctx; + unsigned long it_ctx_usage; /* use test_and_set_bit() for atomicity */ + + u64 ISO_channel_usage; + + /* IEEE-1394 part follows */ + struct hpsb_host *host; + + int phyid, isroot; + + spinlock_t phy_reg_lock; + spinlock_t event_lock; + + int self_id_errors; + + /* Tasklets for iso receive and transmit, used by video1394 + * and dv1394 */ + struct list_head iso_tasklet_list; + spinlock_t iso_tasklet_list_lock; + + /* Swap the selfid buffer? */ + unsigned int selfid_swap:1; + /* Some Apple chipset seem to swap incoming headers for us */ + unsigned int no_swap_incoming:1; + + /* Force extra paranoia checking on bus-reset handling */ + unsigned int check_busreset:1; +}; + +static inline int cross_bound(unsigned long addr, unsigned int size) +{ + if (size == 0) + return 0; + + if (size > PAGE_SIZE) + return 1; + + if (addr >> PAGE_SHIFT != (addr + size - 1) >> PAGE_SHIFT) + return 1; + + return 0; +} + +/* + * Register read and write helper functions. + */ +static inline void reg_write(const struct ti_ohci *ohci, int offset, u32 data) +{ + writel(data, ohci->registers + offset); +} + +static inline u32 reg_read(const struct ti_ohci *ohci, int offset) +{ + return readl(ohci->registers + offset); +} + + +/* 2 KiloBytes of register space */ +#define OHCI1394_REGISTER_SIZE 0x800 + +/* Offsets relative to context bases defined below */ + +#define OHCI1394_ContextControlSet 0x000 +#define OHCI1394_ContextControlClear 0x004 +#define OHCI1394_ContextCommandPtr 0x00C + +/* register map */ +#define OHCI1394_Version 0x000 +#define OHCI1394_GUID_ROM 0x004 +#define OHCI1394_ATRetries 0x008 +#define OHCI1394_CSRData 0x00C +#define OHCI1394_CSRCompareData 0x010 +#define OHCI1394_CSRControl 0x014 +#define OHCI1394_ConfigROMhdr 0x018 +#define OHCI1394_BusID 0x01C +#define OHCI1394_BusOptions 0x020 +#define OHCI1394_GUIDHi 0x024 +#define OHCI1394_GUIDLo 0x028 +#define OHCI1394_ConfigROMmap 0x034 +#define OHCI1394_PostedWriteAddressLo 0x038 +#define OHCI1394_PostedWriteAddressHi 0x03C +#define OHCI1394_VendorID 0x040 +#define OHCI1394_HCControlSet 0x050 +#define OHCI1394_HCControlClear 0x054 +#define OHCI1394_HCControl_noByteSwap 0x40000000 +#define OHCI1394_HCControl_programPhyEnable 0x00800000 +#define OHCI1394_HCControl_aPhyEnhanceEnable 0x00400000 +#define OHCI1394_HCControl_LPS 0x00080000 +#define OHCI1394_HCControl_postedWriteEnable 0x00040000 +#define OHCI1394_HCControl_linkEnable 0x00020000 +#define OHCI1394_HCControl_softReset 0x00010000 +#define OHCI1394_SelfIDBuffer 0x064 +#define OHCI1394_SelfIDCount 0x068 +#define OHCI1394_IRMultiChanMaskHiSet 0x070 +#define OHCI1394_IRMultiChanMaskHiClear 0x074 +#define OHCI1394_IRMultiChanMaskLoSet 0x078 +#define OHCI1394_IRMultiChanMaskLoClear 0x07C +#define OHCI1394_IntEventSet 0x080 +#define OHCI1394_IntEventClear 0x084 +#define OHCI1394_IntMaskSet 0x088 +#define OHCI1394_IntMaskClear 0x08C +#define OHCI1394_IsoXmitIntEventSet 0x090 +#define OHCI1394_IsoXmitIntEventClear 0x094 +#define OHCI1394_IsoXmitIntMaskSet 0x098 +#define OHCI1394_IsoXmitIntMaskClear 0x09C +#define OHCI1394_IsoRecvIntEventSet 0x0A0 +#define OHCI1394_IsoRecvIntEventClear 0x0A4 +#define OHCI1394_IsoRecvIntMaskSet 0x0A8 +#define OHCI1394_IsoRecvIntMaskClear 0x0AC +#define OHCI1394_InitialBandwidthAvailable 0x0B0 +#define OHCI1394_InitialChannelsAvailableHi 0x0B4 +#define OHCI1394_InitialChannelsAvailableLo 0x0B8 +#define OHCI1394_FairnessControl 0x0DC +#define OHCI1394_LinkControlSet 0x0E0 +#define OHCI1394_LinkControlClear 0x0E4 +#define OHCI1394_LinkControl_RcvSelfID 0x00000200 +#define OHCI1394_LinkControl_RcvPhyPkt 0x00000400 +#define OHCI1394_LinkControl_CycleTimerEnable 0x00100000 +#define OHCI1394_LinkControl_CycleMaster 0x00200000 +#define OHCI1394_LinkControl_CycleSource 0x00400000 +#define OHCI1394_NodeID 0x0E8 +#define OHCI1394_PhyControl 0x0EC +#define OHCI1394_IsochronousCycleTimer 0x0F0 +#define OHCI1394_AsReqFilterHiSet 0x100 +#define OHCI1394_AsReqFilterHiClear 0x104 +#define OHCI1394_AsReqFilterLoSet 0x108 +#define OHCI1394_AsReqFilterLoClear 0x10C +#define OHCI1394_PhyReqFilterHiSet 0x110 +#define OHCI1394_PhyReqFilterHiClear 0x114 +#define OHCI1394_PhyReqFilterLoSet 0x118 +#define OHCI1394_PhyReqFilterLoClear 0x11C +#define OHCI1394_PhyUpperBound 0x120 + +#define OHCI1394_AsReqTrContextBase 0x180 +#define OHCI1394_AsReqTrContextControlSet 0x180 +#define OHCI1394_AsReqTrContextControlClear 0x184 +#define OHCI1394_AsReqTrCommandPtr 0x18C + +#define OHCI1394_AsRspTrContextBase 0x1A0 +#define OHCI1394_AsRspTrContextControlSet 0x1A0 +#define OHCI1394_AsRspTrContextControlClear 0x1A4 +#define OHCI1394_AsRspTrCommandPtr 0x1AC + +#define OHCI1394_AsReqRcvContextBase 0x1C0 +#define OHCI1394_AsReqRcvContextControlSet 0x1C0 +#define OHCI1394_AsReqRcvContextControlClear 0x1C4 +#define OHCI1394_AsReqRcvCommandPtr 0x1CC + +#define OHCI1394_AsRspRcvContextBase 0x1E0 +#define OHCI1394_AsRspRcvContextControlSet 0x1E0 +#define OHCI1394_AsRspRcvContextControlClear 0x1E4 +#define OHCI1394_AsRspRcvCommandPtr 0x1EC + +/* Isochronous transmit registers */ +/* Add (16 * n) for context n */ +#define OHCI1394_IsoXmitContextBase 0x200 +#define OHCI1394_IsoXmitContextControlSet 0x200 +#define OHCI1394_IsoXmitContextControlClear 0x204 +#define OHCI1394_IsoXmitCommandPtr 0x20C + +/* Isochronous receive registers */ +/* Add (32 * n) for context n */ +#define OHCI1394_IsoRcvContextBase 0x400 +#define OHCI1394_IsoRcvContextControlSet 0x400 +#define OHCI1394_IsoRcvContextControlClear 0x404 +#define OHCI1394_IsoRcvCommandPtr 0x40C +#define OHCI1394_IsoRcvContextMatch 0x410 + +/* Interrupts Mask/Events */ + +#define OHCI1394_reqTxComplete 0x00000001 +#define OHCI1394_respTxComplete 0x00000002 +#define OHCI1394_ARRQ 0x00000004 +#define OHCI1394_ARRS 0x00000008 +#define OHCI1394_RQPkt 0x00000010 +#define OHCI1394_RSPkt 0x00000020 +#define OHCI1394_isochTx 0x00000040 +#define OHCI1394_isochRx 0x00000080 +#define OHCI1394_postedWriteErr 0x00000100 +#define OHCI1394_lockRespErr 0x00000200 +#define OHCI1394_selfIDComplete 0x00010000 +#define OHCI1394_busReset 0x00020000 +#define OHCI1394_phy 0x00080000 +#define OHCI1394_cycleSynch 0x00100000 +#define OHCI1394_cycle64Seconds 0x00200000 +#define OHCI1394_cycleLost 0x00400000 +#define OHCI1394_cycleInconsistent 0x00800000 +#define OHCI1394_unrecoverableError 0x01000000 +#define OHCI1394_cycleTooLong 0x02000000 +#define OHCI1394_phyRegRcvd 0x04000000 +#define OHCI1394_masterIntEnable 0x80000000 + +/* DMA Control flags */ +#define DMA_CTL_OUTPUT_MORE 0x00000000 +#define DMA_CTL_OUTPUT_LAST 0x10000000 +#define DMA_CTL_INPUT_MORE 0x20000000 +#define DMA_CTL_INPUT_LAST 0x30000000 +#define DMA_CTL_UPDATE 0x08000000 +#define DMA_CTL_IMMEDIATE 0x02000000 +#define DMA_CTL_IRQ 0x00300000 +#define DMA_CTL_BRANCH 0x000c0000 +#define DMA_CTL_WAIT 0x00030000 + +/* OHCI evt_* error types, table 3-2 of the OHCI 1.1 spec. */ +#define EVT_NO_STATUS 0x0 /* No event status */ +#define EVT_RESERVED_A 0x1 /* Reserved, not used !!! */ +#define EVT_LONG_PACKET 0x2 /* The revc data was longer than the buf */ +#define EVT_MISSING_ACK 0x3 /* A subaction gap was detected before an ack + arrived, or recv'd ack had a parity error */ +#define EVT_UNDERRUN 0x4 /* Underrun on corresponding FIFO, packet + truncated */ +#define EVT_OVERRUN 0x5 /* A recv FIFO overflowed on reception of ISO + packet */ +#define EVT_DESCRIPTOR_READ 0x6 /* An unrecoverable error occurred while host was + reading a descriptor block */ +#define EVT_DATA_READ 0x7 /* An error occurred while host controller was + attempting to read from host memory in the data + stage of descriptor processing */ +#define EVT_DATA_WRITE 0x8 /* An error occurred while host controller was + attempting to write either during the data stage + of descriptor processing, or when processing a single + 16-bit host memory write */ +#define EVT_BUS_RESET 0x9 /* Identifies a PHY packet in the recv buffer as + being a synthesized bus reset packet */ +#define EVT_TIMEOUT 0xa /* Indicates that the asynchronous transmit response + packet expired and was not transmitted, or that an + IT DMA context experienced a skip processing overflow */ +#define EVT_TCODE_ERR 0xb /* A bad tCode is associated with this packet. + The packet was flushed */ +#define EVT_RESERVED_B 0xc /* Reserved, not used !!! */ +#define EVT_RESERVED_C 0xd /* Reserved, not used !!! */ +#define EVT_UNKNOWN 0xe /* An error condition has occurred that cannot be + represented by any other event codes defined herein. */ +#define EVT_FLUSHED 0xf /* Send by the link side of output FIFO when asynchronous + packets are being flushed due to a bus reset. */ + +#define OHCI1394_TCODE_PHY 0xE + +/* Node offset map (phys DMA area, posted write area). + * The value of OHCI1394_PHYS_UPPER_BOUND_PROGRAMMED may be modified but must + * be lower than OHCI1394_MIDDLE_ADDRESS_SPACE. + * OHCI1394_PHYS_UPPER_BOUND_FIXED and OHCI1394_MIDDLE_ADDRESS_SPACE are + * constants given by the OHCI spec. + */ +#define OHCI1394_PHYS_UPPER_BOUND_FIXED 0x000100000000ULL /* 4 GB */ +#define OHCI1394_PHYS_UPPER_BOUND_PROGRAMMED 0x010000000000ULL /* 1 TB */ +#define OHCI1394_MIDDLE_ADDRESS_SPACE 0xffff00000000ULL + +void ohci1394_init_iso_tasklet(struct ohci1394_iso_tasklet *tasklet, + int type, + void (*func)(unsigned long), + unsigned long data); +int ohci1394_register_iso_tasklet(struct ti_ohci *ohci, + struct ohci1394_iso_tasklet *tasklet); +void ohci1394_unregister_iso_tasklet(struct ti_ohci *ohci, + struct ohci1394_iso_tasklet *tasklet); +int ohci1394_stop_context(struct ti_ohci *ohci, int reg, char *msg); +struct ti_ohci *ohci1394_get_struct(int card_num); + +#endif diff --git a/trunk/drivers/ieee1394/pcilynx.c b/trunk/drivers/ieee1394/pcilynx.c new file mode 100644 index 000000000000..bf47fee79808 --- /dev/null +++ b/trunk/drivers/ieee1394/pcilynx.c @@ -0,0 +1,1554 @@ +/* + * pcilynx.c - Texas Instruments PCILynx driver + * Copyright (C) 1999,2000 Andreas Bombe , + * Stephan Linz + * Manfred Weihs + * + * 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. + */ + +/* + * Contributions: + * + * Manfred Weihs + * reading bus info block (containing GUID) from serial + * eeprom via i2c and storing it in config ROM + * Reworked code for initiating bus resets + * (long, short, with or without hold-off) + * Enhancements in async and iso send code + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "csr1212.h" +#include "ieee1394.h" +#include "ieee1394_types.h" +#include "hosts.h" +#include "ieee1394_core.h" +#include "highlevel.h" +#include "pcilynx.h" + +#include +#include + +/* print general (card independent) information */ +#define PRINT_G(level, fmt, args...) printk(level "pcilynx: " fmt "\n" , ## args) +/* print card specific information */ +#define PRINT(level, card, fmt, args...) printk(level "pcilynx%d: " fmt "\n" , card , ## args) + +#ifdef CONFIG_IEEE1394_VERBOSEDEBUG +#define PRINT_GD(level, fmt, args...) printk(level "pcilynx: " fmt "\n" , ## args) +#define PRINTD(level, card, fmt, args...) printk(level "pcilynx%d: " fmt "\n" , card , ## args) +#else +#define PRINT_GD(level, fmt, args...) do {} while (0) +#define PRINTD(level, card, fmt, args...) do {} while (0) +#endif + + +/* Module Parameters */ +static int skip_eeprom; +module_param(skip_eeprom, int, 0444); +MODULE_PARM_DESC(skip_eeprom, "Use generic bus info block instead of serial eeprom (default = 0)."); + + +static struct hpsb_host_driver lynx_driver; +static unsigned int card_id; + + + +/* + * I2C stuff + */ + +/* the i2c stuff was inspired by i2c-philips-par.c */ + +static void bit_setscl(void *data, int state) +{ + if (state) { + ((struct ti_lynx *) data)->i2c_driven_state |= 0x00000040; + } else { + ((struct ti_lynx *) data)->i2c_driven_state &= ~0x00000040; + } + reg_write((struct ti_lynx *) data, SERIAL_EEPROM_CONTROL, ((struct ti_lynx *) data)->i2c_driven_state); +} + +static void bit_setsda(void *data, int state) +{ + if (state) { + ((struct ti_lynx *) data)->i2c_driven_state |= 0x00000010; + } else { + ((struct ti_lynx *) data)->i2c_driven_state &= ~0x00000010; + } + reg_write((struct ti_lynx *) data, SERIAL_EEPROM_CONTROL, ((struct ti_lynx *) data)->i2c_driven_state); +} + +static int bit_getscl(void *data) +{ + return reg_read((struct ti_lynx *) data, SERIAL_EEPROM_CONTROL) & 0x00000040; +} + +static int bit_getsda(void *data) +{ + return reg_read((struct ti_lynx *) data, SERIAL_EEPROM_CONTROL) & 0x00000010; +} + +static struct i2c_algo_bit_data bit_data = { + .setsda = bit_setsda, + .setscl = bit_setscl, + .getsda = bit_getsda, + .getscl = bit_getscl, + .udelay = 5, + .timeout = 100, +}; + + +/* + * PCL handling functions. + */ + +static pcl_t alloc_pcl(struct ti_lynx *lynx) +{ + u8 m; + int i, j; + + spin_lock(&lynx->lock); + /* FIXME - use ffz() to make this readable */ + for (i = 0; i < (LOCALRAM_SIZE / 1024); i++) { + m = lynx->pcl_bmap[i]; + for (j = 0; j < 8; j++) { + if (m & 1<pcl_bmap[i] = m; + spin_unlock(&lynx->lock); + return 8 * i + j; + } + } + spin_unlock(&lynx->lock); + + return -1; +} + + +#if 0 +static void free_pcl(struct ti_lynx *lynx, pcl_t pclid) +{ + int off, bit; + + off = pclid / 8; + bit = pclid % 8; + + if (pclid < 0) { + return; + } + + spin_lock(&lynx->lock); + if (lynx->pcl_bmap[off] & 1<pcl_bmap[off] &= ~(1<id, + "attempted to free unallocated PCL %d", pclid); + } + spin_unlock(&lynx->lock); +} + +/* functions useful for debugging */ +static void pretty_print_pcl(const struct ti_pcl *pcl) +{ + int i; + + printk("PCL next %08x, userdata %08x, status %08x, remtrans %08x, nextbuf %08x\n", + pcl->next, pcl->user_data, pcl->pcl_status, + pcl->remaining_transfer_count, pcl->next_data_buffer); + + printk("PCL"); + for (i=0; i<13; i++) { + printk(" c%x:%08x d%x:%08x", + i, pcl->buffer[i].control, i, pcl->buffer[i].pointer); + if (!(i & 0x3) && (i != 12)) printk("\nPCL"); + } + printk("\n"); +} + +static void print_pcl(const struct ti_lynx *lynx, pcl_t pclid) +{ + struct ti_pcl pcl; + + get_pcl(lynx, pclid, &pcl); + pretty_print_pcl(&pcl); +} +#endif + + + +/*********************************** + * IEEE-1394 functionality section * + ***********************************/ + + +static int get_phy_reg(struct ti_lynx *lynx, int addr) +{ + int retval; + int i = 0; + + unsigned long flags; + + if (addr > 15) { + PRINT(KERN_ERR, lynx->id, + "%s: PHY register address %d out of range", + __func__, addr); + return -1; + } + + spin_lock_irqsave(&lynx->phy_reg_lock, flags); + + reg_write(lynx, LINK_PHY, LINK_PHY_READ | LINK_PHY_ADDR(addr)); + do { + retval = reg_read(lynx, LINK_PHY); + + if (i > 10000) { + PRINT(KERN_ERR, lynx->id, "%s: runaway loop, aborting", + __func__); + retval = -1; + break; + } + i++; + } while ((retval & 0xf00) != LINK_PHY_RADDR(addr)); + + reg_write(lynx, LINK_INT_STATUS, LINK_INT_PHY_REG_RCVD); + spin_unlock_irqrestore(&lynx->phy_reg_lock, flags); + + if (retval != -1) { + return retval & 0xff; + } else { + return -1; + } +} + +static int set_phy_reg(struct ti_lynx *lynx, int addr, int val) +{ + unsigned long flags; + + if (addr > 15) { + PRINT(KERN_ERR, lynx->id, + "%s: PHY register address %d out of range", __func__, addr); + return -1; + } + + if (val > 0xff) { + PRINT(KERN_ERR, lynx->id, + "%s: PHY register value %d out of range", __func__, val); + return -1; + } + + spin_lock_irqsave(&lynx->phy_reg_lock, flags); + + reg_write(lynx, LINK_PHY, LINK_PHY_WRITE | LINK_PHY_ADDR(addr) + | LINK_PHY_WDATA(val)); + + spin_unlock_irqrestore(&lynx->phy_reg_lock, flags); + + return 0; +} + +static int sel_phy_reg_page(struct ti_lynx *lynx, int page) +{ + int reg; + + if (page > 7) { + PRINT(KERN_ERR, lynx->id, + "%s: PHY page %d out of range", __func__, page); + return -1; + } + + reg = get_phy_reg(lynx, 7); + if (reg != -1) { + reg &= 0x1f; + reg |= (page << 5); + set_phy_reg(lynx, 7, reg); + return 0; + } else { + return -1; + } +} + +#if 0 /* not needed at this time */ +static int sel_phy_reg_port(struct ti_lynx *lynx, int port) +{ + int reg; + + if (port > 15) { + PRINT(KERN_ERR, lynx->id, + "%s: PHY port %d out of range", __func__, port); + return -1; + } + + reg = get_phy_reg(lynx, 7); + if (reg != -1) { + reg &= 0xf0; + reg |= port; + set_phy_reg(lynx, 7, reg); + return 0; + } else { + return -1; + } +} +#endif + +static u32 get_phy_vendorid(struct ti_lynx *lynx) +{ + u32 pvid = 0; + sel_phy_reg_page(lynx, 1); + pvid |= (get_phy_reg(lynx, 10) << 16); + pvid |= (get_phy_reg(lynx, 11) << 8); + pvid |= get_phy_reg(lynx, 12); + PRINT(KERN_INFO, lynx->id, "PHY vendor id 0x%06x", pvid); + return pvid; +} + +static u32 get_phy_productid(struct ti_lynx *lynx) +{ + u32 id = 0; + sel_phy_reg_page(lynx, 1); + id |= (get_phy_reg(lynx, 13) << 16); + id |= (get_phy_reg(lynx, 14) << 8); + id |= get_phy_reg(lynx, 15); + PRINT(KERN_INFO, lynx->id, "PHY product id 0x%06x", id); + return id; +} + +static quadlet_t generate_own_selfid(struct ti_lynx *lynx, + struct hpsb_host *host) +{ + quadlet_t lsid; + char phyreg[7]; + int i; + + phyreg[0] = lynx->phy_reg0; + for (i = 1; i < 7; i++) { + phyreg[i] = get_phy_reg(lynx, i); + } + + /* FIXME? We assume a TSB21LV03A phy here. This code doesn't support + more than 3 ports on the PHY anyway. */ + + lsid = 0x80400000 | ((phyreg[0] & 0xfc) << 22); + lsid |= (phyreg[1] & 0x3f) << 16; /* gap count */ + lsid |= (phyreg[2] & 0xc0) << 8; /* max speed */ + if (!hpsb_disable_irm) + lsid |= (phyreg[6] & 0x01) << 11; /* contender (phy dependent) */ + /* lsid |= 1 << 11; *//* set contender (hack) */ + lsid |= (phyreg[6] & 0x10) >> 3; /* initiated reset */ + + for (i = 0; i < (phyreg[2] & 0xf); i++) { /* ports */ + if (phyreg[3 + i] & 0x4) { + lsid |= (((phyreg[3 + i] & 0x8) | 0x10) >> 3) + << (6 - i*2); + } else { + lsid |= 1 << (6 - i*2); + } + } + + cpu_to_be32s(&lsid); + PRINT(KERN_DEBUG, lynx->id, "generated own selfid 0x%x", lsid); + return lsid; +} + +static void handle_selfid(struct ti_lynx *lynx, struct hpsb_host *host) +{ + quadlet_t *q = lynx->rcv_page; + int phyid, isroot, size; + quadlet_t lsid = 0; + int i; + + if (lynx->phy_reg0 == -1 || lynx->selfid_size == -1) return; + + size = lynx->selfid_size; + phyid = lynx->phy_reg0; + + i = (size > 16 ? 16 : size) / 4 - 1; + while (i >= 0) { + cpu_to_be32s(&q[i]); + i--; + } + + if (!lynx->phyic.reg_1394a) { + lsid = generate_own_selfid(lynx, host); + } + + isroot = (phyid & 2) != 0; + phyid >>= 2; + PRINT(KERN_INFO, lynx->id, "SelfID process finished (phyid %d, %s)", + phyid, (isroot ? "root" : "not root")); + reg_write(lynx, LINK_ID, (0xffc0 | phyid) << 16); + + if (!lynx->phyic.reg_1394a && !size) { + hpsb_selfid_received(host, lsid); + } + + while (size > 0) { + struct selfid *sid = (struct selfid *)q; + + if (!lynx->phyic.reg_1394a && !sid->extended + && (sid->phy_id == (phyid + 1))) { + hpsb_selfid_received(host, lsid); + } + + if (q[0] == ~q[1]) { + PRINT(KERN_DEBUG, lynx->id, "SelfID packet 0x%x rcvd", + q[0]); + hpsb_selfid_received(host, q[0]); + } else { + PRINT(KERN_INFO, lynx->id, + "inconsistent selfid 0x%x/0x%x", q[0], q[1]); + } + q += 2; + size -= 8; + } + + if (!lynx->phyic.reg_1394a && isroot && phyid != 0) { + hpsb_selfid_received(host, lsid); + } + + hpsb_selfid_complete(host, phyid, isroot); + + if (host->in_bus_reset) return; /* in bus reset again */ + + if (isroot) reg_set_bits(lynx, LINK_CONTROL, LINK_CONTROL_CYCMASTER); //FIXME: I do not think, we need this here + reg_set_bits(lynx, LINK_CONTROL, + LINK_CONTROL_RCV_CMP_VALID | LINK_CONTROL_TX_ASYNC_EN + | LINK_CONTROL_RX_ASYNC_EN | LINK_CONTROL_CYCTIMEREN); +} + + + +/* This must be called with the respective queue_lock held. */ +static void send_next(struct ti_lynx *lynx, int what) +{ + struct ti_pcl pcl; + struct lynx_send_data *d; + struct hpsb_packet *packet; + +#if 0 /* has been removed from ieee1394 core */ + d = (what == hpsb_iso ? &lynx->iso_send : &lynx->async); +#else + d = &lynx->async; +#endif + if (!list_empty(&d->pcl_queue)) { + PRINT(KERN_ERR, lynx->id, "trying to queue a new packet in nonempty fifo"); + BUG(); + } + + packet = driver_packet(d->queue.next); + list_move_tail(&packet->driver_list, &d->pcl_queue); + + d->header_dma = pci_map_single(lynx->dev, packet->header, + packet->header_size, PCI_DMA_TODEVICE); + if (packet->data_size) { + d->data_dma = pci_map_single(lynx->dev, packet->data, + packet->data_size, + PCI_DMA_TODEVICE); + } else { + d->data_dma = 0; + } + + pcl.next = PCL_NEXT_INVALID; + pcl.async_error_next = PCL_NEXT_INVALID; + pcl.pcl_status = 0; + pcl.buffer[0].control = packet->speed_code << 14 | packet->header_size; +#ifndef __BIG_ENDIAN + pcl.buffer[0].control |= PCL_BIGENDIAN; +#endif + pcl.buffer[0].pointer = d->header_dma; + pcl.buffer[1].control = PCL_LAST_BUFF | packet->data_size; + pcl.buffer[1].pointer = d->data_dma; + + switch (packet->type) { + case hpsb_async: + pcl.buffer[0].control |= PCL_CMD_XMT; + break; +#if 0 /* has been removed from ieee1394 core */ + case hpsb_iso: + pcl.buffer[0].control |= PCL_CMD_XMT | PCL_ISOMODE; + break; +#endif + case hpsb_raw: + pcl.buffer[0].control |= PCL_CMD_UNFXMT; + break; + } + + put_pcl(lynx, d->pcl, &pcl); + run_pcl(lynx, d->pcl_start, d->channel); +} + + +/* called from subsystem core */ +static int lynx_transmit(struct hpsb_host *host, struct hpsb_packet *packet) +{ + struct ti_lynx *lynx = host->hostdata; + struct lynx_send_data *d; + unsigned long flags; + + if (packet->data_size >= 4096) { + PRINT(KERN_ERR, lynx->id, "transmit packet data too big (%Zd)", + packet->data_size); + return -EOVERFLOW; + } + + switch (packet->type) { + case hpsb_async: + case hpsb_raw: + d = &lynx->async; + break; +#if 0 /* has been removed from ieee1394 core */ + case hpsb_iso: + d = &lynx->iso_send; + break; +#endif + default: + PRINT(KERN_ERR, lynx->id, "invalid packet type %d", + packet->type); + return -EINVAL; + } + + if (packet->tcode == TCODE_WRITEQ + || packet->tcode == TCODE_READQ_RESPONSE) { + cpu_to_be32s(&packet->header[3]); + } + + spin_lock_irqsave(&d->queue_lock, flags); + + list_add_tail(&packet->driver_list, &d->queue); + if (list_empty(&d->pcl_queue)) + send_next(lynx, packet->type); + + spin_unlock_irqrestore(&d->queue_lock, flags); + + return 0; +} + + +/* called from subsystem core */ +static int lynx_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg) +{ + struct ti_lynx *lynx = host->hostdata; + int retval = 0; + struct hpsb_packet *packet; + LIST_HEAD(packet_list); + unsigned long flags; + int phy_reg; + + switch (cmd) { + case RESET_BUS: + if (reg_read(lynx, LINK_INT_STATUS) & LINK_INT_PHY_BUSRESET) { + retval = 0; + break; + } + + switch (arg) { + case SHORT_RESET: + if (lynx->phyic.reg_1394a) { + phy_reg = get_phy_reg(lynx, 5); + if (phy_reg == -1) { + PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed"); + retval = -1; + break; + } + phy_reg |= 0x40; + + PRINT(KERN_INFO, lynx->id, "resetting bus (short bus reset) on request"); + + lynx->selfid_size = -1; + lynx->phy_reg0 = -1; + set_phy_reg(lynx, 5, phy_reg); /* set ISBR */ + break; + } else { + PRINT(KERN_INFO, lynx->id, "cannot do short bus reset, because of old phy"); + /* fall through to long bus reset */ + } + case LONG_RESET: + phy_reg = get_phy_reg(lynx, 1); + if (phy_reg == -1) { + PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed"); + retval = -1; + break; + } + phy_reg |= 0x40; + + PRINT(KERN_INFO, lynx->id, "resetting bus (long bus reset) on request"); + + lynx->selfid_size = -1; + lynx->phy_reg0 = -1; + set_phy_reg(lynx, 1, phy_reg); /* clear RHB, set IBR */ + break; + case SHORT_RESET_NO_FORCE_ROOT: + if (lynx->phyic.reg_1394a) { + phy_reg = get_phy_reg(lynx, 1); + if (phy_reg == -1) { + PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed"); + retval = -1; + break; + } + if (phy_reg & 0x80) { + phy_reg &= ~0x80; + set_phy_reg(lynx, 1, phy_reg); /* clear RHB */ + } + + phy_reg = get_phy_reg(lynx, 5); + if (phy_reg == -1) { + PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed"); + retval = -1; + break; + } + phy_reg |= 0x40; + + PRINT(KERN_INFO, lynx->id, "resetting bus (short bus reset, no force_root) on request"); + + lynx->selfid_size = -1; + lynx->phy_reg0 = -1; + set_phy_reg(lynx, 5, phy_reg); /* set ISBR */ + break; + } else { + PRINT(KERN_INFO, lynx->id, "cannot do short bus reset, because of old phy"); + /* fall through to long bus reset */ + } + case LONG_RESET_NO_FORCE_ROOT: + phy_reg = get_phy_reg(lynx, 1); + if (phy_reg == -1) { + PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed"); + retval = -1; + break; + } + phy_reg &= ~0x80; + phy_reg |= 0x40; + + PRINT(KERN_INFO, lynx->id, "resetting bus (long bus reset, no force_root) on request"); + + lynx->selfid_size = -1; + lynx->phy_reg0 = -1; + set_phy_reg(lynx, 1, phy_reg); /* clear RHB, set IBR */ + break; + case SHORT_RESET_FORCE_ROOT: + if (lynx->phyic.reg_1394a) { + phy_reg = get_phy_reg(lynx, 1); + if (phy_reg == -1) { + PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed"); + retval = -1; + break; + } + if (!(phy_reg & 0x80)) { + phy_reg |= 0x80; + set_phy_reg(lynx, 1, phy_reg); /* set RHB */ + } + + phy_reg = get_phy_reg(lynx, 5); + if (phy_reg == -1) { + PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed"); + retval = -1; + break; + } + phy_reg |= 0x40; + + PRINT(KERN_INFO, lynx->id, "resetting bus (short bus reset, force_root set) on request"); + + lynx->selfid_size = -1; + lynx->phy_reg0 = -1; + set_phy_reg(lynx, 5, phy_reg); /* set ISBR */ + break; + } else { + PRINT(KERN_INFO, lynx->id, "cannot do short bus reset, because of old phy"); + /* fall through to long bus reset */ + } + case LONG_RESET_FORCE_ROOT: + phy_reg = get_phy_reg(lynx, 1); + if (phy_reg == -1) { + PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed"); + retval = -1; + break; + } + phy_reg |= 0xc0; + + PRINT(KERN_INFO, lynx->id, "resetting bus (long bus reset, force_root set) on request"); + + lynx->selfid_size = -1; + lynx->phy_reg0 = -1; + set_phy_reg(lynx, 1, phy_reg); /* set IBR and RHB */ + break; + default: + PRINT(KERN_ERR, lynx->id, "unknown argument for reset_bus command %d", arg); + retval = -1; + } + + break; + + case GET_CYCLE_COUNTER: + retval = reg_read(lynx, CYCLE_TIMER); + break; + + case SET_CYCLE_COUNTER: + reg_write(lynx, CYCLE_TIMER, arg); + break; + + case SET_BUS_ID: + reg_write(lynx, LINK_ID, + (arg << 22) | (reg_read(lynx, LINK_ID) & 0x003f0000)); + break; + + case ACT_CYCLE_MASTER: + if (arg) { + reg_set_bits(lynx, LINK_CONTROL, + LINK_CONTROL_CYCMASTER); + } else { + reg_clear_bits(lynx, LINK_CONTROL, + LINK_CONTROL_CYCMASTER); + } + break; + + case CANCEL_REQUESTS: + spin_lock_irqsave(&lynx->async.queue_lock, flags); + + reg_write(lynx, DMA_CHAN_CTRL(CHANNEL_ASYNC_SEND), 0); + list_splice_init(&lynx->async.queue, &packet_list); + + if (list_empty(&lynx->async.pcl_queue)) { + spin_unlock_irqrestore(&lynx->async.queue_lock, flags); + PRINTD(KERN_DEBUG, lynx->id, "no async packet in PCL to cancel"); + } else { + struct ti_pcl pcl; + u32 ack; + + PRINT(KERN_INFO, lynx->id, "cancelling async packet, that was already in PCL"); + + get_pcl(lynx, lynx->async.pcl, &pcl); + + packet = driver_packet(lynx->async.pcl_queue.next); + list_del_init(&packet->driver_list); + + pci_unmap_single(lynx->dev, lynx->async.header_dma, + packet->header_size, PCI_DMA_TODEVICE); + if (packet->data_size) { + pci_unmap_single(lynx->dev, lynx->async.data_dma, + packet->data_size, PCI_DMA_TODEVICE); + } + + spin_unlock_irqrestore(&lynx->async.queue_lock, flags); + + if (pcl.pcl_status & DMA_CHAN_STAT_PKTCMPL) { + if (pcl.pcl_status & DMA_CHAN_STAT_SPECIALACK) { + ack = (pcl.pcl_status >> 15) & 0xf; + PRINTD(KERN_INFO, lynx->id, "special ack %d", ack); + ack = (ack == 1 ? ACKX_TIMEOUT : ACKX_SEND_ERROR); + } else { + ack = (pcl.pcl_status >> 15) & 0xf; + } + } else { + PRINT(KERN_INFO, lynx->id, "async packet was not completed"); + ack = ACKX_ABORTED; + } + hpsb_packet_sent(host, packet, ack); + } + + while (!list_empty(&packet_list)) { + packet = driver_packet(packet_list.next); + list_del_init(&packet->driver_list); + hpsb_packet_sent(host, packet, ACKX_ABORTED); + } + + break; +#if 0 /* has been removed from ieee1394 core */ + case ISO_LISTEN_CHANNEL: + spin_lock_irqsave(&lynx->iso_rcv.lock, flags); + + if (lynx->iso_rcv.chan_count++ == 0) { + reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV), + DMA_WORD1_CMP_ENABLE_MASTER); + } + + spin_unlock_irqrestore(&lynx->iso_rcv.lock, flags); + break; + + case ISO_UNLISTEN_CHANNEL: + spin_lock_irqsave(&lynx->iso_rcv.lock, flags); + + if (--lynx->iso_rcv.chan_count == 0) { + reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV), + 0); + } + + spin_unlock_irqrestore(&lynx->iso_rcv.lock, flags); + break; +#endif + default: + PRINT(KERN_ERR, lynx->id, "unknown devctl command %d", cmd); + retval = -1; + } + + return retval; +} + + +/*************************************** + * IEEE-1394 functionality section END * + ***************************************/ + + +/******************************************************** + * Global stuff (interrupt handler, init/shutdown code) * + ********************************************************/ + + +static irqreturn_t lynx_irq_handler(int irq, void *dev_id) +{ + struct ti_lynx *lynx = (struct ti_lynx *)dev_id; + struct hpsb_host *host = lynx->host; + u32 intmask; + u32 linkint; + + linkint = reg_read(lynx, LINK_INT_STATUS); + intmask = reg_read(lynx, PCI_INT_STATUS); + + if (!(intmask & PCI_INT_INT_PEND)) + return IRQ_NONE; + + PRINTD(KERN_DEBUG, lynx->id, "interrupt: 0x%08x / 0x%08x", intmask, + linkint); + + reg_write(lynx, LINK_INT_STATUS, linkint); + reg_write(lynx, PCI_INT_STATUS, intmask); + + if (intmask & PCI_INT_1394) { + if (linkint & LINK_INT_PHY_TIMEOUT) { + PRINT(KERN_INFO, lynx->id, "PHY timeout occurred"); + } + if (linkint & LINK_INT_PHY_BUSRESET) { + PRINT(KERN_INFO, lynx->id, "bus reset interrupt"); + lynx->selfid_size = -1; + lynx->phy_reg0 = -1; + if (!host->in_bus_reset) + hpsb_bus_reset(host); + } + if (linkint & LINK_INT_PHY_REG_RCVD) { + u32 reg; + + spin_lock(&lynx->phy_reg_lock); + reg = reg_read(lynx, LINK_PHY); + spin_unlock(&lynx->phy_reg_lock); + + if (!host->in_bus_reset) { + PRINT(KERN_INFO, lynx->id, + "phy reg received without reset"); + } else if (reg & 0xf00) { + PRINT(KERN_INFO, lynx->id, + "unsolicited phy reg %d received", + (reg >> 8) & 0xf); + } else { + lynx->phy_reg0 = reg & 0xff; + handle_selfid(lynx, host); + } + } + if (linkint & LINK_INT_ISO_STUCK) { + PRINT(KERN_INFO, lynx->id, "isochronous transmitter stuck"); + } + if (linkint & LINK_INT_ASYNC_STUCK) { + PRINT(KERN_INFO, lynx->id, "asynchronous transmitter stuck"); + } + if (linkint & LINK_INT_SENT_REJECT) { + PRINT(KERN_INFO, lynx->id, "sent reject"); + } + if (linkint & LINK_INT_TX_INVALID_TC) { + PRINT(KERN_INFO, lynx->id, "invalid transaction code"); + } + if (linkint & LINK_INT_GRF_OVERFLOW) { + /* flush FIFO if overflow happens during reset */ + if (host->in_bus_reset) + reg_write(lynx, FIFO_CONTROL, + FIFO_CONTROL_GRF_FLUSH); + PRINT(KERN_INFO, lynx->id, "GRF overflow"); + } + if (linkint & LINK_INT_ITF_UNDERFLOW) { + PRINT(KERN_INFO, lynx->id, "ITF underflow"); + } + if (linkint & LINK_INT_ATF_UNDERFLOW) { + PRINT(KERN_INFO, lynx->id, "ATF underflow"); + } + } + + if (intmask & PCI_INT_DMA_HLT(CHANNEL_ISO_RCV)) { + PRINTD(KERN_DEBUG, lynx->id, "iso receive"); + + spin_lock(&lynx->iso_rcv.lock); + + lynx->iso_rcv.stat[lynx->iso_rcv.next] = + reg_read(lynx, DMA_CHAN_STAT(CHANNEL_ISO_RCV)); + + lynx->iso_rcv.used++; + lynx->iso_rcv.next = (lynx->iso_rcv.next + 1) % NUM_ISORCV_PCL; + + if ((lynx->iso_rcv.next == lynx->iso_rcv.last) + || !lynx->iso_rcv.chan_count) { + PRINTD(KERN_DEBUG, lynx->id, "stopped"); + reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV), 0); + } + + run_sub_pcl(lynx, lynx->iso_rcv.pcl_start, lynx->iso_rcv.next, + CHANNEL_ISO_RCV); + + spin_unlock(&lynx->iso_rcv.lock); + + tasklet_schedule(&lynx->iso_rcv.tq); + } + + if (intmask & PCI_INT_DMA_HLT(CHANNEL_ASYNC_SEND)) { + PRINTD(KERN_DEBUG, lynx->id, "async sent"); + spin_lock(&lynx->async.queue_lock); + + if (list_empty(&lynx->async.pcl_queue)) { + spin_unlock(&lynx->async.queue_lock); + PRINT(KERN_WARNING, lynx->id, "async dma halted, but no queued packet (maybe it was cancelled)"); + } else { + struct ti_pcl pcl; + u32 ack; + struct hpsb_packet *packet; + + get_pcl(lynx, lynx->async.pcl, &pcl); + + packet = driver_packet(lynx->async.pcl_queue.next); + list_del_init(&packet->driver_list); + + pci_unmap_single(lynx->dev, lynx->async.header_dma, + packet->header_size, PCI_DMA_TODEVICE); + if (packet->data_size) { + pci_unmap_single(lynx->dev, lynx->async.data_dma, + packet->data_size, PCI_DMA_TODEVICE); + } + + if (!list_empty(&lynx->async.queue)) { + send_next(lynx, hpsb_async); + } + + spin_unlock(&lynx->async.queue_lock); + + if (pcl.pcl_status & DMA_CHAN_STAT_PKTCMPL) { + if (pcl.pcl_status & DMA_CHAN_STAT_SPECIALACK) { + ack = (pcl.pcl_status >> 15) & 0xf; + PRINTD(KERN_INFO, lynx->id, "special ack %d", ack); + ack = (ack == 1 ? ACKX_TIMEOUT : ACKX_SEND_ERROR); + } else { + ack = (pcl.pcl_status >> 15) & 0xf; + } + } else { + PRINT(KERN_INFO, lynx->id, "async packet was not completed"); + ack = ACKX_SEND_ERROR; + } + hpsb_packet_sent(host, packet, ack); + } + } + + if (intmask & PCI_INT_DMA_HLT(CHANNEL_ISO_SEND)) { + PRINTD(KERN_DEBUG, lynx->id, "iso sent"); + spin_lock(&lynx->iso_send.queue_lock); + + if (list_empty(&lynx->iso_send.pcl_queue)) { + spin_unlock(&lynx->iso_send.queue_lock); + PRINT(KERN_ERR, lynx->id, "iso send dma halted, but no queued packet"); + } else { + struct ti_pcl pcl; + u32 ack; + struct hpsb_packet *packet; + + get_pcl(lynx, lynx->iso_send.pcl, &pcl); + + packet = driver_packet(lynx->iso_send.pcl_queue.next); + list_del_init(&packet->driver_list); + + pci_unmap_single(lynx->dev, lynx->iso_send.header_dma, + packet->header_size, PCI_DMA_TODEVICE); + if (packet->data_size) { + pci_unmap_single(lynx->dev, lynx->iso_send.data_dma, + packet->data_size, PCI_DMA_TODEVICE); + } +#if 0 /* has been removed from ieee1394 core */ + if (!list_empty(&lynx->iso_send.queue)) { + send_next(lynx, hpsb_iso); + } +#endif + spin_unlock(&lynx->iso_send.queue_lock); + + if (pcl.pcl_status & DMA_CHAN_STAT_PKTCMPL) { + if (pcl.pcl_status & DMA_CHAN_STAT_SPECIALACK) { + ack = (pcl.pcl_status >> 15) & 0xf; + PRINTD(KERN_INFO, lynx->id, "special ack %d", ack); + ack = (ack == 1 ? ACKX_TIMEOUT : ACKX_SEND_ERROR); + } else { + ack = (pcl.pcl_status >> 15) & 0xf; + } + } else { + PRINT(KERN_INFO, lynx->id, "iso send packet was not completed"); + ack = ACKX_SEND_ERROR; + } + + hpsb_packet_sent(host, packet, ack); //FIXME: maybe we should just use ACK_COMPLETE and ACKX_SEND_ERROR + } + } + + if (intmask & PCI_INT_DMA_HLT(CHANNEL_ASYNC_RCV)) { + /* general receive DMA completed */ + int stat = reg_read(lynx, DMA_CHAN_STAT(CHANNEL_ASYNC_RCV)); + + PRINTD(KERN_DEBUG, lynx->id, "received packet size %d", + stat & 0x1fff); + + if (stat & DMA_CHAN_STAT_SELFID) { + lynx->selfid_size = stat & 0x1fff; + handle_selfid(lynx, host); + } else { + quadlet_t *q_data = lynx->rcv_page; + if ((*q_data >> 4 & 0xf) == TCODE_READQ_RESPONSE + || (*q_data >> 4 & 0xf) == TCODE_WRITEQ) { + cpu_to_be32s(q_data + 3); + } + hpsb_packet_received(host, q_data, stat & 0x1fff, 0); + } + + run_pcl(lynx, lynx->rcv_pcl_start, CHANNEL_ASYNC_RCV); + } + + return IRQ_HANDLED; +} + + +static void iso_rcv_bh(struct ti_lynx *lynx) +{ + unsigned int idx; + quadlet_t *data; + unsigned long flags; + + spin_lock_irqsave(&lynx->iso_rcv.lock, flags); + + while (lynx->iso_rcv.used) { + idx = lynx->iso_rcv.last; + spin_unlock_irqrestore(&lynx->iso_rcv.lock, flags); + + data = lynx->iso_rcv.page[idx / ISORCV_PER_PAGE] + + (idx % ISORCV_PER_PAGE) * MAX_ISORCV_SIZE; + + if ((*data >> 16) + 4 != (lynx->iso_rcv.stat[idx] & 0x1fff)) { + PRINT(KERN_ERR, lynx->id, + "iso length mismatch 0x%08x/0x%08x", *data, + lynx->iso_rcv.stat[idx]); + } + + if (lynx->iso_rcv.stat[idx] + & (DMA_CHAN_STAT_PCIERR | DMA_CHAN_STAT_PKTERR)) { + PRINT(KERN_INFO, lynx->id, + "iso receive error on %d to 0x%p", idx, data); + } else { + hpsb_packet_received(lynx->host, data, + lynx->iso_rcv.stat[idx] & 0x1fff, + 0); + } + + spin_lock_irqsave(&lynx->iso_rcv.lock, flags); + lynx->iso_rcv.last = (idx + 1) % NUM_ISORCV_PCL; + lynx->iso_rcv.used--; + } + + if (lynx->iso_rcv.chan_count) { + reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV), + DMA_WORD1_CMP_ENABLE_MASTER); + } + spin_unlock_irqrestore(&lynx->iso_rcv.lock, flags); +} + + +static void remove_card(struct pci_dev *dev) +{ + struct ti_lynx *lynx; + struct device *lynx_dev; + int i; + + lynx = pci_get_drvdata(dev); + if (!lynx) return; + pci_set_drvdata(dev, NULL); + + lynx_dev = get_device(&lynx->host->device); + + switch (lynx->state) { + case is_host: + reg_write(lynx, PCI_INT_ENABLE, 0); + hpsb_remove_host(lynx->host); + case have_intr: + reg_write(lynx, PCI_INT_ENABLE, 0); + free_irq(lynx->dev->irq, lynx); + + /* Disable IRM Contender and LCtrl */ + if (lynx->phyic.reg_1394a) + set_phy_reg(lynx, 4, ~0xc0 & get_phy_reg(lynx, 4)); + + /* Let all other nodes know to ignore us */ + lynx_devctl(lynx->host, RESET_BUS, LONG_RESET_NO_FORCE_ROOT); + + case have_iomappings: + reg_set_bits(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET); + /* Fix buggy cards with autoboot pin not tied low: */ + reg_write(lynx, DMA0_CHAN_CTRL, 0); + iounmap(lynx->registers); + iounmap(lynx->local_rom); + iounmap(lynx->local_ram); + iounmap(lynx->aux_port); + case have_1394_buffers: + for (i = 0; i < ISORCV_PAGES; i++) { + if (lynx->iso_rcv.page[i]) { + pci_free_consistent(lynx->dev, PAGE_SIZE, + lynx->iso_rcv.page[i], + lynx->iso_rcv.page_dma[i]); + } + } + pci_free_consistent(lynx->dev, PAGE_SIZE, lynx->rcv_page, + lynx->rcv_page_dma); + case have_aux_buf: + case have_pcl_mem: + pci_free_consistent(lynx->dev, LOCALRAM_SIZE, lynx->pcl_mem, + lynx->pcl_mem_dma); + case clear: + /* do nothing - already freed */ + ; + } + + tasklet_kill(&lynx->iso_rcv.tq); + + if (lynx_dev) + put_device(lynx_dev); +} + + +static int __devinit add_card(struct pci_dev *dev, + const struct pci_device_id *devid_is_unused) +{ +#define FAIL(fmt, args...) do { \ + PRINT_G(KERN_ERR, fmt , ## args); \ + remove_card(dev); \ + return error; \ + } while (0) + + char irq_buf[16]; + struct hpsb_host *host; + struct ti_lynx *lynx; /* shortcut to currently handled device */ + struct ti_pcl pcl; + u32 *pcli; + int i; + int error; + + error = -ENXIO; + + if (pci_set_dma_mask(dev, DMA_BIT_MASK(32))) + FAIL("DMA address limits not supported for PCILynx hardware"); + if (pci_enable_device(dev)) + FAIL("failed to enable PCILynx hardware"); + pci_set_master(dev); + + error = -ENOMEM; + + host = hpsb_alloc_host(&lynx_driver, sizeof(struct ti_lynx), &dev->dev); + if (!host) FAIL("failed to allocate control structure memory"); + + lynx = host->hostdata; + lynx->id = card_id++; + lynx->dev = dev; + lynx->state = clear; + lynx->host = host; + host->pdev = dev; + pci_set_drvdata(dev, lynx); + + spin_lock_init(&lynx->lock); + spin_lock_init(&lynx->phy_reg_lock); + + lynx->pcl_mem = pci_alloc_consistent(dev, LOCALRAM_SIZE, + &lynx->pcl_mem_dma); + + if (lynx->pcl_mem != NULL) { + lynx->state = have_pcl_mem; + PRINT(KERN_INFO, lynx->id, + "allocated PCL memory %d Bytes @ 0x%p", LOCALRAM_SIZE, + lynx->pcl_mem); + } else { + FAIL("failed to allocate PCL memory area"); + } + + lynx->rcv_page = pci_alloc_consistent(dev, PAGE_SIZE, + &lynx->rcv_page_dma); + if (lynx->rcv_page == NULL) { + FAIL("failed to allocate receive buffer"); + } + lynx->state = have_1394_buffers; + + for (i = 0; i < ISORCV_PAGES; i++) { + lynx->iso_rcv.page[i] = + pci_alloc_consistent(dev, PAGE_SIZE, + &lynx->iso_rcv.page_dma[i]); + if (lynx->iso_rcv.page[i] == NULL) { + FAIL("failed to allocate iso receive buffers"); + } + } + + lynx->registers = ioremap_nocache(pci_resource_start(dev,0), + PCILYNX_MAX_REGISTER); + lynx->local_ram = ioremap(pci_resource_start(dev,1), PCILYNX_MAX_MEMORY); + lynx->aux_port = ioremap(pci_resource_start(dev,2), PCILYNX_MAX_MEMORY); + lynx->local_rom = ioremap(pci_resource_start(dev,PCI_ROM_RESOURCE), + PCILYNX_MAX_MEMORY); + lynx->state = have_iomappings; + + if (lynx->registers == NULL) { + FAIL("failed to remap registers - card not accessible"); + } + + reg_set_bits(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET); + /* Fix buggy cards with autoboot pin not tied low: */ + reg_write(lynx, DMA0_CHAN_CTRL, 0); + + sprintf (irq_buf, "%d", dev->irq); + + if (!request_irq(dev->irq, lynx_irq_handler, IRQF_SHARED, + PCILYNX_DRIVER_NAME, lynx)) { + PRINT(KERN_INFO, lynx->id, "allocated interrupt %s", irq_buf); + lynx->state = have_intr; + } else { + FAIL("failed to allocate shared interrupt %s", irq_buf); + } + + /* alloc_pcl return values are not checked, it is expected that the + * provided PCL space is sufficient for the initial allocations */ + lynx->rcv_pcl = alloc_pcl(lynx); + lynx->rcv_pcl_start = alloc_pcl(lynx); + lynx->async.pcl = alloc_pcl(lynx); + lynx->async.pcl_start = alloc_pcl(lynx); + lynx->iso_send.pcl = alloc_pcl(lynx); + lynx->iso_send.pcl_start = alloc_pcl(lynx); + + for (i = 0; i < NUM_ISORCV_PCL; i++) { + lynx->iso_rcv.pcl[i] = alloc_pcl(lynx); + } + lynx->iso_rcv.pcl_start = alloc_pcl(lynx); + + /* all allocations successful - simple init stuff follows */ + + reg_write(lynx, PCI_INT_ENABLE, PCI_INT_DMA_ALL); + + tasklet_init(&lynx->iso_rcv.tq, (void (*)(unsigned long))iso_rcv_bh, + (unsigned long)lynx); + + spin_lock_init(&lynx->iso_rcv.lock); + + spin_lock_init(&lynx->async.queue_lock); + lynx->async.channel = CHANNEL_ASYNC_SEND; + spin_lock_init(&lynx->iso_send.queue_lock); + lynx->iso_send.channel = CHANNEL_ISO_SEND; + + PRINT(KERN_INFO, lynx->id, "remapped memory spaces reg 0x%p, rom 0x%p, " + "ram 0x%p, aux 0x%p", lynx->registers, lynx->local_rom, + lynx->local_ram, lynx->aux_port); + + /* now, looking for PHY register set */ + if ((get_phy_reg(lynx, 2) & 0xe0) == 0xe0) { + lynx->phyic.reg_1394a = 1; + PRINT(KERN_INFO, lynx->id, + "found 1394a conform PHY (using extended register set)"); + lynx->phyic.vendor = get_phy_vendorid(lynx); + lynx->phyic.product = get_phy_productid(lynx); + } else { + lynx->phyic.reg_1394a = 0; + PRINT(KERN_INFO, lynx->id, "found old 1394 PHY"); + } + + lynx->selfid_size = -1; + lynx->phy_reg0 = -1; + + INIT_LIST_HEAD(&lynx->async.queue); + INIT_LIST_HEAD(&lynx->async.pcl_queue); + INIT_LIST_HEAD(&lynx->iso_send.queue); + INIT_LIST_HEAD(&lynx->iso_send.pcl_queue); + + pcl.next = pcl_bus(lynx, lynx->rcv_pcl); + put_pcl(lynx, lynx->rcv_pcl_start, &pcl); + + pcl.next = PCL_NEXT_INVALID; + pcl.async_error_next = PCL_NEXT_INVALID; + + pcl.buffer[0].control = PCL_CMD_RCV | 16; +#ifndef __BIG_ENDIAN + pcl.buffer[0].control |= PCL_BIGENDIAN; +#endif + pcl.buffer[1].control = PCL_LAST_BUFF | 4080; + + pcl.buffer[0].pointer = lynx->rcv_page_dma; + pcl.buffer[1].pointer = lynx->rcv_page_dma + 16; + put_pcl(lynx, lynx->rcv_pcl, &pcl); + + pcl.next = pcl_bus(lynx, lynx->async.pcl); + pcl.async_error_next = pcl_bus(lynx, lynx->async.pcl); + put_pcl(lynx, lynx->async.pcl_start, &pcl); + + pcl.next = pcl_bus(lynx, lynx->iso_send.pcl); + pcl.async_error_next = PCL_NEXT_INVALID; + put_pcl(lynx, lynx->iso_send.pcl_start, &pcl); + + pcl.next = PCL_NEXT_INVALID; + pcl.async_error_next = PCL_NEXT_INVALID; + pcl.buffer[0].control = PCL_CMD_RCV | 4; +#ifndef __BIG_ENDIAN + pcl.buffer[0].control |= PCL_BIGENDIAN; +#endif + pcl.buffer[1].control = PCL_LAST_BUFF | 2044; + + for (i = 0; i < NUM_ISORCV_PCL; i++) { + int page = i / ISORCV_PER_PAGE; + int sec = i % ISORCV_PER_PAGE; + + pcl.buffer[0].pointer = lynx->iso_rcv.page_dma[page] + + sec * MAX_ISORCV_SIZE; + pcl.buffer[1].pointer = pcl.buffer[0].pointer + 4; + put_pcl(lynx, lynx->iso_rcv.pcl[i], &pcl); + } + + pcli = (u32 *)&pcl; + for (i = 0; i < NUM_ISORCV_PCL; i++) { + pcli[i] = pcl_bus(lynx, lynx->iso_rcv.pcl[i]); + } + put_pcl(lynx, lynx->iso_rcv.pcl_start, &pcl); + + /* FIFO sizes from left to right: ITF=48 ATF=48 GRF=160 */ + reg_write(lynx, FIFO_SIZES, 0x003030a0); + /* 20 byte threshold before triggering PCI transfer */ + reg_write(lynx, DMA_GLOBAL_REGISTER, 0x2<<24); + /* threshold on both send FIFOs before transmitting: + FIFO size - cache line size - 1 */ + i = reg_read(lynx, PCI_LATENCY_CACHELINE) & 0xff; + i = 0x30 - i - 1; + reg_write(lynx, FIFO_XMIT_THRESHOLD, (i << 8) | i); + + reg_set_bits(lynx, PCI_INT_ENABLE, PCI_INT_1394); + + reg_write(lynx, LINK_INT_ENABLE, LINK_INT_PHY_TIMEOUT + | LINK_INT_PHY_REG_RCVD | LINK_INT_PHY_BUSRESET + | LINK_INT_ISO_STUCK | LINK_INT_ASYNC_STUCK + | LINK_INT_SENT_REJECT | LINK_INT_TX_INVALID_TC + | LINK_INT_GRF_OVERFLOW | LINK_INT_ITF_UNDERFLOW + | LINK_INT_ATF_UNDERFLOW); + + reg_write(lynx, DMA_WORD0_CMP_VALUE(CHANNEL_ASYNC_RCV), 0); + reg_write(lynx, DMA_WORD0_CMP_ENABLE(CHANNEL_ASYNC_RCV), 0xa<<4); + reg_write(lynx, DMA_WORD1_CMP_VALUE(CHANNEL_ASYNC_RCV), 0); + reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ASYNC_RCV), + DMA_WORD1_CMP_MATCH_LOCAL_NODE | DMA_WORD1_CMP_MATCH_BROADCAST + | DMA_WORD1_CMP_MATCH_EXACT | DMA_WORD1_CMP_MATCH_BUS_BCAST + | DMA_WORD1_CMP_ENABLE_SELF_ID | DMA_WORD1_CMP_ENABLE_MASTER); + + run_pcl(lynx, lynx->rcv_pcl_start, CHANNEL_ASYNC_RCV); + + reg_write(lynx, DMA_WORD0_CMP_VALUE(CHANNEL_ISO_RCV), 0); + reg_write(lynx, DMA_WORD0_CMP_ENABLE(CHANNEL_ISO_RCV), 0x9<<4); + reg_write(lynx, DMA_WORD1_CMP_VALUE(CHANNEL_ISO_RCV), 0); + reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV), 0); + + run_sub_pcl(lynx, lynx->iso_rcv.pcl_start, 0, CHANNEL_ISO_RCV); + + reg_write(lynx, LINK_CONTROL, LINK_CONTROL_RCV_CMP_VALID + | LINK_CONTROL_TX_ISO_EN | LINK_CONTROL_RX_ISO_EN + | LINK_CONTROL_TX_ASYNC_EN | LINK_CONTROL_RX_ASYNC_EN + | LINK_CONTROL_RESET_TX | LINK_CONTROL_RESET_RX); + + if (!lynx->phyic.reg_1394a) { + if (!hpsb_disable_irm) { + /* attempt to enable contender bit -FIXME- would this + * work elsewhere? */ + reg_set_bits(lynx, GPIO_CTRL_A, 0x1); + reg_write(lynx, GPIO_DATA_BASE + 0x3c, 0x1); + } + } else { + /* set the contender (if appropriate) and LCtrl bit in the + * extended PHY register set. (Should check that PHY_02_EXTENDED + * is set in register 2?) + */ + i = get_phy_reg(lynx, 4); + i |= PHY_04_LCTRL; + if (hpsb_disable_irm) + i &= ~PHY_04_CONTENDER; + else + i |= PHY_04_CONTENDER; + if (i != -1) set_phy_reg(lynx, 4, i); + } + + if (!skip_eeprom) + { + /* needed for i2c communication with serial eeprom */ + struct i2c_adapter *i2c_ad; + struct i2c_algo_bit_data i2c_adapter_data; + + error = -ENOMEM; + i2c_ad = kzalloc(sizeof(*i2c_ad), GFP_KERNEL); + if (!i2c_ad) FAIL("failed to allocate I2C adapter memory"); + + strlcpy(i2c_ad->name, "PCILynx I2C", sizeof(i2c_ad->name)); + i2c_adapter_data = bit_data; + i2c_ad->algo_data = &i2c_adapter_data; + i2c_adapter_data.data = lynx; + i2c_ad->dev.parent = &dev->dev; + + PRINTD(KERN_DEBUG, lynx->id,"original eeprom control: %d", + reg_read(lynx, SERIAL_EEPROM_CONTROL)); + + /* reset hardware to sane state */ + lynx->i2c_driven_state = 0x00000070; + reg_write(lynx, SERIAL_EEPROM_CONTROL, lynx->i2c_driven_state); + + if (i2c_bit_add_bus(i2c_ad) < 0) + { + kfree(i2c_ad); + error = -ENXIO; + FAIL("unable to register i2c"); + } + else + { + /* do i2c stuff */ + unsigned char i2c_cmd = 0x10; + struct i2c_msg msg[2] = { { 0x50, 0, 1, &i2c_cmd }, + { 0x50, I2C_M_RD, 20, (unsigned char*) lynx->bus_info_block } + }; + + /* we use i2c_transfer because we have no i2c_client + at hand */ + if (i2c_transfer(i2c_ad, msg, 2) < 0) { + PRINT(KERN_ERR, lynx->id, "unable to read bus info block from i2c"); + } else { + PRINT(KERN_INFO, lynx->id, "got bus info block from serial eeprom"); + /* FIXME: probably we should rewrite the max_rec, max_ROM(1394a), + * generation(1394a) and link_spd(1394a) field and recalculate + * the CRC */ + + for (i = 0; i < 5 ; i++) + PRINTD(KERN_DEBUG, lynx->id, "Businfo block quadlet %i: %08x", + i, be32_to_cpu(lynx->bus_info_block[i])); + + /* info_length, crc_length and 1394 magic number to check, if it is really a bus info block */ + if (((be32_to_cpu(lynx->bus_info_block[0]) & 0xffff0000) == 0x04040000) && + (lynx->bus_info_block[1] == IEEE1394_BUSID_MAGIC)) + { + PRINT(KERN_DEBUG, lynx->id, "read a valid bus info block from"); + } else { + kfree(i2c_ad); + error = -ENXIO; + FAIL("read something from serial eeprom, but it does not seem to be a valid bus info block"); + } + + } + + i2c_del_adapter(i2c_ad); + kfree(i2c_ad); + } + } + + host->csr.guid_hi = be32_to_cpu(lynx->bus_info_block[3]); + host->csr.guid_lo = be32_to_cpu(lynx->bus_info_block[4]); + host->csr.cyc_clk_acc = (be32_to_cpu(lynx->bus_info_block[2]) >> 16) & 0xff; + host->csr.max_rec = (be32_to_cpu(lynx->bus_info_block[2]) >> 12) & 0xf; + if (!lynx->phyic.reg_1394a) + host->csr.lnk_spd = (get_phy_reg(lynx, 2) & 0xc0) >> 6; + else + host->csr.lnk_spd = be32_to_cpu(lynx->bus_info_block[2]) & 0x7; + + if (hpsb_add_host(host)) { + error = -ENOMEM; + FAIL("Failed to register host with highlevel"); + } + + lynx->state = is_host; + + return 0; +#undef FAIL +} + + +static struct pci_device_id pci_table[] = { + { + .vendor = PCI_VENDOR_ID_TI, + .device = PCI_DEVICE_ID_TI_PCILYNX, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { } /* Terminating entry */ +}; + +static struct pci_driver lynx_pci_driver = { + .name = PCILYNX_DRIVER_NAME, + .id_table = pci_table, + .probe = add_card, + .remove = remove_card, +}; + +static struct hpsb_host_driver lynx_driver = { + .owner = THIS_MODULE, + .name = PCILYNX_DRIVER_NAME, + .set_hw_config_rom = NULL, + .transmit_packet = lynx_transmit, + .devctl = lynx_devctl, + .isoctl = NULL, +}; + +MODULE_AUTHOR("Andreas E. Bombe "); +MODULE_DESCRIPTION("driver for Texas Instruments PCI Lynx IEEE-1394 controller"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("pcilynx"); +MODULE_DEVICE_TABLE(pci, pci_table); + +static int __init pcilynx_init(void) +{ + int ret; + + ret = pci_register_driver(&lynx_pci_driver); + if (ret < 0) { + PRINT_G(KERN_ERR, "PCI module init failed"); + return ret; + } + + return 0; +} + +static void __exit pcilynx_cleanup(void) +{ + pci_unregister_driver(&lynx_pci_driver); +} + + +module_init(pcilynx_init); +module_exit(pcilynx_cleanup); diff --git a/trunk/drivers/ieee1394/pcilynx.h b/trunk/drivers/ieee1394/pcilynx.h new file mode 100644 index 000000000000..693a169acea3 --- /dev/null +++ b/trunk/drivers/ieee1394/pcilynx.h @@ -0,0 +1,468 @@ +#ifndef __PCILYNX_H__ +#define __PCILYNX_H__ + + +#define PCILYNX_DRIVER_NAME "pcilynx" +#define PCILYNX_MAJOR 177 + +#define PCILYNX_MINOR_AUX_START 0 +#define PCILYNX_MINOR_ROM_START 16 +#define PCILYNX_MINOR_RAM_START 32 + +#define PCILYNX_MAX_REGISTER 0xfff +#define PCILYNX_MAX_MEMORY 0xffff + +#define PCI_DEVICE_ID_TI_PCILYNX 0x8000 +#define MAX_PCILYNX_CARDS 4 +#define LOCALRAM_SIZE 4096 + +#define NUM_ISORCV_PCL 4 +#define MAX_ISORCV_SIZE 2048 +#define ISORCV_PER_PAGE (PAGE_SIZE / MAX_ISORCV_SIZE) +#define ISORCV_PAGES (NUM_ISORCV_PCL / ISORCV_PER_PAGE) + +#define CHANNEL_LOCALBUS 0 +#define CHANNEL_ASYNC_RCV 1 +#define CHANNEL_ISO_RCV 2 +#define CHANNEL_ASYNC_SEND 3 +#define CHANNEL_ISO_SEND 4 + +#define PCILYNX_CONFIG_ROM_LENGTH 1024 + +typedef int pcl_t; + +struct ti_lynx { + int id; /* sequential card number */ + + spinlock_t lock; + + struct pci_dev *dev; + + struct { + unsigned reg_1394a:1; + u32 vendor; + u32 product; + } phyic; + + enum { clear, have_intr, have_aux_buf, have_pcl_mem, + have_1394_buffers, have_iomappings, is_host } state; + + /* remapped memory spaces */ + void __iomem *registers; + void __iomem *local_rom; + void __iomem *local_ram; + void __iomem *aux_port; + __be32 bus_info_block[5]; + + /* + * use local RAM of LOCALRAM_SIZE bytes for PCLs, which allows for + * LOCALRAM_SIZE * 8 PCLs (each sized 128 bytes); + * the following is an allocation bitmap + */ + u8 pcl_bmap[LOCALRAM_SIZE / 1024]; + + /* point to PCLs memory area if needed */ + void *pcl_mem; + dma_addr_t pcl_mem_dma; + + /* PCLs for local mem / aux transfers */ + pcl_t dmem_pcl; + + /* IEEE-1394 part follows */ + struct hpsb_host *host; + + int phyid, isroot; + int selfid_size; + int phy_reg0; + + spinlock_t phy_reg_lock; + + pcl_t rcv_pcl_start, rcv_pcl; + void *rcv_page; + dma_addr_t rcv_page_dma; + int rcv_active; + + struct lynx_send_data { + pcl_t pcl_start, pcl; + struct list_head queue; + struct list_head pcl_queue; /* this queue contains at most one packet */ + spinlock_t queue_lock; + dma_addr_t header_dma, data_dma; + int channel; + } async, iso_send; + + struct { + pcl_t pcl[NUM_ISORCV_PCL]; + u32 stat[NUM_ISORCV_PCL]; + void *page[ISORCV_PAGES]; + dma_addr_t page_dma[ISORCV_PAGES]; + pcl_t pcl_start; + int chan_count; + int next, last, used, running; + struct tasklet_struct tq; + spinlock_t lock; + } iso_rcv; + + u32 i2c_driven_state; /* the state we currently drive the Serial EEPROM Control register */ +}; + +/* the per-file data structure for mem space access */ +struct memdata { + struct ti_lynx *lynx; + int cid; + atomic_t aux_intr_last_seen; + /* enum values are the same as LBUS_ADDR_SEL_* values below */ + enum { rom = 0x10000, aux = 0x20000, ram = 0 } type; +}; + + + +/* + * Register read and write helper functions. + */ +static inline void reg_write(const struct ti_lynx *lynx, int offset, u32 data) +{ + writel(data, lynx->registers + offset); +} + +static inline u32 reg_read(const struct ti_lynx *lynx, int offset) +{ + return readl(lynx->registers + offset); +} + +static inline void reg_set_bits(const struct ti_lynx *lynx, int offset, + u32 mask) +{ + reg_write(lynx, offset, (reg_read(lynx, offset) | mask)); +} + +static inline void reg_clear_bits(const struct ti_lynx *lynx, int offset, + u32 mask) +{ + reg_write(lynx, offset, (reg_read(lynx, offset) & ~mask)); +} + + + +/* chip register definitions follow */ + +#define PCI_LATENCY_CACHELINE 0x0c + +#define MISC_CONTROL 0x40 +#define MISC_CONTROL_SWRESET (1<<0) + +#define SERIAL_EEPROM_CONTROL 0x44 + +#define PCI_INT_STATUS 0x48 +#define PCI_INT_ENABLE 0x4c +/* status and enable have identical bit numbers */ +#define PCI_INT_INT_PEND (1<<31) +#define PCI_INT_FORCED_INT (1<<30) +#define PCI_INT_SLV_ADR_PERR (1<<28) +#define PCI_INT_SLV_DAT_PERR (1<<27) +#define PCI_INT_MST_DAT_PERR (1<<26) +#define PCI_INT_MST_DEV_TIMEOUT (1<<25) +#define PCI_INT_INTERNAL_SLV_TIMEOUT (1<<23) +#define PCI_INT_AUX_TIMEOUT (1<<18) +#define PCI_INT_AUX_INT (1<<17) +#define PCI_INT_1394 (1<<16) +#define PCI_INT_DMA4_PCL (1<<9) +#define PCI_INT_DMA4_HLT (1<<8) +#define PCI_INT_DMA3_PCL (1<<7) +#define PCI_INT_DMA3_HLT (1<<6) +#define PCI_INT_DMA2_PCL (1<<5) +#define PCI_INT_DMA2_HLT (1<<4) +#define PCI_INT_DMA1_PCL (1<<3) +#define PCI_INT_DMA1_HLT (1<<2) +#define PCI_INT_DMA0_PCL (1<<1) +#define PCI_INT_DMA0_HLT (1<<0) +/* all DMA interrupts combined: */ +#define PCI_INT_DMA_ALL 0x3ff + +#define PCI_INT_DMA_HLT(chan) (1 << (chan * 2)) +#define PCI_INT_DMA_PCL(chan) (1 << (chan * 2 + 1)) + +#define LBUS_ADDR 0xb4 +#define LBUS_ADDR_SEL_RAM (0x0<<16) +#define LBUS_ADDR_SEL_ROM (0x1<<16) +#define LBUS_ADDR_SEL_AUX (0x2<<16) +#define LBUS_ADDR_SEL_ZV (0x3<<16) + +#define GPIO_CTRL_A 0xb8 +#define GPIO_CTRL_B 0xbc +#define GPIO_DATA_BASE 0xc0 + +#define DMA_BREG(base, chan) (base + chan * 0x20) +#define DMA_SREG(base, chan) (base + chan * 0x10) + +#define DMA0_PREV_PCL 0x100 +#define DMA1_PREV_PCL 0x120 +#define DMA2_PREV_PCL 0x140 +#define DMA3_PREV_PCL 0x160 +#define DMA4_PREV_PCL 0x180 +#define DMA_PREV_PCL(chan) (DMA_BREG(DMA0_PREV_PCL, chan)) + +#define DMA0_CURRENT_PCL 0x104 +#define DMA1_CURRENT_PCL 0x124 +#define DMA2_CURRENT_PCL 0x144 +#define DMA3_CURRENT_PCL 0x164 +#define DMA4_CURRENT_PCL 0x184 +#define DMA_CURRENT_PCL(chan) (DMA_BREG(DMA0_CURRENT_PCL, chan)) + +#define DMA0_CHAN_STAT 0x10c +#define DMA1_CHAN_STAT 0x12c +#define DMA2_CHAN_STAT 0x14c +#define DMA3_CHAN_STAT 0x16c +#define DMA4_CHAN_STAT 0x18c +#define DMA_CHAN_STAT(chan) (DMA_BREG(DMA0_CHAN_STAT, chan)) +/* CHAN_STATUS registers share bits */ +#define DMA_CHAN_STAT_SELFID (1<<31) +#define DMA_CHAN_STAT_ISOPKT (1<<30) +#define DMA_CHAN_STAT_PCIERR (1<<29) +#define DMA_CHAN_STAT_PKTERR (1<<28) +#define DMA_CHAN_STAT_PKTCMPL (1<<27) +#define DMA_CHAN_STAT_SPECIALACK (1<<14) + + +#define DMA0_CHAN_CTRL 0x110 +#define DMA1_CHAN_CTRL 0x130 +#define DMA2_CHAN_CTRL 0x150 +#define DMA3_CHAN_CTRL 0x170 +#define DMA4_CHAN_CTRL 0x190 +#define DMA_CHAN_CTRL(chan) (DMA_BREG(DMA0_CHAN_CTRL, chan)) +/* CHAN_CTRL registers share bits */ +#define DMA_CHAN_CTRL_ENABLE (1<<31) +#define DMA_CHAN_CTRL_BUSY (1<<30) +#define DMA_CHAN_CTRL_LINK (1<<29) + +#define DMA0_READY 0x114 +#define DMA1_READY 0x134 +#define DMA2_READY 0x154 +#define DMA3_READY 0x174 +#define DMA4_READY 0x194 +#define DMA_READY(chan) (DMA_BREG(DMA0_READY, chan)) + +#define DMA_GLOBAL_REGISTER 0x908 + +#define FIFO_SIZES 0xa00 + +#define FIFO_CONTROL 0xa10 +#define FIFO_CONTROL_GRF_FLUSH (1<<4) +#define FIFO_CONTROL_ITF_FLUSH (1<<3) +#define FIFO_CONTROL_ATF_FLUSH (1<<2) + +#define FIFO_XMIT_THRESHOLD 0xa14 + +#define DMA0_WORD0_CMP_VALUE 0xb00 +#define DMA1_WORD0_CMP_VALUE 0xb10 +#define DMA2_WORD0_CMP_VALUE 0xb20 +#define DMA3_WORD0_CMP_VALUE 0xb30 +#define DMA4_WORD0_CMP_VALUE 0xb40 +#define DMA_WORD0_CMP_VALUE(chan) (DMA_SREG(DMA0_WORD0_CMP_VALUE, chan)) + +#define DMA0_WORD0_CMP_ENABLE 0xb04 +#define DMA1_WORD0_CMP_ENABLE 0xb14 +#define DMA2_WORD0_CMP_ENABLE 0xb24 +#define DMA3_WORD0_CMP_ENABLE 0xb34 +#define DMA4_WORD0_CMP_ENABLE 0xb44 +#define DMA_WORD0_CMP_ENABLE(chan) (DMA_SREG(DMA0_WORD0_CMP_ENABLE,chan)) + +#define DMA0_WORD1_CMP_VALUE 0xb08 +#define DMA1_WORD1_CMP_VALUE 0xb18 +#define DMA2_WORD1_CMP_VALUE 0xb28 +#define DMA3_WORD1_CMP_VALUE 0xb38 +#define DMA4_WORD1_CMP_VALUE 0xb48 +#define DMA_WORD1_CMP_VALUE(chan) (DMA_SREG(DMA0_WORD1_CMP_VALUE, chan)) + +#define DMA0_WORD1_CMP_ENABLE 0xb0c +#define DMA1_WORD1_CMP_ENABLE 0xb1c +#define DMA2_WORD1_CMP_ENABLE 0xb2c +#define DMA3_WORD1_CMP_ENABLE 0xb3c +#define DMA4_WORD1_CMP_ENABLE 0xb4c +#define DMA_WORD1_CMP_ENABLE(chan) (DMA_SREG(DMA0_WORD1_CMP_ENABLE,chan)) +/* word 1 compare enable flags */ +#define DMA_WORD1_CMP_MATCH_OTHERBUS (1<<15) +#define DMA_WORD1_CMP_MATCH_BROADCAST (1<<14) +#define DMA_WORD1_CMP_MATCH_BUS_BCAST (1<<13) +#define DMA_WORD1_CMP_MATCH_LOCAL_NODE (1<<12) +#define DMA_WORD1_CMP_MATCH_EXACT (1<<11) +#define DMA_WORD1_CMP_ENABLE_SELF_ID (1<<10) +#define DMA_WORD1_CMP_ENABLE_MASTER (1<<8) + +#define LINK_ID 0xf00 +#define LINK_ID_BUS(id) (id<<22) +#define LINK_ID_NODE(id) (id<<16) + +#define LINK_CONTROL 0xf04 +#define LINK_CONTROL_BUSY (1<<29) +#define LINK_CONTROL_TX_ISO_EN (1<<26) +#define LINK_CONTROL_RX_ISO_EN (1<<25) +#define LINK_CONTROL_TX_ASYNC_EN (1<<24) +#define LINK_CONTROL_RX_ASYNC_EN (1<<23) +#define LINK_CONTROL_RESET_TX (1<<21) +#define LINK_CONTROL_RESET_RX (1<<20) +#define LINK_CONTROL_CYCMASTER (1<<11) +#define LINK_CONTROL_CYCSOURCE (1<<10) +#define LINK_CONTROL_CYCTIMEREN (1<<9) +#define LINK_CONTROL_RCV_CMP_VALID (1<<7) +#define LINK_CONTROL_SNOOP_ENABLE (1<<6) + +#define CYCLE_TIMER 0xf08 + +#define LINK_PHY 0xf0c +#define LINK_PHY_READ (1<<31) +#define LINK_PHY_WRITE (1<<30) +#define LINK_PHY_ADDR(addr) (addr<<24) +#define LINK_PHY_WDATA(data) (data<<16) +#define LINK_PHY_RADDR(addr) (addr<<8) + + +#define LINK_INT_STATUS 0xf14 +#define LINK_INT_ENABLE 0xf18 +/* status and enable have identical bit numbers */ +#define LINK_INT_LINK_INT (1<<31) +#define LINK_INT_PHY_TIMEOUT (1<<30) +#define LINK_INT_PHY_REG_RCVD (1<<29) +#define LINK_INT_PHY_BUSRESET (1<<28) +#define LINK_INT_TX_RDY (1<<26) +#define LINK_INT_RX_DATA_RDY (1<<25) +#define LINK_INT_ISO_STUCK (1<<20) +#define LINK_INT_ASYNC_STUCK (1<<19) +#define LINK_INT_SENT_REJECT (1<<17) +#define LINK_INT_HDR_ERR (1<<16) +#define LINK_INT_TX_INVALID_TC (1<<15) +#define LINK_INT_CYC_SECOND (1<<11) +#define LINK_INT_CYC_START (1<<10) +#define LINK_INT_CYC_DONE (1<<9) +#define LINK_INT_CYC_PENDING (1<<8) +#define LINK_INT_CYC_LOST (1<<7) +#define LINK_INT_CYC_ARB_FAILED (1<<6) +#define LINK_INT_GRF_OVERFLOW (1<<5) +#define LINK_INT_ITF_UNDERFLOW (1<<4) +#define LINK_INT_ATF_UNDERFLOW (1<<3) +#define LINK_INT_ISOARB_FAILED (1<<0) + +/* PHY specifics */ +#define PHY_VENDORID_TI 0x800028 +#define PHY_PRODUCTID_TSB41LV03 0x000000 + + +/* this is the physical layout of a PCL, its size is 128 bytes */ +struct ti_pcl { + u32 next; + u32 async_error_next; + u32 user_data; + u32 pcl_status; + u32 remaining_transfer_count; + u32 next_data_buffer; + struct { + u32 control; + u32 pointer; + } buffer[13] __attribute__ ((packed)); +} __attribute__ ((packed)); + +#include +#define pcloffs(MEMBER) (offsetof(struct ti_pcl, MEMBER)) + + +static inline void put_pcl(const struct ti_lynx *lynx, pcl_t pclid, + const struct ti_pcl *pcl) +{ + memcpy_le32((u32 *)(lynx->pcl_mem + pclid * sizeof(struct ti_pcl)), + (u32 *)pcl, sizeof(struct ti_pcl)); +} + +static inline void get_pcl(const struct ti_lynx *lynx, pcl_t pclid, + struct ti_pcl *pcl) +{ + memcpy_le32((u32 *)pcl, + (u32 *)(lynx->pcl_mem + pclid * sizeof(struct ti_pcl)), + sizeof(struct ti_pcl)); +} + +static inline u32 pcl_bus(const struct ti_lynx *lynx, pcl_t pclid) +{ + return lynx->pcl_mem_dma + pclid * sizeof(struct ti_pcl); +} + + +#if defined (__BIG_ENDIAN) +typedef struct ti_pcl pcltmp_t; + +static inline struct ti_pcl *edit_pcl(const struct ti_lynx *lynx, pcl_t pclid, + pcltmp_t *tmp) +{ + get_pcl(lynx, pclid, tmp); + return tmp; +} + +static inline void commit_pcl(const struct ti_lynx *lynx, pcl_t pclid, + pcltmp_t *tmp) +{ + put_pcl(lynx, pclid, tmp); +} + +#else +typedef int pcltmp_t; /* just a dummy */ + +static inline struct ti_pcl *edit_pcl(const struct ti_lynx *lynx, pcl_t pclid, + pcltmp_t *tmp) +{ + return lynx->pcl_mem + pclid * sizeof(struct ti_pcl); +} + +static inline void commit_pcl(const struct ti_lynx *lynx, pcl_t pclid, + pcltmp_t *tmp) +{ +} +#endif + + +static inline void run_sub_pcl(const struct ti_lynx *lynx, pcl_t pclid, int idx, + int dmachan) +{ + reg_write(lynx, DMA0_CURRENT_PCL + dmachan * 0x20, + pcl_bus(lynx, pclid) + idx * 4); + reg_write(lynx, DMA0_CHAN_CTRL + dmachan * 0x20, + DMA_CHAN_CTRL_ENABLE | DMA_CHAN_CTRL_LINK); +} + +static inline void run_pcl(const struct ti_lynx *lynx, pcl_t pclid, int dmachan) +{ + run_sub_pcl(lynx, pclid, 0, dmachan); +} + +#define PCL_NEXT_INVALID (1<<0) + +/* transfer commands */ +#define PCL_CMD_RCV (0x1<<24) +#define PCL_CMD_RCV_AND_UPDATE (0xa<<24) +#define PCL_CMD_XMT (0x2<<24) +#define PCL_CMD_UNFXMT (0xc<<24) +#define PCL_CMD_PCI_TO_LBUS (0x8<<24) +#define PCL_CMD_LBUS_TO_PCI (0x9<<24) + +/* aux commands */ +#define PCL_CMD_NOP (0x0<<24) +#define PCL_CMD_LOAD (0x3<<24) +#define PCL_CMD_STOREQ (0x4<<24) +#define PCL_CMD_STORED (0xb<<24) +#define PCL_CMD_STORE0 (0x5<<24) +#define PCL_CMD_STORE1 (0x6<<24) +#define PCL_CMD_COMPARE (0xe<<24) +#define PCL_CMD_SWAP_COMPARE (0xf<<24) +#define PCL_CMD_ADD (0xd<<24) +#define PCL_CMD_BRANCH (0x7<<24) + +/* BRANCH condition codes */ +#define PCL_COND_DMARDY_SET (0x1<<20) +#define PCL_COND_DMARDY_CLEAR (0x2<<20) + +#define PCL_GEN_INTR (1<<19) +#define PCL_LAST_BUFF (1<<18) +#define PCL_LAST_CMD (PCL_LAST_BUFF) +#define PCL_WAITSTAT (1<<17) +#define PCL_BIGENDIAN (1<<16) +#define PCL_ISOMODE (1<<12) + +#endif diff --git a/trunk/drivers/ieee1394/raw1394-private.h b/trunk/drivers/ieee1394/raw1394-private.h new file mode 100644 index 000000000000..7a225a405987 --- /dev/null +++ b/trunk/drivers/ieee1394/raw1394-private.h @@ -0,0 +1,81 @@ +#ifndef IEEE1394_RAW1394_PRIVATE_H +#define IEEE1394_RAW1394_PRIVATE_H + +/* header for definitions that are private to the raw1394 driver + and not visible to user-space */ + +#define RAW1394_DEVICE_MAJOR 171 +#define RAW1394_DEVICE_NAME "raw1394" + +#define RAW1394_MAX_USER_CSR_DIRS 16 + +struct iso_block_store { + atomic_t refcount; + size_t data_size; + quadlet_t data[0]; +}; + +enum raw1394_iso_state { RAW1394_ISO_INACTIVE = 0, + RAW1394_ISO_RECV = 1, + RAW1394_ISO_XMIT = 2 }; + +struct file_info { + struct list_head list; + + struct mutex state_mutex; + enum { opened, initialized, connected } state; + unsigned int protocol_version; + + struct hpsb_host *host; + + struct list_head req_pending; /* protected by reqlists_lock */ + struct list_head req_complete; /* protected by reqlists_lock */ + spinlock_t reqlists_lock; + wait_queue_head_t wait_complete; + + struct list_head addr_list; /* protected by host_info_lock */ + + u8 __user *fcp_buffer; + + u8 notification; /* (busreset-notification) RAW1394_NOTIFY_OFF/ON */ + + /* new rawiso API */ + enum raw1394_iso_state iso_state; + struct hpsb_iso *iso_handle; + + /* User space's CSR1212 dynamic ConfigROM directories */ + struct csr1212_keyval *csr1212_dirs[RAW1394_MAX_USER_CSR_DIRS]; + + /* Legacy ConfigROM update flag */ + u8 cfgrom_upd; +}; + +struct arm_addr { + struct list_head addr_list; /* file_info list */ + u64 start, end; + u64 arm_tag; + u8 access_rights; + u8 notification_options; + u8 client_transactions; + u64 recvb; + u16 rec_length; + u8 *addr_space_buffer; /* accessed by read/write/lock requests */ +}; + +struct pending_request { + struct list_head list; + struct file_info *file_info; + struct hpsb_packet *packet; + struct iso_block_store *ibs; + quadlet_t *data; + int free_data; + struct raw1394_request req; +}; + +struct host_info { + struct list_head list; + struct hpsb_host *host; + struct list_head file_info_list; /* protected by host_info_lock */ +}; + +#endif /* IEEE1394_RAW1394_PRIVATE_H */ diff --git a/trunk/drivers/ieee1394/raw1394.c b/trunk/drivers/ieee1394/raw1394.c new file mode 100644 index 000000000000..f3401427404c --- /dev/null +++ b/trunk/drivers/ieee1394/raw1394.c @@ -0,0 +1,3096 @@ +/* + * IEEE 1394 for Linux + * + * Raw interface to the bus + * + * Copyright (C) 1999, 2000 Andreas E. Bombe + * 2001, 2002 Manfred Weihs + * 2002 Christian Toegel + * + * This code is licensed under the GPL. See the file COPYING in the root + * directory of the kernel sources for details. + * + * + * Contributions: + * + * Manfred Weihs + * configuration ROM manipulation + * address range mapping + * adaptation for new (transparent) loopback mechanism + * sending of arbitrary async packets + * Christian Toegel + * address range mapping + * lock64 request + * transmit physical packet + * busreset notification control (switch on/off) + * busreset with selection of type (short/long) + * request_reply + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "csr1212.h" +#include "highlevel.h" +#include "hosts.h" +#include "ieee1394.h" +#include "ieee1394_core.h" +#include "ieee1394_hotplug.h" +#include "ieee1394_transactions.h" +#include "ieee1394_types.h" +#include "iso.h" +#include "nodemgr.h" +#include "raw1394.h" +#include "raw1394-private.h" + +#define int2ptr(x) ((void __user *)(unsigned long)x) +#define ptr2int(x) ((u64)(unsigned long)(void __user *)x) + +#ifdef CONFIG_IEEE1394_VERBOSEDEBUG +#define RAW1394_DEBUG +#endif + +#ifdef RAW1394_DEBUG +#define DBGMSG(fmt, args...) \ +printk(KERN_INFO "raw1394:" fmt "\n" , ## args) +#else +#define DBGMSG(fmt, args...) do {} while (0) +#endif + +static LIST_HEAD(host_info_list); +static int host_count; +static DEFINE_SPINLOCK(host_info_lock); +static atomic_t internal_generation = ATOMIC_INIT(0); + +static atomic_t iso_buffer_size; +static const int iso_buffer_max = 4 * 1024 * 1024; /* 4 MB */ + +static struct hpsb_highlevel raw1394_highlevel; + +static int arm_read(struct hpsb_host *host, int nodeid, quadlet_t * buffer, + u64 addr, size_t length, u16 flags); +static int arm_write(struct hpsb_host *host, int nodeid, int destid, + quadlet_t * data, u64 addr, size_t length, u16 flags); +static int arm_lock(struct hpsb_host *host, int nodeid, quadlet_t * store, + u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode, + u16 flags); +static int arm_lock64(struct hpsb_host *host, int nodeid, octlet_t * store, + u64 addr, octlet_t data, octlet_t arg, int ext_tcode, + u16 flags); +static const struct hpsb_address_ops arm_ops = { + .read = arm_read, + .write = arm_write, + .lock = arm_lock, + .lock64 = arm_lock64, +}; + +static void queue_complete_cb(struct pending_request *req); + +static struct pending_request *__alloc_pending_request(gfp_t flags) +{ + struct pending_request *req; + + req = kzalloc(sizeof(*req), flags); + if (req) + INIT_LIST_HEAD(&req->list); + + return req; +} + +static inline struct pending_request *alloc_pending_request(void) +{ + return __alloc_pending_request(GFP_KERNEL); +} + +static void free_pending_request(struct pending_request *req) +{ + if (req->ibs) { + if (atomic_dec_and_test(&req->ibs->refcount)) { + atomic_sub(req->ibs->data_size, &iso_buffer_size); + kfree(req->ibs); + } + } else if (req->free_data) { + kfree(req->data); + } + hpsb_free_packet(req->packet); + kfree(req); +} + +/* fi->reqlists_lock must be taken */ +static void __queue_complete_req(struct pending_request *req) +{ + struct file_info *fi = req->file_info; + + list_move_tail(&req->list, &fi->req_complete); + wake_up(&fi->wait_complete); +} + +static void queue_complete_req(struct pending_request *req) +{ + unsigned long flags; + struct file_info *fi = req->file_info; + + spin_lock_irqsave(&fi->reqlists_lock, flags); + __queue_complete_req(req); + spin_unlock_irqrestore(&fi->reqlists_lock, flags); +} + +static void queue_complete_cb(struct pending_request *req) +{ + struct hpsb_packet *packet = req->packet; + int rcode = (packet->header[1] >> 12) & 0xf; + + switch (packet->ack_code) { + case ACKX_NONE: + case ACKX_SEND_ERROR: + req->req.error = RAW1394_ERROR_SEND_ERROR; + break; + case ACKX_ABORTED: + req->req.error = RAW1394_ERROR_ABORTED; + break; + case ACKX_TIMEOUT: + req->req.error = RAW1394_ERROR_TIMEOUT; + break; + default: + req->req.error = (packet->ack_code << 16) | rcode; + break; + } + + if (!((packet->ack_code == ACK_PENDING) && (rcode == RCODE_COMPLETE))) { + req->req.length = 0; + } + + if ((req->req.type == RAW1394_REQ_ASYNC_READ) || + (req->req.type == RAW1394_REQ_ASYNC_WRITE) || + (req->req.type == RAW1394_REQ_ASYNC_STREAM) || + (req->req.type == RAW1394_REQ_LOCK) || + (req->req.type == RAW1394_REQ_LOCK64)) + hpsb_free_tlabel(packet); + + queue_complete_req(req); +} + +static void add_host(struct hpsb_host *host) +{ + struct host_info *hi; + unsigned long flags; + + hi = kmalloc(sizeof(*hi), GFP_KERNEL); + + if (hi) { + INIT_LIST_HEAD(&hi->list); + hi->host = host; + INIT_LIST_HEAD(&hi->file_info_list); + + spin_lock_irqsave(&host_info_lock, flags); + list_add_tail(&hi->list, &host_info_list); + host_count++; + spin_unlock_irqrestore(&host_info_lock, flags); + } + + atomic_inc(&internal_generation); +} + +static struct host_info *find_host_info(struct hpsb_host *host) +{ + struct host_info *hi; + + list_for_each_entry(hi, &host_info_list, list) + if (hi->host == host) + return hi; + + return NULL; +} + +static void remove_host(struct hpsb_host *host) +{ + struct host_info *hi; + unsigned long flags; + + spin_lock_irqsave(&host_info_lock, flags); + hi = find_host_info(host); + + if (hi != NULL) { + list_del(&hi->list); + host_count--; + /* + FIXME: address ranges should be removed + and fileinfo states should be initialized + (including setting generation to + internal-generation ...) + */ + } + spin_unlock_irqrestore(&host_info_lock, flags); + + if (hi == NULL) { + printk(KERN_ERR "raw1394: attempt to remove unknown host " + "0x%p\n", host); + return; + } + + kfree(hi); + + atomic_inc(&internal_generation); +} + +static void host_reset(struct hpsb_host *host) +{ + unsigned long flags; + struct host_info *hi; + struct file_info *fi; + struct pending_request *req; + + spin_lock_irqsave(&host_info_lock, flags); + hi = find_host_info(host); + + if (hi != NULL) { + list_for_each_entry(fi, &hi->file_info_list, list) { + if (fi->notification == RAW1394_NOTIFY_ON) { + req = __alloc_pending_request(GFP_ATOMIC); + + if (req != NULL) { + req->file_info = fi; + req->req.type = RAW1394_REQ_BUS_RESET; + req->req.generation = + get_hpsb_generation(host); + req->req.misc = (host->node_id << 16) + | host->node_count; + if (fi->protocol_version > 3) { + req->req.misc |= + (NODEID_TO_NODE + (host->irm_id) + << 8); + } + + queue_complete_req(req); + } + } + } + } + spin_unlock_irqrestore(&host_info_lock, flags); +} + +static void fcp_request(struct hpsb_host *host, int nodeid, int direction, + int cts, u8 * data, size_t length) +{ + unsigned long flags; + struct host_info *hi; + struct file_info *fi; + struct pending_request *req, *req_next; + struct iso_block_store *ibs = NULL; + LIST_HEAD(reqs); + + if ((atomic_read(&iso_buffer_size) + length) > iso_buffer_max) { + HPSB_INFO("dropped fcp request"); + return; + } + + spin_lock_irqsave(&host_info_lock, flags); + hi = find_host_info(host); + + if (hi != NULL) { + list_for_each_entry(fi, &hi->file_info_list, list) { + if (!fi->fcp_buffer) + continue; + + req = __alloc_pending_request(GFP_ATOMIC); + if (!req) + break; + + if (!ibs) { + ibs = kmalloc(sizeof(*ibs) + length, + GFP_ATOMIC); + if (!ibs) { + kfree(req); + break; + } + + atomic_add(length, &iso_buffer_size); + atomic_set(&ibs->refcount, 0); + ibs->data_size = length; + memcpy(ibs->data, data, length); + } + + atomic_inc(&ibs->refcount); + + req->file_info = fi; + req->ibs = ibs; + req->data = ibs->data; + req->req.type = RAW1394_REQ_FCP_REQUEST; + req->req.generation = get_hpsb_generation(host); + req->req.misc = nodeid | (direction << 16); + req->req.recvb = ptr2int(fi->fcp_buffer); + req->req.length = length; + + list_add_tail(&req->list, &reqs); + } + } + spin_unlock_irqrestore(&host_info_lock, flags); + + list_for_each_entry_safe(req, req_next, &reqs, list) + queue_complete_req(req); +} + +#ifdef CONFIG_COMPAT +struct compat_raw1394_req { + __u32 type; + __s32 error; + __u32 misc; + + __u32 generation; + __u32 length; + + __u64 address; + + __u64 tag; + + __u64 sendb; + __u64 recvb; +} +#if defined(CONFIG_X86_64) || defined(CONFIG_IA64) +__attribute__((packed)) +#endif +; + +static const char __user *raw1394_compat_write(const char __user *buf) +{ + struct compat_raw1394_req __user *cr = (typeof(cr)) buf; + struct raw1394_request __user *r; + + r = compat_alloc_user_space(sizeof(struct raw1394_request)); + +#define C(x) __copy_in_user(&r->x, &cr->x, sizeof(r->x)) + + if (copy_in_user(r, cr, sizeof(struct compat_raw1394_req)) || + C(address) || + C(tag) || + C(sendb) || + C(recvb)) + return (__force const char __user *)ERR_PTR(-EFAULT); + + return (const char __user *)r; +} +#undef C + +#define P(x) __put_user(r->x, &cr->x) + +static int +raw1394_compat_read(const char __user *buf, struct raw1394_request *r) +{ + struct compat_raw1394_req __user *cr = (typeof(cr)) buf; + + if (!access_ok(VERIFY_WRITE, cr, sizeof(struct compat_raw1394_req)) || + P(type) || + P(error) || + P(misc) || + P(generation) || + P(length) || + P(address) || + P(tag) || + P(sendb) || + P(recvb)) + return -EFAULT; + + return sizeof(struct compat_raw1394_req); +} +#undef P + +#endif + +/* get next completed request (caller must hold fi->reqlists_lock) */ +static inline struct pending_request *__next_complete_req(struct file_info *fi) +{ + struct list_head *lh; + struct pending_request *req = NULL; + + if (!list_empty(&fi->req_complete)) { + lh = fi->req_complete.next; + list_del(lh); + req = list_entry(lh, struct pending_request, list); + } + return req; +} + +/* atomically get next completed request */ +static struct pending_request *next_complete_req(struct file_info *fi) +{ + unsigned long flags; + struct pending_request *req; + + spin_lock_irqsave(&fi->reqlists_lock, flags); + req = __next_complete_req(fi); + spin_unlock_irqrestore(&fi->reqlists_lock, flags); + return req; +} + +static ssize_t raw1394_read(struct file *file, char __user * buffer, + size_t count, loff_t * offset_is_ignored) +{ + struct file_info *fi = file->private_data; + struct pending_request *req; + ssize_t ret; + +#ifdef CONFIG_COMPAT + if (count == sizeof(struct compat_raw1394_req)) { + /* ok */ + } else +#endif + if (count != sizeof(struct raw1394_request)) { + return -EINVAL; + } + + if (!access_ok(VERIFY_WRITE, buffer, count)) { + return -EFAULT; + } + + if (file->f_flags & O_NONBLOCK) { + if (!(req = next_complete_req(fi))) + return -EAGAIN; + } else { + /* + * NB: We call the macro wait_event_interruptible() with a + * condition argument with side effect. This is only possible + * because the side effect does not occur until the condition + * became true, and wait_event_interruptible() won't evaluate + * the condition again after that. + */ + if (wait_event_interruptible(fi->wait_complete, + (req = next_complete_req(fi)))) + return -ERESTARTSYS; + } + + if (req->req.length) { + if (copy_to_user(int2ptr(req->req.recvb), req->data, + req->req.length)) { + req->req.error = RAW1394_ERROR_MEMFAULT; + } + } + +#ifdef CONFIG_COMPAT + if (count == sizeof(struct compat_raw1394_req) && + sizeof(struct compat_raw1394_req) != + sizeof(struct raw1394_request)) { + ret = raw1394_compat_read(buffer, &req->req); + } else +#endif + { + if (copy_to_user(buffer, &req->req, sizeof(req->req))) { + ret = -EFAULT; + goto out; + } + ret = (ssize_t) sizeof(struct raw1394_request); + } + out: + free_pending_request(req); + return ret; +} + +static int state_opened(struct file_info *fi, struct pending_request *req) +{ + if (req->req.type == RAW1394_REQ_INITIALIZE) { + switch (req->req.misc) { + case RAW1394_KERNELAPI_VERSION: + case 3: + fi->state = initialized; + fi->protocol_version = req->req.misc; + req->req.error = RAW1394_ERROR_NONE; + req->req.generation = atomic_read(&internal_generation); + break; + + default: + req->req.error = RAW1394_ERROR_COMPAT; + req->req.misc = RAW1394_KERNELAPI_VERSION; + } + } else { + req->req.error = RAW1394_ERROR_STATE_ORDER; + } + + req->req.length = 0; + queue_complete_req(req); + return 0; +} + +static int state_initialized(struct file_info *fi, struct pending_request *req) +{ + unsigned long flags; + struct host_info *hi; + struct raw1394_khost_list *khl; + + if (req->req.generation != atomic_read(&internal_generation)) { + req->req.error = RAW1394_ERROR_GENERATION; + req->req.generation = atomic_read(&internal_generation); + req->req.length = 0; + queue_complete_req(req); + return 0; + } + + switch (req->req.type) { + case RAW1394_REQ_LIST_CARDS: + spin_lock_irqsave(&host_info_lock, flags); + khl = kmalloc(sizeof(*khl) * host_count, GFP_ATOMIC); + + if (khl) { + req->req.misc = host_count; + req->data = (quadlet_t *) khl; + + list_for_each_entry(hi, &host_info_list, list) { + khl->nodes = hi->host->node_count; + strcpy(khl->name, hi->host->driver->name); + khl++; + } + } + spin_unlock_irqrestore(&host_info_lock, flags); + + if (khl) { + req->req.error = RAW1394_ERROR_NONE; + req->req.length = min(req->req.length, + (u32) (sizeof + (struct raw1394_khost_list) + * req->req.misc)); + req->free_data = 1; + } else { + return -ENOMEM; + } + break; + + case RAW1394_REQ_SET_CARD: + spin_lock_irqsave(&host_info_lock, flags); + if (req->req.misc >= host_count) { + req->req.error = RAW1394_ERROR_INVALID_ARG; + goto out_set_card; + } + list_for_each_entry(hi, &host_info_list, list) + if (!req->req.misc--) + break; + get_device(&hi->host->device); /* FIXME handle failure case */ + list_add_tail(&fi->list, &hi->file_info_list); + + /* prevent unloading of the host's low-level driver */ + if (!try_module_get(hi->host->driver->owner)) { + req->req.error = RAW1394_ERROR_ABORTED; + goto out_set_card; + } + WARN_ON(fi->host); + fi->host = hi->host; + fi->state = connected; + + req->req.error = RAW1394_ERROR_NONE; + req->req.generation = get_hpsb_generation(fi->host); + req->req.misc = (fi->host->node_id << 16) + | fi->host->node_count; + if (fi->protocol_version > 3) + req->req.misc |= NODEID_TO_NODE(fi->host->irm_id) << 8; +out_set_card: + spin_unlock_irqrestore(&host_info_lock, flags); + + req->req.length = 0; + break; + + default: + req->req.error = RAW1394_ERROR_STATE_ORDER; + req->req.length = 0; + break; + } + + queue_complete_req(req); + return 0; +} + +static void handle_fcp_listen(struct file_info *fi, struct pending_request *req) +{ + if (req->req.misc) { + if (fi->fcp_buffer) { + req->req.error = RAW1394_ERROR_ALREADY; + } else { + fi->fcp_buffer = int2ptr(req->req.recvb); + } + } else { + if (!fi->fcp_buffer) { + req->req.error = RAW1394_ERROR_ALREADY; + } else { + fi->fcp_buffer = NULL; + } + } + + req->req.length = 0; + queue_complete_req(req); +} + +static int handle_async_request(struct file_info *fi, + struct pending_request *req, int node) +{ + unsigned long flags; + struct hpsb_packet *packet = NULL; + u64 addr = req->req.address & 0xffffffffffffULL; + + switch (req->req.type) { + case RAW1394_REQ_ASYNC_READ: + DBGMSG("read_request called"); + packet = + hpsb_make_readpacket(fi->host, node, addr, req->req.length); + + if (!packet) + return -ENOMEM; + + if (req->req.length == 4) + req->data = &packet->header[3]; + else + req->data = packet->data; + + break; + + case RAW1394_REQ_ASYNC_WRITE: + DBGMSG("write_request called"); + + packet = hpsb_make_writepacket(fi->host, node, addr, NULL, + req->req.length); + if (!packet) + return -ENOMEM; + + if (req->req.length == 4) { + if (copy_from_user + (&packet->header[3], int2ptr(req->req.sendb), + req->req.length)) + req->req.error = RAW1394_ERROR_MEMFAULT; + } else { + if (copy_from_user + (packet->data, int2ptr(req->req.sendb), + req->req.length)) + req->req.error = RAW1394_ERROR_MEMFAULT; + } + + req->req.length = 0; + break; + + case RAW1394_REQ_ASYNC_STREAM: + DBGMSG("stream_request called"); + + packet = + hpsb_make_streampacket(fi->host, NULL, req->req.length, + node & 0x3f /*channel */ , + (req->req.misc >> 16) & 0x3, + req->req.misc & 0xf); + if (!packet) + return -ENOMEM; + + if (copy_from_user(packet->data, int2ptr(req->req.sendb), + req->req.length)) + req->req.error = RAW1394_ERROR_MEMFAULT; + + req->req.length = 0; + break; + + case RAW1394_REQ_LOCK: + DBGMSG("lock_request called"); + if ((req->req.misc == EXTCODE_FETCH_ADD) + || (req->req.misc == EXTCODE_LITTLE_ADD)) { + if (req->req.length != 4) { + req->req.error = RAW1394_ERROR_INVALID_ARG; + break; + } + } else { + if (req->req.length != 8) { + req->req.error = RAW1394_ERROR_INVALID_ARG; + break; + } + } + + packet = hpsb_make_lockpacket(fi->host, node, addr, + req->req.misc, NULL, 0); + if (!packet) + return -ENOMEM; + + if (copy_from_user(packet->data, int2ptr(req->req.sendb), + req->req.length)) { + req->req.error = RAW1394_ERROR_MEMFAULT; + break; + } + + req->data = packet->data; + req->req.length = 4; + break; + + case RAW1394_REQ_LOCK64: + DBGMSG("lock64_request called"); + if ((req->req.misc == EXTCODE_FETCH_ADD) + || (req->req.misc == EXTCODE_LITTLE_ADD)) { + if (req->req.length != 8) { + req->req.error = RAW1394_ERROR_INVALID_ARG; + break; + } + } else { + if (req->req.length != 16) { + req->req.error = RAW1394_ERROR_INVALID_ARG; + break; + } + } + packet = hpsb_make_lock64packet(fi->host, node, addr, + req->req.misc, NULL, 0); + if (!packet) + return -ENOMEM; + + if (copy_from_user(packet->data, int2ptr(req->req.sendb), + req->req.length)) { + req->req.error = RAW1394_ERROR_MEMFAULT; + break; + } + + req->data = packet->data; + req->req.length = 8; + break; + + default: + req->req.error = RAW1394_ERROR_STATE_ORDER; + } + + req->packet = packet; + + if (req->req.error) { + req->req.length = 0; + queue_complete_req(req); + return 0; + } + + hpsb_set_packet_complete_task(packet, + (void (*)(void *))queue_complete_cb, req); + + spin_lock_irqsave(&fi->reqlists_lock, flags); + list_add_tail(&req->list, &fi->req_pending); + spin_unlock_irqrestore(&fi->reqlists_lock, flags); + + packet->generation = req->req.generation; + + if (hpsb_send_packet(packet) < 0) { + req->req.error = RAW1394_ERROR_SEND_ERROR; + req->req.length = 0; + hpsb_free_tlabel(packet); + queue_complete_req(req); + } + return 0; +} + +static int handle_async_send(struct file_info *fi, struct pending_request *req) +{ + unsigned long flags; + struct hpsb_packet *packet; + int header_length = req->req.misc & 0xffff; + int expect_response = req->req.misc >> 16; + size_t data_size; + + if (header_length > req->req.length || header_length < 12 || + header_length > FIELD_SIZEOF(struct hpsb_packet, header)) { + req->req.error = RAW1394_ERROR_INVALID_ARG; + req->req.length = 0; + queue_complete_req(req); + return 0; + } + + data_size = req->req.length - header_length; + packet = hpsb_alloc_packet(data_size); + req->packet = packet; + if (!packet) + return -ENOMEM; + + if (copy_from_user(packet->header, int2ptr(req->req.sendb), + header_length)) { + req->req.error = RAW1394_ERROR_MEMFAULT; + req->req.length = 0; + queue_complete_req(req); + return 0; + } + + if (copy_from_user + (packet->data, int2ptr(req->req.sendb) + header_length, + data_size)) { + req->req.error = RAW1394_ERROR_MEMFAULT; + req->req.length = 0; + queue_complete_req(req); + return 0; + } + + packet->type = hpsb_async; + packet->node_id = packet->header[0] >> 16; + packet->tcode = (packet->header[0] >> 4) & 0xf; + packet->tlabel = (packet->header[0] >> 10) & 0x3f; + packet->host = fi->host; + packet->expect_response = expect_response; + packet->header_size = header_length; + packet->data_size = data_size; + + req->req.length = 0; + hpsb_set_packet_complete_task(packet, + (void (*)(void *))queue_complete_cb, req); + + spin_lock_irqsave(&fi->reqlists_lock, flags); + list_add_tail(&req->list, &fi->req_pending); + spin_unlock_irqrestore(&fi->reqlists_lock, flags); + + /* Update the generation of the packet just before sending. */ + packet->generation = req->req.generation; + + if (hpsb_send_packet(packet) < 0) { + req->req.error = RAW1394_ERROR_SEND_ERROR; + queue_complete_req(req); + } + + return 0; +} + +static int arm_read(struct hpsb_host *host, int nodeid, quadlet_t * buffer, + u64 addr, size_t length, u16 flags) +{ + unsigned long irqflags; + struct pending_request *req; + struct host_info *hi; + struct file_info *fi = NULL; + struct list_head *entry; + struct arm_addr *arm_addr = NULL; + struct arm_request *arm_req = NULL; + struct arm_response *arm_resp = NULL; + int found = 0, size = 0, rcode = -1; + struct arm_request_response *arm_req_resp = NULL; + + DBGMSG("arm_read called by node: %X " + "addr: %4.4x %8.8x length: %Zu", nodeid, + (u16) ((addr >> 32) & 0xFFFF), (u32) (addr & 0xFFFFFFFF), + length); + spin_lock_irqsave(&host_info_lock, irqflags); + hi = find_host_info(host); /* search address-entry */ + if (hi != NULL) { + list_for_each_entry(fi, &hi->file_info_list, list) { + entry = fi->addr_list.next; + while (entry != &(fi->addr_list)) { + arm_addr = + list_entry(entry, struct arm_addr, + addr_list); + if (((arm_addr->start) <= (addr)) + && ((arm_addr->end) >= (addr + length))) { + found = 1; + break; + } + entry = entry->next; + } + if (found) { + break; + } + } + } + rcode = -1; + if (!found) { + printk(KERN_ERR "raw1394: arm_read FAILED addr_entry not found" + " -> rcode_address_error\n"); + spin_unlock_irqrestore(&host_info_lock, irqflags); + return (RCODE_ADDRESS_ERROR); + } else { + DBGMSG("arm_read addr_entry FOUND"); + } + if (arm_addr->rec_length < length) { + DBGMSG("arm_read blocklength too big -> rcode_data_error"); + rcode = RCODE_DATA_ERROR; /* hardware error, data is unavailable */ + } + if (rcode == -1) { + if (arm_addr->access_rights & ARM_READ) { + if (!(arm_addr->client_transactions & ARM_READ)) { + memcpy(buffer, + (arm_addr->addr_space_buffer) + (addr - + (arm_addr-> + start)), + length); + DBGMSG("arm_read -> (rcode_complete)"); + rcode = RCODE_COMPLETE; + } + } else { + rcode = RCODE_TYPE_ERROR; /* function not allowed */ + DBGMSG("arm_read -> rcode_type_error (access denied)"); + } + } + if (arm_addr->notification_options & ARM_READ) { + DBGMSG("arm_read -> entering notification-section"); + req = __alloc_pending_request(GFP_ATOMIC); + if (!req) { + DBGMSG("arm_read -> rcode_conflict_error"); + spin_unlock_irqrestore(&host_info_lock, irqflags); + return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected. + The request may be retried */ + } + if (rcode == RCODE_COMPLETE) { + size = + sizeof(struct arm_request) + + sizeof(struct arm_response) + + length * sizeof(byte_t) + + sizeof(struct arm_request_response); + } else { + size = + sizeof(struct arm_request) + + sizeof(struct arm_response) + + sizeof(struct arm_request_response); + } + req->data = kmalloc(size, GFP_ATOMIC); + if (!(req->data)) { + free_pending_request(req); + DBGMSG("arm_read -> rcode_conflict_error"); + spin_unlock_irqrestore(&host_info_lock, irqflags); + return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected. + The request may be retried */ + } + req->free_data = 1; + req->file_info = fi; + req->req.type = RAW1394_REQ_ARM; + req->req.generation = get_hpsb_generation(host); + req->req.misc = + (((length << 16) & (0xFFFF0000)) | (ARM_READ & 0xFF)); + req->req.tag = arm_addr->arm_tag; + req->req.recvb = arm_addr->recvb; + req->req.length = size; + arm_req_resp = (struct arm_request_response *)(req->data); + arm_req = (struct arm_request *)((byte_t *) (req->data) + + (sizeof + (struct + arm_request_response))); + arm_resp = + (struct arm_response *)((byte_t *) (arm_req) + + (sizeof(struct arm_request))); + arm_req->buffer = NULL; + arm_resp->buffer = NULL; + if (rcode == RCODE_COMPLETE) { + byte_t *buf = + (byte_t *) arm_resp + sizeof(struct arm_response); + memcpy(buf, + (arm_addr->addr_space_buffer) + (addr - + (arm_addr-> + start)), + length); + arm_resp->buffer = + int2ptr((arm_addr->recvb) + + sizeof(struct arm_request_response) + + sizeof(struct arm_request) + + sizeof(struct arm_response)); + } + arm_resp->buffer_length = + (rcode == RCODE_COMPLETE) ? length : 0; + arm_resp->response_code = rcode; + arm_req->buffer_length = 0; + arm_req->generation = req->req.generation; + arm_req->extended_transaction_code = 0; + arm_req->destination_offset = addr; + arm_req->source_nodeid = nodeid; + arm_req->destination_nodeid = host->node_id; + arm_req->tlabel = (flags >> 10) & 0x3f; + arm_req->tcode = (flags >> 4) & 0x0f; + arm_req_resp->request = int2ptr((arm_addr->recvb) + + sizeof(struct + arm_request_response)); + arm_req_resp->response = + int2ptr((arm_addr->recvb) + + sizeof(struct arm_request_response) + + sizeof(struct arm_request)); + queue_complete_req(req); + } + spin_unlock_irqrestore(&host_info_lock, irqflags); + return (rcode); +} + +static int arm_write(struct hpsb_host *host, int nodeid, int destid, + quadlet_t * data, u64 addr, size_t length, u16 flags) +{ + unsigned long irqflags; + struct pending_request *req; + struct host_info *hi; + struct file_info *fi = NULL; + struct list_head *entry; + struct arm_addr *arm_addr = NULL; + struct arm_request *arm_req = NULL; + struct arm_response *arm_resp = NULL; + int found = 0, size = 0, rcode = -1; + struct arm_request_response *arm_req_resp = NULL; + + DBGMSG("arm_write called by node: %X " + "addr: %4.4x %8.8x length: %Zu", nodeid, + (u16) ((addr >> 32) & 0xFFFF), (u32) (addr & 0xFFFFFFFF), + length); + spin_lock_irqsave(&host_info_lock, irqflags); + hi = find_host_info(host); /* search address-entry */ + if (hi != NULL) { + list_for_each_entry(fi, &hi->file_info_list, list) { + entry = fi->addr_list.next; + while (entry != &(fi->addr_list)) { + arm_addr = + list_entry(entry, struct arm_addr, + addr_list); + if (((arm_addr->start) <= (addr)) + && ((arm_addr->end) >= (addr + length))) { + found = 1; + break; + } + entry = entry->next; + } + if (found) { + break; + } + } + } + rcode = -1; + if (!found) { + printk(KERN_ERR "raw1394: arm_write FAILED addr_entry not found" + " -> rcode_address_error\n"); + spin_unlock_irqrestore(&host_info_lock, irqflags); + return (RCODE_ADDRESS_ERROR); + } else { + DBGMSG("arm_write addr_entry FOUND"); + } + if (arm_addr->rec_length < length) { + DBGMSG("arm_write blocklength too big -> rcode_data_error"); + rcode = RCODE_DATA_ERROR; /* hardware error, data is unavailable */ + } + if (rcode == -1) { + if (arm_addr->access_rights & ARM_WRITE) { + if (!(arm_addr->client_transactions & ARM_WRITE)) { + memcpy((arm_addr->addr_space_buffer) + + (addr - (arm_addr->start)), data, + length); + DBGMSG("arm_write -> (rcode_complete)"); + rcode = RCODE_COMPLETE; + } + } else { + rcode = RCODE_TYPE_ERROR; /* function not allowed */ + DBGMSG("arm_write -> rcode_type_error (access denied)"); + } + } + if (arm_addr->notification_options & ARM_WRITE) { + DBGMSG("arm_write -> entering notification-section"); + req = __alloc_pending_request(GFP_ATOMIC); + if (!req) { + DBGMSG("arm_write -> rcode_conflict_error"); + spin_unlock_irqrestore(&host_info_lock, irqflags); + return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected. + The request my be retried */ + } + size = + sizeof(struct arm_request) + sizeof(struct arm_response) + + (length) * sizeof(byte_t) + + sizeof(struct arm_request_response); + req->data = kmalloc(size, GFP_ATOMIC); + if (!(req->data)) { + free_pending_request(req); + DBGMSG("arm_write -> rcode_conflict_error"); + spin_unlock_irqrestore(&host_info_lock, irqflags); + return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected. + The request may be retried */ + } + req->free_data = 1; + req->file_info = fi; + req->req.type = RAW1394_REQ_ARM; + req->req.generation = get_hpsb_generation(host); + req->req.misc = + (((length << 16) & (0xFFFF0000)) | (ARM_WRITE & 0xFF)); + req->req.tag = arm_addr->arm_tag; + req->req.recvb = arm_addr->recvb; + req->req.length = size; + arm_req_resp = (struct arm_request_response *)(req->data); + arm_req = (struct arm_request *)((byte_t *) (req->data) + + (sizeof + (struct + arm_request_response))); + arm_resp = + (struct arm_response *)((byte_t *) (arm_req) + + (sizeof(struct arm_request))); + arm_resp->buffer = NULL; + memcpy((byte_t *) arm_resp + sizeof(struct arm_response), + data, length); + arm_req->buffer = int2ptr((arm_addr->recvb) + + sizeof(struct arm_request_response) + + sizeof(struct arm_request) + + sizeof(struct arm_response)); + arm_req->buffer_length = length; + arm_req->generation = req->req.generation; + arm_req->extended_transaction_code = 0; + arm_req->destination_offset = addr; + arm_req->source_nodeid = nodeid; + arm_req->destination_nodeid = destid; + arm_req->tlabel = (flags >> 10) & 0x3f; + arm_req->tcode = (flags >> 4) & 0x0f; + arm_resp->buffer_length = 0; + arm_resp->response_code = rcode; + arm_req_resp->request = int2ptr((arm_addr->recvb) + + sizeof(struct + arm_request_response)); + arm_req_resp->response = + int2ptr((arm_addr->recvb) + + sizeof(struct arm_request_response) + + sizeof(struct arm_request)); + queue_complete_req(req); + } + spin_unlock_irqrestore(&host_info_lock, irqflags); + return (rcode); +} + +static int arm_lock(struct hpsb_host *host, int nodeid, quadlet_t * store, + u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode, + u16 flags) +{ + unsigned long irqflags; + struct pending_request *req; + struct host_info *hi; + struct file_info *fi = NULL; + struct list_head *entry; + struct arm_addr *arm_addr = NULL; + struct arm_request *arm_req = NULL; + struct arm_response *arm_resp = NULL; + int found = 0, size = 0, rcode = -1; + quadlet_t old, new; + struct arm_request_response *arm_req_resp = NULL; + + if (((ext_tcode & 0xFF) == EXTCODE_FETCH_ADD) || + ((ext_tcode & 0xFF) == EXTCODE_LITTLE_ADD)) { + DBGMSG("arm_lock called by node: %X " + "addr: %4.4x %8.8x extcode: %2.2X data: %8.8X", + nodeid, (u16) ((addr >> 32) & 0xFFFF), + (u32) (addr & 0xFFFFFFFF), ext_tcode & 0xFF, + be32_to_cpu(data)); + } else { + DBGMSG("arm_lock called by node: %X " + "addr: %4.4x %8.8x extcode: %2.2X data: %8.8X arg: %8.8X", + nodeid, (u16) ((addr >> 32) & 0xFFFF), + (u32) (addr & 0xFFFFFFFF), ext_tcode & 0xFF, + be32_to_cpu(data), be32_to_cpu(arg)); + } + spin_lock_irqsave(&host_info_lock, irqflags); + hi = find_host_info(host); /* search address-entry */ + if (hi != NULL) { + list_for_each_entry(fi, &hi->file_info_list, list) { + entry = fi->addr_list.next; + while (entry != &(fi->addr_list)) { + arm_addr = + list_entry(entry, struct arm_addr, + addr_list); + if (((arm_addr->start) <= (addr)) + && ((arm_addr->end) >= + (addr + sizeof(*store)))) { + found = 1; + break; + } + entry = entry->next; + } + if (found) { + break; + } + } + } + rcode = -1; + if (!found) { + printk(KERN_ERR "raw1394: arm_lock FAILED addr_entry not found" + " -> rcode_address_error\n"); + spin_unlock_irqrestore(&host_info_lock, irqflags); + return (RCODE_ADDRESS_ERROR); + } else { + DBGMSG("arm_lock addr_entry FOUND"); + } + if (rcode == -1) { + if (arm_addr->access_rights & ARM_LOCK) { + if (!(arm_addr->client_transactions & ARM_LOCK)) { + memcpy(&old, + (arm_addr->addr_space_buffer) + (addr - + (arm_addr-> + start)), + sizeof(old)); + switch (ext_tcode) { + case (EXTCODE_MASK_SWAP): + new = data | (old & ~arg); + break; + case (EXTCODE_COMPARE_SWAP): + if (old == arg) { + new = data; + } else { + new = old; + } + break; + case (EXTCODE_FETCH_ADD): + new = + cpu_to_be32(be32_to_cpu(data) + + be32_to_cpu(old)); + break; + case (EXTCODE_LITTLE_ADD): + new = + cpu_to_le32(le32_to_cpu(data) + + le32_to_cpu(old)); + break; + case (EXTCODE_BOUNDED_ADD): + if (old != arg) { + new = + cpu_to_be32(be32_to_cpu + (data) + + be32_to_cpu + (old)); + } else { + new = old; + } + break; + case (EXTCODE_WRAP_ADD): + if (old != arg) { + new = + cpu_to_be32(be32_to_cpu + (data) + + be32_to_cpu + (old)); + } else { + new = data; + } + break; + default: + rcode = RCODE_TYPE_ERROR; /* function not allowed */ + printk(KERN_ERR + "raw1394: arm_lock FAILED " + "ext_tcode not allowed -> rcode_type_error\n"); + break; + } /*switch */ + if (rcode == -1) { + DBGMSG("arm_lock -> (rcode_complete)"); + rcode = RCODE_COMPLETE; + memcpy(store, &old, sizeof(*store)); + memcpy((arm_addr->addr_space_buffer) + + (addr - (arm_addr->start)), + &new, sizeof(*store)); + } + } + } else { + rcode = RCODE_TYPE_ERROR; /* function not allowed */ + DBGMSG("arm_lock -> rcode_type_error (access denied)"); + } + } + if (arm_addr->notification_options & ARM_LOCK) { + byte_t *buf1, *buf2; + DBGMSG("arm_lock -> entering notification-section"); + req = __alloc_pending_request(GFP_ATOMIC); + if (!req) { + DBGMSG("arm_lock -> rcode_conflict_error"); + spin_unlock_irqrestore(&host_info_lock, irqflags); + return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected. + The request may be retried */ + } + size = sizeof(struct arm_request) + sizeof(struct arm_response) + 3 * sizeof(*store) + sizeof(struct arm_request_response); /* maximum */ + req->data = kmalloc(size, GFP_ATOMIC); + if (!(req->data)) { + free_pending_request(req); + DBGMSG("arm_lock -> rcode_conflict_error"); + spin_unlock_irqrestore(&host_info_lock, irqflags); + return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected. + The request may be retried */ + } + req->free_data = 1; + arm_req_resp = (struct arm_request_response *)(req->data); + arm_req = (struct arm_request *)((byte_t *) (req->data) + + (sizeof + (struct + arm_request_response))); + arm_resp = + (struct arm_response *)((byte_t *) (arm_req) + + (sizeof(struct arm_request))); + buf1 = (byte_t *) arm_resp + sizeof(struct arm_response); + buf2 = buf1 + 2 * sizeof(*store); + if ((ext_tcode == EXTCODE_FETCH_ADD) || + (ext_tcode == EXTCODE_LITTLE_ADD)) { + arm_req->buffer_length = sizeof(*store); + memcpy(buf1, &data, sizeof(*store)); + + } else { + arm_req->buffer_length = 2 * sizeof(*store); + memcpy(buf1, &arg, sizeof(*store)); + memcpy(buf1 + sizeof(*store), &data, sizeof(*store)); + } + if (rcode == RCODE_COMPLETE) { + arm_resp->buffer_length = sizeof(*store); + memcpy(buf2, &old, sizeof(*store)); + } else { + arm_resp->buffer_length = 0; + } + req->file_info = fi; + req->req.type = RAW1394_REQ_ARM; + req->req.generation = get_hpsb_generation(host); + req->req.misc = ((((sizeof(*store)) << 16) & (0xFFFF0000)) | + (ARM_LOCK & 0xFF)); + req->req.tag = arm_addr->arm_tag; + req->req.recvb = arm_addr->recvb; + req->req.length = size; + arm_req->generation = req->req.generation; + arm_req->extended_transaction_code = ext_tcode; + arm_req->destination_offset = addr; + arm_req->source_nodeid = nodeid; + arm_req->destination_nodeid = host->node_id; + arm_req->tlabel = (flags >> 10) & 0x3f; + arm_req->tcode = (flags >> 4) & 0x0f; + arm_resp->response_code = rcode; + arm_req_resp->request = int2ptr((arm_addr->recvb) + + sizeof(struct + arm_request_response)); + arm_req_resp->response = + int2ptr((arm_addr->recvb) + + sizeof(struct arm_request_response) + + sizeof(struct arm_request)); + arm_req->buffer = + int2ptr((arm_addr->recvb) + + sizeof(struct arm_request_response) + + sizeof(struct arm_request) + + sizeof(struct arm_response)); + arm_resp->buffer = + int2ptr((arm_addr->recvb) + + sizeof(struct arm_request_response) + + sizeof(struct arm_request) + + sizeof(struct arm_response) + 2 * sizeof(*store)); + queue_complete_req(req); + } + spin_unlock_irqrestore(&host_info_lock, irqflags); + return (rcode); +} + +static int arm_lock64(struct hpsb_host *host, int nodeid, octlet_t * store, + u64 addr, octlet_t data, octlet_t arg, int ext_tcode, + u16 flags) +{ + unsigned long irqflags; + struct pending_request *req; + struct host_info *hi; + struct file_info *fi = NULL; + struct list_head *entry; + struct arm_addr *arm_addr = NULL; + struct arm_request *arm_req = NULL; + struct arm_response *arm_resp = NULL; + int found = 0, size = 0, rcode = -1; + octlet_t old, new; + struct arm_request_response *arm_req_resp = NULL; + + if (((ext_tcode & 0xFF) == EXTCODE_FETCH_ADD) || + ((ext_tcode & 0xFF) == EXTCODE_LITTLE_ADD)) { + DBGMSG("arm_lock64 called by node: %X " + "addr: %4.4x %8.8x extcode: %2.2X data: %8.8X %8.8X ", + nodeid, (u16) ((addr >> 32) & 0xFFFF), + (u32) (addr & 0xFFFFFFFF), + ext_tcode & 0xFF, + (u32) ((be64_to_cpu(data) >> 32) & 0xFFFFFFFF), + (u32) (be64_to_cpu(data) & 0xFFFFFFFF)); + } else { + DBGMSG("arm_lock64 called by node: %X " + "addr: %4.4x %8.8x extcode: %2.2X data: %8.8X %8.8X arg: " + "%8.8X %8.8X ", + nodeid, (u16) ((addr >> 32) & 0xFFFF), + (u32) (addr & 0xFFFFFFFF), + ext_tcode & 0xFF, + (u32) ((be64_to_cpu(data) >> 32) & 0xFFFFFFFF), + (u32) (be64_to_cpu(data) & 0xFFFFFFFF), + (u32) ((be64_to_cpu(arg) >> 32) & 0xFFFFFFFF), + (u32) (be64_to_cpu(arg) & 0xFFFFFFFF)); + } + spin_lock_irqsave(&host_info_lock, irqflags); + hi = find_host_info(host); /* search addressentry in file_info's for host */ + if (hi != NULL) { + list_for_each_entry(fi, &hi->file_info_list, list) { + entry = fi->addr_list.next; + while (entry != &(fi->addr_list)) { + arm_addr = + list_entry(entry, struct arm_addr, + addr_list); + if (((arm_addr->start) <= (addr)) + && ((arm_addr->end) >= + (addr + sizeof(*store)))) { + found = 1; + break; + } + entry = entry->next; + } + if (found) { + break; + } + } + } + rcode = -1; + if (!found) { + printk(KERN_ERR + "raw1394: arm_lock64 FAILED addr_entry not found" + " -> rcode_address_error\n"); + spin_unlock_irqrestore(&host_info_lock, irqflags); + return (RCODE_ADDRESS_ERROR); + } else { + DBGMSG("arm_lock64 addr_entry FOUND"); + } + if (rcode == -1) { + if (arm_addr->access_rights & ARM_LOCK) { + if (!(arm_addr->client_transactions & ARM_LOCK)) { + memcpy(&old, + (arm_addr->addr_space_buffer) + (addr - + (arm_addr-> + start)), + sizeof(old)); + switch (ext_tcode) { + case (EXTCODE_MASK_SWAP): + new = data | (old & ~arg); + break; + case (EXTCODE_COMPARE_SWAP): + if (old == arg) { + new = data; + } else { + new = old; + } + break; + case (EXTCODE_FETCH_ADD): + new = + cpu_to_be64(be64_to_cpu(data) + + be64_to_cpu(old)); + break; + case (EXTCODE_LITTLE_ADD): + new = + cpu_to_le64(le64_to_cpu(data) + + le64_to_cpu(old)); + break; + case (EXTCODE_BOUNDED_ADD): + if (old != arg) { + new = + cpu_to_be64(be64_to_cpu + (data) + + be64_to_cpu + (old)); + } else { + new = old; + } + break; + case (EXTCODE_WRAP_ADD): + if (old != arg) { + new = + cpu_to_be64(be64_to_cpu + (data) + + be64_to_cpu + (old)); + } else { + new = data; + } + break; + default: + printk(KERN_ERR + "raw1394: arm_lock64 FAILED " + "ext_tcode not allowed -> rcode_type_error\n"); + rcode = RCODE_TYPE_ERROR; /* function not allowed */ + break; + } /*switch */ + if (rcode == -1) { + DBGMSG + ("arm_lock64 -> (rcode_complete)"); + rcode = RCODE_COMPLETE; + memcpy(store, &old, sizeof(*store)); + memcpy((arm_addr->addr_space_buffer) + + (addr - (arm_addr->start)), + &new, sizeof(*store)); + } + } + } else { + rcode = RCODE_TYPE_ERROR; /* function not allowed */ + DBGMSG + ("arm_lock64 -> rcode_type_error (access denied)"); + } + } + if (arm_addr->notification_options & ARM_LOCK) { + byte_t *buf1, *buf2; + DBGMSG("arm_lock64 -> entering notification-section"); + req = __alloc_pending_request(GFP_ATOMIC); + if (!req) { + spin_unlock_irqrestore(&host_info_lock, irqflags); + DBGMSG("arm_lock64 -> rcode_conflict_error"); + return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected. + The request may be retried */ + } + size = sizeof(struct arm_request) + sizeof(struct arm_response) + 3 * sizeof(*store) + sizeof(struct arm_request_response); /* maximum */ + req->data = kmalloc(size, GFP_ATOMIC); + if (!(req->data)) { + free_pending_request(req); + spin_unlock_irqrestore(&host_info_lock, irqflags); + DBGMSG("arm_lock64 -> rcode_conflict_error"); + return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected. + The request may be retried */ + } + req->free_data = 1; + arm_req_resp = (struct arm_request_response *)(req->data); + arm_req = (struct arm_request *)((byte_t *) (req->data) + + (sizeof + (struct + arm_request_response))); + arm_resp = + (struct arm_response *)((byte_t *) (arm_req) + + (sizeof(struct arm_request))); + buf1 = (byte_t *) arm_resp + sizeof(struct arm_response); + buf2 = buf1 + 2 * sizeof(*store); + if ((ext_tcode == EXTCODE_FETCH_ADD) || + (ext_tcode == EXTCODE_LITTLE_ADD)) { + arm_req->buffer_length = sizeof(*store); + memcpy(buf1, &data, sizeof(*store)); + + } else { + arm_req->buffer_length = 2 * sizeof(*store); + memcpy(buf1, &arg, sizeof(*store)); + memcpy(buf1 + sizeof(*store), &data, sizeof(*store)); + } + if (rcode == RCODE_COMPLETE) { + arm_resp->buffer_length = sizeof(*store); + memcpy(buf2, &old, sizeof(*store)); + } else { + arm_resp->buffer_length = 0; + } + req->file_info = fi; + req->req.type = RAW1394_REQ_ARM; + req->req.generation = get_hpsb_generation(host); + req->req.misc = ((((sizeof(*store)) << 16) & (0xFFFF0000)) | + (ARM_LOCK & 0xFF)); + req->req.tag = arm_addr->arm_tag; + req->req.recvb = arm_addr->recvb; + req->req.length = size; + arm_req->generation = req->req.generation; + arm_req->extended_transaction_code = ext_tcode; + arm_req->destination_offset = addr; + arm_req->source_nodeid = nodeid; + arm_req->destination_nodeid = host->node_id; + arm_req->tlabel = (flags >> 10) & 0x3f; + arm_req->tcode = (flags >> 4) & 0x0f; + arm_resp->response_code = rcode; + arm_req_resp->request = int2ptr((arm_addr->recvb) + + sizeof(struct + arm_request_response)); + arm_req_resp->response = + int2ptr((arm_addr->recvb) + + sizeof(struct arm_request_response) + + sizeof(struct arm_request)); + arm_req->buffer = + int2ptr((arm_addr->recvb) + + sizeof(struct arm_request_response) + + sizeof(struct arm_request) + + sizeof(struct arm_response)); + arm_resp->buffer = + int2ptr((arm_addr->recvb) + + sizeof(struct arm_request_response) + + sizeof(struct arm_request) + + sizeof(struct arm_response) + 2 * sizeof(*store)); + queue_complete_req(req); + } + spin_unlock_irqrestore(&host_info_lock, irqflags); + return (rcode); +} + +static int arm_register(struct file_info *fi, struct pending_request *req) +{ + int retval; + struct arm_addr *addr; + struct host_info *hi; + struct file_info *fi_hlp = NULL; + struct list_head *entry; + struct arm_addr *arm_addr = NULL; + int same_host, another_host; + unsigned long flags; + + DBGMSG("arm_register called " + "addr(Offset): %8.8x %8.8x length: %u " + "rights: %2.2X notify: %2.2X " + "max_blk_len: %4.4X", + (u32) ((req->req.address >> 32) & 0xFFFF), + (u32) (req->req.address & 0xFFFFFFFF), + req->req.length, ((req->req.misc >> 8) & 0xFF), + (req->req.misc & 0xFF), ((req->req.misc >> 16) & 0xFFFF)); + /* check addressrange */ + if ((((req->req.address) & ~(0xFFFFFFFFFFFFULL)) != 0) || + (((req->req.address + req->req.length) & ~(0xFFFFFFFFFFFFULL)) != + 0)) { + req->req.length = 0; + return (-EINVAL); + } + /* addr-list-entry for fileinfo */ + addr = kmalloc(sizeof(*addr), GFP_KERNEL); + if (!addr) { + req->req.length = 0; + return (-ENOMEM); + } + /* allocation of addr_space_buffer */ + addr->addr_space_buffer = vmalloc(req->req.length); + if (!(addr->addr_space_buffer)) { + kfree(addr); + req->req.length = 0; + return (-ENOMEM); + } + /* initialization of addr_space_buffer */ + if ((req->req.sendb) == (unsigned long)NULL) { + /* init: set 0 */ + memset(addr->addr_space_buffer, 0, req->req.length); + } else { + /* init: user -> kernel */ + if (copy_from_user + (addr->addr_space_buffer, int2ptr(req->req.sendb), + req->req.length)) { + vfree(addr->addr_space_buffer); + kfree(addr); + return (-EFAULT); + } + } + INIT_LIST_HEAD(&addr->addr_list); + addr->arm_tag = req->req.tag; + addr->start = req->req.address; + addr->end = req->req.address + req->req.length; + addr->access_rights = (u8) (req->req.misc & 0x0F); + addr->notification_options = (u8) ((req->req.misc >> 4) & 0x0F); + addr->client_transactions = (u8) ((req->req.misc >> 8) & 0x0F); + addr->access_rights |= addr->client_transactions; + addr->notification_options |= addr->client_transactions; + addr->recvb = req->req.recvb; + addr->rec_length = (u16) ((req->req.misc >> 16) & 0xFFFF); + + spin_lock_irqsave(&host_info_lock, flags); + hi = find_host_info(fi->host); + same_host = 0; + another_host = 0; + /* same host with address-entry containing same addressrange ? */ + list_for_each_entry(fi_hlp, &hi->file_info_list, list) { + entry = fi_hlp->addr_list.next; + while (entry != &(fi_hlp->addr_list)) { + arm_addr = + list_entry(entry, struct arm_addr, addr_list); + if ((arm_addr->start == addr->start) + && (arm_addr->end == addr->end)) { + DBGMSG("same host ownes same " + "addressrange -> EALREADY"); + same_host = 1; + break; + } + entry = entry->next; + } + if (same_host) { + break; + } + } + if (same_host) { + /* addressrange occupied by same host */ + spin_unlock_irqrestore(&host_info_lock, flags); + vfree(addr->addr_space_buffer); + kfree(addr); + return (-EALREADY); + } + /* another host with valid address-entry containing same addressrange */ + list_for_each_entry(hi, &host_info_list, list) { + if (hi->host != fi->host) { + list_for_each_entry(fi_hlp, &hi->file_info_list, list) { + entry = fi_hlp->addr_list.next; + while (entry != &(fi_hlp->addr_list)) { + arm_addr = + list_entry(entry, struct arm_addr, + addr_list); + if ((arm_addr->start == addr->start) + && (arm_addr->end == addr->end)) { + DBGMSG + ("another host ownes same " + "addressrange"); + another_host = 1; + break; + } + entry = entry->next; + } + if (another_host) { + break; + } + } + } + } + spin_unlock_irqrestore(&host_info_lock, flags); + + if (another_host) { + DBGMSG("another hosts entry is valid -> SUCCESS"); + if (copy_to_user(int2ptr(req->req.recvb), + &addr->start, sizeof(u64))) { + printk(KERN_ERR "raw1394: arm_register failed " + " address-range-entry is invalid -> EFAULT !!!\n"); + vfree(addr->addr_space_buffer); + kfree(addr); + return (-EFAULT); + } + free_pending_request(req); /* immediate success or fail */ + /* INSERT ENTRY */ + spin_lock_irqsave(&host_info_lock, flags); + list_add_tail(&addr->addr_list, &fi->addr_list); + spin_unlock_irqrestore(&host_info_lock, flags); + return 0; + } + retval = + hpsb_register_addrspace(&raw1394_highlevel, fi->host, &arm_ops, + req->req.address, + req->req.address + req->req.length); + if (retval) { + /* INSERT ENTRY */ + spin_lock_irqsave(&host_info_lock, flags); + list_add_tail(&addr->addr_list, &fi->addr_list); + spin_unlock_irqrestore(&host_info_lock, flags); + } else { + DBGMSG("arm_register failed errno: %d \n", retval); + vfree(addr->addr_space_buffer); + kfree(addr); + return (-EALREADY); + } + free_pending_request(req); /* immediate success or fail */ + return 0; +} + +static int arm_unregister(struct file_info *fi, struct pending_request *req) +{ + int found = 0; + int retval = 0; + struct list_head *entry; + struct arm_addr *addr = NULL; + struct host_info *hi; + struct file_info *fi_hlp = NULL; + struct arm_addr *arm_addr = NULL; + int another_host; + unsigned long flags; + + DBGMSG("arm_Unregister called addr(Offset): " + "%8.8x %8.8x", + (u32) ((req->req.address >> 32) & 0xFFFF), + (u32) (req->req.address & 0xFFFFFFFF)); + spin_lock_irqsave(&host_info_lock, flags); + /* get addr */ + entry = fi->addr_list.next; + while (entry != &(fi->addr_list)) { + addr = list_entry(entry, struct arm_addr, addr_list); + if (addr->start == req->req.address) { + found = 1; + break; + } + entry = entry->next; + } + if (!found) { + DBGMSG("arm_Unregister addr not found"); + spin_unlock_irqrestore(&host_info_lock, flags); + return (-EINVAL); + } + DBGMSG("arm_Unregister addr found"); + another_host = 0; + /* another host with valid address-entry containing + same addressrange */ + list_for_each_entry(hi, &host_info_list, list) { + if (hi->host != fi->host) { + list_for_each_entry(fi_hlp, &hi->file_info_list, list) { + entry = fi_hlp->addr_list.next; + while (entry != &(fi_hlp->addr_list)) { + arm_addr = list_entry(entry, + struct arm_addr, + addr_list); + if (arm_addr->start == addr->start) { + DBGMSG("another host ownes " + "same addressrange"); + another_host = 1; + break; + } + entry = entry->next; + } + if (another_host) { + break; + } + } + } + } + if (another_host) { + DBGMSG("delete entry from list -> success"); + list_del(&addr->addr_list); + spin_unlock_irqrestore(&host_info_lock, flags); + vfree(addr->addr_space_buffer); + kfree(addr); + free_pending_request(req); /* immediate success or fail */ + return 0; + } + retval = + hpsb_unregister_addrspace(&raw1394_highlevel, fi->host, + addr->start); + if (!retval) { + printk(KERN_ERR "raw1394: arm_Unregister failed -> EINVAL\n"); + spin_unlock_irqrestore(&host_info_lock, flags); + return (-EINVAL); + } + DBGMSG("delete entry from list -> success"); + list_del(&addr->addr_list); + spin_unlock_irqrestore(&host_info_lock, flags); + vfree(addr->addr_space_buffer); + kfree(addr); + free_pending_request(req); /* immediate success or fail */ + return 0; +} + +/* Copy data from ARM buffer(s) to user buffer. */ +static int arm_get_buf(struct file_info *fi, struct pending_request *req) +{ + struct arm_addr *arm_addr = NULL; + unsigned long flags; + unsigned long offset; + + struct list_head *entry; + + DBGMSG("arm_get_buf " + "addr(Offset): %04X %08X length: %u", + (u32) ((req->req.address >> 32) & 0xFFFF), + (u32) (req->req.address & 0xFFFFFFFF), (u32) req->req.length); + + spin_lock_irqsave(&host_info_lock, flags); + entry = fi->addr_list.next; + while (entry != &(fi->addr_list)) { + arm_addr = list_entry(entry, struct arm_addr, addr_list); + if ((arm_addr->start <= req->req.address) && + (arm_addr->end > req->req.address)) { + if (req->req.address + req->req.length <= arm_addr->end) { + offset = req->req.address - arm_addr->start; + spin_unlock_irqrestore(&host_info_lock, flags); + + DBGMSG + ("arm_get_buf copy_to_user( %08X, %p, %u )", + (u32) req->req.recvb, + arm_addr->addr_space_buffer + offset, + (u32) req->req.length); + if (copy_to_user + (int2ptr(req->req.recvb), + arm_addr->addr_space_buffer + offset, + req->req.length)) + return (-EFAULT); + + /* We have to free the request, because we + * queue no response, and therefore nobody + * will free it. */ + free_pending_request(req); + return 0; + } else { + DBGMSG("arm_get_buf request exceeded mapping"); + spin_unlock_irqrestore(&host_info_lock, flags); + return (-EINVAL); + } + } + entry = entry->next; + } + spin_unlock_irqrestore(&host_info_lock, flags); + return (-EINVAL); +} + +/* Copy data from user buffer to ARM buffer(s). */ +static int arm_set_buf(struct file_info *fi, struct pending_request *req) +{ + struct arm_addr *arm_addr = NULL; + unsigned long flags; + unsigned long offset; + + struct list_head *entry; + + DBGMSG("arm_set_buf " + "addr(Offset): %04X %08X length: %u", + (u32) ((req->req.address >> 32) & 0xFFFF), + (u32) (req->req.address & 0xFFFFFFFF), (u32) req->req.length); + + spin_lock_irqsave(&host_info_lock, flags); + entry = fi->addr_list.next; + while (entry != &(fi->addr_list)) { + arm_addr = list_entry(entry, struct arm_addr, addr_list); + if ((arm_addr->start <= req->req.address) && + (arm_addr->end > req->req.address)) { + if (req->req.address + req->req.length <= arm_addr->end) { + offset = req->req.address - arm_addr->start; + spin_unlock_irqrestore(&host_info_lock, flags); + + DBGMSG + ("arm_set_buf copy_from_user( %p, %08X, %u )", + arm_addr->addr_space_buffer + offset, + (u32) req->req.sendb, + (u32) req->req.length); + if (copy_from_user + (arm_addr->addr_space_buffer + offset, + int2ptr(req->req.sendb), + req->req.length)) + return (-EFAULT); + + /* We have to free the request, because we + * queue no response, and therefore nobody + * will free it. */ + free_pending_request(req); + return 0; + } else { + DBGMSG("arm_set_buf request exceeded mapping"); + spin_unlock_irqrestore(&host_info_lock, flags); + return (-EINVAL); + } + } + entry = entry->next; + } + spin_unlock_irqrestore(&host_info_lock, flags); + return (-EINVAL); +} + +static int reset_notification(struct file_info *fi, struct pending_request *req) +{ + DBGMSG("reset_notification called - switch %s ", + (req->req.misc == RAW1394_NOTIFY_OFF) ? "OFF" : "ON"); + if ((req->req.misc == RAW1394_NOTIFY_OFF) || + (req->req.misc == RAW1394_NOTIFY_ON)) { + fi->notification = (u8) req->req.misc; + free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */ + return 0; + } + /* error EINVAL (22) invalid argument */ + return (-EINVAL); +} + +static int write_phypacket(struct file_info *fi, struct pending_request *req) +{ + struct hpsb_packet *packet = NULL; + int retval = 0; + quadlet_t data; + unsigned long flags; + + data = be32_to_cpu((u32) req->req.sendb); + DBGMSG("write_phypacket called - quadlet 0x%8.8x ", data); + packet = hpsb_make_phypacket(fi->host, data); + if (!packet) + return -ENOMEM; + req->req.length = 0; + req->packet = packet; + hpsb_set_packet_complete_task(packet, + (void (*)(void *))queue_complete_cb, req); + spin_lock_irqsave(&fi->reqlists_lock, flags); + list_add_tail(&req->list, &fi->req_pending); + spin_unlock_irqrestore(&fi->reqlists_lock, flags); + packet->generation = req->req.generation; + retval = hpsb_send_packet(packet); + DBGMSG("write_phypacket send_packet called => retval: %d ", retval); + if (retval < 0) { + req->req.error = RAW1394_ERROR_SEND_ERROR; + req->req.length = 0; + queue_complete_req(req); + } + return 0; +} + +static int get_config_rom(struct file_info *fi, struct pending_request *req) +{ + int ret = 0; + quadlet_t *data = kmalloc(req->req.length, GFP_KERNEL); + int status; + + if (!data) + return -ENOMEM; + + status = + csr1212_read(fi->host->csr.rom, CSR1212_CONFIG_ROM_SPACE_OFFSET, + data, req->req.length); + if (copy_to_user(int2ptr(req->req.recvb), data, req->req.length)) + ret = -EFAULT; + if (copy_to_user + (int2ptr(req->req.tag), &fi->host->csr.rom->cache_head->len, + sizeof(fi->host->csr.rom->cache_head->len))) + ret = -EFAULT; + if (copy_to_user(int2ptr(req->req.address), &fi->host->csr.generation, + sizeof(fi->host->csr.generation))) + ret = -EFAULT; + if (copy_to_user(int2ptr(req->req.sendb), &status, sizeof(status))) + ret = -EFAULT; + kfree(data); + if (ret >= 0) { + free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */ + } + return ret; +} + +static int update_config_rom(struct file_info *fi, struct pending_request *req) +{ + int ret = 0; + quadlet_t *data = kmalloc(req->req.length, GFP_KERNEL); + if (!data) + return -ENOMEM; + if (copy_from_user(data, int2ptr(req->req.sendb), req->req.length)) { + ret = -EFAULT; + } else { + int status = hpsb_update_config_rom(fi->host, + data, req->req.length, + (unsigned char)req->req. + misc); + if (copy_to_user + (int2ptr(req->req.recvb), &status, sizeof(status))) + ret = -ENOMEM; + } + kfree(data); + if (ret >= 0) { + free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */ + fi->cfgrom_upd = 1; + } + return ret; +} + +static int modify_config_rom(struct file_info *fi, struct pending_request *req) +{ + struct csr1212_keyval *kv; + struct csr1212_csr_rom_cache *cache; + struct csr1212_dentry *dentry; + u32 dr; + int ret = 0; + + if (req->req.misc == ~0) { + if (req->req.length == 0) + return -EINVAL; + + /* Find an unused slot */ + for (dr = 0; + dr < RAW1394_MAX_USER_CSR_DIRS && fi->csr1212_dirs[dr]; + dr++) ; + + if (dr == RAW1394_MAX_USER_CSR_DIRS) + return -ENOMEM; + + fi->csr1212_dirs[dr] = + csr1212_new_directory(CSR1212_KV_ID_VENDOR); + if (!fi->csr1212_dirs[dr]) + return -ENOMEM; + } else { + dr = req->req.misc; + if (!fi->csr1212_dirs[dr]) + return -EINVAL; + + /* Delete old stuff */ + for (dentry = + fi->csr1212_dirs[dr]->value.directory.dentries_head; + dentry; dentry = dentry->next) { + csr1212_detach_keyval_from_directory(fi->host->csr.rom-> + root_kv, + dentry->kv); + } + + if (req->req.length == 0) { + csr1212_release_keyval(fi->csr1212_dirs[dr]); + fi->csr1212_dirs[dr] = NULL; + + hpsb_update_config_rom_image(fi->host); + free_pending_request(req); + return 0; + } + } + + cache = csr1212_rom_cache_malloc(0, req->req.length); + if (!cache) { + csr1212_release_keyval(fi->csr1212_dirs[dr]); + fi->csr1212_dirs[dr] = NULL; + return -ENOMEM; + } + + cache->filled_head = kmalloc(sizeof(*cache->filled_head), GFP_KERNEL); + if (!cache->filled_head) { + csr1212_release_keyval(fi->csr1212_dirs[dr]); + fi->csr1212_dirs[dr] = NULL; + CSR1212_FREE(cache); + return -ENOMEM; + } + cache->filled_tail = cache->filled_head; + + if (copy_from_user(cache->data, int2ptr(req->req.sendb), + req->req.length)) { + csr1212_release_keyval(fi->csr1212_dirs[dr]); + fi->csr1212_dirs[dr] = NULL; + ret = -EFAULT; + } else { + cache->len = req->req.length; + cache->filled_head->offset_start = 0; + cache->filled_head->offset_end = cache->size - 1; + + cache->layout_head = cache->layout_tail = fi->csr1212_dirs[dr]; + + ret = CSR1212_SUCCESS; + /* parse all the items */ + for (kv = cache->layout_head; ret == CSR1212_SUCCESS && kv; + kv = kv->next) { + ret = csr1212_parse_keyval(kv, cache); + } + + /* attach top level items to the root directory */ + for (dentry = + fi->csr1212_dirs[dr]->value.directory.dentries_head; + ret == CSR1212_SUCCESS && dentry; dentry = dentry->next) { + ret = + csr1212_attach_keyval_to_directory(fi->host->csr. + rom->root_kv, + dentry->kv); + } + + if (ret == CSR1212_SUCCESS) { + ret = hpsb_update_config_rom_image(fi->host); + + if (ret >= 0 && copy_to_user(int2ptr(req->req.recvb), + &dr, sizeof(dr))) { + ret = -ENOMEM; + } + } + } + kfree(cache->filled_head); + CSR1212_FREE(cache); + + if (ret >= 0) { + /* we have to free the request, because we queue no response, + * and therefore nobody will free it */ + free_pending_request(req); + return 0; + } else { + for (dentry = + fi->csr1212_dirs[dr]->value.directory.dentries_head; + dentry; dentry = dentry->next) { + csr1212_detach_keyval_from_directory(fi->host->csr.rom-> + root_kv, + dentry->kv); + } + csr1212_release_keyval(fi->csr1212_dirs[dr]); + fi->csr1212_dirs[dr] = NULL; + return ret; + } +} + +static int state_connected(struct file_info *fi, struct pending_request *req) +{ + int node = req->req.address >> 48; + + req->req.error = RAW1394_ERROR_NONE; + + switch (req->req.type) { + + case RAW1394_REQ_ECHO: + queue_complete_req(req); + return 0; + + case RAW1394_REQ_ARM_REGISTER: + return arm_register(fi, req); + + case RAW1394_REQ_ARM_UNREGISTER: + return arm_unregister(fi, req); + + case RAW1394_REQ_ARM_SET_BUF: + return arm_set_buf(fi, req); + + case RAW1394_REQ_ARM_GET_BUF: + return arm_get_buf(fi, req); + + case RAW1394_REQ_RESET_NOTIFY: + return reset_notification(fi, req); + + case RAW1394_REQ_ISO_SEND: + case RAW1394_REQ_ISO_LISTEN: + printk(KERN_DEBUG "raw1394: old iso ABI has been removed\n"); + req->req.error = RAW1394_ERROR_COMPAT; + req->req.misc = RAW1394_KERNELAPI_VERSION; + queue_complete_req(req); + return 0; + + case RAW1394_REQ_FCP_LISTEN: + handle_fcp_listen(fi, req); + return 0; + + case RAW1394_REQ_RESET_BUS: + if (req->req.misc == RAW1394_LONG_RESET) { + DBGMSG("busreset called (type: LONG)"); + hpsb_reset_bus(fi->host, LONG_RESET); + free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */ + return 0; + } + if (req->req.misc == RAW1394_SHORT_RESET) { + DBGMSG("busreset called (type: SHORT)"); + hpsb_reset_bus(fi->host, SHORT_RESET); + free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */ + return 0; + } + /* error EINVAL (22) invalid argument */ + return (-EINVAL); + case RAW1394_REQ_GET_ROM: + return get_config_rom(fi, req); + + case RAW1394_REQ_UPDATE_ROM: + return update_config_rom(fi, req); + + case RAW1394_REQ_MODIFY_ROM: + return modify_config_rom(fi, req); + } + + if (req->req.generation != get_hpsb_generation(fi->host)) { + req->req.error = RAW1394_ERROR_GENERATION; + req->req.generation = get_hpsb_generation(fi->host); + req->req.length = 0; + queue_complete_req(req); + return 0; + } + + switch (req->req.type) { + case RAW1394_REQ_PHYPACKET: + return write_phypacket(fi, req); + case RAW1394_REQ_ASYNC_SEND: + return handle_async_send(fi, req); + } + + if (req->req.length == 0) { + req->req.error = RAW1394_ERROR_INVALID_ARG; + queue_complete_req(req); + return 0; + } + + return handle_async_request(fi, req, node); +} + +static ssize_t raw1394_write(struct file *file, const char __user * buffer, + size_t count, loff_t * offset_is_ignored) +{ + struct file_info *fi = file->private_data; + struct pending_request *req; + ssize_t retval = -EBADFD; + +#ifdef CONFIG_COMPAT + if (count == sizeof(struct compat_raw1394_req) && + sizeof(struct compat_raw1394_req) != + sizeof(struct raw1394_request)) { + buffer = raw1394_compat_write(buffer); + if (IS_ERR((__force void *)buffer)) + return PTR_ERR((__force void *)buffer); + } else +#endif + if (count != sizeof(struct raw1394_request)) { + return -EINVAL; + } + + req = alloc_pending_request(); + if (req == NULL) { + return -ENOMEM; + } + req->file_info = fi; + + if (copy_from_user(&req->req, buffer, sizeof(struct raw1394_request))) { + free_pending_request(req); + return -EFAULT; + } + + if (!mutex_trylock(&fi->state_mutex)) { + free_pending_request(req); + return -EAGAIN; + } + + switch (fi->state) { + case opened: + retval = state_opened(fi, req); + break; + + case initialized: + retval = state_initialized(fi, req); + break; + + case connected: + retval = state_connected(fi, req); + break; + } + + mutex_unlock(&fi->state_mutex); + + if (retval < 0) { + free_pending_request(req); + } else { + BUG_ON(retval); + retval = count; + } + + return retval; +} + +/* rawiso operations */ + +/* check if any RAW1394_REQ_RAWISO_ACTIVITY event is already in the + * completion queue (reqlists_lock must be taken) */ +static inline int __rawiso_event_in_queue(struct file_info *fi) +{ + struct pending_request *req; + + list_for_each_entry(req, &fi->req_complete, list) + if (req->req.type == RAW1394_REQ_RAWISO_ACTIVITY) + return 1; + + return 0; +} + +/* put a RAWISO_ACTIVITY event in the queue, if one isn't there already */ +static void queue_rawiso_event(struct file_info *fi) +{ + unsigned long flags; + + spin_lock_irqsave(&fi->reqlists_lock, flags); + + /* only one ISO activity event may be in the queue */ + if (!__rawiso_event_in_queue(fi)) { + struct pending_request *req = + __alloc_pending_request(GFP_ATOMIC); + + if (req) { + req->file_info = fi; + req->req.type = RAW1394_REQ_RAWISO_ACTIVITY; + req->req.generation = get_hpsb_generation(fi->host); + __queue_complete_req(req); + } else { + /* on allocation failure, signal an overflow */ + if (fi->iso_handle) { + atomic_inc(&fi->iso_handle->overflows); + } + } + } + spin_unlock_irqrestore(&fi->reqlists_lock, flags); +} + +static void rawiso_activity_cb(struct hpsb_iso *iso) +{ + unsigned long flags; + struct host_info *hi; + struct file_info *fi; + + spin_lock_irqsave(&host_info_lock, flags); + hi = find_host_info(iso->host); + + if (hi != NULL) { + list_for_each_entry(fi, &hi->file_info_list, list) { + if (fi->iso_handle == iso) + queue_rawiso_event(fi); + } + } + + spin_unlock_irqrestore(&host_info_lock, flags); +} + +/* helper function - gather all the kernel iso status bits for returning to user-space */ +static void raw1394_iso_fill_status(struct hpsb_iso *iso, + struct raw1394_iso_status *stat) +{ + int overflows = atomic_read(&iso->overflows); + int skips = atomic_read(&iso->skips); + + stat->config.data_buf_size = iso->buf_size; + stat->config.buf_packets = iso->buf_packets; + stat->config.channel = iso->channel; + stat->config.speed = iso->speed; + stat->config.irq_interval = iso->irq_interval; + stat->n_packets = hpsb_iso_n_ready(iso); + stat->overflows = ((skips & 0xFFFF) << 16) | ((overflows & 0xFFFF)); + stat->xmit_cycle = iso->xmit_cycle; +} + +static int raw1394_iso_xmit_init(struct file_info *fi, void __user * uaddr) +{ + struct raw1394_iso_status stat; + + if (!fi->host) + return -EINVAL; + + if (copy_from_user(&stat, uaddr, sizeof(stat))) + return -EFAULT; + + fi->iso_handle = hpsb_iso_xmit_init(fi->host, + stat.config.data_buf_size, + stat.config.buf_packets, + stat.config.channel, + stat.config.speed, + stat.config.irq_interval, + rawiso_activity_cb); + if (!fi->iso_handle) + return -ENOMEM; + + fi->iso_state = RAW1394_ISO_XMIT; + + raw1394_iso_fill_status(fi->iso_handle, &stat); + if (copy_to_user(uaddr, &stat, sizeof(stat))) + return -EFAULT; + + /* queue an event to get things started */ + rawiso_activity_cb(fi->iso_handle); + + return 0; +} + +static int raw1394_iso_recv_init(struct file_info *fi, void __user * uaddr) +{ + struct raw1394_iso_status stat; + + if (!fi->host) + return -EINVAL; + + if (copy_from_user(&stat, uaddr, sizeof(stat))) + return -EFAULT; + + fi->iso_handle = hpsb_iso_recv_init(fi->host, + stat.config.data_buf_size, + stat.config.buf_packets, + stat.config.channel, + stat.config.dma_mode, + stat.config.irq_interval, + rawiso_activity_cb); + if (!fi->iso_handle) + return -ENOMEM; + + fi->iso_state = RAW1394_ISO_RECV; + + raw1394_iso_fill_status(fi->iso_handle, &stat); + if (copy_to_user(uaddr, &stat, sizeof(stat))) + return -EFAULT; + return 0; +} + +static int raw1394_iso_get_status(struct file_info *fi, void __user * uaddr) +{ + struct raw1394_iso_status stat; + struct hpsb_iso *iso = fi->iso_handle; + + raw1394_iso_fill_status(fi->iso_handle, &stat); + if (copy_to_user(uaddr, &stat, sizeof(stat))) + return -EFAULT; + + /* reset overflow counter */ + atomic_set(&iso->overflows, 0); + /* reset skip counter */ + atomic_set(&iso->skips, 0); + + return 0; +} + +/* copy N packet_infos out of the ringbuffer into user-supplied array */ +static int raw1394_iso_recv_packets(struct file_info *fi, void __user * uaddr) +{ + struct raw1394_iso_packets upackets; + unsigned int packet = fi->iso_handle->first_packet; + int i; + + if (copy_from_user(&upackets, uaddr, sizeof(upackets))) + return -EFAULT; + + if (upackets.n_packets > hpsb_iso_n_ready(fi->iso_handle)) + return -EINVAL; + + /* ensure user-supplied buffer is accessible and big enough */ + if (!access_ok(VERIFY_WRITE, upackets.infos, + upackets.n_packets * + sizeof(struct raw1394_iso_packet_info))) + return -EFAULT; + + /* copy the packet_infos out */ + for (i = 0; i < upackets.n_packets; i++) { + if (__copy_to_user(&upackets.infos[i], + &fi->iso_handle->infos[packet], + sizeof(struct raw1394_iso_packet_info))) + return -EFAULT; + + packet = (packet + 1) % fi->iso_handle->buf_packets; + } + + return 0; +} + +/* copy N packet_infos from user to ringbuffer, and queue them for transmission */ +static int raw1394_iso_send_packets(struct file_info *fi, void __user * uaddr) +{ + struct raw1394_iso_packets upackets; + int i, rv; + + if (copy_from_user(&upackets, uaddr, sizeof(upackets))) + return -EFAULT; + + if (upackets.n_packets >= fi->iso_handle->buf_packets) + return -EINVAL; + + if (upackets.n_packets >= hpsb_iso_n_ready(fi->iso_handle)) + return -EAGAIN; + + /* ensure user-supplied buffer is accessible and big enough */ + if (!access_ok(VERIFY_READ, upackets.infos, + upackets.n_packets * + sizeof(struct raw1394_iso_packet_info))) + return -EFAULT; + + /* copy the infos structs in and queue the packets */ + for (i = 0; i < upackets.n_packets; i++) { + struct raw1394_iso_packet_info info; + + if (__copy_from_user(&info, &upackets.infos[i], + sizeof(struct raw1394_iso_packet_info))) + return -EFAULT; + + rv = hpsb_iso_xmit_queue_packet(fi->iso_handle, info.offset, + info.len, info.tag, info.sy); + if (rv) + return rv; + } + + return 0; +} + +static void raw1394_iso_shutdown(struct file_info *fi) +{ + if (fi->iso_handle) + hpsb_iso_shutdown(fi->iso_handle); + + fi->iso_handle = NULL; + fi->iso_state = RAW1394_ISO_INACTIVE; +} + +static int raw1394_read_cycle_timer(struct file_info *fi, void __user * uaddr) +{ + struct raw1394_cycle_timer ct; + int err; + + err = hpsb_read_cycle_timer(fi->host, &ct.cycle_timer, &ct.local_time); + if (!err) + if (copy_to_user(uaddr, &ct, sizeof(ct))) + err = -EFAULT; + return err; +} + +/* mmap the rawiso xmit/recv buffer */ +static int raw1394_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct file_info *fi = file->private_data; + int ret; + + if (!mutex_trylock(&fi->state_mutex)) + return -EAGAIN; + + if (fi->iso_state == RAW1394_ISO_INACTIVE) + ret = -EINVAL; + else + ret = dma_region_mmap(&fi->iso_handle->data_buf, file, vma); + + mutex_unlock(&fi->state_mutex); + + return ret; +} + +static long raw1394_ioctl_inactive(struct file_info *fi, unsigned int cmd, + void __user *argp) +{ + switch (cmd) { + case RAW1394_IOC_ISO_XMIT_INIT: + return raw1394_iso_xmit_init(fi, argp); + case RAW1394_IOC_ISO_RECV_INIT: + return raw1394_iso_recv_init(fi, argp); + default: + return -EINVAL; + } +} + +static long raw1394_ioctl_recv(struct file_info *fi, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + + switch (cmd) { + case RAW1394_IOC_ISO_RECV_START:{ + int args[3]; + + if (copy_from_user(&args[0], argp, sizeof(args))) + return -EFAULT; + return hpsb_iso_recv_start(fi->iso_handle, + args[0], args[1], args[2]); + } + case RAW1394_IOC_ISO_XMIT_RECV_STOP: + hpsb_iso_stop(fi->iso_handle); + return 0; + case RAW1394_IOC_ISO_RECV_LISTEN_CHANNEL: + return hpsb_iso_recv_listen_channel(fi->iso_handle, arg); + case RAW1394_IOC_ISO_RECV_UNLISTEN_CHANNEL: + return hpsb_iso_recv_unlisten_channel(fi->iso_handle, arg); + case RAW1394_IOC_ISO_RECV_SET_CHANNEL_MASK:{ + u64 mask; + + if (copy_from_user(&mask, argp, sizeof(mask))) + return -EFAULT; + return hpsb_iso_recv_set_channel_mask(fi->iso_handle, + mask); + } + case RAW1394_IOC_ISO_GET_STATUS: + return raw1394_iso_get_status(fi, argp); + case RAW1394_IOC_ISO_RECV_PACKETS: + return raw1394_iso_recv_packets(fi, argp); + case RAW1394_IOC_ISO_RECV_RELEASE_PACKETS: + return hpsb_iso_recv_release_packets(fi->iso_handle, arg); + case RAW1394_IOC_ISO_RECV_FLUSH: + return hpsb_iso_recv_flush(fi->iso_handle); + case RAW1394_IOC_ISO_SHUTDOWN: + raw1394_iso_shutdown(fi); + return 0; + case RAW1394_IOC_ISO_QUEUE_ACTIVITY: + queue_rawiso_event(fi); + return 0; + default: + return -EINVAL; + } +} + +static long raw1394_ioctl_xmit(struct file_info *fi, unsigned int cmd, + void __user *argp) +{ + switch (cmd) { + case RAW1394_IOC_ISO_XMIT_START:{ + int args[2]; + + if (copy_from_user(&args[0], argp, sizeof(args))) + return -EFAULT; + return hpsb_iso_xmit_start(fi->iso_handle, + args[0], args[1]); + } + case RAW1394_IOC_ISO_XMIT_SYNC: + return hpsb_iso_xmit_sync(fi->iso_handle); + case RAW1394_IOC_ISO_XMIT_RECV_STOP: + hpsb_iso_stop(fi->iso_handle); + return 0; + case RAW1394_IOC_ISO_GET_STATUS: + return raw1394_iso_get_status(fi, argp); + case RAW1394_IOC_ISO_XMIT_PACKETS: + return raw1394_iso_send_packets(fi, argp); + case RAW1394_IOC_ISO_SHUTDOWN: + raw1394_iso_shutdown(fi); + return 0; + case RAW1394_IOC_ISO_QUEUE_ACTIVITY: + queue_rawiso_event(fi); + return 0; + default: + return -EINVAL; + } +} + +/* ioctl is only used for rawiso operations */ +static long raw1394_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct file_info *fi = file->private_data; + void __user *argp = (void __user *)arg; + long ret; + + /* state-independent commands */ + switch(cmd) { + case RAW1394_IOC_GET_CYCLE_TIMER: + return raw1394_read_cycle_timer(fi, argp); + default: + break; + } + + if (!mutex_trylock(&fi->state_mutex)) + return -EAGAIN; + + switch (fi->iso_state) { + case RAW1394_ISO_INACTIVE: + ret = raw1394_ioctl_inactive(fi, cmd, argp); + break; + case RAW1394_ISO_RECV: + ret = raw1394_ioctl_recv(fi, cmd, arg); + break; + case RAW1394_ISO_XMIT: + ret = raw1394_ioctl_xmit(fi, cmd, argp); + break; + default: + ret = -EINVAL; + break; + } + + mutex_unlock(&fi->state_mutex); + + return ret; +} + +#ifdef CONFIG_COMPAT +struct raw1394_iso_packets32 { + __u32 n_packets; + compat_uptr_t infos; +} __attribute__((packed)); + +struct raw1394_cycle_timer32 { + __u32 cycle_timer; + __u64 local_time; +} +#if defined(CONFIG_X86_64) || defined(CONFIG_IA64) +__attribute__((packed)) +#endif +; + +#define RAW1394_IOC_ISO_RECV_PACKETS32 \ + _IOW ('#', 0x25, struct raw1394_iso_packets32) +#define RAW1394_IOC_ISO_XMIT_PACKETS32 \ + _IOW ('#', 0x27, struct raw1394_iso_packets32) +#define RAW1394_IOC_GET_CYCLE_TIMER32 \ + _IOR ('#', 0x30, struct raw1394_cycle_timer32) + +static long raw1394_iso_xmit_recv_packets32(struct file *file, unsigned int cmd, + struct raw1394_iso_packets32 __user *arg) +{ + compat_uptr_t infos32; + void __user *infos; + long err = -EFAULT; + struct raw1394_iso_packets __user *dst = compat_alloc_user_space(sizeof(struct raw1394_iso_packets)); + + if (!copy_in_user(&dst->n_packets, &arg->n_packets, sizeof arg->n_packets) && + !copy_from_user(&infos32, &arg->infos, sizeof infos32)) { + infos = compat_ptr(infos32); + if (!copy_to_user(&dst->infos, &infos, sizeof infos)) + err = raw1394_ioctl(file, cmd, (unsigned long)dst); + } + return err; +} + +static long raw1394_read_cycle_timer32(struct file_info *fi, void __user * uaddr) +{ + struct raw1394_cycle_timer32 ct; + int err; + + err = hpsb_read_cycle_timer(fi->host, &ct.cycle_timer, &ct.local_time); + if (!err) + if (copy_to_user(uaddr, &ct, sizeof(ct))) + err = -EFAULT; + return err; +} + +static long raw1394_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct file_info *fi = file->private_data; + void __user *argp = (void __user *)arg; + long err; + + switch (cmd) { + /* These requests have same format as long as 'int' has same size. */ + case RAW1394_IOC_ISO_RECV_INIT: + case RAW1394_IOC_ISO_RECV_START: + case RAW1394_IOC_ISO_RECV_LISTEN_CHANNEL: + case RAW1394_IOC_ISO_RECV_UNLISTEN_CHANNEL: + case RAW1394_IOC_ISO_RECV_SET_CHANNEL_MASK: + case RAW1394_IOC_ISO_RECV_RELEASE_PACKETS: + case RAW1394_IOC_ISO_RECV_FLUSH: + case RAW1394_IOC_ISO_XMIT_RECV_STOP: + case RAW1394_IOC_ISO_XMIT_INIT: + case RAW1394_IOC_ISO_XMIT_START: + case RAW1394_IOC_ISO_XMIT_SYNC: + case RAW1394_IOC_ISO_GET_STATUS: + case RAW1394_IOC_ISO_SHUTDOWN: + case RAW1394_IOC_ISO_QUEUE_ACTIVITY: + err = raw1394_ioctl(file, cmd, arg); + break; + /* These request have different format. */ + case RAW1394_IOC_ISO_RECV_PACKETS32: + err = raw1394_iso_xmit_recv_packets32(file, RAW1394_IOC_ISO_RECV_PACKETS, argp); + break; + case RAW1394_IOC_ISO_XMIT_PACKETS32: + err = raw1394_iso_xmit_recv_packets32(file, RAW1394_IOC_ISO_XMIT_PACKETS, argp); + break; + case RAW1394_IOC_GET_CYCLE_TIMER32: + err = raw1394_read_cycle_timer32(fi, argp); + break; + default: + err = -EINVAL; + break; + } + + return err; +} +#endif + +static unsigned int raw1394_poll(struct file *file, poll_table * pt) +{ + struct file_info *fi = file->private_data; + unsigned int mask = POLLOUT | POLLWRNORM; + unsigned long flags; + + poll_wait(file, &fi->wait_complete, pt); + + spin_lock_irqsave(&fi->reqlists_lock, flags); + if (!list_empty(&fi->req_complete)) { + mask |= POLLIN | POLLRDNORM; + } + spin_unlock_irqrestore(&fi->reqlists_lock, flags); + + return mask; +} + +static int raw1394_open(struct inode *inode, struct file *file) +{ + struct file_info *fi; + + fi = kzalloc(sizeof(*fi), GFP_KERNEL); + if (!fi) + return -ENOMEM; + + fi->notification = (u8) RAW1394_NOTIFY_ON; /* busreset notification */ + + INIT_LIST_HEAD(&fi->list); + mutex_init(&fi->state_mutex); + fi->state = opened; + INIT_LIST_HEAD(&fi->req_pending); + INIT_LIST_HEAD(&fi->req_complete); + spin_lock_init(&fi->reqlists_lock); + init_waitqueue_head(&fi->wait_complete); + INIT_LIST_HEAD(&fi->addr_list); + + file->private_data = fi; + + return nonseekable_open(inode, file); +} + +static int raw1394_release(struct inode *inode, struct file *file) +{ + struct file_info *fi = file->private_data; + struct list_head *lh; + struct pending_request *req; + int i, fail; + int retval = 0; + struct list_head *entry; + struct arm_addr *addr = NULL; + struct host_info *hi; + struct file_info *fi_hlp = NULL; + struct arm_addr *arm_addr = NULL; + int another_host; + int csr_mod = 0; + unsigned long flags; + + if (fi->iso_state != RAW1394_ISO_INACTIVE) + raw1394_iso_shutdown(fi); + + spin_lock_irqsave(&host_info_lock, flags); + + fail = 0; + /* set address-entries invalid */ + + while (!list_empty(&fi->addr_list)) { + another_host = 0; + lh = fi->addr_list.next; + addr = list_entry(lh, struct arm_addr, addr_list); + /* another host with valid address-entry containing + same addressrange? */ + list_for_each_entry(hi, &host_info_list, list) { + if (hi->host != fi->host) { + list_for_each_entry(fi_hlp, &hi->file_info_list, + list) { + entry = fi_hlp->addr_list.next; + while (entry != &(fi_hlp->addr_list)) { + arm_addr = list_entry(entry, struct + arm_addr, + addr_list); + if (arm_addr->start == + addr->start) { + DBGMSG + ("raw1394_release: " + "another host ownes " + "same addressrange"); + another_host = 1; + break; + } + entry = entry->next; + } + if (another_host) { + break; + } + } + } + } + if (!another_host) { + DBGMSG("raw1394_release: call hpsb_arm_unregister"); + retval = + hpsb_unregister_addrspace(&raw1394_highlevel, + fi->host, addr->start); + if (!retval) { + ++fail; + printk(KERN_ERR + "raw1394_release arm_Unregister failed\n"); + } + } + DBGMSG("raw1394_release: delete addr_entry from list"); + list_del(&addr->addr_list); + vfree(addr->addr_space_buffer); + kfree(addr); + } /* while */ + spin_unlock_irqrestore(&host_info_lock, flags); + if (fail > 0) { + printk(KERN_ERR "raw1394: during addr_list-release " + "error(s) occurred \n"); + } + + for (;;) { + /* This locked section guarantees that neither + * complete nor pending requests exist once i!=0 */ + spin_lock_irqsave(&fi->reqlists_lock, flags); + while ((req = __next_complete_req(fi))) + free_pending_request(req); + + i = list_empty(&fi->req_pending); + spin_unlock_irqrestore(&fi->reqlists_lock, flags); + + if (i) + break; + /* + * Sleep until more requests can be freed. + * + * NB: We call the macro wait_event() with a condition argument + * with side effect. This is only possible because the side + * effect does not occur until the condition became true, and + * wait_event() won't evaluate the condition again after that. + */ + wait_event(fi->wait_complete, (req = next_complete_req(fi))); + free_pending_request(req); + } + + /* Remove any sub-trees left by user space programs */ + for (i = 0; i < RAW1394_MAX_USER_CSR_DIRS; i++) { + struct csr1212_dentry *dentry; + if (!fi->csr1212_dirs[i]) + continue; + for (dentry = + fi->csr1212_dirs[i]->value.directory.dentries_head; dentry; + dentry = dentry->next) { + csr1212_detach_keyval_from_directory(fi->host->csr.rom-> + root_kv, + dentry->kv); + } + csr1212_release_keyval(fi->csr1212_dirs[i]); + fi->csr1212_dirs[i] = NULL; + csr_mod = 1; + } + + if ((csr_mod || fi->cfgrom_upd) + && hpsb_update_config_rom_image(fi->host) < 0) + HPSB_ERR + ("Failed to generate Configuration ROM image for host %d", + fi->host->id); + + if (fi->state == connected) { + spin_lock_irqsave(&host_info_lock, flags); + list_del(&fi->list); + spin_unlock_irqrestore(&host_info_lock, flags); + + put_device(&fi->host->device); + } + + spin_lock_irqsave(&host_info_lock, flags); + if (fi->host) + module_put(fi->host->driver->owner); + spin_unlock_irqrestore(&host_info_lock, flags); + + kfree(fi); + + return 0; +} + +/*** HOTPLUG STUFF **********************************************************/ +/* + * Export information about protocols/devices supported by this driver. + */ +#ifdef MODULE +static const struct ieee1394_device_id raw1394_id_table[] = { + { + .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff, + .version = AVC_SW_VERSION_ENTRY & 0xffffff}, + { + .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, + .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff, + .version = CAMERA_SW_VERSION_ENTRY & 0xffffff}, + { + .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, + .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff, + .version = (CAMERA_SW_VERSION_ENTRY + 1) & 0xffffff}, + { + .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, + .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff, + .version = (CAMERA_SW_VERSION_ENTRY + 2) & 0xffffff}, + {} +}; + +MODULE_DEVICE_TABLE(ieee1394, raw1394_id_table); +#endif /* MODULE */ + +static struct hpsb_protocol_driver raw1394_driver = { + .name = "raw1394", +}; + +/******************************************************************************/ + +static struct hpsb_highlevel raw1394_highlevel = { + .name = RAW1394_DEVICE_NAME, + .add_host = add_host, + .remove_host = remove_host, + .host_reset = host_reset, + .fcp_request = fcp_request, +}; + +static struct cdev raw1394_cdev; +static const struct file_operations raw1394_fops = { + .owner = THIS_MODULE, + .read = raw1394_read, + .write = raw1394_write, + .mmap = raw1394_mmap, + .unlocked_ioctl = raw1394_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = raw1394_compat_ioctl, +#endif + .poll = raw1394_poll, + .open = raw1394_open, + .release = raw1394_release, + .llseek = no_llseek, +}; + +static int __init init_raw1394(void) +{ + int ret = 0; + + hpsb_register_highlevel(&raw1394_highlevel); + + if (IS_ERR + (device_create(hpsb_protocol_class, NULL, + MKDEV(IEEE1394_MAJOR, + IEEE1394_MINOR_BLOCK_RAW1394 * 16), + NULL, RAW1394_DEVICE_NAME))) { + ret = -EFAULT; + goto out_unreg; + } + + cdev_init(&raw1394_cdev, &raw1394_fops); + raw1394_cdev.owner = THIS_MODULE; + ret = cdev_add(&raw1394_cdev, IEEE1394_RAW1394_DEV, 1); + if (ret) { + HPSB_ERR("raw1394 failed to register minor device block"); + goto out_dev; + } + + HPSB_INFO("raw1394: /dev/%s device initialized", RAW1394_DEVICE_NAME); + + ret = hpsb_register_protocol(&raw1394_driver); + if (ret) { + HPSB_ERR("raw1394: failed to register protocol"); + cdev_del(&raw1394_cdev); + goto out_dev; + } + + goto out; + + out_dev: + device_destroy(hpsb_protocol_class, + MKDEV(IEEE1394_MAJOR, + IEEE1394_MINOR_BLOCK_RAW1394 * 16)); + out_unreg: + hpsb_unregister_highlevel(&raw1394_highlevel); + out: + return ret; +} + +static void __exit cleanup_raw1394(void) +{ + device_destroy(hpsb_protocol_class, + MKDEV(IEEE1394_MAJOR, + IEEE1394_MINOR_BLOCK_RAW1394 * 16)); + cdev_del(&raw1394_cdev); + hpsb_unregister_highlevel(&raw1394_highlevel); + hpsb_unregister_protocol(&raw1394_driver); +} + +module_init(init_raw1394); +module_exit(cleanup_raw1394); +MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/ieee1394/raw1394.h b/trunk/drivers/ieee1394/raw1394.h new file mode 100644 index 000000000000..963ac20373d2 --- /dev/null +++ b/trunk/drivers/ieee1394/raw1394.h @@ -0,0 +1,191 @@ +#ifndef IEEE1394_RAW1394_H +#define IEEE1394_RAW1394_H + +/* header for the raw1394 API that is exported to user-space */ + +#define RAW1394_KERNELAPI_VERSION 4 + +/* state: opened */ +#define RAW1394_REQ_INITIALIZE 1 + +/* state: initialized */ +#define RAW1394_REQ_LIST_CARDS 2 +#define RAW1394_REQ_SET_CARD 3 + +/* state: connected */ +#define RAW1394_REQ_ASYNC_READ 100 +#define RAW1394_REQ_ASYNC_WRITE 101 +#define RAW1394_REQ_LOCK 102 +#define RAW1394_REQ_LOCK64 103 +#define RAW1394_REQ_ISO_SEND 104 /* removed ABI, now a no-op */ +#define RAW1394_REQ_ASYNC_SEND 105 +#define RAW1394_REQ_ASYNC_STREAM 106 + +#define RAW1394_REQ_ISO_LISTEN 200 /* removed ABI, now a no-op */ +#define RAW1394_REQ_FCP_LISTEN 201 +#define RAW1394_REQ_RESET_BUS 202 +#define RAW1394_REQ_GET_ROM 203 +#define RAW1394_REQ_UPDATE_ROM 204 +#define RAW1394_REQ_ECHO 205 +#define RAW1394_REQ_MODIFY_ROM 206 + +#define RAW1394_REQ_ARM_REGISTER 300 +#define RAW1394_REQ_ARM_UNREGISTER 301 +#define RAW1394_REQ_ARM_SET_BUF 302 +#define RAW1394_REQ_ARM_GET_BUF 303 + +#define RAW1394_REQ_RESET_NOTIFY 400 + +#define RAW1394_REQ_PHYPACKET 500 + +/* kernel to user */ +#define RAW1394_REQ_BUS_RESET 10000 +#define RAW1394_REQ_ISO_RECEIVE 10001 +#define RAW1394_REQ_FCP_REQUEST 10002 +#define RAW1394_REQ_ARM 10003 +#define RAW1394_REQ_RAWISO_ACTIVITY 10004 + +/* error codes */ +#define RAW1394_ERROR_NONE 0 +#define RAW1394_ERROR_COMPAT (-1001) +#define RAW1394_ERROR_STATE_ORDER (-1002) +#define RAW1394_ERROR_GENERATION (-1003) +#define RAW1394_ERROR_INVALID_ARG (-1004) +#define RAW1394_ERROR_MEMFAULT (-1005) +#define RAW1394_ERROR_ALREADY (-1006) + +#define RAW1394_ERROR_EXCESSIVE (-1020) +#define RAW1394_ERROR_UNTIDY_LEN (-1021) + +#define RAW1394_ERROR_SEND_ERROR (-1100) +#define RAW1394_ERROR_ABORTED (-1101) +#define RAW1394_ERROR_TIMEOUT (-1102) + +/* arm_codes */ +#define ARM_READ 1 +#define ARM_WRITE 2 +#define ARM_LOCK 4 + +#define RAW1394_LONG_RESET 0 +#define RAW1394_SHORT_RESET 1 + +/* busresetnotify ... */ +#define RAW1394_NOTIFY_OFF 0 +#define RAW1394_NOTIFY_ON 1 + +#include + +struct raw1394_request { + __u32 type; + __s32 error; + __u32 misc; + + __u32 generation; + __u32 length; + + __u64 address; + + __u64 tag; + + __u64 sendb; + __u64 recvb; +}; + +struct raw1394_khost_list { + __u32 nodes; + __u8 name[32]; +}; + +typedef struct arm_request { + __u16 destination_nodeid; + __u16 source_nodeid; + __u64 destination_offset; + __u8 tlabel; + __u8 tcode; + __u8 extended_transaction_code; + __u32 generation; + __u16 buffer_length; + __u8 __user *buffer; +} *arm_request_t; + +typedef struct arm_response { + __s32 response_code; + __u16 buffer_length; + __u8 __user *buffer; +} *arm_response_t; + +typedef struct arm_request_response { + struct arm_request __user *request; + struct arm_response __user *response; +} *arm_request_response_t; + +/* rawiso API */ +#include "ieee1394-ioctl.h" + +/* per-packet metadata embedded in the ringbuffer */ +/* must be identical to hpsb_iso_packet_info in iso.h! */ +struct raw1394_iso_packet_info { + __u32 offset; + __u16 len; + __u16 cycle; /* recv only */ + __u8 channel; /* recv only */ + __u8 tag; + __u8 sy; +}; + +/* argument for RAW1394_ISO_RECV/XMIT_PACKETS ioctls */ +struct raw1394_iso_packets { + __u32 n_packets; + struct raw1394_iso_packet_info __user *infos; +}; + +struct raw1394_iso_config { + /* size of packet data buffer, in bytes (will be rounded up to PAGE_SIZE) */ + __u32 data_buf_size; + + /* # of packets to buffer */ + __u32 buf_packets; + + /* iso channel (set to -1 for multi-channel recv) */ + __s32 channel; + + /* xmit only - iso transmission speed */ + __u8 speed; + + /* The mode of the dma when receiving iso data. Must be supported by chip */ + __u8 dma_mode; + + /* max. latency of buffer, in packets (-1 if you don't care) */ + __s32 irq_interval; +}; + +/* argument to RAW1394_ISO_XMIT/RECV_INIT and RAW1394_ISO_GET_STATUS */ +struct raw1394_iso_status { + /* current settings */ + struct raw1394_iso_config config; + + /* number of packets waiting to be filled with data (ISO transmission) + or containing data received (ISO reception) */ + __u32 n_packets; + + /* approximate number of packets dropped due to overflow or + underflow of the packet buffer (a value of zero guarantees + that no packets have been dropped) */ + __u32 overflows; + + /* cycle number at which next packet will be transmitted; + -1 if not known */ + __s16 xmit_cycle; +}; + +/* argument to RAW1394_IOC_GET_CYCLE_TIMER ioctl */ +struct raw1394_cycle_timer { + /* contents of Isochronous Cycle Timer register, + as in OHCI 1.1 clause 5.13 (also with non-OHCI hosts) */ + __u32 cycle_timer; + + /* local time in microseconds since Epoch, + simultaneously read with cycle timer */ + __u64 local_time; +}; +#endif /* IEEE1394_RAW1394_H */ diff --git a/trunk/drivers/ieee1394/sbp2.c b/trunk/drivers/ieee1394/sbp2.c new file mode 100644 index 000000000000..d6e251a300ce --- /dev/null +++ b/trunk/drivers/ieee1394/sbp2.c @@ -0,0 +1,2138 @@ +/* + * sbp2.c - SBP-2 protocol driver for IEEE-1394 + * + * Copyright (C) 2000 James Goodwin, Filanet Corporation (www.filanet.com) + * jamesg@filanet.com (JSG) + * + * Copyright (C) 2003 Ben Collins + * + * 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. + */ + +/* + * Brief Description: + * + * This driver implements the Serial Bus Protocol 2 (SBP-2) over IEEE-1394 + * under Linux. The SBP-2 driver is implemented as an IEEE-1394 high-level + * driver. It also registers as a SCSI lower-level driver in order to accept + * SCSI commands for transport using SBP-2. + * + * You may access any attached SBP-2 (usually storage devices) as regular + * SCSI devices. E.g. mount /dev/sda1, fdisk, mkfs, etc.. + * + * See http://www.t10.org/drafts.htm#sbp2 for the final draft of the SBP-2 + * specification and for where to purchase the official standard. + * + * TODO: + * - look into possible improvements of the SCSI error handlers + * - handle Unit_Characteristics.mgt_ORB_timeout and .ORB_size + * - handle Logical_Unit_Number.ordered + * - handle src == 1 in status blocks + * - reimplement the DMA mapping in absence of physical DMA so that + * bus_to_virt is no longer required + * - debug the handling of absent physical DMA + * - replace CONFIG_IEEE1394_SBP2_PHYS_DMA by automatic detection + * (this is easy but depends on the previous two TODO items) + * - make the parameter serialize_io configurable per device + * - move all requests to fetch agent registers into non-atomic context, + * replace all usages of sbp2util_node_write_no_wait by true transactions + * Grep for inline FIXME comments below. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA +#include /* for bus_to_virt */ +#endif + +#include +#include +#include +#include +#include + +#include "csr1212.h" +#include "highlevel.h" +#include "hosts.h" +#include "ieee1394.h" +#include "ieee1394_core.h" +#include "ieee1394_hotplug.h" +#include "ieee1394_transactions.h" +#include "ieee1394_types.h" +#include "nodemgr.h" +#include "sbp2.h" + +/* + * Module load parameter definitions + */ + +/* + * Change max_speed on module load if you have a bad IEEE-1394 + * controller that has trouble running 2KB packets at 400mb. + * + * NOTE: On certain OHCI parts I have seen short packets on async transmit + * (probably due to PCI latency/throughput issues with the part). You can + * bump down the speed if you are running into problems. + */ +static int sbp2_max_speed = IEEE1394_SPEED_MAX; +module_param_named(max_speed, sbp2_max_speed, int, 0644); +MODULE_PARM_DESC(max_speed, "Limit data transfer speed (5 <= 3200, " + "4 <= 1600, 3 <= 800, 2 <= 400, 1 <= 200, 0 = 100 Mb/s)"); + +/* + * Set serialize_io to 0 or N to use dynamically appended lists of command ORBs. + * This is and always has been buggy in multiple subtle ways. See above TODOs. + */ +static int sbp2_serialize_io = 1; +module_param_named(serialize_io, sbp2_serialize_io, bool, 0444); +MODULE_PARM_DESC(serialize_io, "Serialize requests coming from SCSI drivers " + "(default = Y, faster but buggy = N)"); + +/* + * Adjust max_sectors if you'd like to influence how many sectors each SCSI + * command can transfer at most. Please note that some older SBP-2 bridge + * chips are broken for transfers greater or equal to 128KB, therefore + * max_sectors used to be a safe 255 sectors for many years. We now have a + * default of 0 here which means that we let the SCSI stack choose a limit. + * + * The SBP2_WORKAROUND_128K_MAX_TRANS flag, if set either in the workarounds + * module parameter or in the sbp2_workarounds_table[], will override the + * value of max_sectors. We should use sbp2_workarounds_table[] to cover any + * bridge chip which becomes known to need the 255 sectors limit. + */ +static int sbp2_max_sectors; +module_param_named(max_sectors, sbp2_max_sectors, int, 0444); +MODULE_PARM_DESC(max_sectors, "Change max sectors per I/O supported " + "(default = 0 = use SCSI stack's default)"); + +/* + * Exclusive login to sbp2 device? In most cases, the sbp2 driver should + * do an exclusive login, as it's generally unsafe to have two hosts + * talking to a single sbp2 device at the same time (filesystem coherency, + * etc.). If you're running an sbp2 device that supports multiple logins, + * and you're either running read-only filesystems or some sort of special + * filesystem supporting multiple hosts, e.g. OpenGFS, Oracle Cluster + * File System, or Lustre, then set exclusive_login to zero. + * + * So far only bridges from Oxford Semiconductor are known to support + * concurrent logins. Depending on firmware, four or two concurrent logins + * are possible on OXFW911 and newer Oxsemi bridges. + */ +static int sbp2_exclusive_login = 1; +module_param_named(exclusive_login, sbp2_exclusive_login, bool, 0644); +MODULE_PARM_DESC(exclusive_login, "Exclusive login to sbp2 device " + "(default = Y, use N for concurrent initiators)"); + +/* + * If any of the following workarounds is required for your device to work, + * please submit the kernel messages logged by sbp2 to the linux1394-devel + * mailing list. + * + * - 128kB max transfer + * Limit transfer size. Necessary for some old bridges. + * + * - 36 byte inquiry + * When scsi_mod probes the device, let the inquiry command look like that + * from MS Windows. + * + * - skip mode page 8 + * Suppress sending of mode_sense for mode page 8 if the device pretends to + * support the SCSI Primary Block commands instead of Reduced Block Commands. + * + * - fix capacity + * Tell sd_mod to correct the last sector number reported by read_capacity. + * Avoids access beyond actual disk limits on devices with an off-by-one bug. + * Don't use this with devices which don't have this bug. + * + * - delay inquiry + * Wait extra SBP2_INQUIRY_DELAY seconds after login before SCSI inquiry. + * + * - power condition + * Set the power condition field in the START STOP UNIT commands sent by + * sd_mod on suspend, resume, and shutdown (if manage_start_stop is on). + * Some disks need this to spin down or to resume properly. + * + * - override internal blacklist + * Instead of adding to the built-in blacklist, use only the workarounds + * specified in the module load parameter. + * Useful if a blacklist entry interfered with a non-broken device. + */ +static int sbp2_default_workarounds; +module_param_named(workarounds, sbp2_default_workarounds, int, 0644); +MODULE_PARM_DESC(workarounds, "Work around device bugs (default = 0" + ", 128kB max transfer = " __stringify(SBP2_WORKAROUND_128K_MAX_TRANS) + ", 36 byte inquiry = " __stringify(SBP2_WORKAROUND_INQUIRY_36) + ", skip mode page 8 = " __stringify(SBP2_WORKAROUND_MODE_SENSE_8) + ", fix capacity = " __stringify(SBP2_WORKAROUND_FIX_CAPACITY) + ", delay inquiry = " __stringify(SBP2_WORKAROUND_DELAY_INQUIRY) + ", set power condition in start stop unit = " + __stringify(SBP2_WORKAROUND_POWER_CONDITION) + ", override internal blacklist = " __stringify(SBP2_WORKAROUND_OVERRIDE) + ", or a combination)"); + +/* + * This influences the format of the sysfs attribute + * /sys/bus/scsi/devices/.../ieee1394_id. + * + * The default format is like in older kernels: %016Lx:%d:%d + * It contains the target's EUI-64, a number given to the logical unit by + * the ieee1394 driver's nodemgr (starting at 0), and the LUN. + * + * The long format is: %016Lx:%06x:%04x + * It contains the target's EUI-64, the unit directory's directory_ID as per + * IEEE 1212 clause 7.7.19, and the LUN. This format comes closest to the + * format of SBP(-3) target port and logical unit identifier as per SAM (SCSI + * Architecture Model) rev.2 to 4 annex A. Therefore and because it is + * independent of the implementation of the ieee1394 nodemgr, the longer format + * is recommended for future use. + */ +static int sbp2_long_sysfs_ieee1394_id; +module_param_named(long_ieee1394_id, sbp2_long_sysfs_ieee1394_id, bool, 0644); +MODULE_PARM_DESC(long_ieee1394_id, "8+3+2 bytes format of ieee1394_id in sysfs " + "(default = backwards-compatible = N, SAM-conforming = Y)"); + + +#define SBP2_INFO(fmt, args...) HPSB_INFO("sbp2: "fmt, ## args) +#define SBP2_ERR(fmt, args...) HPSB_ERR("sbp2: "fmt, ## args) + +/* + * Globals + */ +static void sbp2scsi_complete_all_commands(struct sbp2_lu *, u32); +static void sbp2scsi_complete_command(struct sbp2_lu *, u32, struct scsi_cmnd *, + void (*)(struct scsi_cmnd *)); +static struct sbp2_lu *sbp2_alloc_device(struct unit_directory *); +static int sbp2_start_device(struct sbp2_lu *); +static void sbp2_remove_device(struct sbp2_lu *); +static int sbp2_login_device(struct sbp2_lu *); +static int sbp2_reconnect_device(struct sbp2_lu *); +static int sbp2_logout_device(struct sbp2_lu *); +static void sbp2_host_reset(struct hpsb_host *); +static int sbp2_handle_status_write(struct hpsb_host *, int, int, quadlet_t *, + u64, size_t, u16); +static int sbp2_agent_reset(struct sbp2_lu *, int); +static void sbp2_parse_unit_directory(struct sbp2_lu *, + struct unit_directory *); +static int sbp2_set_busy_timeout(struct sbp2_lu *); +static int sbp2_max_speed_and_size(struct sbp2_lu *); + + +static const u8 sbp2_speedto_max_payload[] = { 0x7, 0x8, 0x9, 0xa, 0xa, 0xa }; + +static DEFINE_RWLOCK(sbp2_hi_logical_units_lock); + +static struct hpsb_highlevel sbp2_highlevel = { + .name = SBP2_DEVICE_NAME, + .host_reset = sbp2_host_reset, +}; + +static const struct hpsb_address_ops sbp2_ops = { + .write = sbp2_handle_status_write +}; + +#ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA +static int sbp2_handle_physdma_write(struct hpsb_host *, int, int, quadlet_t *, + u64, size_t, u16); +static int sbp2_handle_physdma_read(struct hpsb_host *, int, quadlet_t *, u64, + size_t, u16); + +static const struct hpsb_address_ops sbp2_physdma_ops = { + .read = sbp2_handle_physdma_read, + .write = sbp2_handle_physdma_write, +}; +#endif + + +/* + * Interface to driver core and IEEE 1394 core + */ +static const struct ieee1394_device_id sbp2_id_table[] = { + { + .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, + .specifier_id = SBP2_UNIT_SPEC_ID_ENTRY & 0xffffff, + .version = SBP2_SW_VERSION_ENTRY & 0xffffff}, + {} +}; +MODULE_DEVICE_TABLE(ieee1394, sbp2_id_table); + +static int sbp2_probe(struct device *); +static int sbp2_remove(struct device *); +static int sbp2_update(struct unit_directory *); + +static struct hpsb_protocol_driver sbp2_driver = { + .name = SBP2_DEVICE_NAME, + .id_table = sbp2_id_table, + .update = sbp2_update, + .driver = { + .probe = sbp2_probe, + .remove = sbp2_remove, + }, +}; + + +/* + * Interface to SCSI core + */ +static int sbp2scsi_queuecommand(struct scsi_cmnd *, + void (*)(struct scsi_cmnd *)); +static int sbp2scsi_abort(struct scsi_cmnd *); +static int sbp2scsi_reset(struct scsi_cmnd *); +static int sbp2scsi_slave_alloc(struct scsi_device *); +static int sbp2scsi_slave_configure(struct scsi_device *); +static void sbp2scsi_slave_destroy(struct scsi_device *); +static ssize_t sbp2_sysfs_ieee1394_id_show(struct device *, + struct device_attribute *, char *); + +static DEVICE_ATTR(ieee1394_id, S_IRUGO, sbp2_sysfs_ieee1394_id_show, NULL); + +static struct device_attribute *sbp2_sysfs_sdev_attrs[] = { + &dev_attr_ieee1394_id, + NULL +}; + +static struct scsi_host_template sbp2_shost_template = { + .module = THIS_MODULE, + .name = "SBP-2 IEEE-1394", + .proc_name = SBP2_DEVICE_NAME, + .queuecommand = sbp2scsi_queuecommand, + .eh_abort_handler = sbp2scsi_abort, + .eh_device_reset_handler = sbp2scsi_reset, + .slave_alloc = sbp2scsi_slave_alloc, + .slave_configure = sbp2scsi_slave_configure, + .slave_destroy = sbp2scsi_slave_destroy, + .this_id = -1, + .sg_tablesize = SG_ALL, + .use_clustering = ENABLE_CLUSTERING, + .cmd_per_lun = SBP2_MAX_CMDS, + .can_queue = SBP2_MAX_CMDS, + .sdev_attrs = sbp2_sysfs_sdev_attrs, +}; + +#define SBP2_ROM_VALUE_WILDCARD ~0 /* match all */ +#define SBP2_ROM_VALUE_MISSING 0xff000000 /* not present in the unit dir. */ + +/* + * List of devices with known bugs. + * + * The firmware_revision field, masked with 0xffff00, is the best indicator + * for the type of bridge chip of a device. It yields a few false positives + * but this did not break correctly behaving devices so far. + */ +static const struct { + u32 firmware_revision; + u32 model; + unsigned workarounds; +} sbp2_workarounds_table[] = { + /* DViCO Momobay CX-1 with TSB42AA9 bridge */ { + .firmware_revision = 0x002800, + .model = 0x001010, + .workarounds = SBP2_WORKAROUND_INQUIRY_36 | + SBP2_WORKAROUND_MODE_SENSE_8 | + SBP2_WORKAROUND_POWER_CONDITION, + }, + /* DViCO Momobay FX-3A with TSB42AA9A bridge */ { + .firmware_revision = 0x002800, + .model = 0x000000, + .workarounds = SBP2_WORKAROUND_POWER_CONDITION, + }, + /* Initio bridges, actually only needed for some older ones */ { + .firmware_revision = 0x000200, + .model = SBP2_ROM_VALUE_WILDCARD, + .workarounds = SBP2_WORKAROUND_INQUIRY_36, + }, + /* PL-3507 bridge with Prolific firmware */ { + .firmware_revision = 0x012800, + .model = SBP2_ROM_VALUE_WILDCARD, + .workarounds = SBP2_WORKAROUND_POWER_CONDITION, + }, + /* Symbios bridge */ { + .firmware_revision = 0xa0b800, + .model = SBP2_ROM_VALUE_WILDCARD, + .workarounds = SBP2_WORKAROUND_128K_MAX_TRANS, + }, + /* Datafab MD2-FW2 with Symbios/LSILogic SYM13FW500 bridge */ { + .firmware_revision = 0x002600, + .model = SBP2_ROM_VALUE_WILDCARD, + .workarounds = SBP2_WORKAROUND_128K_MAX_TRANS, + }, + /* + * iPod 2nd generation: needs 128k max transfer size workaround + * iPod 3rd generation: needs fix capacity workaround + */ + { + .firmware_revision = 0x0a2700, + .model = 0x000000, + .workarounds = SBP2_WORKAROUND_128K_MAX_TRANS | + SBP2_WORKAROUND_FIX_CAPACITY, + }, + /* iPod 4th generation */ { + .firmware_revision = 0x0a2700, + .model = 0x000021, + .workarounds = SBP2_WORKAROUND_FIX_CAPACITY, + }, + /* iPod mini */ { + .firmware_revision = 0x0a2700, + .model = 0x000022, + .workarounds = SBP2_WORKAROUND_FIX_CAPACITY, + }, + /* iPod mini */ { + .firmware_revision = 0x0a2700, + .model = 0x000023, + .workarounds = SBP2_WORKAROUND_FIX_CAPACITY, + }, + /* iPod Photo */ { + .firmware_revision = 0x0a2700, + .model = 0x00007e, + .workarounds = SBP2_WORKAROUND_FIX_CAPACITY, + } +}; + +/************************************** + * General utility functions + **************************************/ + +#ifndef __BIG_ENDIAN +/* + * Converts a buffer from be32 to cpu byte ordering. Length is in bytes. + */ +static inline void sbp2util_be32_to_cpu_buffer(void *buffer, int length) +{ + u32 *temp = buffer; + + for (length = (length >> 2); length--; ) + temp[length] = be32_to_cpu(temp[length]); +} + +/* + * Converts a buffer from cpu to be32 byte ordering. Length is in bytes. + */ +static inline void sbp2util_cpu_to_be32_buffer(void *buffer, int length) +{ + u32 *temp = buffer; + + for (length = (length >> 2); length--; ) + temp[length] = cpu_to_be32(temp[length]); +} +#else /* BIG_ENDIAN */ +/* Why waste the cpu cycles? */ +#define sbp2util_be32_to_cpu_buffer(x,y) do {} while (0) +#define sbp2util_cpu_to_be32_buffer(x,y) do {} while (0) +#endif + +static DECLARE_WAIT_QUEUE_HEAD(sbp2_access_wq); + +/* + * Waits for completion of an SBP-2 access request. + * Returns nonzero if timed out or prematurely interrupted. + */ +static int sbp2util_access_timeout(struct sbp2_lu *lu, int timeout) +{ + long leftover; + + leftover = wait_event_interruptible_timeout( + sbp2_access_wq, lu->access_complete, timeout); + lu->access_complete = 0; + return leftover <= 0; +} + +static void sbp2_free_packet(void *packet) +{ + hpsb_free_tlabel(packet); + hpsb_free_packet(packet); +} + +/* + * This is much like hpsb_node_write(), except it ignores the response + * subaction and returns immediately. Can be used from atomic context. + */ +static int sbp2util_node_write_no_wait(struct node_entry *ne, u64 addr, + quadlet_t *buf, size_t len) +{ + struct hpsb_packet *packet; + + packet = hpsb_make_writepacket(ne->host, ne->nodeid, addr, buf, len); + if (!packet) + return -ENOMEM; + + hpsb_set_packet_complete_task(packet, sbp2_free_packet, packet); + hpsb_node_fill_packet(ne, packet); + if (hpsb_send_packet(packet) < 0) { + sbp2_free_packet(packet); + return -EIO; + } + return 0; +} + +static void sbp2util_notify_fetch_agent(struct sbp2_lu *lu, u64 offset, + quadlet_t *data, size_t len) +{ + /* There is a small window after a bus reset within which the node + * entry's generation is current but the reconnect wasn't completed. */ + if (unlikely(atomic_read(&lu->state) == SBP2LU_STATE_IN_RESET)) + return; + + if (hpsb_node_write(lu->ne, lu->command_block_agent_addr + offset, + data, len)) + SBP2_ERR("sbp2util_notify_fetch_agent failed."); + + /* Now accept new SCSI commands, unless a bus reset happended during + * hpsb_node_write. */ + if (likely(atomic_read(&lu->state) != SBP2LU_STATE_IN_RESET)) + scsi_unblock_requests(lu->shost); +} + +static void sbp2util_write_orb_pointer(struct work_struct *work) +{ + struct sbp2_lu *lu = container_of(work, struct sbp2_lu, protocol_work); + quadlet_t data[2]; + + data[0] = ORB_SET_NODE_ID(lu->hi->host->node_id); + data[1] = lu->last_orb_dma; + sbp2util_cpu_to_be32_buffer(data, 8); + sbp2util_notify_fetch_agent(lu, SBP2_ORB_POINTER_OFFSET, data, 8); +} + +static void sbp2util_write_doorbell(struct work_struct *work) +{ + struct sbp2_lu *lu = container_of(work, struct sbp2_lu, protocol_work); + + sbp2util_notify_fetch_agent(lu, SBP2_DOORBELL_OFFSET, NULL, 4); +} + +static int sbp2util_create_command_orb_pool(struct sbp2_lu *lu) +{ + struct sbp2_command_info *cmd; + struct device *dmadev = lu->hi->host->device.parent; + int i, orbs = sbp2_serialize_io ? 2 : SBP2_MAX_CMDS; + + for (i = 0; i < orbs; i++) { + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + goto failed_alloc; + + cmd->command_orb_dma = + dma_map_single(dmadev, &cmd->command_orb, + sizeof(struct sbp2_command_orb), + DMA_TO_DEVICE); + if (dma_mapping_error(dmadev, cmd->command_orb_dma)) + goto failed_orb; + + cmd->sge_dma = + dma_map_single(dmadev, &cmd->scatter_gather_element, + sizeof(cmd->scatter_gather_element), + DMA_TO_DEVICE); + if (dma_mapping_error(dmadev, cmd->sge_dma)) + goto failed_sge; + + INIT_LIST_HEAD(&cmd->list); + list_add_tail(&cmd->list, &lu->cmd_orb_completed); + } + return 0; + +failed_sge: + dma_unmap_single(dmadev, cmd->command_orb_dma, + sizeof(struct sbp2_command_orb), DMA_TO_DEVICE); +failed_orb: + kfree(cmd); +failed_alloc: + return -ENOMEM; +} + +static void sbp2util_remove_command_orb_pool(struct sbp2_lu *lu, + struct hpsb_host *host) +{ + struct list_head *lh, *next; + struct sbp2_command_info *cmd; + unsigned long flags; + + spin_lock_irqsave(&lu->cmd_orb_lock, flags); + if (!list_empty(&lu->cmd_orb_completed)) + list_for_each_safe(lh, next, &lu->cmd_orb_completed) { + cmd = list_entry(lh, struct sbp2_command_info, list); + dma_unmap_single(host->device.parent, + cmd->command_orb_dma, + sizeof(struct sbp2_command_orb), + DMA_TO_DEVICE); + dma_unmap_single(host->device.parent, cmd->sge_dma, + sizeof(cmd->scatter_gather_element), + DMA_TO_DEVICE); + kfree(cmd); + } + spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); + return; +} + +/* + * Finds the sbp2_command for a given outstanding command ORB. + * Only looks at the in-use list. + */ +static struct sbp2_command_info *sbp2util_find_command_for_orb( + struct sbp2_lu *lu, dma_addr_t orb) +{ + struct sbp2_command_info *cmd; + unsigned long flags; + + spin_lock_irqsave(&lu->cmd_orb_lock, flags); + if (!list_empty(&lu->cmd_orb_inuse)) + list_for_each_entry(cmd, &lu->cmd_orb_inuse, list) + if (cmd->command_orb_dma == orb) { + spin_unlock_irqrestore( + &lu->cmd_orb_lock, flags); + return cmd; + } + spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); + return NULL; +} + +/* + * Finds the sbp2_command for a given outstanding SCpnt. + * Only looks at the in-use list. + * Must be called with lu->cmd_orb_lock held. + */ +static struct sbp2_command_info *sbp2util_find_command_for_SCpnt( + struct sbp2_lu *lu, void *SCpnt) +{ + struct sbp2_command_info *cmd; + + if (!list_empty(&lu->cmd_orb_inuse)) + list_for_each_entry(cmd, &lu->cmd_orb_inuse, list) + if (cmd->Current_SCpnt == SCpnt) + return cmd; + return NULL; +} + +static struct sbp2_command_info *sbp2util_allocate_command_orb( + struct sbp2_lu *lu, + struct scsi_cmnd *Current_SCpnt, + void (*Current_done)(struct scsi_cmnd *)) +{ + struct list_head *lh; + struct sbp2_command_info *cmd = NULL; + unsigned long flags; + + spin_lock_irqsave(&lu->cmd_orb_lock, flags); + if (!list_empty(&lu->cmd_orb_completed)) { + lh = lu->cmd_orb_completed.next; + list_del(lh); + cmd = list_entry(lh, struct sbp2_command_info, list); + cmd->Current_done = Current_done; + cmd->Current_SCpnt = Current_SCpnt; + list_add_tail(&cmd->list, &lu->cmd_orb_inuse); + } else + SBP2_ERR("%s: no orbs available", __func__); + spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); + return cmd; +} + +/* + * Unmaps the DMAs of a command and moves the command to the completed ORB list. + * Must be called with lu->cmd_orb_lock held. + */ +static void sbp2util_mark_command_completed(struct sbp2_lu *lu, + struct sbp2_command_info *cmd) +{ + if (scsi_sg_count(cmd->Current_SCpnt)) + dma_unmap_sg(lu->ud->ne->host->device.parent, + scsi_sglist(cmd->Current_SCpnt), + scsi_sg_count(cmd->Current_SCpnt), + cmd->Current_SCpnt->sc_data_direction); + list_move_tail(&cmd->list, &lu->cmd_orb_completed); +} + +/* + * Is lu valid? Is the 1394 node still present? + */ +static inline int sbp2util_node_is_available(struct sbp2_lu *lu) +{ + return lu && lu->ne && !lu->ne->in_limbo; +} + +/********************************************* + * IEEE-1394 core driver stack related section + *********************************************/ + +static int sbp2_probe(struct device *dev) +{ + struct unit_directory *ud; + struct sbp2_lu *lu; + + ud = container_of(dev, struct unit_directory, device); + + /* Don't probe UD's that have the LUN flag. We'll probe the LUN(s) + * instead. */ + if (ud->flags & UNIT_DIRECTORY_HAS_LUN_DIRECTORY) + return -ENODEV; + + lu = sbp2_alloc_device(ud); + if (!lu) + return -ENOMEM; + + sbp2_parse_unit_directory(lu, ud); + return sbp2_start_device(lu); +} + +static int sbp2_remove(struct device *dev) +{ + struct unit_directory *ud; + struct sbp2_lu *lu; + struct scsi_device *sdev; + + ud = container_of(dev, struct unit_directory, device); + lu = dev_get_drvdata(&ud->device); + if (!lu) + return 0; + + if (lu->shost) { + /* Get rid of enqueued commands if there is no chance to + * send them. */ + if (!sbp2util_node_is_available(lu)) + sbp2scsi_complete_all_commands(lu, DID_NO_CONNECT); + /* scsi_remove_device() may trigger shutdown functions of SCSI + * highlevel drivers which would deadlock if blocked. */ + atomic_set(&lu->state, SBP2LU_STATE_IN_SHUTDOWN); + scsi_unblock_requests(lu->shost); + } + sdev = lu->sdev; + if (sdev) { + lu->sdev = NULL; + scsi_remove_device(sdev); + } + + sbp2_logout_device(lu); + sbp2_remove_device(lu); + + return 0; +} + +static int sbp2_update(struct unit_directory *ud) +{ + struct sbp2_lu *lu = dev_get_drvdata(&ud->device); + + if (sbp2_reconnect_device(lu) != 0) { + /* + * Reconnect failed. If another bus reset happened, + * let nodemgr proceed and call sbp2_update again later + * (or sbp2_remove if this node went away). + */ + if (!hpsb_node_entry_valid(lu->ne)) + return 0; + /* + * Or the target rejected the reconnect because we weren't + * fast enough. Try a regular login, but first log out + * just in case of any weirdness. + */ + sbp2_logout_device(lu); + + if (sbp2_login_device(lu) != 0) { + if (!hpsb_node_entry_valid(lu->ne)) + return 0; + + /* Maybe another initiator won the login. */ + SBP2_ERR("Failed to reconnect to sbp2 device!"); + return -EBUSY; + } + } + + sbp2_set_busy_timeout(lu); + sbp2_agent_reset(lu, 1); + sbp2_max_speed_and_size(lu); + + /* Complete any pending commands with busy (so they get retried) + * and remove them from our queue. */ + sbp2scsi_complete_all_commands(lu, DID_BUS_BUSY); + + /* Accept new commands unless there was another bus reset in the + * meantime. */ + if (hpsb_node_entry_valid(lu->ne)) { + atomic_set(&lu->state, SBP2LU_STATE_RUNNING); + scsi_unblock_requests(lu->shost); + } + return 0; +} + +static struct sbp2_lu *sbp2_alloc_device(struct unit_directory *ud) +{ + struct sbp2_fwhost_info *hi; + struct Scsi_Host *shost = NULL; + struct sbp2_lu *lu = NULL; + unsigned long flags; + + lu = kzalloc(sizeof(*lu), GFP_KERNEL); + if (!lu) { + SBP2_ERR("failed to create lu"); + goto failed_alloc; + } + + lu->ne = ud->ne; + lu->ud = ud; + lu->speed_code = IEEE1394_SPEED_100; + lu->max_payload_size = sbp2_speedto_max_payload[IEEE1394_SPEED_100]; + lu->status_fifo_addr = CSR1212_INVALID_ADDR_SPACE; + INIT_LIST_HEAD(&lu->cmd_orb_inuse); + INIT_LIST_HEAD(&lu->cmd_orb_completed); + INIT_LIST_HEAD(&lu->lu_list); + spin_lock_init(&lu->cmd_orb_lock); + atomic_set(&lu->state, SBP2LU_STATE_RUNNING); + INIT_WORK(&lu->protocol_work, NULL); + + dev_set_drvdata(&ud->device, lu); + + hi = hpsb_get_hostinfo(&sbp2_highlevel, ud->ne->host); + if (!hi) { + hi = hpsb_create_hostinfo(&sbp2_highlevel, ud->ne->host, + sizeof(*hi)); + if (!hi) { + SBP2_ERR("failed to allocate hostinfo"); + goto failed_alloc; + } + hi->host = ud->ne->host; + INIT_LIST_HEAD(&hi->logical_units); + +#ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA + /* Handle data movement if physical dma is not + * enabled or not supported on host controller */ + if (!hpsb_register_addrspace(&sbp2_highlevel, ud->ne->host, + &sbp2_physdma_ops, + 0x0ULL, 0xfffffffcULL)) { + SBP2_ERR("failed to register lower 4GB address range"); + goto failed_alloc; + } +#endif + } + + if (dma_get_max_seg_size(hi->host->device.parent) > SBP2_MAX_SEG_SIZE) + BUG_ON(dma_set_max_seg_size(hi->host->device.parent, + SBP2_MAX_SEG_SIZE)); + + /* Prevent unloading of the 1394 host */ + if (!try_module_get(hi->host->driver->owner)) { + SBP2_ERR("failed to get a reference on 1394 host driver"); + goto failed_alloc; + } + + lu->hi = hi; + + write_lock_irqsave(&sbp2_hi_logical_units_lock, flags); + list_add_tail(&lu->lu_list, &hi->logical_units); + write_unlock_irqrestore(&sbp2_hi_logical_units_lock, flags); + + /* Register the status FIFO address range. We could use the same FIFO + * for targets at different nodes. However we need different FIFOs per + * target in order to support multi-unit devices. + * The FIFO is located out of the local host controller's physical range + * but, if possible, within the posted write area. Status writes will + * then be performed as unified transactions. This slightly reduces + * bandwidth usage, and some Prolific based devices seem to require it. + */ + lu->status_fifo_addr = hpsb_allocate_and_register_addrspace( + &sbp2_highlevel, ud->ne->host, &sbp2_ops, + sizeof(struct sbp2_status_block), sizeof(quadlet_t), + ud->ne->host->low_addr_space, CSR1212_ALL_SPACE_END); + if (lu->status_fifo_addr == CSR1212_INVALID_ADDR_SPACE) { + SBP2_ERR("failed to allocate status FIFO address range"); + goto failed_alloc; + } + + shost = scsi_host_alloc(&sbp2_shost_template, sizeof(unsigned long)); + if (!shost) { + SBP2_ERR("failed to register scsi host"); + goto failed_alloc; + } + + shost->hostdata[0] = (unsigned long)lu; + shost->max_cmd_len = SBP2_MAX_CDB_SIZE; + + if (!scsi_add_host(shost, &ud->device)) { + lu->shost = shost; + return lu; + } + + SBP2_ERR("failed to add scsi host"); + scsi_host_put(shost); + +failed_alloc: + sbp2_remove_device(lu); + return NULL; +} + +static void sbp2_host_reset(struct hpsb_host *host) +{ + struct sbp2_fwhost_info *hi; + struct sbp2_lu *lu; + unsigned long flags; + + hi = hpsb_get_hostinfo(&sbp2_highlevel, host); + if (!hi) + return; + + read_lock_irqsave(&sbp2_hi_logical_units_lock, flags); + + list_for_each_entry(lu, &hi->logical_units, lu_list) + if (atomic_cmpxchg(&lu->state, + SBP2LU_STATE_RUNNING, SBP2LU_STATE_IN_RESET) + == SBP2LU_STATE_RUNNING) + scsi_block_requests(lu->shost); + + read_unlock_irqrestore(&sbp2_hi_logical_units_lock, flags); +} + +static int sbp2_start_device(struct sbp2_lu *lu) +{ + struct sbp2_fwhost_info *hi = lu->hi; + int error; + + lu->login_response = dma_alloc_coherent(hi->host->device.parent, + sizeof(struct sbp2_login_response), + &lu->login_response_dma, GFP_KERNEL); + if (!lu->login_response) + goto alloc_fail; + + lu->query_logins_orb = dma_alloc_coherent(hi->host->device.parent, + sizeof(struct sbp2_query_logins_orb), + &lu->query_logins_orb_dma, GFP_KERNEL); + if (!lu->query_logins_orb) + goto alloc_fail; + + lu->query_logins_response = dma_alloc_coherent(hi->host->device.parent, + sizeof(struct sbp2_query_logins_response), + &lu->query_logins_response_dma, GFP_KERNEL); + if (!lu->query_logins_response) + goto alloc_fail; + + lu->reconnect_orb = dma_alloc_coherent(hi->host->device.parent, + sizeof(struct sbp2_reconnect_orb), + &lu->reconnect_orb_dma, GFP_KERNEL); + if (!lu->reconnect_orb) + goto alloc_fail; + + lu->logout_orb = dma_alloc_coherent(hi->host->device.parent, + sizeof(struct sbp2_logout_orb), + &lu->logout_orb_dma, GFP_KERNEL); + if (!lu->logout_orb) + goto alloc_fail; + + lu->login_orb = dma_alloc_coherent(hi->host->device.parent, + sizeof(struct sbp2_login_orb), + &lu->login_orb_dma, GFP_KERNEL); + if (!lu->login_orb) + goto alloc_fail; + + if (sbp2util_create_command_orb_pool(lu)) + goto alloc_fail; + + /* Wait a second before trying to log in. Previously logged in + * initiators need a chance to reconnect. */ + if (msleep_interruptible(1000)) { + sbp2_remove_device(lu); + return -EINTR; + } + + if (sbp2_login_device(lu)) { + sbp2_remove_device(lu); + return -EBUSY; + } + + sbp2_set_busy_timeout(lu); + sbp2_agent_reset(lu, 1); + sbp2_max_speed_and_size(lu); + + if (lu->workarounds & SBP2_WORKAROUND_DELAY_INQUIRY) + ssleep(SBP2_INQUIRY_DELAY); + + error = scsi_add_device(lu->shost, 0, lu->ud->id, 0); + if (error) { + SBP2_ERR("scsi_add_device failed"); + sbp2_logout_device(lu); + sbp2_remove_device(lu); + return error; + } + + return 0; + +alloc_fail: + SBP2_ERR("Could not allocate memory for lu"); + sbp2_remove_device(lu); + return -ENOMEM; +} + +static void sbp2_remove_device(struct sbp2_lu *lu) +{ + struct sbp2_fwhost_info *hi; + unsigned long flags; + + if (!lu) + return; + hi = lu->hi; + if (!hi) + goto no_hi; + + if (lu->shost) { + scsi_remove_host(lu->shost); + scsi_host_put(lu->shost); + } + flush_scheduled_work(); + sbp2util_remove_command_orb_pool(lu, hi->host); + + write_lock_irqsave(&sbp2_hi_logical_units_lock, flags); + list_del(&lu->lu_list); + write_unlock_irqrestore(&sbp2_hi_logical_units_lock, flags); + + if (lu->login_response) + dma_free_coherent(hi->host->device.parent, + sizeof(struct sbp2_login_response), + lu->login_response, + lu->login_response_dma); + if (lu->login_orb) + dma_free_coherent(hi->host->device.parent, + sizeof(struct sbp2_login_orb), + lu->login_orb, + lu->login_orb_dma); + if (lu->reconnect_orb) + dma_free_coherent(hi->host->device.parent, + sizeof(struct sbp2_reconnect_orb), + lu->reconnect_orb, + lu->reconnect_orb_dma); + if (lu->logout_orb) + dma_free_coherent(hi->host->device.parent, + sizeof(struct sbp2_logout_orb), + lu->logout_orb, + lu->logout_orb_dma); + if (lu->query_logins_orb) + dma_free_coherent(hi->host->device.parent, + sizeof(struct sbp2_query_logins_orb), + lu->query_logins_orb, + lu->query_logins_orb_dma); + if (lu->query_logins_response) + dma_free_coherent(hi->host->device.parent, + sizeof(struct sbp2_query_logins_response), + lu->query_logins_response, + lu->query_logins_response_dma); + + if (lu->status_fifo_addr != CSR1212_INVALID_ADDR_SPACE) + hpsb_unregister_addrspace(&sbp2_highlevel, hi->host, + lu->status_fifo_addr); + + dev_set_drvdata(&lu->ud->device, NULL); + + module_put(hi->host->driver->owner); +no_hi: + kfree(lu); +} + +#ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA +/* + * Deal with write requests on adapters which do not support physical DMA or + * have it switched off. + */ +static int sbp2_handle_physdma_write(struct hpsb_host *host, int nodeid, + int destid, quadlet_t *data, u64 addr, + size_t length, u16 flags) +{ + memcpy(bus_to_virt((u32) addr), data, length); + return RCODE_COMPLETE; +} + +/* + * Deal with read requests on adapters which do not support physical DMA or + * have it switched off. + */ +static int sbp2_handle_physdma_read(struct hpsb_host *host, int nodeid, + quadlet_t *data, u64 addr, size_t length, + u16 flags) +{ + memcpy(data, bus_to_virt((u32) addr), length); + return RCODE_COMPLETE; +} +#endif + +/************************************** + * SBP-2 protocol related section + **************************************/ + +static int sbp2_query_logins(struct sbp2_lu *lu) +{ + struct sbp2_fwhost_info *hi = lu->hi; + quadlet_t data[2]; + int max_logins; + int active_logins; + + lu->query_logins_orb->reserved1 = 0x0; + lu->query_logins_orb->reserved2 = 0x0; + + lu->query_logins_orb->query_response_lo = lu->query_logins_response_dma; + lu->query_logins_orb->query_response_hi = + ORB_SET_NODE_ID(hi->host->node_id); + lu->query_logins_orb->lun_misc = + ORB_SET_FUNCTION(SBP2_QUERY_LOGINS_REQUEST); + lu->query_logins_orb->lun_misc |= ORB_SET_NOTIFY(1); + lu->query_logins_orb->lun_misc |= ORB_SET_LUN(lu->lun); + + lu->query_logins_orb->reserved_resp_length = + ORB_SET_QUERY_LOGINS_RESP_LENGTH( + sizeof(struct sbp2_query_logins_response)); + + lu->query_logins_orb->status_fifo_hi = + ORB_SET_STATUS_FIFO_HI(lu->status_fifo_addr, hi->host->node_id); + lu->query_logins_orb->status_fifo_lo = + ORB_SET_STATUS_FIFO_LO(lu->status_fifo_addr); + + sbp2util_cpu_to_be32_buffer(lu->query_logins_orb, + sizeof(struct sbp2_query_logins_orb)); + + memset(lu->query_logins_response, 0, + sizeof(struct sbp2_query_logins_response)); + + data[0] = ORB_SET_NODE_ID(hi->host->node_id); + data[1] = lu->query_logins_orb_dma; + sbp2util_cpu_to_be32_buffer(data, 8); + + hpsb_node_write(lu->ne, lu->management_agent_addr, data, 8); + + if (sbp2util_access_timeout(lu, 2*HZ)) { + SBP2_INFO("Error querying logins to SBP-2 device - timed out"); + return -EIO; + } + + if (lu->status_block.ORB_offset_lo != lu->query_logins_orb_dma) { + SBP2_INFO("Error querying logins to SBP-2 device - timed out"); + return -EIO; + } + + if (STATUS_TEST_RDS(lu->status_block.ORB_offset_hi_misc)) { + SBP2_INFO("Error querying logins to SBP-2 device - failed"); + return -EIO; + } + + sbp2util_cpu_to_be32_buffer(lu->query_logins_response, + sizeof(struct sbp2_query_logins_response)); + + max_logins = RESPONSE_GET_MAX_LOGINS( + lu->query_logins_response->length_max_logins); + SBP2_INFO("Maximum concurrent logins supported: %d", max_logins); + + active_logins = RESPONSE_GET_ACTIVE_LOGINS( + lu->query_logins_response->length_max_logins); + SBP2_INFO("Number of active logins: %d", active_logins); + + if (active_logins >= max_logins) { + return -EIO; + } + + return 0; +} + +static int sbp2_login_device(struct sbp2_lu *lu) +{ + struct sbp2_fwhost_info *hi = lu->hi; + quadlet_t data[2]; + + if (!lu->login_orb) + return -EIO; + + if (!sbp2_exclusive_login && sbp2_query_logins(lu)) { + SBP2_INFO("Device does not support any more concurrent logins"); + return -EIO; + } + + /* assume no password */ + lu->login_orb->password_hi = 0; + lu->login_orb->password_lo = 0; + + lu->login_orb->login_response_lo = lu->login_response_dma; + lu->login_orb->login_response_hi = ORB_SET_NODE_ID(hi->host->node_id); + lu->login_orb->lun_misc = ORB_SET_FUNCTION(SBP2_LOGIN_REQUEST); + + /* one second reconnect time */ + lu->login_orb->lun_misc |= ORB_SET_RECONNECT(0); + lu->login_orb->lun_misc |= ORB_SET_EXCLUSIVE(sbp2_exclusive_login); + lu->login_orb->lun_misc |= ORB_SET_NOTIFY(1); + lu->login_orb->lun_misc |= ORB_SET_LUN(lu->lun); + + lu->login_orb->passwd_resp_lengths = + ORB_SET_LOGIN_RESP_LENGTH(sizeof(struct sbp2_login_response)); + + lu->login_orb->status_fifo_hi = + ORB_SET_STATUS_FIFO_HI(lu->status_fifo_addr, hi->host->node_id); + lu->login_orb->status_fifo_lo = + ORB_SET_STATUS_FIFO_LO(lu->status_fifo_addr); + + sbp2util_cpu_to_be32_buffer(lu->login_orb, + sizeof(struct sbp2_login_orb)); + + memset(lu->login_response, 0, sizeof(struct sbp2_login_response)); + + data[0] = ORB_SET_NODE_ID(hi->host->node_id); + data[1] = lu->login_orb_dma; + sbp2util_cpu_to_be32_buffer(data, 8); + + hpsb_node_write(lu->ne, lu->management_agent_addr, data, 8); + + /* wait up to 20 seconds for login status */ + if (sbp2util_access_timeout(lu, 20*HZ)) { + SBP2_ERR("Error logging into SBP-2 device - timed out"); + return -EIO; + } + + /* make sure that the returned status matches the login ORB */ + if (lu->status_block.ORB_offset_lo != lu->login_orb_dma) { + SBP2_ERR("Error logging into SBP-2 device - timed out"); + return -EIO; + } + + if (STATUS_TEST_RDS(lu->status_block.ORB_offset_hi_misc)) { + SBP2_ERR("Error logging into SBP-2 device - failed"); + return -EIO; + } + + sbp2util_cpu_to_be32_buffer(lu->login_response, + sizeof(struct sbp2_login_response)); + lu->command_block_agent_addr = + ((u64)lu->login_response->command_block_agent_hi) << 32; + lu->command_block_agent_addr |= + ((u64)lu->login_response->command_block_agent_lo); + lu->command_block_agent_addr &= 0x0000ffffffffffffULL; + + SBP2_INFO("Logged into SBP-2 device"); + return 0; +} + +static int sbp2_logout_device(struct sbp2_lu *lu) +{ + struct sbp2_fwhost_info *hi = lu->hi; + quadlet_t data[2]; + int error; + + lu->logout_orb->reserved1 = 0x0; + lu->logout_orb->reserved2 = 0x0; + lu->logout_orb->reserved3 = 0x0; + lu->logout_orb->reserved4 = 0x0; + + lu->logout_orb->login_ID_misc = ORB_SET_FUNCTION(SBP2_LOGOUT_REQUEST); + lu->logout_orb->login_ID_misc |= + ORB_SET_LOGIN_ID(lu->login_response->length_login_ID); + lu->logout_orb->login_ID_misc |= ORB_SET_NOTIFY(1); + + lu->logout_orb->reserved5 = 0x0; + lu->logout_orb->status_fifo_hi = + ORB_SET_STATUS_FIFO_HI(lu->status_fifo_addr, hi->host->node_id); + lu->logout_orb->status_fifo_lo = + ORB_SET_STATUS_FIFO_LO(lu->status_fifo_addr); + + sbp2util_cpu_to_be32_buffer(lu->logout_orb, + sizeof(struct sbp2_logout_orb)); + + data[0] = ORB_SET_NODE_ID(hi->host->node_id); + data[1] = lu->logout_orb_dma; + sbp2util_cpu_to_be32_buffer(data, 8); + + error = hpsb_node_write(lu->ne, lu->management_agent_addr, data, 8); + if (error) + return error; + + /* wait up to 1 second for the device to complete logout */ + if (sbp2util_access_timeout(lu, HZ)) + return -EIO; + + SBP2_INFO("Logged out of SBP-2 device"); + return 0; +} + +static int sbp2_reconnect_device(struct sbp2_lu *lu) +{ + struct sbp2_fwhost_info *hi = lu->hi; + quadlet_t data[2]; + int error; + + lu->reconnect_orb->reserved1 = 0x0; + lu->reconnect_orb->reserved2 = 0x0; + lu->reconnect_orb->reserved3 = 0x0; + lu->reconnect_orb->reserved4 = 0x0; + + lu->reconnect_orb->login_ID_misc = + ORB_SET_FUNCTION(SBP2_RECONNECT_REQUEST); + lu->reconnect_orb->login_ID_misc |= + ORB_SET_LOGIN_ID(lu->login_response->length_login_ID); + lu->reconnect_orb->login_ID_misc |= ORB_SET_NOTIFY(1); + + lu->reconnect_orb->reserved5 = 0x0; + lu->reconnect_orb->status_fifo_hi = + ORB_SET_STATUS_FIFO_HI(lu->status_fifo_addr, hi->host->node_id); + lu->reconnect_orb->status_fifo_lo = + ORB_SET_STATUS_FIFO_LO(lu->status_fifo_addr); + + sbp2util_cpu_to_be32_buffer(lu->reconnect_orb, + sizeof(struct sbp2_reconnect_orb)); + + data[0] = ORB_SET_NODE_ID(hi->host->node_id); + data[1] = lu->reconnect_orb_dma; + sbp2util_cpu_to_be32_buffer(data, 8); + + error = hpsb_node_write(lu->ne, lu->management_agent_addr, data, 8); + if (error) + return error; + + /* wait up to 1 second for reconnect status */ + if (sbp2util_access_timeout(lu, HZ)) { + SBP2_ERR("Error reconnecting to SBP-2 device - timed out"); + return -EIO; + } + + /* make sure that the returned status matches the reconnect ORB */ + if (lu->status_block.ORB_offset_lo != lu->reconnect_orb_dma) { + SBP2_ERR("Error reconnecting to SBP-2 device - timed out"); + return -EIO; + } + + if (STATUS_TEST_RDS(lu->status_block.ORB_offset_hi_misc)) { + SBP2_ERR("Error reconnecting to SBP-2 device - failed"); + return -EIO; + } + + SBP2_INFO("Reconnected to SBP-2 device"); + return 0; +} + +/* + * Set the target node's Single Phase Retry limit. Affects the target's retry + * behaviour if our node is too busy to accept requests. + */ +static int sbp2_set_busy_timeout(struct sbp2_lu *lu) +{ + quadlet_t data; + + data = cpu_to_be32(SBP2_BUSY_TIMEOUT_VALUE); + if (hpsb_node_write(lu->ne, SBP2_BUSY_TIMEOUT_ADDRESS, &data, 4)) + SBP2_ERR("%s error", __func__); + return 0; +} + +static void sbp2_parse_unit_directory(struct sbp2_lu *lu, + struct unit_directory *ud) +{ + struct csr1212_keyval *kv; + struct csr1212_dentry *dentry; + u64 management_agent_addr; + u32 firmware_revision, model; + unsigned workarounds; + int i; + + management_agent_addr = 0; + firmware_revision = SBP2_ROM_VALUE_MISSING; + model = ud->flags & UNIT_DIRECTORY_MODEL_ID ? + ud->model_id : SBP2_ROM_VALUE_MISSING; + + csr1212_for_each_dir_entry(ud->ne->csr, kv, ud->ud_kv, dentry) { + switch (kv->key.id) { + case CSR1212_KV_ID_DEPENDENT_INFO: + if (kv->key.type == CSR1212_KV_TYPE_CSR_OFFSET) + management_agent_addr = + CSR1212_REGISTER_SPACE_BASE + + (kv->value.csr_offset << 2); + + else if (kv->key.type == CSR1212_KV_TYPE_IMMEDIATE) + lu->lun = ORB_SET_LUN(kv->value.immediate); + break; + + + case SBP2_FIRMWARE_REVISION_KEY: + firmware_revision = kv->value.immediate; + break; + + default: + /* FIXME: Check for SBP2_UNIT_CHARACTERISTICS_KEY + * mgt_ORB_timeout and ORB_size, SBP-2 clause 7.4.8. */ + + /* FIXME: Check for SBP2_DEVICE_TYPE_AND_LUN_KEY. + * Its "ordered" bit has consequences for command ORB + * list handling. See SBP-2 clauses 4.6, 7.4.11, 10.2 */ + break; + } + } + + workarounds = sbp2_default_workarounds; + + if (!(workarounds & SBP2_WORKAROUND_OVERRIDE)) + for (i = 0; i < ARRAY_SIZE(sbp2_workarounds_table); i++) { + if (sbp2_workarounds_table[i].firmware_revision != + SBP2_ROM_VALUE_WILDCARD && + sbp2_workarounds_table[i].firmware_revision != + (firmware_revision & 0xffff00)) + continue; + if (sbp2_workarounds_table[i].model != + SBP2_ROM_VALUE_WILDCARD && + sbp2_workarounds_table[i].model != model) + continue; + workarounds |= sbp2_workarounds_table[i].workarounds; + break; + } + + if (workarounds) + SBP2_INFO("Workarounds for node " NODE_BUS_FMT ": 0x%x " + "(firmware_revision 0x%06x, vendor_id 0x%06x," + " model_id 0x%06x)", + NODE_BUS_ARGS(ud->ne->host, ud->ne->nodeid), + workarounds, firmware_revision, ud->vendor_id, + model); + + /* We would need one SCSI host template for each target to adjust + * max_sectors on the fly, therefore warn only. */ + if (workarounds & SBP2_WORKAROUND_128K_MAX_TRANS && + (sbp2_max_sectors * 512) > (128 * 1024)) + SBP2_INFO("Node " NODE_BUS_FMT ": Bridge only supports 128KB " + "max transfer size. WARNING: Current max_sectors " + "setting is larger than 128KB (%d sectors)", + NODE_BUS_ARGS(ud->ne->host, ud->ne->nodeid), + sbp2_max_sectors); + + /* If this is a logical unit directory entry, process the parent + * to get the values. */ + if (ud->flags & UNIT_DIRECTORY_LUN_DIRECTORY) { + struct unit_directory *parent_ud = container_of( + ud->device.parent, struct unit_directory, device); + sbp2_parse_unit_directory(lu, parent_ud); + } else { + lu->management_agent_addr = management_agent_addr; + lu->workarounds = workarounds; + if (ud->flags & UNIT_DIRECTORY_HAS_LUN) + lu->lun = ORB_SET_LUN(ud->lun); + } +} + +#define SBP2_PAYLOAD_TO_BYTES(p) (1 << ((p) + 2)) + +/* + * This function is called in order to determine the max speed and packet + * size we can use in our ORBs. Note, that we (the driver and host) only + * initiate the transaction. The SBP-2 device actually transfers the data + * (by reading from the DMA area we tell it). This means that the SBP-2 + * device decides the actual maximum data it can transfer. We just tell it + * the speed that it needs to use, and the max_rec the host supports, and + * it takes care of the rest. + */ +static int sbp2_max_speed_and_size(struct sbp2_lu *lu) +{ + struct sbp2_fwhost_info *hi = lu->hi; + u8 payload; + + lu->speed_code = hi->host->speed[NODEID_TO_NODE(lu->ne->nodeid)]; + + if (lu->speed_code > sbp2_max_speed) { + lu->speed_code = sbp2_max_speed; + SBP2_INFO("Reducing speed to %s", + hpsb_speedto_str[sbp2_max_speed]); + } + + /* Payload size is the lesser of what our speed supports and what + * our host supports. */ + payload = min(sbp2_speedto_max_payload[lu->speed_code], + (u8) (hi->host->csr.max_rec - 1)); + + /* If physical DMA is off, work around limitation in ohci1394: + * packet size must not exceed PAGE_SIZE */ + if (lu->ne->host->low_addr_space < (1ULL << 32)) + while (SBP2_PAYLOAD_TO_BYTES(payload) + 24 > PAGE_SIZE && + payload) + payload--; + + SBP2_INFO("Node " NODE_BUS_FMT ": Max speed [%s] - Max payload [%u]", + NODE_BUS_ARGS(hi->host, lu->ne->nodeid), + hpsb_speedto_str[lu->speed_code], + SBP2_PAYLOAD_TO_BYTES(payload)); + + lu->max_payload_size = payload; + return 0; +} + +static int sbp2_agent_reset(struct sbp2_lu *lu, int wait) +{ + quadlet_t data; + u64 addr; + int retval; + unsigned long flags; + + /* flush lu->protocol_work */ + if (wait) + flush_scheduled_work(); + + data = ntohl(SBP2_AGENT_RESET_DATA); + addr = lu->command_block_agent_addr + SBP2_AGENT_RESET_OFFSET; + + if (wait) + retval = hpsb_node_write(lu->ne, addr, &data, 4); + else + retval = sbp2util_node_write_no_wait(lu->ne, addr, &data, 4); + + if (retval < 0) { + SBP2_ERR("hpsb_node_write failed.\n"); + return -EIO; + } + + /* make sure that the ORB_POINTER is written on next command */ + spin_lock_irqsave(&lu->cmd_orb_lock, flags); + lu->last_orb = NULL; + spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); + + return 0; +} + +static int sbp2_prep_command_orb_sg(struct sbp2_command_orb *orb, + struct sbp2_fwhost_info *hi, + struct sbp2_command_info *cmd, + unsigned int sg_count, + struct scatterlist *sg, + u32 orb_direction, + enum dma_data_direction dma_dir) +{ + struct device *dmadev = hi->host->device.parent; + struct sbp2_unrestricted_page_table *pt; + int i, n; + + n = dma_map_sg(dmadev, sg, sg_count, dma_dir); + if (n == 0) + return -ENOMEM; + + orb->data_descriptor_hi = ORB_SET_NODE_ID(hi->host->node_id); + orb->misc |= ORB_SET_DIRECTION(orb_direction); + + /* special case if only one element (and less than 64KB in size) */ + if (n == 1) { + orb->misc |= ORB_SET_DATA_SIZE(sg_dma_len(sg)); + orb->data_descriptor_lo = sg_dma_address(sg); + } else { + pt = &cmd->scatter_gather_element[0]; + + dma_sync_single_for_cpu(dmadev, cmd->sge_dma, + sizeof(cmd->scatter_gather_element), + DMA_TO_DEVICE); + + for_each_sg(sg, sg, n, i) { + pt[i].high = cpu_to_be32(sg_dma_len(sg) << 16); + pt[i].low = cpu_to_be32(sg_dma_address(sg)); + } + + orb->misc |= ORB_SET_PAGE_TABLE_PRESENT(0x1) | + ORB_SET_DATA_SIZE(n); + orb->data_descriptor_lo = cmd->sge_dma; + + dma_sync_single_for_device(dmadev, cmd->sge_dma, + sizeof(cmd->scatter_gather_element), + DMA_TO_DEVICE); + } + return 0; +} + +static int sbp2_create_command_orb(struct sbp2_lu *lu, + struct sbp2_command_info *cmd, + struct scsi_cmnd *SCpnt) +{ + struct device *dmadev = lu->hi->host->device.parent; + struct sbp2_command_orb *orb = &cmd->command_orb; + unsigned int scsi_request_bufflen = scsi_bufflen(SCpnt); + enum dma_data_direction dma_dir = SCpnt->sc_data_direction; + u32 orb_direction; + int ret; + + dma_sync_single_for_cpu(dmadev, cmd->command_orb_dma, + sizeof(struct sbp2_command_orb), DMA_TO_DEVICE); + /* + * Set-up our command ORB. + * + * NOTE: We're doing unrestricted page tables (s/g), as this is + * best performance (at least with the devices I have). This means + * that data_size becomes the number of s/g elements, and + * page_size should be zero (for unrestricted). + */ + orb->next_ORB_hi = ORB_SET_NULL_PTR(1); + orb->next_ORB_lo = 0x0; + orb->misc = ORB_SET_MAX_PAYLOAD(lu->max_payload_size); + orb->misc |= ORB_SET_SPEED(lu->speed_code); + orb->misc |= ORB_SET_NOTIFY(1); + + if (dma_dir == DMA_NONE) + orb_direction = ORB_DIRECTION_NO_DATA_TRANSFER; + else if (dma_dir == DMA_TO_DEVICE && scsi_request_bufflen) + orb_direction = ORB_DIRECTION_WRITE_TO_MEDIA; + else if (dma_dir == DMA_FROM_DEVICE && scsi_request_bufflen) + orb_direction = ORB_DIRECTION_READ_FROM_MEDIA; + else { + SBP2_INFO("Falling back to DMA_NONE"); + orb_direction = ORB_DIRECTION_NO_DATA_TRANSFER; + } + + /* set up our page table stuff */ + if (orb_direction == ORB_DIRECTION_NO_DATA_TRANSFER) { + orb->data_descriptor_hi = 0x0; + orb->data_descriptor_lo = 0x0; + orb->misc |= ORB_SET_DIRECTION(1); + ret = 0; + } else { + ret = sbp2_prep_command_orb_sg(orb, lu->hi, cmd, + scsi_sg_count(SCpnt), + scsi_sglist(SCpnt), + orb_direction, dma_dir); + } + sbp2util_cpu_to_be32_buffer(orb, sizeof(*orb)); + + memset(orb->cdb, 0, sizeof(orb->cdb)); + memcpy(orb->cdb, SCpnt->cmnd, SCpnt->cmd_len); + + dma_sync_single_for_device(dmadev, cmd->command_orb_dma, + sizeof(struct sbp2_command_orb), DMA_TO_DEVICE); + return ret; +} + +static void sbp2_link_orb_command(struct sbp2_lu *lu, + struct sbp2_command_info *cmd) +{ + struct sbp2_fwhost_info *hi = lu->hi; + struct sbp2_command_orb *last_orb; + dma_addr_t last_orb_dma; + u64 addr = lu->command_block_agent_addr; + quadlet_t data[2]; + size_t length; + unsigned long flags; + + /* check to see if there are any previous orbs to use */ + spin_lock_irqsave(&lu->cmd_orb_lock, flags); + last_orb = lu->last_orb; + last_orb_dma = lu->last_orb_dma; + if (!last_orb) { + /* + * last_orb == NULL means: We know that the target's fetch agent + * is not active right now. + */ + addr += SBP2_ORB_POINTER_OFFSET; + data[0] = ORB_SET_NODE_ID(hi->host->node_id); + data[1] = cmd->command_orb_dma; + sbp2util_cpu_to_be32_buffer(data, 8); + length = 8; + } else { + /* + * last_orb != NULL means: We know that the target's fetch agent + * is (very probably) not dead or in reset state right now. + * We have an ORB already sent that we can append a new one to. + * The target's fetch agent may or may not have read this + * previous ORB yet. + */ + dma_sync_single_for_cpu(hi->host->device.parent, last_orb_dma, + sizeof(struct sbp2_command_orb), + DMA_TO_DEVICE); + last_orb->next_ORB_lo = cpu_to_be32(cmd->command_orb_dma); + wmb(); + /* Tells hardware that this pointer is valid */ + last_orb->next_ORB_hi = 0; + dma_sync_single_for_device(hi->host->device.parent, + last_orb_dma, + sizeof(struct sbp2_command_orb), + DMA_TO_DEVICE); + addr += SBP2_DOORBELL_OFFSET; + data[0] = 0; + length = 4; + } + lu->last_orb = &cmd->command_orb; + lu->last_orb_dma = cmd->command_orb_dma; + spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); + + if (sbp2util_node_write_no_wait(lu->ne, addr, data, length)) { + /* + * sbp2util_node_write_no_wait failed. We certainly ran out + * of transaction labels, perhaps just because there were no + * context switches which gave khpsbpkt a chance to collect + * free tlabels. Try again in non-atomic context. If necessary, + * the workqueue job will sleep to guaranteedly get a tlabel. + * We do not accept new commands until the job is over. + */ + scsi_block_requests(lu->shost); + PREPARE_WORK(&lu->protocol_work, + last_orb ? sbp2util_write_doorbell: + sbp2util_write_orb_pointer); + schedule_work(&lu->protocol_work); + } +} + +static int sbp2_send_command(struct sbp2_lu *lu, struct scsi_cmnd *SCpnt, + void (*done)(struct scsi_cmnd *)) +{ + struct sbp2_command_info *cmd; + + cmd = sbp2util_allocate_command_orb(lu, SCpnt, done); + if (!cmd) + return -EIO; + + if (sbp2_create_command_orb(lu, cmd, SCpnt)) + return -ENOMEM; + + sbp2_link_orb_command(lu, cmd); + return 0; +} + +/* + * Translates SBP-2 status into SCSI sense data for check conditions + */ +static unsigned int sbp2_status_to_sense_data(unchar *sbp2_status, + unchar *sense_data) +{ + /* OK, it's pretty ugly... ;-) */ + sense_data[0] = 0x70; + sense_data[1] = 0x0; + sense_data[2] = sbp2_status[9]; + sense_data[3] = sbp2_status[12]; + sense_data[4] = sbp2_status[13]; + sense_data[5] = sbp2_status[14]; + sense_data[6] = sbp2_status[15]; + sense_data[7] = 10; + sense_data[8] = sbp2_status[16]; + sense_data[9] = sbp2_status[17]; + sense_data[10] = sbp2_status[18]; + sense_data[11] = sbp2_status[19]; + sense_data[12] = sbp2_status[10]; + sense_data[13] = sbp2_status[11]; + sense_data[14] = sbp2_status[20]; + sense_data[15] = sbp2_status[21]; + + return sbp2_status[8] & 0x3f; +} + +static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, + int destid, quadlet_t *data, u64 addr, + size_t length, u16 fl) +{ + struct sbp2_fwhost_info *hi; + struct sbp2_lu *lu = NULL, *lu_tmp; + struct scsi_cmnd *SCpnt = NULL; + struct sbp2_status_block *sb; + u32 scsi_status = SBP2_SCSI_STATUS_GOOD; + struct sbp2_command_info *cmd; + unsigned long flags; + + if (unlikely(length < 8 || length > sizeof(struct sbp2_status_block))) { + SBP2_ERR("Wrong size of status block"); + return RCODE_ADDRESS_ERROR; + } + if (unlikely(!host)) { + SBP2_ERR("host is NULL - this is bad!"); + return RCODE_ADDRESS_ERROR; + } + hi = hpsb_get_hostinfo(&sbp2_highlevel, host); + if (unlikely(!hi)) { + SBP2_ERR("host info is NULL - this is bad!"); + return RCODE_ADDRESS_ERROR; + } + + /* Find the unit which wrote the status. */ + read_lock_irqsave(&sbp2_hi_logical_units_lock, flags); + list_for_each_entry(lu_tmp, &hi->logical_units, lu_list) { + if (lu_tmp->ne->nodeid == nodeid && + lu_tmp->status_fifo_addr == addr) { + lu = lu_tmp; + break; + } + } + read_unlock_irqrestore(&sbp2_hi_logical_units_lock, flags); + + if (unlikely(!lu)) { + SBP2_ERR("lu is NULL - device is gone?"); + return RCODE_ADDRESS_ERROR; + } + + /* Put response into lu status fifo buffer. The first two bytes + * come in big endian bit order. Often the target writes only a + * truncated status block, minimally the first two quadlets. The rest + * is implied to be zeros. */ + sb = &lu->status_block; + memset(sb->command_set_dependent, 0, sizeof(sb->command_set_dependent)); + memcpy(sb, data, length); + sbp2util_be32_to_cpu_buffer(sb, 8); + + /* Ignore unsolicited status. Handle command ORB status. */ + if (unlikely(STATUS_GET_SRC(sb->ORB_offset_hi_misc) == 2)) + cmd = NULL; + else + cmd = sbp2util_find_command_for_orb(lu, sb->ORB_offset_lo); + if (cmd) { + /* Grab SCSI command pointers and check status. */ + /* + * FIXME: If the src field in the status is 1, the ORB DMA must + * not be reused until status for a subsequent ORB is received. + */ + SCpnt = cmd->Current_SCpnt; + spin_lock_irqsave(&lu->cmd_orb_lock, flags); + sbp2util_mark_command_completed(lu, cmd); + spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); + + if (SCpnt) { + u32 h = sb->ORB_offset_hi_misc; + u32 r = STATUS_GET_RESP(h); + + if (r != RESP_STATUS_REQUEST_COMPLETE) { + SBP2_INFO("resp 0x%x, sbp_status 0x%x", + r, STATUS_GET_SBP_STATUS(h)); + scsi_status = + r == RESP_STATUS_TRANSPORT_FAILURE ? + SBP2_SCSI_STATUS_BUSY : + SBP2_SCSI_STATUS_COMMAND_TERMINATED; + } + + if (STATUS_GET_LEN(h) > 1) + scsi_status = sbp2_status_to_sense_data( + (unchar *)sb, SCpnt->sense_buffer); + + if (STATUS_TEST_DEAD(h)) + sbp2_agent_reset(lu, 0); + } + + /* Check here to see if there are no commands in-use. If there + * are none, we know that the fetch agent left the active state + * _and_ that we did not reactivate it yet. Therefore clear + * last_orb so that next time we write directly to the + * ORB_POINTER register. That way the fetch agent does not need + * to refetch the next_ORB. */ + spin_lock_irqsave(&lu->cmd_orb_lock, flags); + if (list_empty(&lu->cmd_orb_inuse)) + lu->last_orb = NULL; + spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); + + } else { + /* It's probably status after a management request. */ + if ((sb->ORB_offset_lo == lu->reconnect_orb_dma) || + (sb->ORB_offset_lo == lu->login_orb_dma) || + (sb->ORB_offset_lo == lu->query_logins_orb_dma) || + (sb->ORB_offset_lo == lu->logout_orb_dma)) { + lu->access_complete = 1; + wake_up_interruptible(&sbp2_access_wq); + } + } + + if (SCpnt) + sbp2scsi_complete_command(lu, scsi_status, SCpnt, + cmd->Current_done); + return RCODE_COMPLETE; +} + +/************************************** + * SCSI interface related section + **************************************/ + +static int sbp2scsi_queuecommand(struct scsi_cmnd *SCpnt, + void (*done)(struct scsi_cmnd *)) +{ + struct sbp2_lu *lu = (struct sbp2_lu *)SCpnt->device->host->hostdata[0]; + struct sbp2_fwhost_info *hi; + int result = DID_NO_CONNECT << 16; + + if (unlikely(!sbp2util_node_is_available(lu))) + goto done; + + hi = lu->hi; + + if (unlikely(!hi)) { + SBP2_ERR("sbp2_fwhost_info is NULL - this is bad!"); + goto done; + } + + /* Multiple units are currently represented to the SCSI core as separate + * targets, not as one target with multiple LUs. Therefore return + * selection time-out to any IO directed at non-zero LUNs. */ + if (unlikely(SCpnt->device->lun)) + goto done; + + if (unlikely(!hpsb_node_entry_valid(lu->ne))) { + SBP2_ERR("Bus reset in progress - rejecting command"); + result = DID_BUS_BUSY << 16; + goto done; + } + + /* Bidirectional commands are not yet implemented, + * and unknown transfer direction not handled. */ + if (unlikely(SCpnt->sc_data_direction == DMA_BIDIRECTIONAL)) { + SBP2_ERR("Cannot handle DMA_BIDIRECTIONAL - rejecting command"); + result = DID_ERROR << 16; + goto done; + } + + if (sbp2_send_command(lu, SCpnt, done)) { + SBP2_ERR("Error sending SCSI command"); + sbp2scsi_complete_command(lu, + SBP2_SCSI_STATUS_SELECTION_TIMEOUT, + SCpnt, done); + } + return 0; + +done: + SCpnt->result = result; + done(SCpnt); + return 0; +} + +static void sbp2scsi_complete_all_commands(struct sbp2_lu *lu, u32 status) +{ + struct list_head *lh; + struct sbp2_command_info *cmd; + unsigned long flags; + + spin_lock_irqsave(&lu->cmd_orb_lock, flags); + while (!list_empty(&lu->cmd_orb_inuse)) { + lh = lu->cmd_orb_inuse.next; + cmd = list_entry(lh, struct sbp2_command_info, list); + sbp2util_mark_command_completed(lu, cmd); + if (cmd->Current_SCpnt) { + cmd->Current_SCpnt->result = status << 16; + cmd->Current_done(cmd->Current_SCpnt); + } + } + spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); + + return; +} + +/* + * Complete a regular SCSI command. Can be called in atomic context. + */ +static void sbp2scsi_complete_command(struct sbp2_lu *lu, u32 scsi_status, + struct scsi_cmnd *SCpnt, + void (*done)(struct scsi_cmnd *)) +{ + if (!SCpnt) { + SBP2_ERR("SCpnt is NULL"); + return; + } + + switch (scsi_status) { + case SBP2_SCSI_STATUS_GOOD: + SCpnt->result = DID_OK << 16; + break; + + case SBP2_SCSI_STATUS_BUSY: + SBP2_ERR("SBP2_SCSI_STATUS_BUSY"); + SCpnt->result = DID_BUS_BUSY << 16; + break; + + case SBP2_SCSI_STATUS_CHECK_CONDITION: + SCpnt->result = CHECK_CONDITION << 1 | DID_OK << 16; + break; + + case SBP2_SCSI_STATUS_SELECTION_TIMEOUT: + SBP2_ERR("SBP2_SCSI_STATUS_SELECTION_TIMEOUT"); + SCpnt->result = DID_NO_CONNECT << 16; + scsi_print_command(SCpnt); + break; + + case SBP2_SCSI_STATUS_CONDITION_MET: + case SBP2_SCSI_STATUS_RESERVATION_CONFLICT: + case SBP2_SCSI_STATUS_COMMAND_TERMINATED: + SBP2_ERR("Bad SCSI status = %x", scsi_status); + SCpnt->result = DID_ERROR << 16; + scsi_print_command(SCpnt); + break; + + default: + SBP2_ERR("Unsupported SCSI status = %x", scsi_status); + SCpnt->result = DID_ERROR << 16; + } + + /* If a bus reset is in progress and there was an error, complete + * the command as busy so that it will get retried. */ + if (!hpsb_node_entry_valid(lu->ne) + && (scsi_status != SBP2_SCSI_STATUS_GOOD)) { + SBP2_ERR("Completing command with busy (bus reset)"); + SCpnt->result = DID_BUS_BUSY << 16; + } + + /* Tell the SCSI stack that we're done with this command. */ + done(SCpnt); +} + +static int sbp2scsi_slave_alloc(struct scsi_device *sdev) +{ + struct sbp2_lu *lu = (struct sbp2_lu *)sdev->host->hostdata[0]; + + if (sdev->lun != 0 || sdev->id != lu->ud->id || sdev->channel != 0) + return -ENODEV; + + lu->sdev = sdev; + sdev->allow_restart = 1; + + /* SBP-2 requires quadlet alignment of the data buffers. */ + blk_queue_update_dma_alignment(sdev->request_queue, 4 - 1); + + if (lu->workarounds & SBP2_WORKAROUND_INQUIRY_36) + sdev->inquiry_len = 36; + return 0; +} + +static int sbp2scsi_slave_configure(struct scsi_device *sdev) +{ + struct sbp2_lu *lu = (struct sbp2_lu *)sdev->host->hostdata[0]; + + sdev->use_10_for_rw = 1; + + if (sbp2_exclusive_login) + sdev->manage_start_stop = 1; + if (sdev->type == TYPE_ROM) + sdev->use_10_for_ms = 1; + if (sdev->type == TYPE_DISK && + lu->workarounds & SBP2_WORKAROUND_MODE_SENSE_8) + sdev->skip_ms_page_8 = 1; + if (lu->workarounds & SBP2_WORKAROUND_FIX_CAPACITY) + sdev->fix_capacity = 1; + if (lu->workarounds & SBP2_WORKAROUND_POWER_CONDITION) + sdev->start_stop_pwr_cond = 1; + if (lu->workarounds & SBP2_WORKAROUND_128K_MAX_TRANS) + blk_queue_max_hw_sectors(sdev->request_queue, 128 * 1024 / 512); + + blk_queue_max_segment_size(sdev->request_queue, SBP2_MAX_SEG_SIZE); + return 0; +} + +static void sbp2scsi_slave_destroy(struct scsi_device *sdev) +{ + ((struct sbp2_lu *)sdev->host->hostdata[0])->sdev = NULL; + return; +} + +/* + * Called by scsi stack when something has really gone wrong. + * Usually called when a command has timed-out for some reason. + */ +static int sbp2scsi_abort(struct scsi_cmnd *SCpnt) +{ + struct sbp2_lu *lu = (struct sbp2_lu *)SCpnt->device->host->hostdata[0]; + struct sbp2_command_info *cmd; + unsigned long flags; + + SBP2_INFO("aborting sbp2 command"); + scsi_print_command(SCpnt); + + if (sbp2util_node_is_available(lu)) { + sbp2_agent_reset(lu, 1); + + /* Return a matching command structure to the free pool. */ + spin_lock_irqsave(&lu->cmd_orb_lock, flags); + cmd = sbp2util_find_command_for_SCpnt(lu, SCpnt); + if (cmd) { + sbp2util_mark_command_completed(lu, cmd); + if (cmd->Current_SCpnt) { + cmd->Current_SCpnt->result = DID_ABORT << 16; + cmd->Current_done(cmd->Current_SCpnt); + } + } + spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); + + sbp2scsi_complete_all_commands(lu, DID_BUS_BUSY); + } + + return SUCCESS; +} + +/* + * Called by scsi stack when something has really gone wrong. + */ +static int sbp2scsi_reset(struct scsi_cmnd *SCpnt) +{ + struct sbp2_lu *lu = (struct sbp2_lu *)SCpnt->device->host->hostdata[0]; + + SBP2_INFO("reset requested"); + + if (sbp2util_node_is_available(lu)) { + SBP2_INFO("generating sbp2 fetch agent reset"); + sbp2_agent_reset(lu, 1); + } + + return SUCCESS; +} + +static ssize_t sbp2_sysfs_ieee1394_id_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct scsi_device *sdev; + struct sbp2_lu *lu; + + if (!(sdev = to_scsi_device(dev))) + return 0; + + if (!(lu = (struct sbp2_lu *)sdev->host->hostdata[0])) + return 0; + + if (sbp2_long_sysfs_ieee1394_id) + return sprintf(buf, "%016Lx:%06x:%04x\n", + (unsigned long long)lu->ne->guid, + lu->ud->directory_id, ORB_SET_LUN(lu->lun)); + else + return sprintf(buf, "%016Lx:%d:%d\n", + (unsigned long long)lu->ne->guid, + lu->ud->id, ORB_SET_LUN(lu->lun)); +} + +MODULE_AUTHOR("Ben Collins "); +MODULE_DESCRIPTION("IEEE-1394 SBP-2 protocol driver"); +MODULE_SUPPORTED_DEVICE(SBP2_DEVICE_NAME); +MODULE_LICENSE("GPL"); + +static int sbp2_module_init(void) +{ + int ret; + + if (sbp2_serialize_io) { + sbp2_shost_template.can_queue = 1; + sbp2_shost_template.cmd_per_lun = 1; + } + + sbp2_shost_template.max_sectors = sbp2_max_sectors; + + hpsb_register_highlevel(&sbp2_highlevel); + ret = hpsb_register_protocol(&sbp2_driver); + if (ret) { + SBP2_ERR("Failed to register protocol"); + hpsb_unregister_highlevel(&sbp2_highlevel); + return ret; + } + return 0; +} + +static void __exit sbp2_module_exit(void) +{ + hpsb_unregister_protocol(&sbp2_driver); + hpsb_unregister_highlevel(&sbp2_highlevel); +} + +module_init(sbp2_module_init); +module_exit(sbp2_module_exit); diff --git a/trunk/drivers/ieee1394/sbp2.h b/trunk/drivers/ieee1394/sbp2.h new file mode 100644 index 000000000000..64a3a66a8a39 --- /dev/null +++ b/trunk/drivers/ieee1394/sbp2.h @@ -0,0 +1,346 @@ +/* + * sbp2.h - Defines and prototypes for sbp2.c + * + * Copyright (C) 2000 James Goodwin, Filanet Corporation (www.filanet.com) + * jamesg@filanet.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. + */ + +#ifndef SBP2_H +#define SBP2_H + +#define SBP2_DEVICE_NAME "sbp2" + +/* + * There is no transport protocol limit to the CDB length, but we implement + * a fixed length only. 16 bytes is enough for disks larger than 2 TB. + */ +#define SBP2_MAX_CDB_SIZE 16 + +/* + * SBP-2 specific definitions + */ + +#define ORB_DIRECTION_WRITE_TO_MEDIA 0x0 +#define ORB_DIRECTION_READ_FROM_MEDIA 0x1 +#define ORB_DIRECTION_NO_DATA_TRANSFER 0x2 + +#define ORB_SET_NULL_PTR(v) (((v) & 0x1) << 31) +#define ORB_SET_NOTIFY(v) (((v) & 0x1) << 31) +#define ORB_SET_RQ_FMT(v) (((v) & 0x3) << 29) +#define ORB_SET_NODE_ID(v) (((v) & 0xffff) << 16) +#define ORB_SET_STATUS_FIFO_HI(v, id) ((v) >> 32 | ORB_SET_NODE_ID(id)) +#define ORB_SET_STATUS_FIFO_LO(v) ((v) & 0xffffffff) +#define ORB_SET_DATA_SIZE(v) ((v) & 0xffff) +#define ORB_SET_PAGE_SIZE(v) (((v) & 0x7) << 16) +#define ORB_SET_PAGE_TABLE_PRESENT(v) (((v) & 0x1) << 19) +#define ORB_SET_MAX_PAYLOAD(v) (((v) & 0xf) << 20) +#define ORB_SET_SPEED(v) (((v) & 0x7) << 24) +#define ORB_SET_DIRECTION(v) (((v) & 0x1) << 27) + +struct sbp2_command_orb { + u32 next_ORB_hi; + u32 next_ORB_lo; + u32 data_descriptor_hi; + u32 data_descriptor_lo; + u32 misc; + u8 cdb[SBP2_MAX_CDB_SIZE]; +} __attribute__((packed)); + +#define SBP2_LOGIN_REQUEST 0x0 +#define SBP2_QUERY_LOGINS_REQUEST 0x1 +#define SBP2_RECONNECT_REQUEST 0x3 +#define SBP2_SET_PASSWORD_REQUEST 0x4 +#define SBP2_LOGOUT_REQUEST 0x7 +#define SBP2_ABORT_TASK_REQUEST 0xb +#define SBP2_ABORT_TASK_SET 0xc +#define SBP2_LOGICAL_UNIT_RESET 0xe +#define SBP2_TARGET_RESET_REQUEST 0xf + +#define ORB_SET_LUN(v) ((v) & 0xffff) +#define ORB_SET_FUNCTION(v) (((v) & 0xf) << 16) +#define ORB_SET_RECONNECT(v) (((v) & 0xf) << 20) +#define ORB_SET_EXCLUSIVE(v) ((v) ? 1 << 28 : 0) +#define ORB_SET_LOGIN_RESP_LENGTH(v) ((v) & 0xffff) +#define ORB_SET_PASSWD_LENGTH(v) (((v) & 0xffff) << 16) + +struct sbp2_login_orb { + u32 password_hi; + u32 password_lo; + u32 login_response_hi; + u32 login_response_lo; + u32 lun_misc; + u32 passwd_resp_lengths; + u32 status_fifo_hi; + u32 status_fifo_lo; +} __attribute__((packed)); + +#define RESPONSE_GET_LOGIN_ID(v) ((v) & 0xffff) +#define RESPONSE_GET_LENGTH(v) (((v) >> 16) & 0xffff) +#define RESPONSE_GET_RECONNECT_HOLD(v) ((v) & 0xffff) + +struct sbp2_login_response { + u32 length_login_ID; + u32 command_block_agent_hi; + u32 command_block_agent_lo; + u32 reconnect_hold; +} __attribute__((packed)); + +#define ORB_SET_LOGIN_ID(v) ((v) & 0xffff) +#define ORB_SET_QUERY_LOGINS_RESP_LENGTH(v) ((v) & 0xffff) + +struct sbp2_query_logins_orb { + u32 reserved1; + u32 reserved2; + u32 query_response_hi; + u32 query_response_lo; + u32 lun_misc; + u32 reserved_resp_length; + u32 status_fifo_hi; + u32 status_fifo_lo; +} __attribute__((packed)); + +#define RESPONSE_GET_MAX_LOGINS(v) ((v) & 0xffff) +#define RESPONSE_GET_ACTIVE_LOGINS(v) ((RESPONSE_GET_LENGTH((v)) - 4) / 12) + +struct sbp2_query_logins_response { + u32 length_max_logins; + u32 misc_IDs; + u32 initiator_misc_hi; + u32 initiator_misc_lo; +} __attribute__((packed)); + +struct sbp2_reconnect_orb { + u32 reserved1; + u32 reserved2; + u32 reserved3; + u32 reserved4; + u32 login_ID_misc; + u32 reserved5; + u32 status_fifo_hi; + u32 status_fifo_lo; +} __attribute__((packed)); + +struct sbp2_logout_orb { + u32 reserved1; + u32 reserved2; + u32 reserved3; + u32 reserved4; + u32 login_ID_misc; + u32 reserved5; + u32 status_fifo_hi; + u32 status_fifo_lo; +} __attribute__((packed)); + +struct sbp2_unrestricted_page_table { + __be32 high; + __be32 low; +}; + +#define RESP_STATUS_REQUEST_COMPLETE 0x0 +#define RESP_STATUS_TRANSPORT_FAILURE 0x1 +#define RESP_STATUS_ILLEGAL_REQUEST 0x2 +#define RESP_STATUS_VENDOR_DEPENDENT 0x3 + +#define SBP2_STATUS_NO_ADDITIONAL_INFO 0x0 +#define SBP2_STATUS_REQ_TYPE_NOT_SUPPORTED 0x1 +#define SBP2_STATUS_SPEED_NOT_SUPPORTED 0x2 +#define SBP2_STATUS_PAGE_SIZE_NOT_SUPPORTED 0x3 +#define SBP2_STATUS_ACCESS_DENIED 0x4 +#define SBP2_STATUS_LU_NOT_SUPPORTED 0x5 +#define SBP2_STATUS_MAX_PAYLOAD_TOO_SMALL 0x6 +#define SBP2_STATUS_RESOURCES_UNAVAILABLE 0x8 +#define SBP2_STATUS_FUNCTION_REJECTED 0x9 +#define SBP2_STATUS_LOGIN_ID_NOT_RECOGNIZED 0xa +#define SBP2_STATUS_DUMMY_ORB_COMPLETED 0xb +#define SBP2_STATUS_REQUEST_ABORTED 0xc +#define SBP2_STATUS_UNSPECIFIED_ERROR 0xff + +#define SFMT_CURRENT_ERROR 0x0 +#define SFMT_DEFERRED_ERROR 0x1 +#define SFMT_VENDOR_DEPENDENT_STATUS 0x3 + +#define STATUS_GET_SRC(v) (((v) >> 30) & 0x3) +#define STATUS_GET_RESP(v) (((v) >> 28) & 0x3) +#define STATUS_GET_LEN(v) (((v) >> 24) & 0x7) +#define STATUS_GET_SBP_STATUS(v) (((v) >> 16) & 0xff) +#define STATUS_GET_ORB_OFFSET_HI(v) ((v) & 0x0000ffff) +#define STATUS_TEST_DEAD(v) ((v) & 0x08000000) +/* test 'resp' | 'dead' | 'sbp2_status' */ +#define STATUS_TEST_RDS(v) ((v) & 0x38ff0000) + +struct sbp2_status_block { + u32 ORB_offset_hi_misc; + u32 ORB_offset_lo; + u8 command_set_dependent[24]; +} __attribute__((packed)); + + +/* + * SBP2 related configuration ROM definitions + */ + +#define SBP2_UNIT_DIRECTORY_OFFSET_KEY 0xd1 +#define SBP2_CSR_OFFSET_KEY 0x54 +#define SBP2_UNIT_SPEC_ID_KEY 0x12 +#define SBP2_UNIT_SW_VERSION_KEY 0x13 +#define SBP2_COMMAND_SET_SPEC_ID_KEY 0x38 +#define SBP2_COMMAND_SET_KEY 0x39 +#define SBP2_UNIT_CHARACTERISTICS_KEY 0x3a +#define SBP2_DEVICE_TYPE_AND_LUN_KEY 0x14 +#define SBP2_FIRMWARE_REVISION_KEY 0x3c + +#define SBP2_AGENT_STATE_OFFSET 0x00ULL +#define SBP2_AGENT_RESET_OFFSET 0x04ULL +#define SBP2_ORB_POINTER_OFFSET 0x08ULL +#define SBP2_DOORBELL_OFFSET 0x10ULL +#define SBP2_UNSOLICITED_STATUS_ENABLE_OFFSET 0x14ULL +#define SBP2_UNSOLICITED_STATUS_VALUE 0xf + +#define SBP2_BUSY_TIMEOUT_ADDRESS 0xfffff0000210ULL +/* biggest possible value for Single Phase Retry count is 0xf */ +#define SBP2_BUSY_TIMEOUT_VALUE 0xf + +#define SBP2_AGENT_RESET_DATA 0xf + +#define SBP2_UNIT_SPEC_ID_ENTRY 0x0000609e +#define SBP2_SW_VERSION_ENTRY 0x00010483 + +/* + * The default maximum s/g segment size of a FireWire controller is + * usually 0x10000, but SBP-2 only allows 0xffff. Since buffers have to + * be quadlet-aligned, we set the length limit to 0xffff & ~3. + */ +#define SBP2_MAX_SEG_SIZE 0xfffc + +/* + * There is no real limitation of the queue depth (i.e. length of the linked + * list of command ORBs) at the target. The chosen depth is merely an + * implementation detail of the sbp2 driver. + */ +#define SBP2_MAX_CMDS 8 + +#define SBP2_SCSI_STATUS_GOOD 0x0 +#define SBP2_SCSI_STATUS_CHECK_CONDITION 0x2 +#define SBP2_SCSI_STATUS_CONDITION_MET 0x4 +#define SBP2_SCSI_STATUS_BUSY 0x8 +#define SBP2_SCSI_STATUS_RESERVATION_CONFLICT 0x18 +#define SBP2_SCSI_STATUS_COMMAND_TERMINATED 0x22 +#define SBP2_SCSI_STATUS_SELECTION_TIMEOUT 0xff + + +/* + * Representations of commands and devices + */ + +/* Per SCSI command */ +struct sbp2_command_info { + struct list_head list; + struct sbp2_command_orb command_orb; + dma_addr_t command_orb_dma; + struct scsi_cmnd *Current_SCpnt; + void (*Current_done)(struct scsi_cmnd *); + + /* Also need s/g structure for each sbp2 command */ + struct sbp2_unrestricted_page_table + scatter_gather_element[SG_ALL] __attribute__((aligned(8))); + dma_addr_t sge_dma; +}; + +/* Per FireWire host */ +struct sbp2_fwhost_info { + struct hpsb_host *host; + struct list_head logical_units; +}; + +/* Per logical unit */ +struct sbp2_lu { + /* Operation request blocks */ + struct sbp2_command_orb *last_orb; + dma_addr_t last_orb_dma; + struct sbp2_login_orb *login_orb; + dma_addr_t login_orb_dma; + struct sbp2_login_response *login_response; + dma_addr_t login_response_dma; + struct sbp2_query_logins_orb *query_logins_orb; + dma_addr_t query_logins_orb_dma; + struct sbp2_query_logins_response *query_logins_response; + dma_addr_t query_logins_response_dma; + struct sbp2_reconnect_orb *reconnect_orb; + dma_addr_t reconnect_orb_dma; + struct sbp2_logout_orb *logout_orb; + dma_addr_t logout_orb_dma; + struct sbp2_status_block status_block; + + /* How to talk to the unit */ + u64 management_agent_addr; + u64 command_block_agent_addr; + u32 speed_code; + u32 max_payload_size; + u16 lun; + + /* Address for the unit to write status blocks to */ + u64 status_fifo_addr; + + /* Waitqueue flag for logins, reconnects, logouts, query logins */ + unsigned int access_complete:1; + + /* Pool of command ORBs for this logical unit */ + spinlock_t cmd_orb_lock; + struct list_head cmd_orb_inuse; + struct list_head cmd_orb_completed; + + /* Backlink to FireWire host; list of units attached to the host */ + struct sbp2_fwhost_info *hi; + struct list_head lu_list; + + /* IEEE 1394 core's device representations */ + struct node_entry *ne; + struct unit_directory *ud; + + /* SCSI core's device representations */ + struct scsi_device *sdev; + struct Scsi_Host *shost; + + /* Device specific workarounds/brokeness */ + unsigned workarounds; + + /* Connection state */ + atomic_t state; + + /* For deferred requests to the fetch agent */ + struct work_struct protocol_work; +}; + +/* For use in sbp2_lu.state */ +enum sbp2lu_state_types { + SBP2LU_STATE_RUNNING, /* all normal */ + SBP2LU_STATE_IN_RESET, /* between bus reset and reconnect */ + SBP2LU_STATE_IN_SHUTDOWN /* when sbp2_remove was called */ +}; + +/* For use in sbp2_lu.workarounds and in the corresponding + * module load parameter */ +#define SBP2_WORKAROUND_128K_MAX_TRANS 0x1 +#define SBP2_WORKAROUND_INQUIRY_36 0x2 +#define SBP2_WORKAROUND_MODE_SENSE_8 0x4 +#define SBP2_WORKAROUND_FIX_CAPACITY 0x8 +#define SBP2_WORKAROUND_DELAY_INQUIRY 0x10 +#define SBP2_INQUIRY_DELAY 12 +#define SBP2_WORKAROUND_POWER_CONDITION 0x20 +#define SBP2_WORKAROUND_OVERRIDE 0x100 + +#endif /* SBP2_H */ diff --git a/trunk/drivers/ieee1394/video1394.c b/trunk/drivers/ieee1394/video1394.c new file mode 100644 index 000000000000..5c74f796d7f1 --- /dev/null +++ b/trunk/drivers/ieee1394/video1394.c @@ -0,0 +1,1528 @@ +/* + * video1394.c - video driver for OHCI 1394 boards + * Copyright (C)1999,2000 Sebastien Rougeaux + * Peter Schlaile + * + * 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. + * + * NOTES: + * + * ioctl return codes: + * EFAULT is only for invalid address for the argp + * EINVAL for out of range values + * EBUSY when trying to use an already used resource + * ESRCH when trying to free/stop a not used resource + * EAGAIN for resource allocation failure that could perhaps succeed later + * ENOTTY for unsupported ioctl request + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dma.h" +#include "highlevel.h" +#include "hosts.h" +#include "ieee1394.h" +#include "ieee1394_core.h" +#include "ieee1394_hotplug.h" +#include "ieee1394_types.h" +#include "nodemgr.h" +#include "ohci1394.h" +#include "video1394.h" + +#define ISO_CHANNELS 64 + +struct it_dma_prg { + struct dma_cmd begin; + quadlet_t data[4]; + struct dma_cmd end; + quadlet_t pad[4]; /* FIXME: quick hack for memory alignment */ +}; + +struct dma_iso_ctx { + struct ti_ohci *ohci; + int type; /* OHCI_ISO_TRANSMIT or OHCI_ISO_RECEIVE */ + struct ohci1394_iso_tasklet iso_tasklet; + int channel; + int ctx; + int last_buffer; + int * next_buffer; /* For ISO Transmit of video packets + to write the correct SYT field + into the next block */ + unsigned int num_desc; + unsigned int buf_size; + unsigned int frame_size; + unsigned int packet_size; + unsigned int left_size; + unsigned int nb_cmd; + + struct dma_region dma; + + struct dma_prog_region *prg_reg; + + struct dma_cmd **ir_prg; + struct it_dma_prg **it_prg; + + unsigned int *buffer_status; + unsigned int *buffer_prg_assignment; + struct timeval *buffer_time; /* time when the buffer was received */ + unsigned int *last_used_cmd; /* For ISO Transmit with + variable sized packets only ! */ + int ctrlClear; + int ctrlSet; + int cmdPtr; + int ctxMatch; + wait_queue_head_t waitq; + spinlock_t lock; + unsigned int syt_offset; + int flags; + + struct list_head link; +}; + + +struct file_ctx { + struct ti_ohci *ohci; + struct list_head context_list; + struct dma_iso_ctx *current_ctx; +}; + +#ifdef CONFIG_IEEE1394_VERBOSEDEBUG +#define VIDEO1394_DEBUG +#endif + +#ifdef DBGMSG +#undef DBGMSG +#endif + +#ifdef VIDEO1394_DEBUG +#define DBGMSG(card, fmt, args...) \ +printk(KERN_INFO "video1394_%d: " fmt "\n" , card , ## args) +#else +#define DBGMSG(card, fmt, args...) do {} while (0) +#endif + +/* print general (card independent) information */ +#define PRINT_G(level, fmt, args...) \ +printk(level "video1394: " fmt "\n" , ## args) + +/* print card specific information */ +#define PRINT(level, card, fmt, args...) \ +printk(level "video1394_%d: " fmt "\n" , card , ## args) + +static void wakeup_dma_ir_ctx(unsigned long l); +static void wakeup_dma_it_ctx(unsigned long l); + +static struct hpsb_highlevel video1394_highlevel; + +static int free_dma_iso_ctx(struct dma_iso_ctx *d) +{ + int i; + + DBGMSG(d->ohci->host->id, "Freeing dma_iso_ctx %d", d->ctx); + + ohci1394_stop_context(d->ohci, d->ctrlClear, NULL); + if (d->iso_tasklet.link.next != NULL) + ohci1394_unregister_iso_tasklet(d->ohci, &d->iso_tasklet); + + dma_region_free(&d->dma); + + if (d->prg_reg) { + for (i = 0; i < d->num_desc; i++) + dma_prog_region_free(&d->prg_reg[i]); + kfree(d->prg_reg); + } + + kfree(d->ir_prg); + kfree(d->it_prg); + kfree(d->buffer_status); + kfree(d->buffer_prg_assignment); + kfree(d->buffer_time); + kfree(d->last_used_cmd); + kfree(d->next_buffer); + list_del(&d->link); + kfree(d); + + return 0; +} + +static struct dma_iso_ctx * +alloc_dma_iso_ctx(struct ti_ohci *ohci, int type, int num_desc, + int buf_size, int channel, unsigned int packet_size) +{ + struct dma_iso_ctx *d; + int i; + + d = kzalloc(sizeof(*d), GFP_KERNEL); + if (!d) { + PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma_iso_ctx"); + return NULL; + } + + d->ohci = ohci; + d->type = type; + d->channel = channel; + d->num_desc = num_desc; + d->frame_size = buf_size; + d->buf_size = PAGE_ALIGN(buf_size); + d->last_buffer = -1; + INIT_LIST_HEAD(&d->link); + init_waitqueue_head(&d->waitq); + + /* Init the regions for easy cleanup */ + dma_region_init(&d->dma); + + if (dma_region_alloc(&d->dma, (d->num_desc - 1) * d->buf_size, ohci->dev, + PCI_DMA_BIDIRECTIONAL)) { + PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma buffer"); + free_dma_iso_ctx(d); + return NULL; + } + + if (type == OHCI_ISO_RECEIVE) + ohci1394_init_iso_tasklet(&d->iso_tasklet, type, + wakeup_dma_ir_ctx, + (unsigned long) d); + else + ohci1394_init_iso_tasklet(&d->iso_tasklet, type, + wakeup_dma_it_ctx, + (unsigned long) d); + + if (ohci1394_register_iso_tasklet(ohci, &d->iso_tasklet) < 0) { + PRINT(KERN_ERR, ohci->host->id, "no free iso %s contexts", + type == OHCI_ISO_RECEIVE ? "receive" : "transmit"); + free_dma_iso_ctx(d); + return NULL; + } + d->ctx = d->iso_tasklet.context; + + d->prg_reg = kmalloc(d->num_desc * sizeof(*d->prg_reg), GFP_KERNEL); + if (!d->prg_reg) { + PRINT(KERN_ERR, ohci->host->id, "Failed to allocate ir prg regs"); + free_dma_iso_ctx(d); + return NULL; + } + /* Makes for easier cleanup */ + for (i = 0; i < d->num_desc; i++) + dma_prog_region_init(&d->prg_reg[i]); + + if (type == OHCI_ISO_RECEIVE) { + d->ctrlSet = OHCI1394_IsoRcvContextControlSet+32*d->ctx; + d->ctrlClear = OHCI1394_IsoRcvContextControlClear+32*d->ctx; + d->cmdPtr = OHCI1394_IsoRcvCommandPtr+32*d->ctx; + d->ctxMatch = OHCI1394_IsoRcvContextMatch+32*d->ctx; + + d->ir_prg = kzalloc(d->num_desc * sizeof(*d->ir_prg), + GFP_KERNEL); + + if (!d->ir_prg) { + PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma ir prg"); + free_dma_iso_ctx(d); + return NULL; + } + + d->nb_cmd = d->buf_size / PAGE_SIZE + 1; + d->left_size = (d->frame_size % PAGE_SIZE) ? + d->frame_size % PAGE_SIZE : PAGE_SIZE; + + for (i = 0;i < d->num_desc; i++) { + if (dma_prog_region_alloc(&d->prg_reg[i], d->nb_cmd * + sizeof(struct dma_cmd), ohci->dev)) { + PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma ir prg"); + free_dma_iso_ctx(d); + return NULL; + } + d->ir_prg[i] = (struct dma_cmd *)d->prg_reg[i].kvirt; + } + + } else { /* OHCI_ISO_TRANSMIT */ + d->ctrlSet = OHCI1394_IsoXmitContextControlSet+16*d->ctx; + d->ctrlClear = OHCI1394_IsoXmitContextControlClear+16*d->ctx; + d->cmdPtr = OHCI1394_IsoXmitCommandPtr+16*d->ctx; + + d->it_prg = kzalloc(d->num_desc * sizeof(*d->it_prg), + GFP_KERNEL); + + if (!d->it_prg) { + PRINT(KERN_ERR, ohci->host->id, + "Failed to allocate dma it prg"); + free_dma_iso_ctx(d); + return NULL; + } + + d->packet_size = packet_size; + + if (PAGE_SIZE % packet_size || packet_size>4096) { + PRINT(KERN_ERR, ohci->host->id, + "Packet size %d (page_size: %ld) " + "not yet supported\n", + packet_size, PAGE_SIZE); + free_dma_iso_ctx(d); + return NULL; + } + + d->nb_cmd = d->frame_size / d->packet_size; + if (d->frame_size % d->packet_size) { + d->nb_cmd++; + d->left_size = d->frame_size % d->packet_size; + } else + d->left_size = d->packet_size; + + for (i = 0; i < d->num_desc; i++) { + if (dma_prog_region_alloc(&d->prg_reg[i], d->nb_cmd * + sizeof(struct it_dma_prg), ohci->dev)) { + PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma it prg"); + free_dma_iso_ctx(d); + return NULL; + } + d->it_prg[i] = (struct it_dma_prg *)d->prg_reg[i].kvirt; + } + } + + d->buffer_status = + kzalloc(d->num_desc * sizeof(*d->buffer_status), GFP_KERNEL); + d->buffer_prg_assignment = + kzalloc(d->num_desc * sizeof(*d->buffer_prg_assignment), GFP_KERNEL); + d->buffer_time = + kzalloc(d->num_desc * sizeof(*d->buffer_time), GFP_KERNEL); + d->last_used_cmd = + kzalloc(d->num_desc * sizeof(*d->last_used_cmd), GFP_KERNEL); + d->next_buffer = + kzalloc(d->num_desc * sizeof(*d->next_buffer), GFP_KERNEL); + + if (!d->buffer_status || !d->buffer_prg_assignment || !d->buffer_time || + !d->last_used_cmd || !d->next_buffer) { + PRINT(KERN_ERR, ohci->host->id, + "Failed to allocate dma_iso_ctx member"); + free_dma_iso_ctx(d); + return NULL; + } + + spin_lock_init(&d->lock); + + DBGMSG(ohci->host->id, "Iso %s DMA: %d buffers " + "of size %d allocated for a frame size %d, each with %d prgs", + (type == OHCI_ISO_RECEIVE) ? "receive" : "transmit", + d->num_desc - 1, d->buf_size, d->frame_size, d->nb_cmd); + + return d; +} + +static void reset_ir_status(struct dma_iso_ctx *d, int n) +{ + int i; + d->ir_prg[n][0].status = cpu_to_le32(4); + d->ir_prg[n][1].status = cpu_to_le32(PAGE_SIZE-4); + for (i = 2; i < d->nb_cmd - 1; i++) + d->ir_prg[n][i].status = cpu_to_le32(PAGE_SIZE); + d->ir_prg[n][i].status = cpu_to_le32(d->left_size); +} + +static void reprogram_dma_ir_prg(struct dma_iso_ctx *d, int n, int buffer, int flags) +{ + struct dma_cmd *ir_prg = d->ir_prg[n]; + unsigned long buf = (unsigned long)d->dma.kvirt + buffer * d->buf_size; + int i; + + d->buffer_prg_assignment[n] = buffer; + + ir_prg[0].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, buf - + (unsigned long)d->dma.kvirt)); + ir_prg[1].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, + (buf + 4) - (unsigned long)d->dma.kvirt)); + + for (i=2;inb_cmd-1;i++) { + ir_prg[i].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, + (buf+(i-1)*PAGE_SIZE) - + (unsigned long)d->dma.kvirt)); + } + + ir_prg[i].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | + DMA_CTL_IRQ | DMA_CTL_BRANCH | d->left_size); + ir_prg[i].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, + (buf+(i-1)*PAGE_SIZE) - (unsigned long)d->dma.kvirt)); +} + +static void initialize_dma_ir_prg(struct dma_iso_ctx *d, int n, int flags) +{ + struct dma_cmd *ir_prg = d->ir_prg[n]; + struct dma_prog_region *ir_reg = &d->prg_reg[n]; + unsigned long buf = (unsigned long)d->dma.kvirt; + int i; + + /* the first descriptor will read only 4 bytes */ + ir_prg[0].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | + DMA_CTL_BRANCH | 4); + + /* set the sync flag */ + if (flags & VIDEO1394_SYNC_FRAMES) + ir_prg[0].control |= cpu_to_le32(DMA_CTL_WAIT); + + ir_prg[0].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, buf - + (unsigned long)d->dma.kvirt)); + ir_prg[0].branchAddress = cpu_to_le32((dma_prog_region_offset_to_bus(ir_reg, + 1 * sizeof(struct dma_cmd)) & 0xfffffff0) | 0x1); + + /* If there is *not* only one DMA page per frame (hence, d->nb_cmd==2) */ + if (d->nb_cmd > 2) { + /* The second descriptor will read PAGE_SIZE-4 bytes */ + ir_prg[1].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | + DMA_CTL_BRANCH | (PAGE_SIZE-4)); + ir_prg[1].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, (buf + 4) - + (unsigned long)d->dma.kvirt)); + ir_prg[1].branchAddress = cpu_to_le32((dma_prog_region_offset_to_bus(ir_reg, + 2 * sizeof(struct dma_cmd)) & 0xfffffff0) | 0x1); + + for (i = 2; i < d->nb_cmd - 1; i++) { + ir_prg[i].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | + DMA_CTL_BRANCH | PAGE_SIZE); + ir_prg[i].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, + (buf+(i-1)*PAGE_SIZE) - + (unsigned long)d->dma.kvirt)); + + ir_prg[i].branchAddress = + cpu_to_le32((dma_prog_region_offset_to_bus(ir_reg, + (i + 1) * sizeof(struct dma_cmd)) & 0xfffffff0) | 0x1); + } + + /* The last descriptor will generate an interrupt */ + ir_prg[i].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | + DMA_CTL_IRQ | DMA_CTL_BRANCH | d->left_size); + ir_prg[i].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, + (buf+(i-1)*PAGE_SIZE) - + (unsigned long)d->dma.kvirt)); + } else { + /* Only one DMA page is used. Read d->left_size immediately and */ + /* generate an interrupt as this is also the last page. */ + ir_prg[1].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | + DMA_CTL_IRQ | DMA_CTL_BRANCH | (d->left_size-4)); + ir_prg[1].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, + (buf + 4) - (unsigned long)d->dma.kvirt)); + } +} + +static void initialize_dma_ir_ctx(struct dma_iso_ctx *d, int tag, int flags) +{ + struct ti_ohci *ohci = (struct ti_ohci *)d->ohci; + int i; + + d->flags = flags; + + ohci1394_stop_context(ohci, d->ctrlClear, NULL); + + for (i=0;inum_desc;i++) { + initialize_dma_ir_prg(d, i, flags); + reset_ir_status(d, i); + } + + /* reset the ctrl register */ + reg_write(ohci, d->ctrlClear, 0xf0000000); + + /* Set bufferFill */ + reg_write(ohci, d->ctrlSet, 0x80000000); + + /* Set isoch header */ + if (flags & VIDEO1394_INCLUDE_ISO_HEADERS) + reg_write(ohci, d->ctrlSet, 0x40000000); + + /* Set the context match register to match on all tags, + sync for sync tag, and listen to d->channel */ + reg_write(ohci, d->ctxMatch, 0xf0000000|((tag&0xf)<<8)|d->channel); + + /* Set up isoRecvIntMask to generate interrupts */ + reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 1<ctx); +} + +/* find which context is listening to this channel */ +static struct dma_iso_ctx * +find_ctx(struct list_head *list, int type, int channel) +{ + struct dma_iso_ctx *ctx; + + list_for_each_entry(ctx, list, link) { + if (ctx->type == type && ctx->channel == channel) + return ctx; + } + + return NULL; +} + +static void wakeup_dma_ir_ctx(unsigned long l) +{ + struct dma_iso_ctx *d = (struct dma_iso_ctx *) l; + int i; + + spin_lock(&d->lock); + + for (i = 0; i < d->num_desc; i++) { + if (d->ir_prg[i][d->nb_cmd-1].status & cpu_to_le32(0xFFFF0000)) { + reset_ir_status(d, i); + d->buffer_status[d->buffer_prg_assignment[i]] = VIDEO1394_BUFFER_READY; + do_gettimeofday(&d->buffer_time[d->buffer_prg_assignment[i]]); + dma_region_sync_for_cpu(&d->dma, + d->buffer_prg_assignment[i] * d->buf_size, + d->buf_size); + } + } + + spin_unlock(&d->lock); + + if (waitqueue_active(&d->waitq)) + wake_up_interruptible(&d->waitq); +} + +static inline void put_timestamp(struct ti_ohci *ohci, struct dma_iso_ctx * d, + int n) +{ + unsigned char* buf = d->dma.kvirt + n * d->buf_size; + u32 cycleTimer; + u32 timeStamp; + + if (n == -1) { + return; + } + + cycleTimer = reg_read(ohci, OHCI1394_IsochronousCycleTimer); + + timeStamp = ((cycleTimer & 0x0fff) + d->syt_offset); /* 11059 = 450 us */ + timeStamp = (timeStamp % 3072 + ((timeStamp / 3072) << 12) + + (cycleTimer & 0xf000)) & 0xffff; + + buf[6] = timeStamp >> 8; + buf[7] = timeStamp & 0xff; + + /* if first packet is empty packet, then put timestamp into the next full one too */ + if ( (le32_to_cpu(d->it_prg[n][0].data[1]) >>16) == 0x008) { + buf += d->packet_size; + buf[6] = timeStamp >> 8; + buf[7] = timeStamp & 0xff; + } + + /* do the next buffer frame too in case of irq latency */ + n = d->next_buffer[n]; + if (n == -1) { + return; + } + buf = d->dma.kvirt + n * d->buf_size; + + timeStamp += (d->last_used_cmd[n] << 12) & 0xffff; + + buf[6] = timeStamp >> 8; + buf[7] = timeStamp & 0xff; + + /* if first packet is empty packet, then put timestamp into the next full one too */ + if ( (le32_to_cpu(d->it_prg[n][0].data[1]) >>16) == 0x008) { + buf += d->packet_size; + buf[6] = timeStamp >> 8; + buf[7] = timeStamp & 0xff; + } + +#if 0 + printk("curr: %d, next: %d, cycleTimer: %08x timeStamp: %08x\n", + curr, n, cycleTimer, timeStamp); +#endif +} + +static void wakeup_dma_it_ctx(unsigned long l) +{ + struct dma_iso_ctx *d = (struct dma_iso_ctx *) l; + struct ti_ohci *ohci = d->ohci; + int i; + + spin_lock(&d->lock); + + for (i = 0; i < d->num_desc; i++) { + if (d->it_prg[i][d->last_used_cmd[i]].end.status & + cpu_to_le32(0xFFFF0000)) { + int next = d->next_buffer[i]; + put_timestamp(ohci, d, next); + d->it_prg[i][d->last_used_cmd[i]].end.status = 0; + d->buffer_status[d->buffer_prg_assignment[i]] = VIDEO1394_BUFFER_READY; + } + } + + spin_unlock(&d->lock); + + if (waitqueue_active(&d->waitq)) + wake_up_interruptible(&d->waitq); +} + +static void reprogram_dma_it_prg(struct dma_iso_ctx *d, int n, int buffer) +{ + struct it_dma_prg *it_prg = d->it_prg[n]; + unsigned long buf = (unsigned long)d->dma.kvirt + buffer * d->buf_size; + int i; + + d->buffer_prg_assignment[n] = buffer; + for (i=0;inb_cmd;i++) { + it_prg[i].end.address = + cpu_to_le32(dma_region_offset_to_bus(&d->dma, + (buf+i*d->packet_size) - (unsigned long)d->dma.kvirt)); + } +} + +static void initialize_dma_it_prg(struct dma_iso_ctx *d, int n, int sync_tag) +{ + struct it_dma_prg *it_prg = d->it_prg[n]; + struct dma_prog_region *it_reg = &d->prg_reg[n]; + unsigned long buf = (unsigned long)d->dma.kvirt; + int i; + d->last_used_cmd[n] = d->nb_cmd - 1; + for (i=0;inb_cmd;i++) { + + it_prg[i].begin.control = cpu_to_le32(DMA_CTL_OUTPUT_MORE | + DMA_CTL_IMMEDIATE | 8) ; + it_prg[i].begin.address = 0; + + it_prg[i].begin.status = 0; + + it_prg[i].data[0] = cpu_to_le32( + (IEEE1394_SPEED_100 << 16) + | (/* tag */ 1 << 14) + | (d->channel << 8) + | (TCODE_ISO_DATA << 4)); + if (i==0) it_prg[i].data[0] |= cpu_to_le32(sync_tag); + it_prg[i].data[1] = cpu_to_le32(d->packet_size << 16); + it_prg[i].data[2] = 0; + it_prg[i].data[3] = 0; + + it_prg[i].end.control = cpu_to_le32(DMA_CTL_OUTPUT_LAST | + DMA_CTL_BRANCH); + it_prg[i].end.address = + cpu_to_le32(dma_region_offset_to_bus(&d->dma, (buf+i*d->packet_size) - + (unsigned long)d->dma.kvirt)); + + if (inb_cmd-1) { + it_prg[i].end.control |= cpu_to_le32(d->packet_size); + it_prg[i].begin.branchAddress = + cpu_to_le32((dma_prog_region_offset_to_bus(it_reg, (i + 1) * + sizeof(struct it_dma_prg)) & 0xfffffff0) | 0x3); + it_prg[i].end.branchAddress = + cpu_to_le32((dma_prog_region_offset_to_bus(it_reg, (i + 1) * + sizeof(struct it_dma_prg)) & 0xfffffff0) | 0x3); + } else { + /* the last prg generates an interrupt */ + it_prg[i].end.control |= cpu_to_le32(DMA_CTL_UPDATE | + DMA_CTL_IRQ | d->left_size); + /* the last prg doesn't branch */ + it_prg[i].begin.branchAddress = 0; + it_prg[i].end.branchAddress = 0; + } + it_prg[i].end.status = 0; + } +} + +static void initialize_dma_it_prg_var_packet_queue( + struct dma_iso_ctx *d, int n, unsigned int * packet_sizes, + struct ti_ohci *ohci) +{ + struct it_dma_prg *it_prg = d->it_prg[n]; + struct dma_prog_region *it_reg = &d->prg_reg[n]; + int i; + +#if 0 + if (n != -1) { + put_timestamp(ohci, d, n); + } +#endif + d->last_used_cmd[n] = d->nb_cmd - 1; + + for (i = 0; i < d->nb_cmd; i++) { + unsigned int size; + if (packet_sizes[i] > d->packet_size) { + size = d->packet_size; + } else { + size = packet_sizes[i]; + } + it_prg[i].data[1] = cpu_to_le32(size << 16); + it_prg[i].end.control = cpu_to_le32(DMA_CTL_OUTPUT_LAST | DMA_CTL_BRANCH); + + if (i < d->nb_cmd-1 && packet_sizes[i+1] != 0) { + it_prg[i].end.control |= cpu_to_le32(size); + it_prg[i].begin.branchAddress = + cpu_to_le32((dma_prog_region_offset_to_bus(it_reg, (i + 1) * + sizeof(struct it_dma_prg)) & 0xfffffff0) | 0x3); + it_prg[i].end.branchAddress = + cpu_to_le32((dma_prog_region_offset_to_bus(it_reg, (i + 1) * + sizeof(struct it_dma_prg)) & 0xfffffff0) | 0x3); + } else { + /* the last prg generates an interrupt */ + it_prg[i].end.control |= cpu_to_le32(DMA_CTL_UPDATE | + DMA_CTL_IRQ | size); + /* the last prg doesn't branch */ + it_prg[i].begin.branchAddress = 0; + it_prg[i].end.branchAddress = 0; + d->last_used_cmd[n] = i; + break; + } + } +} + +static void initialize_dma_it_ctx(struct dma_iso_ctx *d, int sync_tag, + unsigned int syt_offset, int flags) +{ + struct ti_ohci *ohci = (struct ti_ohci *)d->ohci; + int i; + + d->flags = flags; + d->syt_offset = (syt_offset == 0 ? 11000 : syt_offset); + + ohci1394_stop_context(ohci, d->ctrlClear, NULL); + + for (i=0;inum_desc;i++) + initialize_dma_it_prg(d, i, sync_tag); + + /* Set up isoRecvIntMask to generate interrupts */ + reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, 1<ctx); +} + +static inline unsigned video1394_buffer_state(struct dma_iso_ctx *d, + unsigned int buffer) +{ + unsigned long flags; + unsigned int ret; + spin_lock_irqsave(&d->lock, flags); + ret = d->buffer_status[buffer]; + spin_unlock_irqrestore(&d->lock, flags); + return ret; +} + +static long video1394_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct file_ctx *ctx = file->private_data; + struct ti_ohci *ohci = ctx->ohci; + unsigned long flags; + void __user *argp = (void __user *)arg; + + switch(cmd) + { + case VIDEO1394_IOC_LISTEN_CHANNEL: + case VIDEO1394_IOC_TALK_CHANNEL: + { + struct video1394_mmap v; + u64 mask; + struct dma_iso_ctx *d; + int i; + + if (copy_from_user(&v, argp, sizeof(v))) + return -EFAULT; + + /* if channel < 0, find lowest available one */ + if (v.channel < 0) { + mask = (u64)0x1; + for (i=0; ; i++) { + if (i == ISO_CHANNELS) { + PRINT(KERN_ERR, ohci->host->id, + "No free channel found"); + return -EAGAIN; + } + if (!(ohci->ISO_channel_usage & mask)) { + v.channel = i; + PRINT(KERN_INFO, ohci->host->id, "Found free channel %d", i); + break; + } + mask = mask << 1; + } + } else if (v.channel >= ISO_CHANNELS) { + PRINT(KERN_ERR, ohci->host->id, + "Iso channel %d out of bounds", v.channel); + return -EINVAL; + } else { + mask = (u64)0x1<host->id, "mask: %08X%08X usage: %08X%08X\n", + (u32)(mask>>32),(u32)(mask&0xffffffff), + (u32)(ohci->ISO_channel_usage>>32), + (u32)(ohci->ISO_channel_usage&0xffffffff)); + if (ohci->ISO_channel_usage & mask) { + PRINT(KERN_ERR, ohci->host->id, + "Channel %d is already taken", v.channel); + return -EBUSY; + } + + if (v.buf_size == 0 || v.buf_size > VIDEO1394_MAX_SIZE) { + PRINT(KERN_ERR, ohci->host->id, + "Invalid %d length buffer requested",v.buf_size); + return -EINVAL; + } + + if (v.nb_buffers == 0 || v.nb_buffers > VIDEO1394_MAX_SIZE) { + PRINT(KERN_ERR, ohci->host->id, + "Invalid %d buffers requested",v.nb_buffers); + return -EINVAL; + } + + if (v.nb_buffers * v.buf_size > VIDEO1394_MAX_SIZE) { + PRINT(KERN_ERR, ohci->host->id, + "%d buffers of size %d bytes is too big", + v.nb_buffers, v.buf_size); + return -EINVAL; + } + + if (cmd == VIDEO1394_IOC_LISTEN_CHANNEL) { + d = alloc_dma_iso_ctx(ohci, OHCI_ISO_RECEIVE, + v.nb_buffers + 1, v.buf_size, + v.channel, 0); + + if (d == NULL) { + PRINT(KERN_ERR, ohci->host->id, + "Couldn't allocate ir context"); + return -EAGAIN; + } + initialize_dma_ir_ctx(d, v.sync_tag, v.flags); + + ctx->current_ctx = d; + + v.buf_size = d->buf_size; + list_add_tail(&d->link, &ctx->context_list); + + DBGMSG(ohci->host->id, + "iso context %d listen on channel %d", + d->ctx, v.channel); + } + else { + d = alloc_dma_iso_ctx(ohci, OHCI_ISO_TRANSMIT, + v.nb_buffers + 1, v.buf_size, + v.channel, v.packet_size); + + if (d == NULL) { + PRINT(KERN_ERR, ohci->host->id, + "Couldn't allocate it context"); + return -EAGAIN; + } + initialize_dma_it_ctx(d, v.sync_tag, + v.syt_offset, v.flags); + + ctx->current_ctx = d; + + v.buf_size = d->buf_size; + + list_add_tail(&d->link, &ctx->context_list); + + DBGMSG(ohci->host->id, + "Iso context %d talk on channel %d", d->ctx, + v.channel); + } + + if (copy_to_user(argp, &v, sizeof(v))) { + /* FIXME : free allocated dma resources */ + return -EFAULT; + } + + ohci->ISO_channel_usage |= mask; + + return 0; + } + case VIDEO1394_IOC_UNLISTEN_CHANNEL: + case VIDEO1394_IOC_UNTALK_CHANNEL: + { + int channel; + u64 mask; + struct dma_iso_ctx *d; + + if (copy_from_user(&channel, argp, sizeof(int))) + return -EFAULT; + + if (channel < 0 || channel >= ISO_CHANNELS) { + PRINT(KERN_ERR, ohci->host->id, + "Iso channel %d out of bound", channel); + return -EINVAL; + } + mask = (u64)0x1<ISO_channel_usage & mask)) { + PRINT(KERN_ERR, ohci->host->id, + "Channel %d is not being used", channel); + return -ESRCH; + } + + /* Mark this channel as unused */ + ohci->ISO_channel_usage &= ~mask; + + if (cmd == VIDEO1394_IOC_UNLISTEN_CHANNEL) + d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, channel); + else + d = find_ctx(&ctx->context_list, OHCI_ISO_TRANSMIT, channel); + + if (d == NULL) return -ESRCH; + DBGMSG(ohci->host->id, "Iso context %d " + "stop talking on channel %d", d->ctx, channel); + free_dma_iso_ctx(d); + + return 0; + } + case VIDEO1394_IOC_LISTEN_QUEUE_BUFFER: + { + struct video1394_wait v; + struct dma_iso_ctx *d; + int next_prg; + + if (unlikely(copy_from_user(&v, argp, sizeof(v)))) + return -EFAULT; + + d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, v.channel); + if (unlikely(d == NULL)) + return -EFAULT; + + if (unlikely(v.buffer >= d->num_desc - 1)) { + PRINT(KERN_ERR, ohci->host->id, + "Buffer %d out of range",v.buffer); + return -EINVAL; + } + + spin_lock_irqsave(&d->lock,flags); + + if (unlikely(d->buffer_status[v.buffer]==VIDEO1394_BUFFER_QUEUED)) { + PRINT(KERN_ERR, ohci->host->id, + "Buffer %d is already used",v.buffer); + spin_unlock_irqrestore(&d->lock,flags); + return -EBUSY; + } + + d->buffer_status[v.buffer]=VIDEO1394_BUFFER_QUEUED; + + next_prg = (d->last_buffer + 1) % d->num_desc; + if (d->last_buffer>=0) + d->ir_prg[d->last_buffer][d->nb_cmd-1].branchAddress = + cpu_to_le32((dma_prog_region_offset_to_bus(&d->prg_reg[next_prg], 0) + & 0xfffffff0) | 0x1); + + d->last_buffer = next_prg; + reprogram_dma_ir_prg(d, d->last_buffer, v.buffer, d->flags); + + d->ir_prg[d->last_buffer][d->nb_cmd-1].branchAddress = 0; + + spin_unlock_irqrestore(&d->lock,flags); + + if (!(reg_read(ohci, d->ctrlSet) & 0x8000)) + { + DBGMSG(ohci->host->id, "Starting iso DMA ctx=%d",d->ctx); + + /* Tell the controller where the first program is */ + reg_write(ohci, d->cmdPtr, + dma_prog_region_offset_to_bus(&d->prg_reg[d->last_buffer], 0) | 0x1); + + /* Run IR context */ + reg_write(ohci, d->ctrlSet, 0x8000); + } + else { + /* Wake up dma context if necessary */ + if (!(reg_read(ohci, d->ctrlSet) & 0x400)) { + DBGMSG(ohci->host->id, + "Waking up iso dma ctx=%d", d->ctx); + reg_write(ohci, d->ctrlSet, 0x1000); + } + } + return 0; + + } + case VIDEO1394_IOC_LISTEN_WAIT_BUFFER: + case VIDEO1394_IOC_LISTEN_POLL_BUFFER: + { + struct video1394_wait v; + struct dma_iso_ctx *d; + int i = 0; + + if (unlikely(copy_from_user(&v, argp, sizeof(v)))) + return -EFAULT; + + d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, v.channel); + if (unlikely(d == NULL)) + return -EFAULT; + + if (unlikely(v.buffer > d->num_desc - 1)) { + PRINT(KERN_ERR, ohci->host->id, + "Buffer %d out of range",v.buffer); + return -EINVAL; + } + + /* + * I change the way it works so that it returns + * the last received frame. + */ + spin_lock_irqsave(&d->lock, flags); + switch(d->buffer_status[v.buffer]) { + case VIDEO1394_BUFFER_READY: + d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE; + break; + case VIDEO1394_BUFFER_QUEUED: + if (cmd == VIDEO1394_IOC_LISTEN_POLL_BUFFER) { + /* for polling, return error code EINTR */ + spin_unlock_irqrestore(&d->lock, flags); + return -EINTR; + } + + spin_unlock_irqrestore(&d->lock, flags); + wait_event_interruptible(d->waitq, + video1394_buffer_state(d, v.buffer) == + VIDEO1394_BUFFER_READY); + if (signal_pending(current)) + return -EINTR; + spin_lock_irqsave(&d->lock, flags); + d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE; + break; + default: + PRINT(KERN_ERR, ohci->host->id, + "Buffer %d is not queued",v.buffer); + spin_unlock_irqrestore(&d->lock, flags); + return -ESRCH; + } + + /* set time of buffer */ + v.filltime = d->buffer_time[v.buffer]; + + /* + * Look ahead to see how many more buffers have been received + */ + i=0; + while (d->buffer_status[(v.buffer+1)%(d->num_desc - 1)]== + VIDEO1394_BUFFER_READY) { + v.buffer=(v.buffer+1)%(d->num_desc - 1); + i++; + } + spin_unlock_irqrestore(&d->lock, flags); + + v.buffer=i; + if (unlikely(copy_to_user(argp, &v, sizeof(v)))) + return -EFAULT; + + return 0; + } + case VIDEO1394_IOC_TALK_QUEUE_BUFFER: + { + struct video1394_wait v; + unsigned int *psizes = NULL; + struct dma_iso_ctx *d; + int next_prg; + + if (copy_from_user(&v, argp, sizeof(v))) + return -EFAULT; + + d = find_ctx(&ctx->context_list, OHCI_ISO_TRANSMIT, v.channel); + if (d == NULL) return -EFAULT; + + if (v.buffer >= d->num_desc - 1) { + PRINT(KERN_ERR, ohci->host->id, + "Buffer %d out of range",v.buffer); + return -EINVAL; + } + + if (d->flags & VIDEO1394_VARIABLE_PACKET_SIZE) { + int buf_size = d->nb_cmd * sizeof(*psizes); + struct video1394_queue_variable __user *p = argp; + unsigned int __user *qv; + + if (get_user(qv, &p->packet_sizes)) + return -EFAULT; + + psizes = memdup_user(qv, buf_size); + if (IS_ERR(psizes)) + return PTR_ERR(psizes); + } + + spin_lock_irqsave(&d->lock,flags); + + /* last_buffer is last_prg */ + next_prg = (d->last_buffer + 1) % d->num_desc; + if (d->buffer_status[v.buffer]!=VIDEO1394_BUFFER_FREE) { + PRINT(KERN_ERR, ohci->host->id, + "Buffer %d is already used",v.buffer); + spin_unlock_irqrestore(&d->lock,flags); + kfree(psizes); + return -EBUSY; + } + + if (d->flags & VIDEO1394_VARIABLE_PACKET_SIZE) { + initialize_dma_it_prg_var_packet_queue( + d, next_prg, psizes, ohci); + } + + d->buffer_status[v.buffer]=VIDEO1394_BUFFER_QUEUED; + + if (d->last_buffer >= 0) { + d->it_prg[d->last_buffer] + [ d->last_used_cmd[d->last_buffer] ].end.branchAddress = + cpu_to_le32((dma_prog_region_offset_to_bus(&d->prg_reg[next_prg], + 0) & 0xfffffff0) | 0x3); + + d->it_prg[d->last_buffer] + [ d->last_used_cmd[d->last_buffer] ].begin.branchAddress = + cpu_to_le32((dma_prog_region_offset_to_bus(&d->prg_reg[next_prg], + 0) & 0xfffffff0) | 0x3); + d->next_buffer[d->last_buffer] = (v.buffer + 1) % (d->num_desc - 1); + } + d->last_buffer = next_prg; + reprogram_dma_it_prg(d, d->last_buffer, v.buffer); + d->next_buffer[d->last_buffer] = -1; + + d->it_prg[d->last_buffer][d->last_used_cmd[d->last_buffer]].end.branchAddress = 0; + + spin_unlock_irqrestore(&d->lock,flags); + + if (!(reg_read(ohci, d->ctrlSet) & 0x8000)) + { + DBGMSG(ohci->host->id, "Starting iso transmit DMA ctx=%d", + d->ctx); + put_timestamp(ohci, d, d->last_buffer); + dma_region_sync_for_device(&d->dma, + v.buffer * d->buf_size, d->buf_size); + + /* Tell the controller where the first program is */ + reg_write(ohci, d->cmdPtr, + dma_prog_region_offset_to_bus(&d->prg_reg[next_prg], 0) | 0x3); + + /* Run IT context */ + reg_write(ohci, d->ctrlSet, 0x8000); + } + else { + /* Wake up dma context if necessary */ + if (!(reg_read(ohci, d->ctrlSet) & 0x400)) { + DBGMSG(ohci->host->id, + "Waking up iso transmit dma ctx=%d", + d->ctx); + put_timestamp(ohci, d, d->last_buffer); + dma_region_sync_for_device(&d->dma, + v.buffer * d->buf_size, d->buf_size); + + reg_write(ohci, d->ctrlSet, 0x1000); + } + } + + kfree(psizes); + return 0; + + } + case VIDEO1394_IOC_TALK_WAIT_BUFFER: + { + struct video1394_wait v; + struct dma_iso_ctx *d; + + if (copy_from_user(&v, argp, sizeof(v))) + return -EFAULT; + + d = find_ctx(&ctx->context_list, OHCI_ISO_TRANSMIT, v.channel); + if (d == NULL) return -EFAULT; + + if (v.buffer >= d->num_desc - 1) { + PRINT(KERN_ERR, ohci->host->id, + "Buffer %d out of range",v.buffer); + return -EINVAL; + } + + switch(d->buffer_status[v.buffer]) { + case VIDEO1394_BUFFER_READY: + d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE; + return 0; + case VIDEO1394_BUFFER_QUEUED: + wait_event_interruptible(d->waitq, + (d->buffer_status[v.buffer] == VIDEO1394_BUFFER_READY)); + if (signal_pending(current)) + return -EINTR; + d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE; + return 0; + default: + PRINT(KERN_ERR, ohci->host->id, + "Buffer %d is not queued",v.buffer); + return -ESRCH; + } + } + default: + return -ENOTTY; + } +} + +/* + * This maps the vmalloced and reserved buffer to user space. + * + * FIXME: + * - PAGE_READONLY should suffice!? + * - remap_pfn_range is kind of inefficient for page by page remapping. + * But e.g. pte_alloc() does not work in modules ... :-( + */ + +static int video1394_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct file_ctx *ctx = file->private_data; + + if (ctx->current_ctx == NULL) { + PRINT(KERN_ERR, ctx->ohci->host->id, + "Current iso context not set"); + return -EINVAL; + } + + return dma_region_mmap(&ctx->current_ctx->dma, file, vma); +} + +static unsigned int video1394_poll(struct file *file, poll_table *pt) +{ + struct file_ctx *ctx; + unsigned int mask = 0; + unsigned long flags; + struct dma_iso_ctx *d; + int i; + + ctx = file->private_data; + d = ctx->current_ctx; + if (d == NULL) { + PRINT(KERN_ERR, ctx->ohci->host->id, + "Current iso context not set"); + return POLLERR; + } + + poll_wait(file, &d->waitq, pt); + + spin_lock_irqsave(&d->lock, flags); + for (i = 0; i < d->num_desc; i++) { + if (d->buffer_status[i] == VIDEO1394_BUFFER_READY) { + mask |= POLLIN | POLLRDNORM; + break; + } + } + spin_unlock_irqrestore(&d->lock, flags); + + return mask; +} + +static int video1394_open(struct inode *inode, struct file *file) +{ + int i = ieee1394_file_to_instance(file); + struct ti_ohci *ohci; + struct file_ctx *ctx; + + ohci = hpsb_get_hostinfo_bykey(&video1394_highlevel, i); + if (ohci == NULL) + return -EIO; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + PRINT(KERN_ERR, ohci->host->id, "Cannot malloc file_ctx"); + return -ENOMEM; + } + + ctx->ohci = ohci; + INIT_LIST_HEAD(&ctx->context_list); + ctx->current_ctx = NULL; + file->private_data = ctx; + + return nonseekable_open(inode, file); +} + +static int video1394_release(struct inode *inode, struct file *file) +{ + struct file_ctx *ctx = file->private_data; + struct ti_ohci *ohci = ctx->ohci; + struct list_head *lh, *next; + u64 mask; + + list_for_each_safe(lh, next, &ctx->context_list) { + struct dma_iso_ctx *d; + d = list_entry(lh, struct dma_iso_ctx, link); + mask = (u64) 1 << d->channel; + + if (!(ohci->ISO_channel_usage & mask)) + PRINT(KERN_ERR, ohci->host->id, "On release: Channel %d " + "is not being used", d->channel); + else + ohci->ISO_channel_usage &= ~mask; + DBGMSG(ohci->host->id, "On release: Iso %s context " + "%d stop listening on channel %d", + d->type == OHCI_ISO_RECEIVE ? "receive" : "transmit", + d->ctx, d->channel); + free_dma_iso_ctx(d); + } + + kfree(ctx); + file->private_data = NULL; + + return 0; +} + +#ifdef CONFIG_COMPAT +static long video1394_compat_ioctl(struct file *f, unsigned cmd, unsigned long arg); +#endif + +static struct cdev video1394_cdev; +static const struct file_operations video1394_fops= +{ + .owner = THIS_MODULE, + .unlocked_ioctl = video1394_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = video1394_compat_ioctl, +#endif + .poll = video1394_poll, + .mmap = video1394_mmap, + .open = video1394_open, + .release = video1394_release, + .llseek = no_llseek, +}; + +/*** HOTPLUG STUFF **********************************************************/ +/* + * Export information about protocols/devices supported by this driver. + */ +#ifdef MODULE +static const struct ieee1394_device_id video1394_id_table[] = { + { + .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, + .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff, + .version = CAMERA_SW_VERSION_ENTRY & 0xffffff + }, + { + .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, + .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff, + .version = (CAMERA_SW_VERSION_ENTRY + 1) & 0xffffff + }, + { + .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, + .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff, + .version = (CAMERA_SW_VERSION_ENTRY + 2) & 0xffffff + }, + { } +}; + +MODULE_DEVICE_TABLE(ieee1394, video1394_id_table); +#endif /* MODULE */ + +static struct hpsb_protocol_driver video1394_driver = { + .name = VIDEO1394_DRIVER_NAME, +}; + + +static void video1394_add_host (struct hpsb_host *host) +{ + struct ti_ohci *ohci; + int minor; + + /* We only work with the OHCI-1394 driver */ + if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME)) + return; + + ohci = (struct ti_ohci *)host->hostdata; + + if (!hpsb_create_hostinfo(&video1394_highlevel, host, 0)) { + PRINT(KERN_ERR, ohci->host->id, "Cannot allocate hostinfo"); + return; + } + + hpsb_set_hostinfo(&video1394_highlevel, host, ohci); + hpsb_set_hostinfo_key(&video1394_highlevel, host, ohci->host->id); + + minor = IEEE1394_MINOR_BLOCK_VIDEO1394 * 16 + ohci->host->id; + device_create(hpsb_protocol_class, NULL, MKDEV(IEEE1394_MAJOR, minor), + NULL, "%s-%d", VIDEO1394_DRIVER_NAME, ohci->host->id); +} + + +static void video1394_remove_host (struct hpsb_host *host) +{ + struct ti_ohci *ohci = hpsb_get_hostinfo(&video1394_highlevel, host); + + if (ohci) + device_destroy(hpsb_protocol_class, MKDEV(IEEE1394_MAJOR, + IEEE1394_MINOR_BLOCK_VIDEO1394 * 16 + ohci->host->id)); + return; +} + + +static struct hpsb_highlevel video1394_highlevel = { + .name = VIDEO1394_DRIVER_NAME, + .add_host = video1394_add_host, + .remove_host = video1394_remove_host, +}; + +MODULE_AUTHOR("Sebastien Rougeaux "); +MODULE_DESCRIPTION("driver for digital video on OHCI board"); +MODULE_SUPPORTED_DEVICE(VIDEO1394_DRIVER_NAME); +MODULE_LICENSE("GPL"); + +#ifdef CONFIG_COMPAT + +#define VIDEO1394_IOC32_LISTEN_QUEUE_BUFFER \ + _IOW ('#', 0x12, struct video1394_wait32) +#define VIDEO1394_IOC32_LISTEN_WAIT_BUFFER \ + _IOWR('#', 0x13, struct video1394_wait32) +#define VIDEO1394_IOC32_TALK_WAIT_BUFFER \ + _IOW ('#', 0x17, struct video1394_wait32) +#define VIDEO1394_IOC32_LISTEN_POLL_BUFFER \ + _IOWR('#', 0x18, struct video1394_wait32) + +struct video1394_wait32 { + u32 channel; + u32 buffer; + struct compat_timeval filltime; +}; + +static int video1394_wr_wait32(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct video1394_wait32 __user *argp = (void __user *)arg; + struct video1394_wait32 wait32; + struct video1394_wait wait; + mm_segment_t old_fs; + int ret; + + if (copy_from_user(&wait32, argp, sizeof(wait32))) + return -EFAULT; + + wait.channel = wait32.channel; + wait.buffer = wait32.buffer; + wait.filltime.tv_sec = (time_t)wait32.filltime.tv_sec; + wait.filltime.tv_usec = (suseconds_t)wait32.filltime.tv_usec; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + if (cmd == VIDEO1394_IOC32_LISTEN_WAIT_BUFFER) + ret = video1394_ioctl(file, + VIDEO1394_IOC_LISTEN_WAIT_BUFFER, + (unsigned long) &wait); + else + ret = video1394_ioctl(file, + VIDEO1394_IOC_LISTEN_POLL_BUFFER, + (unsigned long) &wait); + set_fs(old_fs); + + if (!ret) { + wait32.channel = wait.channel; + wait32.buffer = wait.buffer; + wait32.filltime.tv_sec = (int)wait.filltime.tv_sec; + wait32.filltime.tv_usec = (int)wait.filltime.tv_usec; + + if (copy_to_user(argp, &wait32, sizeof(wait32))) + ret = -EFAULT; + } + + return ret; +} + +static int video1394_w_wait32(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct video1394_wait32 wait32; + struct video1394_wait wait; + mm_segment_t old_fs; + int ret; + + if (copy_from_user(&wait32, (void __user *)arg, sizeof(wait32))) + return -EFAULT; + + wait.channel = wait32.channel; + wait.buffer = wait32.buffer; + wait.filltime.tv_sec = (time_t)wait32.filltime.tv_sec; + wait.filltime.tv_usec = (suseconds_t)wait32.filltime.tv_usec; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + if (cmd == VIDEO1394_IOC32_LISTEN_QUEUE_BUFFER) + ret = video1394_ioctl(file, + VIDEO1394_IOC_LISTEN_QUEUE_BUFFER, + (unsigned long) &wait); + else + ret = video1394_ioctl(file, + VIDEO1394_IOC_TALK_WAIT_BUFFER, + (unsigned long) &wait); + set_fs(old_fs); + + return ret; +} + +static int video1394_queue_buf32(struct file *file, unsigned int cmd, unsigned long arg) +{ + return -EFAULT; /* ??? was there before. */ + + return video1394_ioctl(file, + VIDEO1394_IOC_TALK_QUEUE_BUFFER, arg); +} + +static long video1394_compat_ioctl(struct file *f, unsigned cmd, unsigned long arg) +{ + switch (cmd) { + case VIDEO1394_IOC_LISTEN_CHANNEL: + case VIDEO1394_IOC_UNLISTEN_CHANNEL: + case VIDEO1394_IOC_TALK_CHANNEL: + case VIDEO1394_IOC_UNTALK_CHANNEL: + return video1394_ioctl(f, cmd, arg); + + case VIDEO1394_IOC32_LISTEN_QUEUE_BUFFER: + return video1394_w_wait32(f, cmd, arg); + case VIDEO1394_IOC32_LISTEN_WAIT_BUFFER: + return video1394_wr_wait32(f, cmd, arg); + case VIDEO1394_IOC_TALK_QUEUE_BUFFER: + return video1394_queue_buf32(f, cmd, arg); + case VIDEO1394_IOC32_TALK_WAIT_BUFFER: + return video1394_w_wait32(f, cmd, arg); + case VIDEO1394_IOC32_LISTEN_POLL_BUFFER: + return video1394_wr_wait32(f, cmd, arg); + default: + return -ENOIOCTLCMD; + } +} + +#endif /* CONFIG_COMPAT */ + +static void __exit video1394_exit_module (void) +{ + hpsb_unregister_protocol(&video1394_driver); + hpsb_unregister_highlevel(&video1394_highlevel); + cdev_del(&video1394_cdev); + PRINT_G(KERN_INFO, "Removed " VIDEO1394_DRIVER_NAME " module"); +} + +static int __init video1394_init_module (void) +{ + int ret; + + hpsb_init_highlevel(&video1394_highlevel); + + cdev_init(&video1394_cdev, &video1394_fops); + video1394_cdev.owner = THIS_MODULE; + ret = cdev_add(&video1394_cdev, IEEE1394_VIDEO1394_DEV, 16); + if (ret) { + PRINT_G(KERN_ERR, "video1394: unable to get minor device block"); + return ret; + } + + hpsb_register_highlevel(&video1394_highlevel); + + ret = hpsb_register_protocol(&video1394_driver); + if (ret) { + PRINT_G(KERN_ERR, "video1394: failed to register protocol"); + hpsb_unregister_highlevel(&video1394_highlevel); + cdev_del(&video1394_cdev); + return ret; + } + + PRINT_G(KERN_INFO, "Installed " VIDEO1394_DRIVER_NAME " module"); + return 0; +} + + +module_init(video1394_init_module); +module_exit(video1394_exit_module); diff --git a/trunk/drivers/ieee1394/video1394.h b/trunk/drivers/ieee1394/video1394.h new file mode 100644 index 000000000000..9a89d9cc3c85 --- /dev/null +++ b/trunk/drivers/ieee1394/video1394.h @@ -0,0 +1,67 @@ +/* + * video1394.h - driver for OHCI 1394 boards + * Copyright (C)1999,2000 Sebastien Rougeaux + * Peter Schlaile + * + * 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. + */ + +#ifndef _VIDEO_1394_H +#define _VIDEO_1394_H + +#include "ieee1394-ioctl.h" + +#define VIDEO1394_DRIVER_NAME "video1394" + +#define VIDEO1394_MAX_SIZE 0x4000000 + +enum { + VIDEO1394_BUFFER_FREE = 0, + VIDEO1394_BUFFER_QUEUED, + VIDEO1394_BUFFER_READY +}; + +#define VIDEO1394_SYNC_FRAMES 0x00000001 +#define VIDEO1394_INCLUDE_ISO_HEADERS 0x00000002 +#define VIDEO1394_VARIABLE_PACKET_SIZE 0x00000004 + +struct video1394_mmap { + int channel; /* -1 to find an open channel in LISTEN/TALK */ + unsigned int sync_tag; + unsigned int nb_buffers; + unsigned int buf_size; + unsigned int packet_size; /* For VARIABLE_PACKET_SIZE: + Maximum packet size */ + unsigned int fps; + unsigned int syt_offset; + unsigned int flags; +}; + +/* For TALK_QUEUE_BUFFER with VIDEO1394_VARIABLE_PACKET_SIZE use */ +struct video1394_queue_variable { + unsigned int channel; + unsigned int buffer; + unsigned int __user * packet_sizes; /* Buffer of size: + buf_size / packet_size */ +}; + +struct video1394_wait { + unsigned int channel; + unsigned int buffer; + struct timeval filltime; /* time of buffer full */ +}; + + +#endif diff --git a/trunk/drivers/input/evdev.c b/trunk/drivers/input/evdev.c index e3f7fc6f9565..535fea4fe67f 100644 --- a/trunk/drivers/input/evdev.c +++ b/trunk/drivers/input/evdev.c @@ -534,80 +534,6 @@ static int handle_eviocgbit(struct input_dev *dev, } #undef OLD_KEY_MAX -static int evdev_handle_get_keycode(struct input_dev *dev, - void __user *p, size_t size) -{ - struct input_keymap_entry ke; - int error; - - memset(&ke, 0, sizeof(ke)); - - if (size == sizeof(unsigned int[2])) { - /* legacy case */ - int __user *ip = (int __user *)p; - - if (copy_from_user(ke.scancode, p, sizeof(unsigned int))) - return -EFAULT; - - ke.len = sizeof(unsigned int); - ke.flags = 0; - - error = input_get_keycode(dev, &ke); - if (error) - return error; - - if (put_user(ke.keycode, ip + 1)) - return -EFAULT; - - } else { - size = min(size, sizeof(ke)); - - if (copy_from_user(&ke, p, size)) - return -EFAULT; - - error = input_get_keycode(dev, &ke); - if (error) - return error; - - if (copy_to_user(p, &ke, size)) - return -EFAULT; - } - return 0; -} - -static int evdev_handle_set_keycode(struct input_dev *dev, - void __user *p, size_t size) -{ - struct input_keymap_entry ke; - - memset(&ke, 0, sizeof(ke)); - - if (size == sizeof(unsigned int[2])) { - /* legacy case */ - int __user *ip = (int __user *)p; - - if (copy_from_user(ke.scancode, p, sizeof(unsigned int))) - return -EFAULT; - - if (get_user(ke.keycode, ip + 1)) - return -EFAULT; - - ke.len = sizeof(unsigned int); - ke.flags = 0; - - } else { - size = min(size, sizeof(ke)); - - if (copy_from_user(&ke, p, size)) - return -EFAULT; - - if (ke.len > sizeof(ke.scancode)) - return -EINVAL; - } - - return input_set_keycode(dev, &ke); -} - static long evdev_do_ioctl(struct file *file, unsigned int cmd, void __user *p, int compat_mode) { @@ -654,6 +580,25 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, return 0; + case EVIOCGKEYCODE: + if (get_user(t, ip)) + return -EFAULT; + + error = input_get_keycode(dev, t, &v); + if (error) + return error; + + if (put_user(v, ip + 1)) + return -EFAULT; + + return 0; + + case EVIOCSKEYCODE: + if (get_user(t, ip) || get_user(v, ip + 1)) + return -EFAULT; + + return input_set_keycode(dev, t, v); + case EVIOCRMFF: return input_ff_erase(dev, (int)(unsigned long) p, file); @@ -675,6 +620,7 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, /* Now check variable-length commands */ #define EVIOC_MASK_SIZE(nr) ((nr) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) + switch (EVIOC_MASK_SIZE(cmd)) { case EVIOCGKEY(0): @@ -708,12 +654,6 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, return -EFAULT; return error; - - case EVIOC_MASK_SIZE(EVIOCGKEYCODE): - return evdev_handle_get_keycode(dev, p, size); - - case EVIOC_MASK_SIZE(EVIOCSKEYCODE): - return evdev_handle_set_keycode(dev, p, size); } /* Multi-number variable-length handlers */ diff --git a/trunk/drivers/input/gameport/emu10k1-gp.c b/trunk/drivers/input/gameport/emu10k1-gp.c index 422aa0a6b77f..7392992da424 100644 --- a/trunk/drivers/input/gameport/emu10k1-gp.c +++ b/trunk/drivers/input/gameport/emu10k1-gp.c @@ -59,52 +59,44 @@ MODULE_DEVICE_TABLE(pci, emu_tbl); static int __devinit emu_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { + int ioport, iolen; struct emu *emu; struct gameport *port; - int error; + + if (pci_enable_device(pdev)) + return -EBUSY; + + ioport = pci_resource_start(pdev, 0); + iolen = pci_resource_len(pdev, 0); + + if (!request_region(ioport, iolen, "emu10k1-gp")) + return -EBUSY; emu = kzalloc(sizeof(struct emu), GFP_KERNEL); port = gameport_allocate_port(); if (!emu || !port) { printk(KERN_ERR "emu10k1-gp: Memory allocation failed\n"); - error = -ENOMEM; - goto err_out_free; + release_region(ioport, iolen); + kfree(emu); + gameport_free_port(port); + return -ENOMEM; } - error = pci_enable_device(pdev); - if (error) - goto err_out_free; - - emu->io = pci_resource_start(pdev, 0); - emu->size = pci_resource_len(pdev, 0); - + emu->io = ioport; + emu->size = iolen; emu->dev = pdev; emu->gameport = port; gameport_set_name(port, "EMU10K1"); gameport_set_phys(port, "pci%s/gameport0", pci_name(pdev)); port->dev.parent = &pdev->dev; - port->io = emu->io; - - if (!request_region(emu->io, emu->size, "emu10k1-gp")) { - printk(KERN_ERR "emu10k1-gp: unable to grab region 0x%x-0x%x\n", - emu->io, emu->io + emu->size - 1); - error = -EBUSY; - goto err_out_disable_dev; - } + port->io = ioport; pci_set_drvdata(pdev, emu); gameport_register_port(port); return 0; - - err_out_disable_dev: - pci_disable_device(pdev); - err_out_free: - gameport_free_port(port); - kfree(emu); - return error; } static void __devexit emu_remove(struct pci_dev *pdev) @@ -114,8 +106,6 @@ static void __devexit emu_remove(struct pci_dev *pdev) gameport_unregister_port(emu->gameport); release_region(emu->io, emu->size); kfree(emu); - - pci_disable_device(pdev); } static struct pci_driver emu_driver = { diff --git a/trunk/drivers/input/gameport/fm801-gp.c b/trunk/drivers/input/gameport/fm801-gp.c index a3b70ff21018..14d3f3e208a2 100644 --- a/trunk/drivers/input/gameport/fm801-gp.c +++ b/trunk/drivers/input/gameport/fm801-gp.c @@ -133,11 +133,11 @@ static void __devexit fm801_gp_remove(struct pci_dev *pci) { struct fm801_gp *gp = pci_get_drvdata(pci); - gameport_unregister_port(gp->gameport); - release_resource(gp->res_port); - kfree(gp); - - pci_disable_device(pci); + if (gp) { + gameport_unregister_port(gp->gameport); + release_resource(gp->res_port); + kfree(gp); + } } static const struct pci_device_id fm801_gp_id_table[] = { diff --git a/trunk/drivers/input/input.c b/trunk/drivers/input/input.c index d092ef9291da..7919c2537225 100644 --- a/trunk/drivers/input/input.c +++ b/trunk/drivers/input/input.c @@ -171,7 +171,7 @@ static int input_handle_abs_event(struct input_dev *dev, if (code == ABS_MT_SLOT) { /* * "Stage" the event; we'll flush it later, when we - * get actual touch data. + * get actiual touch data. */ if (*pval >= 0 && *pval < dev->mtsize) dev->slot = *pval; @@ -188,7 +188,7 @@ static int input_handle_abs_event(struct input_dev *dev, pold = &mtslot->abs[code - ABS_MT_FIRST]; } else { /* - * Bypass filtering for multi-touch events when + * Bypass filtering for multitouch events when * not employing slots. */ pold = NULL; @@ -634,141 +634,78 @@ static void input_disconnect_device(struct input_dev *dev) spin_unlock_irq(&dev->event_lock); } -/** - * input_scancode_to_scalar() - converts scancode in &struct input_keymap_entry - * @ke: keymap entry containing scancode to be converted. - * @scancode: pointer to the location where converted scancode should - * be stored. - * - * This function is used to convert scancode stored in &struct keymap_entry - * into scalar form understood by legacy keymap handling methods. These - * methods expect scancodes to be represented as 'unsigned int'. - */ -int input_scancode_to_scalar(const struct input_keymap_entry *ke, - unsigned int *scancode) -{ - switch (ke->len) { - case 1: - *scancode = *((u8 *)ke->scancode); - break; - - case 2: - *scancode = *((u16 *)ke->scancode); - break; - - case 4: - *scancode = *((u32 *)ke->scancode); - break; - - default: - return -EINVAL; - } - - return 0; -} -EXPORT_SYMBOL(input_scancode_to_scalar); - -/* - * Those routines handle the default case where no [gs]etkeycode() is - * defined. In this case, an array indexed by the scancode is used. - */ - -static unsigned int input_fetch_keycode(struct input_dev *dev, - unsigned int index) +static int input_fetch_keycode(struct input_dev *dev, int scancode) { switch (dev->keycodesize) { - case 1: - return ((u8 *)dev->keycode)[index]; + case 1: + return ((u8 *)dev->keycode)[scancode]; - case 2: - return ((u16 *)dev->keycode)[index]; + case 2: + return ((u16 *)dev->keycode)[scancode]; - default: - return ((u32 *)dev->keycode)[index]; + default: + return ((u32 *)dev->keycode)[scancode]; } } static int input_default_getkeycode(struct input_dev *dev, - struct input_keymap_entry *ke) + unsigned int scancode, + unsigned int *keycode) { - unsigned int index; - int error; - if (!dev->keycodesize) return -EINVAL; - if (ke->flags & INPUT_KEYMAP_BY_INDEX) - index = ke->index; - else { - error = input_scancode_to_scalar(ke, &index); - if (error) - return error; - } - - if (index >= dev->keycodemax) + if (scancode >= dev->keycodemax) return -EINVAL; - ke->keycode = input_fetch_keycode(dev, index); - ke->index = index; - ke->len = sizeof(index); - memcpy(ke->scancode, &index, sizeof(index)); + *keycode = input_fetch_keycode(dev, scancode); return 0; } static int input_default_setkeycode(struct input_dev *dev, - const struct input_keymap_entry *ke, - unsigned int *old_keycode) + unsigned int scancode, + unsigned int keycode) { - unsigned int index; - int error; + int old_keycode; int i; - if (!dev->keycodesize) + if (scancode >= dev->keycodemax) return -EINVAL; - if (ke->flags & INPUT_KEYMAP_BY_INDEX) { - index = ke->index; - } else { - error = input_scancode_to_scalar(ke, &index); - if (error) - return error; - } - - if (index >= dev->keycodemax) + if (!dev->keycodesize) return -EINVAL; - if (dev->keycodesize < sizeof(dev->keycode) && - (ke->keycode >> (dev->keycodesize * 8))) + if (dev->keycodesize < sizeof(keycode) && (keycode >> (dev->keycodesize * 8))) return -EINVAL; switch (dev->keycodesize) { case 1: { u8 *k = (u8 *)dev->keycode; - *old_keycode = k[index]; - k[index] = ke->keycode; + old_keycode = k[scancode]; + k[scancode] = keycode; break; } case 2: { u16 *k = (u16 *)dev->keycode; - *old_keycode = k[index]; - k[index] = ke->keycode; + old_keycode = k[scancode]; + k[scancode] = keycode; break; } default: { u32 *k = (u32 *)dev->keycode; - *old_keycode = k[index]; - k[index] = ke->keycode; + old_keycode = k[scancode]; + k[scancode] = keycode; break; } } - __clear_bit(*old_keycode, dev->keybit); - __set_bit(ke->keycode, dev->keybit); + __clear_bit(old_keycode, dev->keybit); + __set_bit(keycode, dev->keybit); for (i = 0; i < dev->keycodemax; i++) { - if (input_fetch_keycode(dev, i) == *old_keycode) { - __set_bit(*old_keycode, dev->keybit); + if (input_fetch_keycode(dev, i) == old_keycode) { + __set_bit(old_keycode, dev->keybit); break; /* Setting the bit twice is useless, so break */ } } @@ -779,86 +716,53 @@ static int input_default_setkeycode(struct input_dev *dev, /** * input_get_keycode - retrieve keycode currently mapped to a given scancode * @dev: input device which keymap is being queried - * @ke: keymap entry + * @scancode: scancode (or its equivalent for device in question) for which + * keycode is needed + * @keycode: result * * This function should be called by anyone interested in retrieving current - * keymap. Presently evdev handlers use it. + * keymap. Presently keyboard and evdev handlers use it. */ -int input_get_keycode(struct input_dev *dev, struct input_keymap_entry *ke) +int input_get_keycode(struct input_dev *dev, + unsigned int scancode, unsigned int *keycode) { unsigned long flags; int retval; spin_lock_irqsave(&dev->event_lock, flags); - - if (dev->getkeycode) { - /* - * Support for legacy drivers, that don't implement the new - * ioctls - */ - u32 scancode = ke->index; - - memcpy(ke->scancode, &scancode, sizeof(scancode)); - ke->len = sizeof(scancode); - retval = dev->getkeycode(dev, scancode, &ke->keycode); - } else { - retval = dev->getkeycode_new(dev, ke); - } - + retval = dev->getkeycode(dev, scancode, keycode); spin_unlock_irqrestore(&dev->event_lock, flags); + return retval; } EXPORT_SYMBOL(input_get_keycode); /** - * input_set_keycode - attribute a keycode to a given scancode + * input_get_keycode - assign new keycode to a given scancode * @dev: input device which keymap is being updated - * @ke: new keymap entry + * @scancode: scancode (or its equivalent for device in question) + * @keycode: new keycode to be assigned to the scancode * * This function should be called by anyone needing to update current * keymap. Presently keyboard and evdev handlers use it. */ int input_set_keycode(struct input_dev *dev, - const struct input_keymap_entry *ke) + unsigned int scancode, unsigned int keycode) { unsigned long flags; unsigned int old_keycode; int retval; - if (ke->keycode > KEY_MAX) + if (keycode > KEY_MAX) return -EINVAL; spin_lock_irqsave(&dev->event_lock, flags); - if (dev->setkeycode) { - /* - * Support for legacy drivers, that don't implement the new - * ioctls - */ - unsigned int scancode; - - retval = input_scancode_to_scalar(ke, &scancode); - if (retval) - goto out; - - /* - * We need to know the old scancode, in order to generate a - * keyup effect, if the set operation happens successfully - */ - if (!dev->getkeycode) { - retval = -EINVAL; - goto out; - } - - retval = dev->getkeycode(dev, scancode, &old_keycode); - if (retval) - goto out; - - retval = dev->setkeycode(dev, scancode, ke->keycode); - } else { - retval = dev->setkeycode_new(dev, ke, &old_keycode); - } + retval = dev->getkeycode(dev, scancode, &old_keycode); + if (retval) + goto out; + retval = dev->setkeycode(dev, scancode, keycode); if (retval) goto out; @@ -1697,7 +1601,7 @@ EXPORT_SYMBOL(input_free_device); * * This function allocates all necessary memory for MT slot handling in the * input device, and adds ABS_MT_SLOT to the device capabilities. All slots - * are initially marked as unused by setting ABS_MT_TRACKING_ID to -1. + * are initially marked as unused iby setting ABS_MT_TRACKING_ID to -1. */ int input_mt_create_slots(struct input_dev *dev, unsigned int num_slots) { @@ -1855,11 +1759,11 @@ int input_register_device(struct input_dev *dev) dev->rep[REP_PERIOD] = 33; } - if (!dev->getkeycode && !dev->getkeycode_new) - dev->getkeycode_new = input_default_getkeycode; + if (!dev->getkeycode) + dev->getkeycode = input_default_getkeycode; - if (!dev->setkeycode && !dev->setkeycode_new) - dev->setkeycode_new = input_default_setkeycode; + if (!dev->setkeycode) + dev->setkeycode = input_default_setkeycode; dev_set_name(&dev->dev, "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1); diff --git a/trunk/drivers/input/keyboard/Kconfig b/trunk/drivers/input/keyboard/Kconfig index 0426630f0e98..aa037fec2f86 100644 --- a/trunk/drivers/input/keyboard/Kconfig +++ b/trunk/drivers/input/keyboard/Kconfig @@ -327,16 +327,6 @@ config KEYBOARD_NEWTON To compile this driver as a module, choose M here: the module will be called newtonkbd. -config KEYBOARD_NOMADIK - tristate "ST-Ericsson Nomadik SKE keyboard" - depends on PLAT_NOMADIK - help - Say Y here if you want to use a keypad provided on the SKE controller - used on the Ux500 and Nomadik platforms - - To compile this driver as a module, choose M here: the - module will be called nmk-ske-keypad. - config KEYBOARD_OPENCORES tristate "OpenCores Keyboard Controller" help @@ -434,15 +424,6 @@ config KEYBOARD_OMAP To compile this driver as a module, choose M here: the module will be called omap-keypad. -config KEYBOARD_OMAP4 - tristate "TI OMAP4 keypad support" - depends on ARCH_OMAP4 - help - Say Y here if you want to use the OMAP4 keypad. - - To compile this driver as a module, choose M here: the - module will be called omap4-keypad. - config KEYBOARD_TWL4030 tristate "TI TWL4030/TWL5030/TPS659x0 keypad support" depends on TWL4030_CORE diff --git a/trunk/drivers/input/keyboard/Makefile b/trunk/drivers/input/keyboard/Makefile index c13809c5c3a7..504b591be0cd 100644 --- a/trunk/drivers/input/keyboard/Makefile +++ b/trunk/drivers/input/keyboard/Makefile @@ -28,9 +28,7 @@ obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o obj-$(CONFIG_KEYBOARD_MCS) += mcs_touchkey.o obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o -obj-$(CONFIG_KEYBOARD_NOMADIK) += nomadik-ske-keypad.o obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o -obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-keypad.o obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o diff --git a/trunk/drivers/input/keyboard/adp5588-keys.c b/trunk/drivers/input/keyboard/adp5588-keys.c index b92d1cd5cba1..d6918cb966c0 100644 --- a/trunk/drivers/input/keyboard/adp5588-keys.c +++ b/trunk/drivers/input/keyboard/adp5588-keys.c @@ -660,7 +660,7 @@ static const struct dev_pm_ops adp5588_dev_pm_ops = { #endif static const struct i2c_device_id adp5588_id[] = { - { "adp5588-keys", 0 }, + { KBUILD_MODNAME, 0 }, { "adp5587-keys", 0 }, { } }; diff --git a/trunk/drivers/input/keyboard/hil_kbd.c b/trunk/drivers/input/keyboard/hil_kbd.c index fed31e0947a1..19fa94af207a 100644 --- a/trunk/drivers/input/keyboard/hil_kbd.c +++ b/trunk/drivers/input/keyboard/hil_kbd.c @@ -570,8 +570,6 @@ static struct serio_device_id hil_dev_ids[] = { { 0 } }; -MODULE_DEVICE_TABLE(serio, hil_dev_ids); - static struct serio_driver hil_serio_drv = { .driver = { .name = "hil_dev", diff --git a/trunk/drivers/input/keyboard/nomadik-ske-keypad.c b/trunk/drivers/input/keyboard/nomadik-ske-keypad.c deleted file mode 100644 index 6e0f23091360..000000000000 --- a/trunk/drivers/input/keyboard/nomadik-ske-keypad.c +++ /dev/null @@ -1,408 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2010 - * - * Author: Naveen Kumar G for ST-Ericsson - * Author: Sundar Iyer for ST-Ericsson - * - * License terms:GNU General Public License (GPL) version 2 - * - * Keypad controller driver for the SKE (Scroll Key Encoder) module used in - * the Nomadik 8815 and Ux500 platforms. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/* SKE_CR bits */ -#define SKE_KPMLT (0x1 << 6) -#define SKE_KPCN (0x7 << 3) -#define SKE_KPASEN (0x1 << 2) -#define SKE_KPASON (0x1 << 7) - -/* SKE_IMSC bits */ -#define SKE_KPIMA (0x1 << 2) - -/* SKE_ICR bits */ -#define SKE_KPICS (0x1 << 3) -#define SKE_KPICA (0x1 << 2) - -/* SKE_RIS bits */ -#define SKE_KPRISA (0x1 << 2) - -#define SKE_KEYPAD_ROW_SHIFT 3 -#define SKE_KPD_KEYMAP_SIZE (8 * 8) - -/* keypad auto scan registers */ -#define SKE_ASR0 0x20 -#define SKE_ASR1 0x24 -#define SKE_ASR2 0x28 -#define SKE_ASR3 0x2C - -#define SKE_NUM_ASRX_REGISTERS (4) - -/** - * struct ske_keypad - data structure used by keypad driver - * @irq: irq no - * @reg_base: ske regsiters base address - * @input: pointer to input device object - * @board: keypad platform device - * @keymap: matrix scan code table for keycodes - * @clk: clock structure pointer - */ -struct ske_keypad { - int irq; - void __iomem *reg_base; - struct input_dev *input; - const struct ske_keypad_platform_data *board; - unsigned short keymap[SKE_KPD_KEYMAP_SIZE]; - struct clk *clk; - spinlock_t ske_keypad_lock; -}; - -static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr, - u8 mask, u8 data) -{ - u32 ret; - - spin_lock(&keypad->ske_keypad_lock); - - ret = readl(keypad->reg_base + addr); - ret &= ~mask; - ret |= data; - writel(ret, keypad->reg_base + addr); - - spin_unlock(&keypad->ske_keypad_lock); -} - -/* - * ske_keypad_chip_init: init keypad controller configuration - * - * Enable Multi key press detection, auto scan mode - */ -static int __devinit ske_keypad_chip_init(struct ske_keypad *keypad) -{ - u32 value; - int timeout = 50; - - /* check SKE_RIS to be 0 */ - while ((readl(keypad->reg_base + SKE_RIS) != 0x00000000) && timeout--) - cpu_relax(); - - if (!timeout) - return -EINVAL; - - /* - * set debounce value - * keypad dbounce is configured in DBCR[15:8] - * dbounce value in steps of 32/32.768 ms - */ - spin_lock(&keypad->ske_keypad_lock); - value = readl(keypad->reg_base + SKE_DBCR); - value = value & 0xff; - value |= ((keypad->board->debounce_ms * 32000)/32768) << 8; - writel(value, keypad->reg_base + SKE_DBCR); - spin_unlock(&keypad->ske_keypad_lock); - - /* enable multi key detection */ - ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPMLT); - - /* - * set up the number of columns - * KPCN[5:3] defines no. of keypad columns to be auto scanned - */ - value = (keypad->board->kcol - 1) << 3; - ske_keypad_set_bits(keypad, SKE_CR, SKE_KPCN, value); - - /* clear keypad interrupt for auto(and pending SW) scans */ - ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA | SKE_KPICS); - - /* un-mask keypad interrupts */ - ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA); - - /* enable automatic scan */ - ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPASEN); - - return 0; -} - -static void ske_keypad_read_data(struct ske_keypad *keypad) -{ - struct input_dev *input = keypad->input; - u16 status; - int col = 0, row = 0, code; - int ske_asr, ske_ris, key_pressed, i; - - /* - * Read the auto scan registers - * - * Each SKE_ASRx (x=0 to x=3) contains two row values. - * lower byte contains row value for column 2*x, - * upper byte contains row value for column 2*x + 1 - */ - for (i = 0; i < SKE_NUM_ASRX_REGISTERS; i++) { - ske_asr = readl(keypad->reg_base + SKE_ASR0 + (4 * i)); - if (!ske_asr) - continue; - - /* now that ASRx is zero, find out the column x and row y*/ - if (ske_asr & 0xff) { - col = i * 2; - status = ske_asr & 0xff; - } else { - col = (i * 2) + 1; - status = (ske_asr & 0xff00) >> 8; - } - - /* find out the row */ - row = __ffs(status); - - code = MATRIX_SCAN_CODE(row, col, SKE_KEYPAD_ROW_SHIFT); - ske_ris = readl(keypad->reg_base + SKE_RIS); - key_pressed = ske_ris & SKE_KPRISA; - - input_event(input, EV_MSC, MSC_SCAN, code); - input_report_key(input, keypad->keymap[code], key_pressed); - input_sync(input); - } -} - -static irqreturn_t ske_keypad_irq(int irq, void *dev_id) -{ - struct ske_keypad *keypad = dev_id; - int retries = 20; - - /* disable auto scan interrupt; mask the interrupt generated */ - ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0); - ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA); - - while ((readl(keypad->reg_base + SKE_CR) & SKE_KPASON) && --retries) - msleep(5); - - if (retries) { - /* SKEx registers are stable and can be read */ - ske_keypad_read_data(keypad); - } - - /* enable auto scan interrupts */ - ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA); - - return IRQ_HANDLED; -} - -static int __devinit ske_keypad_probe(struct platform_device *pdev) -{ - const struct ske_keypad_platform_data *plat = pdev->dev.platform_data; - struct ske_keypad *keypad; - struct input_dev *input; - struct resource *res; - int irq; - int error; - - if (!plat) { - dev_err(&pdev->dev, "invalid keypad platform data\n"); - return -EINVAL; - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "failed to get keypad irq\n"); - return -EINVAL; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "missing platform resources\n"); - return -EINVAL; - } - - keypad = kzalloc(sizeof(struct ske_keypad), GFP_KERNEL); - input = input_allocate_device(); - if (!keypad || !input) { - dev_err(&pdev->dev, "failed to allocate keypad memory\n"); - error = -ENOMEM; - goto err_free_mem; - } - - keypad->irq = irq; - keypad->board = plat; - keypad->input = input; - spin_lock_init(&keypad->ske_keypad_lock); - - if (!request_mem_region(res->start, resource_size(res), pdev->name)) { - dev_err(&pdev->dev, "failed to request I/O memory\n"); - error = -EBUSY; - goto err_free_mem; - } - - keypad->reg_base = ioremap(res->start, resource_size(res)); - if (!keypad->reg_base) { - dev_err(&pdev->dev, "failed to remap I/O memory\n"); - error = -ENXIO; - goto err_free_mem_region; - } - - keypad->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(keypad->clk)) { - dev_err(&pdev->dev, "failed to get clk\n"); - error = PTR_ERR(keypad->clk); - goto err_iounmap; - } - - input->id.bustype = BUS_HOST; - input->name = "ux500-ske-keypad"; - input->dev.parent = &pdev->dev; - - input->keycode = keypad->keymap; - input->keycodesize = sizeof(keypad->keymap[0]); - input->keycodemax = ARRAY_SIZE(keypad->keymap); - - input_set_capability(input, EV_MSC, MSC_SCAN); - - __set_bit(EV_KEY, input->evbit); - if (!plat->no_autorepeat) - __set_bit(EV_REP, input->evbit); - - matrix_keypad_build_keymap(plat->keymap_data, SKE_KEYPAD_ROW_SHIFT, - input->keycode, input->keybit); - - clk_enable(keypad->clk); - - /* go through board initialization helpers */ - if (keypad->board->init) - keypad->board->init(); - - error = ske_keypad_chip_init(keypad); - if (error) { - dev_err(&pdev->dev, "unable to init keypad hardware\n"); - goto err_clk_disable; - } - - error = request_threaded_irq(keypad->irq, NULL, ske_keypad_irq, - IRQF_ONESHOT, "ske-keypad", keypad); - if (error) { - dev_err(&pdev->dev, "allocate irq %d failed\n", keypad->irq); - goto err_clk_disable; - } - - error = input_register_device(input); - if (error) { - dev_err(&pdev->dev, - "unable to register input device: %d\n", error); - goto err_free_irq; - } - - if (plat->wakeup_enable) - device_init_wakeup(&pdev->dev, true); - - platform_set_drvdata(pdev, keypad); - - return 0; - -err_free_irq: - free_irq(keypad->irq, keypad); -err_clk_disable: - clk_disable(keypad->clk); - clk_put(keypad->clk); -err_iounmap: - iounmap(keypad->reg_base); -err_free_mem_region: - release_mem_region(res->start, resource_size(res)); -err_free_mem: - input_free_device(input); - kfree(keypad); - return error; -} - -static int __devexit ske_keypad_remove(struct platform_device *pdev) -{ - struct ske_keypad *keypad = platform_get_drvdata(pdev); - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - free_irq(keypad->irq, keypad); - - input_unregister_device(keypad->input); - - clk_disable(keypad->clk); - clk_put(keypad->clk); - - if (keypad->board->exit) - keypad->board->exit(); - - iounmap(keypad->reg_base); - release_mem_region(res->start, resource_size(res)); - kfree(keypad); - - return 0; -} - -#ifdef CONFIG_PM -static int ske_keypad_suspend(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct ske_keypad *keypad = platform_get_drvdata(pdev); - int irq = platform_get_irq(pdev, 0); - - if (device_may_wakeup(dev)) - enable_irq_wake(irq); - else - ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0); - - return 0; -} - -static int ske_keypad_resume(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct ske_keypad *keypad = platform_get_drvdata(pdev); - int irq = platform_get_irq(pdev, 0); - - if (device_may_wakeup(dev)) - disable_irq_wake(irq); - else - ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA); - - return 0; -} - -static const struct dev_pm_ops ske_keypad_dev_pm_ops = { - .suspend = ske_keypad_suspend, - .resume = ske_keypad_resume, -}; -#endif - -struct platform_driver ske_keypad_driver = { - .driver = { - .name = "nmk-ske-keypad", - .owner = THIS_MODULE, -#ifdef CONFIG_PM - .pm = &ske_keypad_dev_pm_ops, -#endif - }, - .probe = ske_keypad_probe, - .remove = __devexit_p(ske_keypad_remove), -}; - -static int __init ske_keypad_init(void) -{ - return platform_driver_probe(&ske_keypad_driver, ske_keypad_probe); -} -module_init(ske_keypad_init); - -static void __exit ske_keypad_exit(void) -{ - platform_driver_unregister(&ske_keypad_driver); -} -module_exit(ske_keypad_exit); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Naveen Kumar / Sundar Iyer "); -MODULE_DESCRIPTION("Nomadik Scroll-Key-Encoder Keypad Driver"); -MODULE_ALIAS("platform:nomadik-ske-keypad"); diff --git a/trunk/drivers/input/keyboard/omap4-keypad.c b/trunk/drivers/input/keyboard/omap4-keypad.c deleted file mode 100644 index 45bd0977d006..000000000000 --- a/trunk/drivers/input/keyboard/omap4-keypad.c +++ /dev/null @@ -1,318 +0,0 @@ -/* - * OMAP4 Keypad Driver - * - * Copyright (C) 2010 Texas Instruments - * - * Author: Abraham Arce - * Initial Code: Syed Rafiuddin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/* OMAP4 registers */ -#define OMAP4_KBD_REVISION 0x00 -#define OMAP4_KBD_SYSCONFIG 0x10 -#define OMAP4_KBD_SYSSTATUS 0x14 -#define OMAP4_KBD_IRQSTATUS 0x18 -#define OMAP4_KBD_IRQENABLE 0x1C -#define OMAP4_KBD_WAKEUPENABLE 0x20 -#define OMAP4_KBD_PENDING 0x24 -#define OMAP4_KBD_CTRL 0x28 -#define OMAP4_KBD_DEBOUNCINGTIME 0x2C -#define OMAP4_KBD_LONGKEYTIME 0x30 -#define OMAP4_KBD_TIMEOUT 0x34 -#define OMAP4_KBD_STATEMACHINE 0x38 -#define OMAP4_KBD_ROWINPUTS 0x3C -#define OMAP4_KBD_COLUMNOUTPUTS 0x40 -#define OMAP4_KBD_FULLCODE31_0 0x44 -#define OMAP4_KBD_FULLCODE63_32 0x48 - -/* OMAP4 bit definitions */ -#define OMAP4_DEF_IRQENABLE_EVENTEN (1 << 0) -#define OMAP4_DEF_IRQENABLE_LONGKEY (1 << 1) -#define OMAP4_DEF_IRQENABLE_TIMEOUTEN (1 << 2) -#define OMAP4_DEF_WUP_EVENT_ENA (1 << 0) -#define OMAP4_DEF_WUP_LONG_KEY_ENA (1 << 1) -#define OMAP4_DEF_CTRL_NOSOFTMODE (1 << 1) -#define OMAP4_DEF_CTRLPTVVALUE (1 << 2) -#define OMAP4_DEF_CTRLPTV (1 << 1) - -/* OMAP4 values */ -#define OMAP4_VAL_IRQDISABLE 0x00 -#define OMAP4_VAL_DEBOUNCINGTIME 0x07 -#define OMAP4_VAL_FUNCTIONALCFG 0x1E - -#define OMAP4_MASK_IRQSTATUSDISABLE 0xFFFF - -struct omap4_keypad { - struct input_dev *input; - - void __iomem *base; - int irq; - - unsigned int rows; - unsigned int cols; - unsigned int row_shift; - unsigned char key_state[8]; - unsigned short keymap[]; -}; - -static void __devinit omap4_keypad_config(struct omap4_keypad *keypad_data) -{ - __raw_writel(OMAP4_VAL_FUNCTIONALCFG, - keypad_data->base + OMAP4_KBD_CTRL); - __raw_writel(OMAP4_VAL_DEBOUNCINGTIME, - keypad_data->base + OMAP4_KBD_DEBOUNCINGTIME); - __raw_writel(OMAP4_VAL_IRQDISABLE, - keypad_data->base + OMAP4_KBD_IRQSTATUS); - __raw_writel(OMAP4_DEF_IRQENABLE_EVENTEN | OMAP4_DEF_IRQENABLE_LONGKEY, - keypad_data->base + OMAP4_KBD_IRQENABLE); - __raw_writel(OMAP4_DEF_WUP_EVENT_ENA | OMAP4_DEF_WUP_LONG_KEY_ENA, - keypad_data->base + OMAP4_KBD_WAKEUPENABLE); -} - -/* Interrupt handler */ -static irqreturn_t omap4_keypad_interrupt(int irq, void *dev_id) -{ - struct omap4_keypad *keypad_data = dev_id; - struct input_dev *input_dev = keypad_data->input; - unsigned char key_state[ARRAY_SIZE(keypad_data->key_state)]; - unsigned int col, row, code, changed; - u32 *new_state = (u32 *) key_state; - - /* Disable interrupts */ - __raw_writel(OMAP4_VAL_IRQDISABLE, - keypad_data->base + OMAP4_KBD_IRQENABLE); - - *new_state = __raw_readl(keypad_data->base + OMAP4_KBD_FULLCODE31_0); - *(new_state + 1) = __raw_readl(keypad_data->base - + OMAP4_KBD_FULLCODE63_32); - - for (row = 0; row < keypad_data->rows; row++) { - changed = key_state[row] ^ keypad_data->key_state[row]; - if (!changed) - continue; - - for (col = 0; col < keypad_data->cols; col++) { - if (changed & (1 << col)) { - code = MATRIX_SCAN_CODE(row, col, - keypad_data->row_shift); - input_event(input_dev, EV_MSC, MSC_SCAN, code); - input_report_key(input_dev, - keypad_data->keymap[code], - key_state[row] & (1 << col)); - } - } - } - - input_sync(input_dev); - - memcpy(keypad_data->key_state, key_state, - sizeof(keypad_data->key_state)); - - /* clear pending interrupts */ - __raw_writel(__raw_readl(keypad_data->base + OMAP4_KBD_IRQSTATUS), - keypad_data->base + OMAP4_KBD_IRQSTATUS); - - /* enable interrupts */ - __raw_writel(OMAP4_DEF_IRQENABLE_EVENTEN | OMAP4_DEF_IRQENABLE_LONGKEY, - keypad_data->base + OMAP4_KBD_IRQENABLE); - - return IRQ_HANDLED; -} - -static int __devinit omap4_keypad_probe(struct platform_device *pdev) -{ - const struct omap4_keypad_platform_data *pdata; - struct omap4_keypad *keypad_data; - struct input_dev *input_dev; - struct resource *res; - resource_size_t size; - unsigned int row_shift, max_keys; - int irq; - int error; - - /* platform data */ - pdata = pdev->dev.platform_data; - if (!pdata) { - dev_err(&pdev->dev, "no platform data defined\n"); - return -EINVAL; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "no base address specified\n"); - return -EINVAL; - } - - irq = platform_get_irq(pdev, 0); - if (!irq) { - dev_err(&pdev->dev, "no keyboard irq assigned\n"); - return -EINVAL; - } - - if (!pdata->keymap_data) { - dev_err(&pdev->dev, "no keymap data defined\n"); - return -EINVAL; - } - - row_shift = get_count_order(pdata->cols); - max_keys = pdata->rows << row_shift; - - keypad_data = kzalloc(sizeof(struct omap4_keypad) + - max_keys * sizeof(keypad_data->keymap[0]), - GFP_KERNEL); - if (!keypad_data) { - dev_err(&pdev->dev, "keypad_data memory allocation failed\n"); - return -ENOMEM; - } - - size = resource_size(res); - - res = request_mem_region(res->start, size, pdev->name); - if (!res) { - dev_err(&pdev->dev, "can't request mem region\n"); - error = -EBUSY; - goto err_free_keypad; - } - - keypad_data->base = ioremap(res->start, resource_size(res)); - if (!keypad_data->base) { - dev_err(&pdev->dev, "can't ioremap mem resource\n"); - error = -ENOMEM; - goto err_release_mem; - } - - keypad_data->irq = irq; - keypad_data->row_shift = row_shift; - keypad_data->rows = pdata->rows; - keypad_data->cols = pdata->cols; - - /* input device allocation */ - keypad_data->input = input_dev = input_allocate_device(); - if (!input_dev) { - error = -ENOMEM; - goto err_unmap; - } - - input_dev->name = pdev->name; - input_dev->dev.parent = &pdev->dev; - input_dev->id.bustype = BUS_HOST; - input_dev->id.vendor = 0x0001; - input_dev->id.product = 0x0001; - input_dev->id.version = 0x0001; - - input_dev->keycode = keypad_data->keymap; - input_dev->keycodesize = sizeof(keypad_data->keymap[0]); - input_dev->keycodemax = max_keys; - - __set_bit(EV_KEY, input_dev->evbit); - __set_bit(EV_REP, input_dev->evbit); - - input_set_capability(input_dev, EV_MSC, MSC_SCAN); - - input_set_drvdata(input_dev, keypad_data); - - matrix_keypad_build_keymap(pdata->keymap_data, row_shift, - input_dev->keycode, input_dev->keybit); - - omap4_keypad_config(keypad_data); - - error = request_irq(keypad_data->irq, omap4_keypad_interrupt, - IRQF_TRIGGER_RISING, - "omap4-keypad", keypad_data); - if (error) { - dev_err(&pdev->dev, "failed to register interrupt\n"); - goto err_free_input; - } - - error = input_register_device(keypad_data->input); - if (error < 0) { - dev_err(&pdev->dev, "failed to register input device\n"); - goto err_free_irq; - } - - - platform_set_drvdata(pdev, keypad_data); - return 0; - -err_free_irq: - free_irq(keypad_data->irq, keypad_data); -err_free_input: - input_free_device(input_dev); -err_unmap: - iounmap(keypad_data->base); -err_release_mem: - release_mem_region(res->start, size); -err_free_keypad: - kfree(keypad_data); - return error; -} - -static int __devexit omap4_keypad_remove(struct platform_device *pdev) -{ - struct omap4_keypad *keypad_data = platform_get_drvdata(pdev); - struct resource *res; - - free_irq(keypad_data->irq, keypad_data); - input_unregister_device(keypad_data->input); - - iounmap(keypad_data->base); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, resource_size(res)); - - kfree(keypad_data); - platform_set_drvdata(pdev, NULL); - - return 0; -} - -static struct platform_driver omap4_keypad_driver = { - .probe = omap4_keypad_probe, - .remove = __devexit_p(omap4_keypad_remove), - .driver = { - .name = "omap4-keypad", - .owner = THIS_MODULE, - }, -}; - -static int __init omap4_keypad_init(void) -{ - return platform_driver_register(&omap4_keypad_driver); -} -module_init(omap4_keypad_init); - -static void __exit omap4_keypad_exit(void) -{ - platform_driver_unregister(&omap4_keypad_driver); -} -module_exit(omap4_keypad_exit); - -MODULE_AUTHOR("Texas Instruments"); -MODULE_DESCRIPTION("OMAP4 Keypad Driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:omap4-keypad"); diff --git a/trunk/drivers/input/keyboard/twl4030_keypad.c b/trunk/drivers/input/keyboard/twl4030_keypad.c index 09bef79d9da1..fb16b5e5ea13 100644 --- a/trunk/drivers/input/keyboard/twl4030_keypad.c +++ b/trunk/drivers/input/keyboard/twl4030_keypad.c @@ -406,22 +406,23 @@ static int __devinit twl4030_kp_probe(struct platform_device *pdev) if (error) { dev_info(kp->dbg_dev, "request_irq failed for irq no=%d\n", kp->irq); - goto err2; + goto err3; } /* Enable KP and TO interrupts now. */ reg = (u8) ~(KEYP_IMR1_KP | KEYP_IMR1_TO); if (twl4030_kpwrite_u8(kp, reg, KEYP_IMR1)) { error = -EIO; - goto err3; + goto err4; } platform_set_drvdata(pdev, kp); return 0; -err3: +err4: /* mask all events - we don't care about the result */ (void) twl4030_kpwrite_u8(kp, 0xff, KEYP_IMR1); +err3: free_irq(kp->irq, NULL); err2: input_unregister_device(input); diff --git a/trunk/drivers/input/misc/Kconfig b/trunk/drivers/input/misc/Kconfig index b99b8cbde02f..b49e23379723 100644 --- a/trunk/drivers/input/misc/Kconfig +++ b/trunk/drivers/input/misc/Kconfig @@ -22,16 +22,6 @@ config INPUT_88PM860X_ONKEY To compile this driver as a module, choose M here: the module will be called 88pm860x_onkey. -config INPUT_AB8500_PONKEY - tristate "AB8500 Pon (PowerOn) Key" - depends on AB8500_CORE - help - Say Y here to use the PowerOn Key for ST-Ericsson's AB8500 - Mix-Sig PMIC. - - To compile this driver as a module, choose M here: the module - will be called ab8500-ponkey. - config INPUT_AD714X tristate "Analog Devices AD714x Capacitance Touch Sensor" help diff --git a/trunk/drivers/input/misc/Makefile b/trunk/drivers/input/misc/Makefile index 1fe1f6c8b737..19ccca78fa76 100644 --- a/trunk/drivers/input/misc/Makefile +++ b/trunk/drivers/input/misc/Makefile @@ -5,7 +5,6 @@ # Each configuration option enables a list of files. obj-$(CONFIG_INPUT_88PM860X_ONKEY) += 88pm860x_onkey.o -obj-$(CONFIG_INPUT_AB8500_PONKEY) += ab8500-ponkey.o obj-$(CONFIG_INPUT_AD714X) += ad714x.o obj-$(CONFIG_INPUT_AD714X_I2C) += ad714x-i2c.o obj-$(CONFIG_INPUT_AD714X_SPI) += ad714x-spi.o diff --git a/trunk/drivers/input/misc/ab8500-ponkey.c b/trunk/drivers/input/misc/ab8500-ponkey.c deleted file mode 100644 index 3d3288a78fdc..000000000000 --- a/trunk/drivers/input/misc/ab8500-ponkey.c +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2010 - * - * License Terms: GNU General Public License v2 - * Author: Sundar Iyer for ST-Ericsson - * - * AB8500 Power-On Key handler - */ - -#include -#include -#include -#include -#include -#include -#include - -/** - * struct ab8500_ponkey - ab8500 ponkey information - * @input_dev: pointer to input device - * @ab8500: ab8500 parent - * @irq_dbf: irq number for falling transition - * @irq_dbr: irq number for rising transition - */ -struct ab8500_ponkey { - struct input_dev *idev; - struct ab8500 *ab8500; - int irq_dbf; - int irq_dbr; -}; - -/* AB8500 gives us an interrupt when ONKEY is held */ -static irqreturn_t ab8500_ponkey_handler(int irq, void *data) -{ - struct ab8500_ponkey *ponkey = data; - - if (irq == ponkey->irq_dbf) - input_report_key(ponkey->idev, KEY_POWER, true); - else if (irq == ponkey->irq_dbr) - input_report_key(ponkey->idev, KEY_POWER, false); - - input_sync(ponkey->idev); - - return IRQ_HANDLED; -} - -static int __devinit ab8500_ponkey_probe(struct platform_device *pdev) -{ - struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); - struct ab8500_ponkey *ponkey; - struct input_dev *input; - int irq_dbf, irq_dbr; - int error; - - irq_dbf = platform_get_irq_byname(pdev, "ONKEY_DBF"); - if (irq_dbf < 0) { - dev_err(&pdev->dev, "No IRQ for ONKEY_DBF, error=%d\n", irq_dbf); - return irq_dbf; - } - - irq_dbr = platform_get_irq_byname(pdev, "ONKEY_DBR"); - if (irq_dbr < 0) { - dev_err(&pdev->dev, "No IRQ for ONKEY_DBR, error=%d\n", irq_dbr); - return irq_dbr; - } - - ponkey = kzalloc(sizeof(struct ab8500_ponkey), GFP_KERNEL); - input = input_allocate_device(); - if (!ponkey || !input) { - error = -ENOMEM; - goto err_free_mem; - } - - ponkey->idev = input; - ponkey->ab8500 = ab8500; - ponkey->irq_dbf = irq_dbf; - ponkey->irq_dbr = irq_dbr; - - input->name = "AB8500 POn(PowerOn) Key"; - input->dev.parent = &pdev->dev; - - input_set_capability(input, EV_KEY, KEY_POWER); - - error = request_any_context_irq(ponkey->irq_dbf, ab8500_ponkey_handler, - 0, "ab8500-ponkey-dbf", ponkey); - if (error < 0) { - dev_err(ab8500->dev, "Failed to request dbf IRQ#%d: %d\n", - ponkey->irq_dbf, error); - goto err_free_mem; - } - - error = request_any_context_irq(ponkey->irq_dbr, ab8500_ponkey_handler, - 0, "ab8500-ponkey-dbr", ponkey); - if (error < 0) { - dev_err(ab8500->dev, "Failed to request dbr IRQ#%d: %d\n", - ponkey->irq_dbr, error); - goto err_free_dbf_irq; - } - - error = input_register_device(ponkey->idev); - if (error) { - dev_err(ab8500->dev, "Can't register input device: %d\n", error); - goto err_free_dbr_irq; - } - - platform_set_drvdata(pdev, ponkey); - return 0; - -err_free_dbr_irq: - free_irq(ponkey->irq_dbr, ponkey); -err_free_dbf_irq: - free_irq(ponkey->irq_dbf, ponkey); -err_free_mem: - input_free_device(input); - kfree(ponkey); - - return error; -} - -static int __devexit ab8500_ponkey_remove(struct platform_device *pdev) -{ - struct ab8500_ponkey *ponkey = platform_get_drvdata(pdev); - - free_irq(ponkey->irq_dbf, ponkey); - free_irq(ponkey->irq_dbr, ponkey); - input_unregister_device(ponkey->idev); - kfree(ponkey); - - platform_set_drvdata(pdev, NULL); - - return 0; -} - -static struct platform_driver ab8500_ponkey_driver = { - .driver = { - .name = "ab8500-poweron-key", - .owner = THIS_MODULE, - }, - .probe = ab8500_ponkey_probe, - .remove = __devexit_p(ab8500_ponkey_remove), -}; - -static int __init ab8500_ponkey_init(void) -{ - return platform_driver_register(&ab8500_ponkey_driver); -} -module_init(ab8500_ponkey_init); - -static void __exit ab8500_ponkey_exit(void) -{ - platform_driver_unregister(&ab8500_ponkey_driver); -} -module_exit(ab8500_ponkey_exit); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Sundar Iyer "); -MODULE_DESCRIPTION("ST-Ericsson AB8500 Power-ON(Pon) Key driver"); diff --git a/trunk/drivers/input/misc/ati_remote2.c b/trunk/drivers/input/misc/ati_remote2.c index 0b0e9be63542..23257652b8e8 100644 --- a/trunk/drivers/input/misc/ati_remote2.c +++ b/trunk/drivers/input/misc/ati_remote2.c @@ -483,88 +483,51 @@ static void ati_remote2_complete_key(struct urb *urb) } static int ati_remote2_getkeycode(struct input_dev *idev, - struct input_keymap_entry *ke) + unsigned int scancode, unsigned int *keycode) { struct ati_remote2 *ar2 = input_get_drvdata(idev); unsigned int mode; - int offset; - unsigned int index; - unsigned int scancode; - - if (ke->flags & INPUT_KEYMAP_BY_INDEX) { - index = ke->index; - if (index >= ATI_REMOTE2_MODES * - ARRAY_SIZE(ati_remote2_key_table)) - return -EINVAL; - - mode = ke->index / ARRAY_SIZE(ati_remote2_key_table); - offset = ke->index % ARRAY_SIZE(ati_remote2_key_table); - scancode = (mode << 8) + ati_remote2_key_table[offset].hw_code; - } else { - if (input_scancode_to_scalar(ke, &scancode)) - return -EINVAL; - - mode = scancode >> 8; - if (mode > ATI_REMOTE2_PC) - return -EINVAL; - - offset = ati_remote2_lookup(scancode & 0xff); - if (offset < 0) - return -EINVAL; - - index = mode * ARRAY_SIZE(ati_remote2_key_table) + offset; - } + int index; - ke->keycode = ar2->keycode[mode][offset]; - ke->len = sizeof(scancode); - memcpy(&ke->scancode, &scancode, sizeof(scancode)); - ke->index = index; + mode = scancode >> 8; + if (mode > ATI_REMOTE2_PC || !((1 << mode) & ar2->mode_mask)) + return -EINVAL; + index = ati_remote2_lookup(scancode & 0xFF); + if (index < 0) + return -EINVAL; + + *keycode = ar2->keycode[mode][index]; return 0; } static int ati_remote2_setkeycode(struct input_dev *idev, - const struct input_keymap_entry *ke, - unsigned int *old_keycode) + unsigned int scancode, unsigned int keycode) { struct ati_remote2 *ar2 = input_get_drvdata(idev); - unsigned int mode; - int offset; - unsigned int index; - unsigned int scancode; - - if (ke->flags & INPUT_KEYMAP_BY_INDEX) { - if (ke->index >= ATI_REMOTE2_MODES * - ARRAY_SIZE(ati_remote2_key_table)) - return -EINVAL; - - mode = ke->index / ARRAY_SIZE(ati_remote2_key_table); - offset = ke->index % ARRAY_SIZE(ati_remote2_key_table); - } else { - if (input_scancode_to_scalar(ke, &scancode)) - return -EINVAL; - - mode = scancode >> 8; - if (mode > ATI_REMOTE2_PC) - return -EINVAL; - - offset = ati_remote2_lookup(scancode & 0xff); - if (offset < 0) - return -EINVAL; - } + unsigned int mode, old_keycode; + int index; + + mode = scancode >> 8; + if (mode > ATI_REMOTE2_PC || !((1 << mode) & ar2->mode_mask)) + return -EINVAL; + + index = ati_remote2_lookup(scancode & 0xFF); + if (index < 0) + return -EINVAL; - *old_keycode = ar2->keycode[mode][offset]; - ar2->keycode[mode][offset] = ke->keycode; - __set_bit(ke->keycode, idev->keybit); + old_keycode = ar2->keycode[mode][index]; + ar2->keycode[mode][index] = keycode; + __set_bit(keycode, idev->keybit); for (mode = 0; mode < ATI_REMOTE2_MODES; mode++) { for (index = 0; index < ARRAY_SIZE(ati_remote2_key_table); index++) { - if (ar2->keycode[mode][index] == *old_keycode) + if (ar2->keycode[mode][index] == old_keycode) return 0; } } - __clear_bit(*old_keycode, idev->keybit); + __clear_bit(old_keycode, idev->keybit); return 0; } @@ -612,8 +575,8 @@ static int ati_remote2_input_init(struct ati_remote2 *ar2) idev->open = ati_remote2_open; idev->close = ati_remote2_close; - idev->getkeycode_new = ati_remote2_getkeycode; - idev->setkeycode_new = ati_remote2_setkeycode; + idev->getkeycode = ati_remote2_getkeycode; + idev->setkeycode = ati_remote2_setkeycode; idev->name = ar2->name; idev->phys = ar2->phys; diff --git a/trunk/drivers/input/misc/powermate.c b/trunk/drivers/input/misc/powermate.c index f45947190e4f..bf170f6b4422 100644 --- a/trunk/drivers/input/misc/powermate.c +++ b/trunk/drivers/input/misc/powermate.c @@ -280,7 +280,7 @@ static int powermate_alloc_buffers(struct usb_device *udev, struct powermate_dev pm->configcr = kmalloc(sizeof(*(pm->configcr)), GFP_KERNEL); if (!pm->configcr) - return -ENOMEM; + return -1; return 0; } diff --git a/trunk/drivers/input/misc/twl4030-vibra.c b/trunk/drivers/input/misc/twl4030-vibra.c index 014dd4ad0d4f..4f9b2afc24e8 100644 --- a/trunk/drivers/input/misc/twl4030-vibra.c +++ b/trunk/drivers/input/misc/twl4030-vibra.c @@ -271,7 +271,7 @@ static struct platform_driver twl4030_vibra_driver = { .probe = twl4030_vibra_probe, .remove = __devexit_p(twl4030_vibra_remove), .driver = { - .name = "twl4030-vibra", + .name = "twl4030_codec_vibra", .owner = THIS_MODULE, #ifdef CONFIG_PM .pm = &twl4030_vibra_pm_ops, @@ -291,7 +291,7 @@ static void __exit twl4030_vibra_exit(void) } module_exit(twl4030_vibra_exit); -MODULE_ALIAS("platform:twl4030-vibra"); +MODULE_ALIAS("platform:twl4030_codec_vibra"); MODULE_DESCRIPTION("TWL4030 Vibra driver"); MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/input/mouse/elantech.c b/trunk/drivers/input/mouse/elantech.c index 04d9bf320a4f..48311204ba51 100644 --- a/trunk/drivers/input/mouse/elantech.c +++ b/trunk/drivers/input/mouse/elantech.c @@ -699,7 +699,7 @@ int elantech_init(struct psmouse *psmouse) psmouse->private = etd = kzalloc(sizeof(struct elantech_data), GFP_KERNEL); if (!etd) - return -ENOMEM; + return -1; etd->parity[0] = 1; for (i = 1; i < 256; i++) diff --git a/trunk/drivers/input/mouse/psmouse-base.c b/trunk/drivers/input/mouse/psmouse-base.c index cd9d0c97e429..73a7af2542a8 100644 --- a/trunk/drivers/input/mouse/psmouse-base.c +++ b/trunk/drivers/input/mouse/psmouse-base.c @@ -1584,10 +1584,10 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co if (!new_dev) return -ENOMEM; - while (!list_empty(&serio->children)) { + while (serio->child) { if (++retry > 3) { printk(KERN_WARNING - "psmouse: failed to destroy children ports, " + "psmouse: failed to destroy child port, " "protocol change aborted.\n"); input_free_device(new_dev); return -EIO; diff --git a/trunk/drivers/input/mouse/synaptics.c b/trunk/drivers/input/mouse/synaptics.c index 2e300a460556..96b70a43515f 100644 --- a/trunk/drivers/input/mouse/synaptics.c +++ b/trunk/drivers/input/mouse/synaptics.c @@ -294,29 +294,7 @@ static int synaptics_pt_write(struct serio *serio, unsigned char c) return 0; } -static int synaptics_pt_start(struct serio *serio) -{ - struct psmouse *parent = serio_get_drvdata(serio->parent); - struct synaptics_data *priv = parent->private; - - serio_pause_rx(parent->ps2dev.serio); - priv->pt_port = serio; - serio_continue_rx(parent->ps2dev.serio); - - return 0; -} - -static void synaptics_pt_stop(struct serio *serio) -{ - struct psmouse *parent = serio_get_drvdata(serio->parent); - struct synaptics_data *priv = parent->private; - - serio_pause_rx(parent->ps2dev.serio); - priv->pt_port = NULL; - serio_continue_rx(parent->ps2dev.serio); -} - -static int synaptics_is_pt_packet(unsigned char *buf) +static inline int synaptics_is_pt_packet(unsigned char *buf) { return (buf[0] & 0xFC) == 0x84 && (buf[3] & 0xCC) == 0xC4; } @@ -337,8 +315,9 @@ static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet static void synaptics_pt_activate(struct psmouse *psmouse) { + struct serio *ptport = psmouse->ps2dev.serio->child; + struct psmouse *child = serio_get_drvdata(ptport); struct synaptics_data *priv = psmouse->private; - struct psmouse *child = serio_get_drvdata(priv->pt_port); /* adjust the touchpad to child's choice of protocol */ if (child) { @@ -366,8 +345,6 @@ static void synaptics_pt_create(struct psmouse *psmouse) strlcpy(serio->name, "Synaptics pass-through", sizeof(serio->name)); strlcpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->name)); serio->write = synaptics_pt_write; - serio->start = synaptics_pt_start; - serio->stop = synaptics_pt_stop; serio->parent = psmouse->ps2dev.serio; psmouse->pt_activate = synaptics_pt_activate; @@ -601,10 +578,9 @@ static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse) if (unlikely(priv->pkt_type == SYN_NEWABS)) priv->pkt_type = synaptics_detect_pkt_type(psmouse); - if (SYN_CAP_PASS_THROUGH(priv->capabilities) && - synaptics_is_pt_packet(psmouse->packet)) { - if (priv->pt_port) - synaptics_pass_pt_packet(priv->pt_port, psmouse->packet); + if (SYN_CAP_PASS_THROUGH(priv->capabilities) && synaptics_is_pt_packet(psmouse->packet)) { + if (psmouse->ps2dev.serio->child) + synaptics_pass_pt_packet(psmouse->ps2dev.serio->child, psmouse->packet); } else synaptics_process_packet(psmouse); @@ -755,7 +731,7 @@ int synaptics_init(struct psmouse *psmouse) psmouse->private = priv = kzalloc(sizeof(struct synaptics_data), GFP_KERNEL); if (!priv) - return -ENOMEM; + return -1; psmouse_reset(psmouse); diff --git a/trunk/drivers/input/mouse/synaptics.h b/trunk/drivers/input/mouse/synaptics.h index 613a3652f98f..b6aa7d20d8a3 100644 --- a/trunk/drivers/input/mouse/synaptics.h +++ b/trunk/drivers/input/mouse/synaptics.h @@ -110,8 +110,6 @@ struct synaptics_data { unsigned char pkt_type; /* packet type - old, new, etc */ unsigned char mode; /* current mode byte */ int scroll; - - struct serio *pt_port; /* Pass-through serio port */ }; void synaptics_module_init(void); diff --git a/trunk/drivers/input/mouse/trackpoint.c b/trunk/drivers/input/mouse/trackpoint.c index 54b2fa892e19..0643e49ca603 100644 --- a/trunk/drivers/input/mouse/trackpoint.c +++ b/trunk/drivers/input/mouse/trackpoint.c @@ -303,7 +303,7 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties) psmouse->private = kzalloc(sizeof(struct trackpoint_data), GFP_KERNEL); if (!psmouse->private) - return -ENOMEM; + return -1; psmouse->vendor = "IBM"; psmouse->name = "TrackPoint"; diff --git a/trunk/drivers/input/mousedev.c b/trunk/drivers/input/mousedev.c index 2a00ddf4f23a..31ec7265aac6 100644 --- a/trunk/drivers/input/mousedev.c +++ b/trunk/drivers/input/mousedev.c @@ -867,7 +867,7 @@ static struct mousedev *mousedev_create(struct input_dev *dev, spin_lock_init(&mousedev->client_lock); mutex_init(&mousedev->mutex); lockdep_set_subclass(&mousedev->mutex, - minor == MOUSEDEV_MIX ? SINGLE_DEPTH_NESTING : 0); + minor == MOUSEDEV_MIX ? MOUSEDEV_MIX : 0); init_waitqueue_head(&mousedev->wait); if (minor == MOUSEDEV_MIX) diff --git a/trunk/drivers/input/serio/Kconfig b/trunk/drivers/input/serio/Kconfig index 6256233d2bfb..3bfe8fafc6ad 100644 --- a/trunk/drivers/input/serio/Kconfig +++ b/trunk/drivers/input/serio/Kconfig @@ -226,13 +226,4 @@ config SERIO_AMS_DELTA To compile this driver as a module, choose M here; the module will be called ams_delta_serio. -config SERIO_PS2MULT - tristate "TQC PS/2 multiplexer" - help - Say Y here if you have the PS/2 line multiplexer like the one - present on TQC boads. - - To compile this driver as a module, choose M here: the - module will be called ps2mult. - endif diff --git a/trunk/drivers/input/serio/Makefile b/trunk/drivers/input/serio/Makefile index dbbe37616c92..84c80bf7185e 100644 --- a/trunk/drivers/input/serio/Makefile +++ b/trunk/drivers/input/serio/Makefile @@ -18,7 +18,6 @@ obj-$(CONFIG_SERIO_GSCPS2) += gscps2.o obj-$(CONFIG_HP_SDC) += hp_sdc.o obj-$(CONFIG_HIL_MLC) += hp_sdc_mlc.o hil_mlc.o obj-$(CONFIG_SERIO_PCIPS2) += pcips2.o -obj-$(CONFIG_SERIO_PS2MULT) += ps2mult.o obj-$(CONFIG_SERIO_MACEPS2) += maceps2.o obj-$(CONFIG_SERIO_LIBPS2) += libps2.o obj-$(CONFIG_SERIO_RAW) += serio_raw.o diff --git a/trunk/drivers/input/serio/i8042.c b/trunk/drivers/input/serio/i8042.c index 18db5a8c7478..f58513160480 100644 --- a/trunk/drivers/input/serio/i8042.c +++ b/trunk/drivers/input/serio/i8042.c @@ -1063,7 +1063,7 @@ static long i8042_panic_blink(int state) #ifdef CONFIG_X86 static void i8042_dritek_enable(void) { - unsigned char param = 0x90; + char param = 0x90; int error; error = i8042_command(¶m, 0x1059); diff --git a/trunk/drivers/input/serio/ps2mult.c b/trunk/drivers/input/serio/ps2mult.c deleted file mode 100644 index 6bce22e4e495..000000000000 --- a/trunk/drivers/input/serio/ps2mult.c +++ /dev/null @@ -1,318 +0,0 @@ -/* - * TQC PS/2 Multiplexer driver - * - * Copyright (C) 2010 Dmitry Eremin-Solenikov - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - */ - - -#include -#include -#include -#include - -MODULE_AUTHOR("Dmitry Eremin-Solenikov "); -MODULE_DESCRIPTION("TQC PS/2 Multiplexer driver"); -MODULE_LICENSE("GPL"); - -#define PS2MULT_KB_SELECTOR 0xA0 -#define PS2MULT_MS_SELECTOR 0xA1 -#define PS2MULT_ESCAPE 0x7D -#define PS2MULT_BSYNC 0x7E -#define PS2MULT_SESSION_START 0x55 -#define PS2MULT_SESSION_END 0x56 - -struct ps2mult_port { - struct serio *serio; - unsigned char sel; - bool registered; -}; - -#define PS2MULT_NUM_PORTS 2 -#define PS2MULT_KBD_PORT 0 -#define PS2MULT_MOUSE_PORT 1 - -struct ps2mult { - struct serio *mx_serio; - struct ps2mult_port ports[PS2MULT_NUM_PORTS]; - - spinlock_t lock; - struct ps2mult_port *in_port; - struct ps2mult_port *out_port; - bool escape; -}; - -/* First MUST come PS2MULT_NUM_PORTS selectors */ -static const unsigned char ps2mult_controls[] = { - PS2MULT_KB_SELECTOR, PS2MULT_MS_SELECTOR, - PS2MULT_ESCAPE, PS2MULT_BSYNC, - PS2MULT_SESSION_START, PS2MULT_SESSION_END, -}; - -static const struct serio_device_id ps2mult_serio_ids[] = { - { - .type = SERIO_RS232, - .proto = SERIO_PS2MULT, - .id = SERIO_ANY, - .extra = SERIO_ANY, - }, - { 0 } -}; - -MODULE_DEVICE_TABLE(serio, ps2mult_serio_ids); - -static void ps2mult_select_port(struct ps2mult *psm, struct ps2mult_port *port) -{ - struct serio *mx_serio = psm->mx_serio; - - serio_write(mx_serio, port->sel); - psm->out_port = port; - dev_dbg(&mx_serio->dev, "switched to sel %02x\n", port->sel); -} - -static int ps2mult_serio_write(struct serio *serio, unsigned char data) -{ - struct serio *mx_port = serio->parent; - struct ps2mult *psm = serio_get_drvdata(mx_port); - struct ps2mult_port *port = serio->port_data; - bool need_escape; - unsigned long flags; - - spin_lock_irqsave(&psm->lock, flags); - - if (psm->out_port != port) - ps2mult_select_port(psm, port); - - need_escape = memchr(ps2mult_controls, data, sizeof(ps2mult_controls)); - - dev_dbg(&serio->dev, - "write: %s%02x\n", need_escape ? "ESC " : "", data); - - if (need_escape) - serio_write(mx_port, PS2MULT_ESCAPE); - - serio_write(mx_port, data); - - spin_unlock_irqrestore(&psm->lock, flags); - - return 0; -} - -static int ps2mult_serio_start(struct serio *serio) -{ - struct ps2mult *psm = serio_get_drvdata(serio->parent); - struct ps2mult_port *port = serio->port_data; - unsigned long flags; - - spin_lock_irqsave(&psm->lock, flags); - port->registered = true; - spin_unlock_irqrestore(&psm->lock, flags); - - return 0; -} - -static void ps2mult_serio_stop(struct serio *serio) -{ - struct ps2mult *psm = serio_get_drvdata(serio->parent); - struct ps2mult_port *port = serio->port_data; - unsigned long flags; - - spin_lock_irqsave(&psm->lock, flags); - port->registered = false; - spin_unlock_irqrestore(&psm->lock, flags); -} - -static int ps2mult_create_port(struct ps2mult *psm, int i) -{ - struct serio *mx_serio = psm->mx_serio; - struct serio *serio; - - serio = kzalloc(sizeof(struct serio), GFP_KERNEL); - if (!serio) - return -ENOMEM; - - strlcpy(serio->name, "TQC PS/2 Multiplexer", sizeof(serio->name)); - snprintf(serio->phys, sizeof(serio->phys), - "%s/port%d", mx_serio->phys, i); - serio->id.type = SERIO_8042; - serio->write = ps2mult_serio_write; - serio->start = ps2mult_serio_start; - serio->stop = ps2mult_serio_stop; - serio->parent = psm->mx_serio; - serio->port_data = &psm->ports[i]; - - psm->ports[i].serio = serio; - - return 0; -} - -static void ps2mult_reset(struct ps2mult *psm) -{ - unsigned long flags; - - spin_lock_irqsave(&psm->lock, flags); - - serio_write(psm->mx_serio, PS2MULT_SESSION_END); - serio_write(psm->mx_serio, PS2MULT_SESSION_START); - - ps2mult_select_port(psm, &psm->ports[PS2MULT_KBD_PORT]); - - spin_unlock_irqrestore(&psm->lock, flags); -} - -static int ps2mult_connect(struct serio *serio, struct serio_driver *drv) -{ - struct ps2mult *psm; - int i; - int error; - - if (!serio->write) - return -EINVAL; - - psm = kzalloc(sizeof(*psm), GFP_KERNEL); - if (!psm) - return -ENOMEM; - - spin_lock_init(&psm->lock); - psm->mx_serio = serio; - - for (i = 0; i < PS2MULT_NUM_PORTS; i++) { - psm->ports[i].sel = ps2mult_controls[i]; - error = ps2mult_create_port(psm, i); - if (error) - goto err_out; - } - - psm->in_port = psm->out_port = &psm->ports[PS2MULT_KBD_PORT]; - - serio_set_drvdata(serio, psm); - error = serio_open(serio, drv); - if (error) - goto err_out; - - ps2mult_reset(psm); - - for (i = 0; i < PS2MULT_NUM_PORTS; i++) { - struct serio *s = psm->ports[i].serio; - - dev_info(&serio->dev, "%s port at %s\n", s->name, serio->phys); - serio_register_port(s); - } - - return 0; - -err_out: - while (--i >= 0) - kfree(psm->ports[i].serio); - kfree(serio); - return error; -} - -static void ps2mult_disconnect(struct serio *serio) -{ - struct ps2mult *psm = serio_get_drvdata(serio); - - /* Note that serio core already take care of children ports */ - serio_write(serio, PS2MULT_SESSION_END); - serio_close(serio); - kfree(psm); - - serio_set_drvdata(serio, NULL); -} - -static int ps2mult_reconnect(struct serio *serio) -{ - struct ps2mult *psm = serio_get_drvdata(serio); - - ps2mult_reset(psm); - - return 0; -} - -static irqreturn_t ps2mult_interrupt(struct serio *serio, - unsigned char data, unsigned int dfl) -{ - struct ps2mult *psm = serio_get_drvdata(serio); - struct ps2mult_port *in_port; - unsigned long flags; - - dev_dbg(&serio->dev, "Received %02x flags %02x\n", data, dfl); - - spin_lock_irqsave(&psm->lock, flags); - - if (psm->escape) { - psm->escape = false; - in_port = psm->in_port; - if (in_port->registered) - serio_interrupt(in_port->serio, data, dfl); - goto out; - } - - switch (data) { - case PS2MULT_ESCAPE: - dev_dbg(&serio->dev, "ESCAPE\n"); - psm->escape = true; - break; - - case PS2MULT_BSYNC: - dev_dbg(&serio->dev, "BSYNC\n"); - psm->in_port = psm->out_port; - break; - - case PS2MULT_SESSION_START: - dev_dbg(&serio->dev, "SS\n"); - break; - - case PS2MULT_SESSION_END: - dev_dbg(&serio->dev, "SE\n"); - break; - - case PS2MULT_KB_SELECTOR: - dev_dbg(&serio->dev, "KB\n"); - psm->in_port = &psm->ports[PS2MULT_KBD_PORT]; - break; - - case PS2MULT_MS_SELECTOR: - dev_dbg(&serio->dev, "MS\n"); - psm->in_port = &psm->ports[PS2MULT_MOUSE_PORT]; - break; - - default: - in_port = psm->in_port; - if (in_port->registered) - serio_interrupt(in_port->serio, data, dfl); - break; - } - - out: - spin_unlock_irqrestore(&psm->lock, flags); - return IRQ_HANDLED; -} - -static struct serio_driver ps2mult_drv = { - .driver = { - .name = "ps2mult", - }, - .description = "TQC PS/2 Multiplexer driver", - .id_table = ps2mult_serio_ids, - .interrupt = ps2mult_interrupt, - .connect = ps2mult_connect, - .disconnect = ps2mult_disconnect, - .reconnect = ps2mult_reconnect, -}; - -static int __init ps2mult_init(void) -{ - return serio_register_driver(&ps2mult_drv); -} - -static void __exit ps2mult_exit(void) -{ - serio_unregister_driver(&ps2mult_drv); -} - -module_init(ps2mult_init); -module_exit(ps2mult_exit); diff --git a/trunk/drivers/input/serio/serio.c b/trunk/drivers/input/serio/serio.c index 405bf214527c..c3b626e9eae7 100644 --- a/trunk/drivers/input/serio/serio.c +++ b/trunk/drivers/input/serio/serio.c @@ -37,6 +37,7 @@ #include #include #include +#include MODULE_AUTHOR("Vojtech Pavlik "); MODULE_DESCRIPTION("Serio abstraction core"); @@ -55,7 +56,7 @@ static struct bus_type serio_bus; static void serio_add_port(struct serio *serio); static int serio_reconnect_port(struct serio *serio); static void serio_disconnect_port(struct serio *serio); -static void serio_reconnect_subtree(struct serio *serio); +static void serio_reconnect_chain(struct serio *serio); static void serio_attach_driver(struct serio_driver *drv); static int serio_connect_driver(struct serio *serio, struct serio_driver *drv) @@ -151,7 +152,7 @@ static void serio_find_driver(struct serio *serio) enum serio_event_type { SERIO_RESCAN_PORT, SERIO_RECONNECT_PORT, - SERIO_RECONNECT_SUBTREE, + SERIO_RECONNECT_CHAIN, SERIO_REGISTER_PORT, SERIO_ATTACH_DRIVER, }; @@ -291,8 +292,8 @@ static void serio_handle_event(void) serio_find_driver(event->object); break; - case SERIO_RECONNECT_SUBTREE: - serio_reconnect_subtree(event->object); + case SERIO_RECONNECT_CHAIN: + serio_reconnect_chain(event->object); break; case SERIO_ATTACH_DRIVER: @@ -329,10 +330,12 @@ static void serio_remove_pending_events(void *object) } /* - * Locate child serio port (if any) that has not been fully registered yet. + * Destroy child serio port (if any) that has not been fully registered yet. * - * Children are registered by driver's connect() handler so there can't be a - * grandchild pending registration together with a child. + * Note that we rely on the fact that port can have only one child and therefore + * only one child registration request can be pending. Additionally, children + * are registered by driver's connect() handler so there can't be a grandchild + * pending registration together with a child. */ static struct serio *serio_get_pending_child(struct serio *parent) { @@ -446,7 +449,7 @@ static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute * if (!strncmp(buf, "none", count)) { serio_disconnect_port(serio); } else if (!strncmp(buf, "reconnect", count)) { - serio_reconnect_subtree(serio); + serio_reconnect_chain(serio); } else if (!strncmp(buf, "rescan", count)) { serio_disconnect_port(serio); serio_find_driver(serio); @@ -513,8 +516,6 @@ static void serio_init_port(struct serio *serio) __module_get(THIS_MODULE); INIT_LIST_HEAD(&serio->node); - INIT_LIST_HEAD(&serio->child_node); - INIT_LIST_HEAD(&serio->children); spin_lock_init(&serio->lock); mutex_init(&serio->drv_mutex); device_initialize(&serio->dev); @@ -537,13 +538,12 @@ static void serio_init_port(struct serio *serio) */ static void serio_add_port(struct serio *serio) { - struct serio *parent = serio->parent; int error; - if (parent) { - serio_pause_rx(parent); - list_add_tail(&serio->child_node, &parent->children); - serio_continue_rx(parent); + if (serio->parent) { + serio_pause_rx(serio->parent); + serio->parent->child = serio; + serio_continue_rx(serio->parent); } list_add_tail(&serio->node, &serio_list); @@ -559,14 +559,15 @@ static void serio_add_port(struct serio *serio) } /* - * serio_destroy_port() completes unregistration process and removes + * serio_destroy_port() completes deregistration process and removes * port from the system */ static void serio_destroy_port(struct serio *serio) { struct serio *child; - while ((child = serio_get_pending_child(serio)) != NULL) { + child = serio_get_pending_child(serio); + if (child) { serio_remove_pending_events(child); put_device(&child->dev); } @@ -576,7 +577,7 @@ static void serio_destroy_port(struct serio *serio) if (serio->parent) { serio_pause_rx(serio->parent); - list_del_init(&serio->child_node); + serio->parent->child = NULL; serio_continue_rx(serio->parent); serio->parent = NULL; } @@ -608,82 +609,46 @@ static int serio_reconnect_port(struct serio *serio) } /* - * Reconnect serio port and all its children (re-initialize attached - * devices). + * Reconnect serio port and all its children (re-initialize attached devices) */ -static void serio_reconnect_subtree(struct serio *root) +static void serio_reconnect_chain(struct serio *serio) { - struct serio *s = root; - int error; - do { - error = serio_reconnect_port(s); - if (!error) { - /* - * Reconnect was successful, move on to do the - * first child. - */ - if (!list_empty(&s->children)) { - s = list_first_entry(&s->children, - struct serio, child_node); - continue; - } - } - - /* - * Either it was a leaf node or reconnect failed and it - * became a leaf node. Continue reconnecting starting with - * the next sibling of the parent node. - */ - while (s != root) { - struct serio *parent = s->parent; - - if (!list_is_last(&s->child_node, &parent->children)) { - s = list_entry(s->child_node.next, - struct serio, child_node); - break; - } - - s = parent; + if (serio_reconnect_port(serio)) { + /* Ok, old children are now gone, we are done */ + break; } - } while (s != root); + serio = serio->child; + } while (serio); } /* * serio_disconnect_port() unbinds a port from its driver. As a side effect - * all children ports are unbound and destroyed. + * all child ports are unbound and destroyed. */ static void serio_disconnect_port(struct serio *serio) { - struct serio *s = serio; - - /* - * Children ports should be disconnected and destroyed - * first; we travel the tree in depth-first order. - */ - while (!list_empty(&serio->children)) { - - /* Locate a leaf */ - while (!list_empty(&s->children)) - s = list_first_entry(&s->children, - struct serio, child_node); + struct serio *s, *parent; + if (serio->child) { /* - * Prune this leaf node unless it is the one we - * started with. + * Children ports should be disconnected and destroyed + * first, staring with the leaf one, since we don't want + * to do recursion */ - if (s != serio) { - struct serio *parent = s->parent; + for (s = serio; s->child; s = s->child) + /* empty */; + + do { + parent = s->parent; device_release_driver(&s->dev); serio_destroy_port(s); - - s = parent; - } + } while ((s = parent) != serio); } /* - * OK, no children left, now disconnect this port. + * Ok, no children left, now disconnect this port */ device_release_driver(&serio->dev); } @@ -696,7 +661,7 @@ EXPORT_SYMBOL(serio_rescan); void serio_reconnect(struct serio *serio) { - serio_queue_event(serio, NULL, SERIO_RECONNECT_SUBTREE); + serio_queue_event(serio, NULL, SERIO_RECONNECT_CHAIN); } EXPORT_SYMBOL(serio_reconnect); @@ -724,16 +689,14 @@ void serio_unregister_port(struct serio *serio) EXPORT_SYMBOL(serio_unregister_port); /* - * Safely unregisters children ports if they are present. + * Safely unregisters child port if one is present. */ void serio_unregister_child_port(struct serio *serio) { - struct serio *s, *next; - mutex_lock(&serio_mutex); - list_for_each_entry_safe(s, next, &serio->children, child_node) { - serio_disconnect_port(s); - serio_destroy_port(s); + if (serio->child) { + serio_disconnect_port(serio->child); + serio_destroy_port(serio->child); } mutex_unlock(&serio_mutex); } diff --git a/trunk/drivers/input/sparse-keymap.c b/trunk/drivers/input/sparse-keymap.c index a29a7812bd46..014248344763 100644 --- a/trunk/drivers/input/sparse-keymap.c +++ b/trunk/drivers/input/sparse-keymap.c @@ -22,37 +22,6 @@ MODULE_DESCRIPTION("Generic support for sparse keymaps"); MODULE_LICENSE("GPL v2"); MODULE_VERSION("0.1"); -static unsigned int sparse_keymap_get_key_index(struct input_dev *dev, - const struct key_entry *k) -{ - struct key_entry *key; - unsigned int idx = 0; - - for (key = dev->keycode; key->type != KE_END; key++) { - if (key->type == KE_KEY) { - if (key == k) - break; - idx++; - } - } - - return idx; -} - -static struct key_entry *sparse_keymap_entry_by_index(struct input_dev *dev, - unsigned int index) -{ - struct key_entry *key; - unsigned int key_cnt = 0; - - for (key = dev->keycode; key->type != KE_END; key++) - if (key->type == KE_KEY) - if (key_cnt++ == index) - return key; - - return NULL; -} - /** * sparse_keymap_entry_from_scancode - perform sparse keymap lookup * @dev: Input device using sparse keymap @@ -95,36 +64,16 @@ struct key_entry *sparse_keymap_entry_from_keycode(struct input_dev *dev, } EXPORT_SYMBOL(sparse_keymap_entry_from_keycode); -static struct key_entry *sparse_keymap_locate(struct input_dev *dev, - const struct input_keymap_entry *ke) -{ - struct key_entry *key; - unsigned int scancode; - - if (ke->flags & INPUT_KEYMAP_BY_INDEX) - key = sparse_keymap_entry_by_index(dev, ke->index); - else if (input_scancode_to_scalar(ke, &scancode) == 0) - key = sparse_keymap_entry_from_scancode(dev, scancode); - else - key = NULL; - - return key; -} - static int sparse_keymap_getkeycode(struct input_dev *dev, - struct input_keymap_entry *ke) + unsigned int scancode, + unsigned int *keycode) { const struct key_entry *key; if (dev->keycode) { - key = sparse_keymap_locate(dev, ke); + key = sparse_keymap_entry_from_scancode(dev, scancode); if (key && key->type == KE_KEY) { - ke->keycode = key->keycode; - if (!(ke->flags & INPUT_KEYMAP_BY_INDEX)) - ke->index = - sparse_keymap_get_key_index(dev, key); - ke->len = sizeof(key->code); - memcpy(ke->scancode, &key->code, sizeof(key->code)); + *keycode = key->keycode; return 0; } } @@ -133,19 +82,20 @@ static int sparse_keymap_getkeycode(struct input_dev *dev, } static int sparse_keymap_setkeycode(struct input_dev *dev, - const struct input_keymap_entry *ke, - unsigned int *old_keycode) + unsigned int scancode, + unsigned int keycode) { struct key_entry *key; + int old_keycode; if (dev->keycode) { - key = sparse_keymap_locate(dev, ke); + key = sparse_keymap_entry_from_scancode(dev, scancode); if (key && key->type == KE_KEY) { - *old_keycode = key->keycode; - key->keycode = ke->keycode; - set_bit(ke->keycode, dev->keybit); - if (!sparse_keymap_entry_from_keycode(dev, *old_keycode)) - clear_bit(*old_keycode, dev->keybit); + old_keycode = key->keycode; + key->keycode = keycode; + set_bit(keycode, dev->keybit); + if (!sparse_keymap_entry_from_keycode(dev, old_keycode)) + clear_bit(old_keycode, dev->keybit); return 0; } } @@ -209,14 +159,15 @@ int sparse_keymap_setup(struct input_dev *dev, dev->keycode = map; dev->keycodemax = map_size; - dev->getkeycode_new = sparse_keymap_getkeycode; - dev->setkeycode_new = sparse_keymap_setkeycode; + dev->getkeycode = sparse_keymap_getkeycode; + dev->setkeycode = sparse_keymap_setkeycode; return 0; err_out: kfree(map); return error; + } EXPORT_SYMBOL(sparse_keymap_setup); diff --git a/trunk/drivers/input/tablet/Kconfig b/trunk/drivers/input/tablet/Kconfig index 58a87755b936..effb49ea24aa 100644 --- a/trunk/drivers/input/tablet/Kconfig +++ b/trunk/drivers/input/tablet/Kconfig @@ -49,17 +49,6 @@ config TABLET_USB_GTCO To compile this driver as a module, choose M here: the module will be called gtco. -config TABLET_USB_HANWANG - tristate "Hanwang Art Master III tablet support (USB)" - depends on USB_ARCH_HAS_HCD - select USB - help - Say Y here if you want to use the USB version of the Hanwang Art - Master III tablet. - - To compile this driver as a module, choose M here: the - module will be called hanwang. - config TABLET_USB_KBTAB tristate "KB Gear JamStudio tablet support (USB)" depends on USB_ARCH_HAS_HCD diff --git a/trunk/drivers/input/tablet/Makefile b/trunk/drivers/input/tablet/Makefile index 3f6c25220638..ce8b9a9cfa40 100644 --- a/trunk/drivers/input/tablet/Makefile +++ b/trunk/drivers/input/tablet/Makefile @@ -8,6 +8,5 @@ wacom-objs := wacom_wac.o wacom_sys.o obj-$(CONFIG_TABLET_USB_ACECAD) += acecad.o obj-$(CONFIG_TABLET_USB_AIPTEK) += aiptek.o obj-$(CONFIG_TABLET_USB_GTCO) += gtco.o -obj-$(CONFIG_TABLET_USB_HANWANG) += hanwang.o obj-$(CONFIG_TABLET_USB_KBTAB) += kbtab.o obj-$(CONFIG_TABLET_USB_WACOM) += wacom.o diff --git a/trunk/drivers/input/tablet/hanwang.c b/trunk/drivers/input/tablet/hanwang.c deleted file mode 100644 index 6504b627b234..000000000000 --- a/trunk/drivers/input/tablet/hanwang.c +++ /dev/null @@ -1,446 +0,0 @@ -/* - * USB Hanwang tablet support - * - * Copyright (c) 2010 Xing Wei - * - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include -#include -#include -#include -#include -#include - -#define DRIVER_AUTHOR "Xing Wei " -#define DRIVER_DESC "USB Hanwang tablet driver" -#define DRIVER_LICENSE "GPL" - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE(DRIVER_LICENSE); - -#define USB_VENDOR_ID_HANWANG 0x0b57 -#define HANWANG_TABLET_INT_CLASS 0x0003 -#define HANWANG_TABLET_INT_SUB_CLASS 0x0001 -#define HANWANG_TABLET_INT_PROTOCOL 0x0002 - -#define ART_MASTER_PKGLEN_MAX 10 - -/* device IDs */ -#define STYLUS_DEVICE_ID 0x02 -#define TOUCH_DEVICE_ID 0x03 -#define CURSOR_DEVICE_ID 0x06 -#define ERASER_DEVICE_ID 0x0A -#define PAD_DEVICE_ID 0x0F - -/* match vendor and interface info */ -#define HANWANG_TABLET_DEVICE(vend, cl, sc, pr) \ - .match_flags = USB_DEVICE_ID_MATCH_VENDOR \ - | USB_DEVICE_ID_MATCH_INT_INFO, \ - .idVendor = (vend), \ - .bInterfaceClass = (cl), \ - .bInterfaceSubClass = (sc), \ - .bInterfaceProtocol = (pr) - -enum hanwang_tablet_type { - HANWANG_ART_MASTER_III, - HANWANG_ART_MASTER_HD, -}; - -struct hanwang { - unsigned char *data; - dma_addr_t data_dma; - struct input_dev *dev; - struct usb_device *usbdev; - struct urb *irq; - const struct hanwang_features *features; - unsigned int current_tool; - unsigned int current_id; - char name[64]; - char phys[32]; -}; - -struct hanwang_features { - unsigned short pid; - char *name; - enum hanwang_tablet_type type; - int pkg_len; - int max_x; - int max_y; - int max_tilt_x; - int max_tilt_y; - int max_pressure; -}; - -static const struct hanwang_features features_array[] = { - { 0x8528, "Hanwang Art Master III 0906", HANWANG_ART_MASTER_III, - ART_MASTER_PKGLEN_MAX, 0x5757, 0x3692, 0x3f, 0x7f, 2048 }, - { 0x8529, "Hanwang Art Master III 0604", HANWANG_ART_MASTER_III, - ART_MASTER_PKGLEN_MAX, 0x3d84, 0x2672, 0x3f, 0x7f, 2048 }, - { 0x852a, "Hanwang Art Master III 1308", HANWANG_ART_MASTER_III, - ART_MASTER_PKGLEN_MAX, 0x7f00, 0x4f60, 0x3f, 0x7f, 2048 }, - { 0x8401, "Hanwang Art Master HD 5012", HANWANG_ART_MASTER_HD, - ART_MASTER_PKGLEN_MAX, 0x678e, 0x4150, 0x3f, 0x7f, 1024 }, -}; - -static const int hw_eventtypes[] = { - EV_KEY, EV_ABS, EV_MSC, -}; - -static const int hw_absevents[] = { - ABS_X, ABS_Y, ABS_TILT_X, ABS_TILT_Y, ABS_WHEEL, - ABS_RX, ABS_RY, ABS_PRESSURE, ABS_MISC, -}; - -static const int hw_btnevents[] = { - BTN_STYLUS, BTN_STYLUS2, BTN_TOOL_PEN, BTN_TOOL_RUBBER, - BTN_TOOL_MOUSE, BTN_TOOL_FINGER, - BTN_0, BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7, BTN_8, -}; - -static const int hw_mscevents[] = { - MSC_SERIAL, -}; - -static void hanwang_parse_packet(struct hanwang *hanwang) -{ - unsigned char *data = hanwang->data; - struct input_dev *input_dev = hanwang->dev; - struct usb_device *dev = hanwang->usbdev; - enum hanwang_tablet_type type = hanwang->features->type; - int i; - u16 x, y, p; - - switch (data[0]) { - case 0x02: /* data packet */ - switch (data[1]) { - case 0x80: /* tool prox out */ - hanwang->current_id = 0; - input_report_key(input_dev, hanwang->current_tool, 0); - break; - - case 0xc2: /* first time tool prox in */ - switch (data[3] & 0xf0) { - case 0x20: /* art_master III */ - case 0x30: /* art_master_HD */ - hanwang->current_id = STYLUS_DEVICE_ID; - hanwang->current_tool = BTN_TOOL_PEN; - input_report_key(input_dev, BTN_TOOL_PEN, 1); - break; - case 0xa0: /* art_master III */ - case 0xb0: /* art_master_HD */ - hanwang->current_id = ERASER_DEVICE_ID; - hanwang->current_tool = BTN_TOOL_RUBBER; - input_report_key(input_dev, BTN_TOOL_RUBBER, 1); - break; - default: - hanwang->current_id = 0; - dev_dbg(&dev->dev, - "unknown tablet tool %02x ", data[0]); - break; - } - break; - - default: /* tool data packet */ - x = (data[2] << 8) | data[3]; - y = (data[4] << 8) | data[5]; - - switch (type) { - case HANWANG_ART_MASTER_III: - p = (data[6] << 3) | - ((data[7] & 0xc0) >> 5) | - (data[1] & 0x01); - break; - - case HANWANG_ART_MASTER_HD: - p = (data[7] >> 6) | (data[6] << 2); - break; - - default: - p = 0; - break; - } - - input_report_abs(input_dev, ABS_X, - le16_to_cpup((__le16 *)&x)); - input_report_abs(input_dev, ABS_Y, - le16_to_cpup((__le16 *)&y)); - input_report_abs(input_dev, ABS_PRESSURE, - le16_to_cpup((__le16 *)&p)); - input_report_abs(input_dev, ABS_TILT_X, data[7] & 0x3f); - input_report_abs(input_dev, ABS_TILT_Y, data[8] & 0x7f); - input_report_key(input_dev, BTN_STYLUS, data[1] & 0x02); - input_report_key(input_dev, BTN_STYLUS2, data[1] & 0x04); - break; - } - input_report_abs(input_dev, ABS_MISC, hanwang->current_id); - input_event(input_dev, EV_MSC, MSC_SERIAL, - hanwang->features->pid); - break; - - case 0x0c: - /* roll wheel */ - hanwang->current_id = PAD_DEVICE_ID; - - switch (type) { - case HANWANG_ART_MASTER_III: - input_report_key(input_dev, BTN_TOOL_FINGER, data[1] || - data[2] || data[3]); - input_report_abs(input_dev, ABS_WHEEL, data[1]); - input_report_key(input_dev, BTN_0, data[2]); - for (i = 0; i < 8; i++) - input_report_key(input_dev, - BTN_1 + i, data[3] & (1 << i)); - break; - - case HANWANG_ART_MASTER_HD: - input_report_key(input_dev, BTN_TOOL_FINGER, data[1] || - data[2] || data[3] || data[4] || - data[5] || data[6]); - input_report_abs(input_dev, ABS_RX, - ((data[1] & 0x1f) << 8) | data[2]); - input_report_abs(input_dev, ABS_RY, - ((data[3] & 0x1f) << 8) | data[4]); - input_report_key(input_dev, BTN_0, data[5] & 0x01); - for (i = 0; i < 4; i++) { - input_report_key(input_dev, - BTN_1 + i, data[5] & (1 << i)); - input_report_key(input_dev, - BTN_5 + i, data[6] & (1 << i)); - } - break; - } - - input_report_abs(input_dev, ABS_MISC, hanwang->current_id); - input_event(input_dev, EV_MSC, MSC_SERIAL, 0xffffffff); - break; - - default: - dev_dbg(&dev->dev, "error packet %02x ", data[0]); - break; - } - - input_sync(input_dev); -} - -static void hanwang_irq(struct urb *urb) -{ - struct hanwang *hanwang = urb->context; - struct usb_device *dev = hanwang->usbdev; - int retval; - - switch (urb->status) { - case 0: - /* success */; - hanwang_parse_packet(hanwang); - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dev_err(&dev->dev, "%s - urb shutting down with status: %d", - __func__, urb->status); - return; - default: - dev_err(&dev->dev, "%s - nonzero urb status received: %d", - __func__, urb->status); - break; - } - - retval = usb_submit_urb(urb, GFP_ATOMIC); - if (retval) - dev_err(&dev->dev, "%s - usb_submit_urb failed with result %d", - __func__, retval); -} - -static int hanwang_open(struct input_dev *dev) -{ - struct hanwang *hanwang = input_get_drvdata(dev); - - hanwang->irq->dev = hanwang->usbdev; - if (usb_submit_urb(hanwang->irq, GFP_KERNEL)) - return -EIO; - - return 0; -} - -static void hanwang_close(struct input_dev *dev) -{ - struct hanwang *hanwang = input_get_drvdata(dev); - - usb_kill_urb(hanwang->irq); -} - -static bool get_features(struct usb_device *dev, struct hanwang *hanwang) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(features_array); i++) { - if (le16_to_cpu(dev->descriptor.idProduct) == - features_array[i].pid) { - hanwang->features = &features_array[i]; - return true; - } - } - - return false; -} - - -static int hanwang_probe(struct usb_interface *intf, const struct usb_device_id *id) -{ - struct usb_device *dev = interface_to_usbdev(intf); - struct usb_endpoint_descriptor *endpoint; - struct hanwang *hanwang; - struct input_dev *input_dev; - int error; - int i; - - hanwang = kzalloc(sizeof(struct hanwang), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!hanwang || !input_dev) { - error = -ENOMEM; - goto fail1; - } - - if (!get_features(dev, hanwang)) { - error = -ENXIO; - goto fail1; - } - - hanwang->data = usb_alloc_coherent(dev, hanwang->features->pkg_len, - GFP_KERNEL, &hanwang->data_dma); - if (!hanwang->data) { - error = -ENOMEM; - goto fail1; - } - - hanwang->irq = usb_alloc_urb(0, GFP_KERNEL); - if (!hanwang->irq) { - error = -ENOMEM; - goto fail2; - } - - hanwang->usbdev = dev; - hanwang->dev = input_dev; - - usb_make_path(dev, hanwang->phys, sizeof(hanwang->phys)); - strlcat(hanwang->phys, "/input0", sizeof(hanwang->phys)); - - strlcpy(hanwang->name, hanwang->features->name, sizeof(hanwang->name)); - input_dev->name = hanwang->name; - input_dev->phys = hanwang->phys; - usb_to_input_id(dev, &input_dev->id); - input_dev->dev.parent = &intf->dev; - - input_set_drvdata(input_dev, hanwang); - - input_dev->open = hanwang_open; - input_dev->close = hanwang_close; - - for (i = 0; i < ARRAY_SIZE(hw_eventtypes); ++i) - __set_bit(hw_eventtypes[i], input_dev->evbit); - - for (i = 0; i < ARRAY_SIZE(hw_absevents); ++i) - __set_bit(hw_absevents[i], input_dev->absbit); - - for (i = 0; i < ARRAY_SIZE(hw_btnevents); ++i) - __set_bit(hw_btnevents[i], input_dev->keybit); - - for (i = 0; i < ARRAY_SIZE(hw_mscevents); ++i) - __set_bit(hw_mscevents[i], input_dev->mscbit); - - input_set_abs_params(input_dev, ABS_X, - 0, hanwang->features->max_x, 4, 0); - input_set_abs_params(input_dev, ABS_Y, - 0, hanwang->features->max_y, 4, 0); - input_set_abs_params(input_dev, ABS_TILT_X, - 0, hanwang->features->max_tilt_x, 0, 0); - input_set_abs_params(input_dev, ABS_TILT_Y, - 0, hanwang->features->max_tilt_y, 0, 0); - input_set_abs_params(input_dev, ABS_PRESSURE, - 0, hanwang->features->max_pressure, 0, 0); - - endpoint = &intf->cur_altsetting->endpoint[0].desc; - usb_fill_int_urb(hanwang->irq, dev, - usb_rcvintpipe(dev, endpoint->bEndpointAddress), - hanwang->data, hanwang->features->pkg_len, - hanwang_irq, hanwang, endpoint->bInterval); - hanwang->irq->transfer_dma = hanwang->data_dma; - hanwang->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - - error = input_register_device(hanwang->dev); - if (error) - goto fail3; - - usb_set_intfdata(intf, hanwang); - - return 0; - - fail3: usb_free_urb(hanwang->irq); - fail2: usb_free_coherent(dev, hanwang->features->pkg_len, - hanwang->data, hanwang->data_dma); - fail1: input_free_device(input_dev); - kfree(hanwang); - return error; - -} - -static void hanwang_disconnect(struct usb_interface *intf) -{ - struct hanwang *hanwang = usb_get_intfdata(intf); - - input_unregister_device(hanwang->dev); - usb_free_urb(hanwang->irq); - usb_free_coherent(interface_to_usbdev(intf), - hanwang->features->pkg_len, hanwang->data, - hanwang->data_dma); - kfree(hanwang); - usb_set_intfdata(intf, NULL); -} - -static const struct usb_device_id hanwang_ids[] = { - { HANWANG_TABLET_DEVICE(USB_VENDOR_ID_HANWANG, HANWANG_TABLET_INT_CLASS, - HANWANG_TABLET_INT_SUB_CLASS, HANWANG_TABLET_INT_PROTOCOL) }, - {} -}; - -MODULE_DEVICE_TABLE(usb, hanwang_ids); - -static struct usb_driver hanwang_driver = { - .name = "hanwang", - .probe = hanwang_probe, - .disconnect = hanwang_disconnect, - .id_table = hanwang_ids, -}; - -static int __init hanwang_init(void) -{ - return usb_register(&hanwang_driver); -} - -static void __exit hanwang_exit(void) -{ - usb_deregister(&hanwang_driver); -} - -module_init(hanwang_init); -module_exit(hanwang_exit); diff --git a/trunk/drivers/input/tablet/wacom.h b/trunk/drivers/input/tablet/wacom.h index de5adb109030..284dfaab6b8c 100644 --- a/trunk/drivers/input/tablet/wacom.h +++ b/trunk/drivers/input/tablet/wacom.h @@ -118,7 +118,6 @@ struct wacom { extern const struct usb_device_id wacom_ids[]; void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len); -void wacom_setup_device_quirks(struct wacom_features *features); void wacom_setup_input_capabilities(struct input_dev *input_dev, struct wacom_wac *wacom_wac); #endif diff --git a/trunk/drivers/input/tablet/wacom_sys.c b/trunk/drivers/input/tablet/wacom_sys.c index fc381498b798..b35876ee6908 100644 --- a/trunk/drivers/input/tablet/wacom_sys.c +++ b/trunk/drivers/input/tablet/wacom_sys.c @@ -120,16 +120,14 @@ static int wacom_open(struct input_dev *dev) out: mutex_unlock(&wacom->lock); - usb_autopm_put_interface(wacom->intf); + if (retval) + usb_autopm_put_interface(wacom->intf); return retval; } static void wacom_close(struct input_dev *dev) { struct wacom *wacom = input_get_drvdata(dev); - int autopm_error; - - autopm_error = usb_autopm_get_interface(wacom->intf); mutex_lock(&wacom->lock); usb_kill_urb(wacom->irq); @@ -137,8 +135,7 @@ static void wacom_close(struct input_dev *dev) wacom->intf->needs_remote_wakeup = 0; mutex_unlock(&wacom->lock); - if (!autopm_error) - usb_autopm_put_interface(wacom->intf); + usb_autopm_put_interface(wacom->intf); } static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hid_desc, @@ -199,30 +196,17 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi features->pktlen = WACOM_PKGLEN_TPC2FG; features->device_type = BTN_TOOL_TRIPLETAP; } - if (features->type == BAMBOO_PT) { - /* need to reset back */ - features->pktlen = WACOM_PKGLEN_BBTOUCH; - features->device_type = BTN_TOOL_TRIPLETAP; - features->x_phy = - get_unaligned_le16(&report[i + 5]); - features->x_max = - get_unaligned_le16(&report[i + 8]); - i += 15; - } else { - features->x_max = - get_unaligned_le16(&report[i + 3]); - features->x_phy = - get_unaligned_le16(&report[i + 6]); - features->unit = report[i + 9]; - features->unitExpo = report[i + 11]; - i += 12; - } + features->x_max = + get_unaligned_le16(&report[i + 3]); + features->x_phy = + get_unaligned_le16(&report[i + 6]); + features->unit = report[i + 9]; + features->unitExpo = report[i + 11]; + i += 12; } else if (pen) { /* penabled only accepts exact bytes of data */ if (features->type == TABLETPC2FG) features->pktlen = WACOM_PKGLEN_GRAPHIRE; - if (features->type == BAMBOO_PT) - features->pktlen = WACOM_PKGLEN_BBFUN; features->device_type = BTN_TOOL_PEN; features->x_max = get_unaligned_le16(&report[i + 3]); @@ -251,15 +235,6 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi features->y_phy = get_unaligned_le16(&report[i + 6]); i += 7; - } else if (features->type == BAMBOO_PT) { - /* need to reset back */ - features->pktlen = WACOM_PKGLEN_BBTOUCH; - features->device_type = BTN_TOOL_TRIPLETAP; - features->y_phy = - get_unaligned_le16(&report[i + 3]); - features->y_max = - get_unaligned_le16(&report[i + 6]); - i += 12; } else { features->y_max = features->x_max; @@ -271,8 +246,6 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi /* penabled only accepts exact bytes of data */ if (features->type == TABLETPC2FG) features->pktlen = WACOM_PKGLEN_GRAPHIRE; - if (features->type == BAMBOO_PT) - features->pktlen = WACOM_PKGLEN_BBFUN; features->device_type = BTN_TOOL_PEN; features->y_max = get_unaligned_le16(&report[i + 3]); @@ -323,9 +296,8 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat if (!rep_data) return error; - /* ask to report tablet data if it is 2FGT Tablet PC or - * not a Tablet PC */ - if (features->type == TABLETPC2FG) { + /* ask to report tablet data if it is 2FGT or not a Tablet PC */ + if (features->device_type == BTN_TOOL_TRIPLETAP) { do { rep_data[0] = 3; rep_data[1] = 4; @@ -337,7 +309,7 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat WAC_HID_FEATURE_REPORT, report_id, rep_data, 3); } while ((error < 0 || rep_data[1] != 4) && limit++ < 5); - } else if (features->type != TABLETPC) { + } else if (features->type != TABLETPC && features->type != TABLETPC2FG) { do { rep_data[0] = 2; rep_data[1] = 2; @@ -362,16 +334,11 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf, struct usb_host_interface *interface = intf->cur_altsetting; struct hid_descriptor *hid_desc; - /* default features */ + /* default device to penabled */ features->device_type = BTN_TOOL_PEN; - features->x_fuzz = 4; - features->y_fuzz = 4; - features->pressure_fuzz = 0; - features->distance_fuzz = 0; - - /* only Tablet PCs and Bamboo P&T need to retrieve the info */ - if ((features->type != TABLETPC) && (features->type != TABLETPC2FG) && - (features->type != BAMBOO_PT)) + + /* only Tablet PCs need to retrieve the info */ + if ((features->type != TABLETPC) && (features->type != TABLETPC2FG)) goto out; if (usb_get_extra_descriptor(interface, HID_DEVICET_HID, &hid_desc)) { @@ -386,6 +353,12 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf, if (error) goto out; + /* touch device found but size is not defined. use default */ + if (features->device_type == BTN_TOOL_DOUBLETAP && !features->x_max) { + features->x_max = 1023; + features->y_max = 1023; + } + out: return error; } @@ -521,11 +494,9 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i if (error) goto fail2; - wacom_setup_device_quirks(features); - strlcpy(wacom_wac->name, features->name, sizeof(wacom_wac->name)); - if (features->quirks & WACOM_QUIRK_MULTI_INPUT) { + if (features->type == TABLETPC || features->type == TABLETPC2FG) { /* Append the device type to the name */ strlcat(wacom_wac->name, features->device_type == BTN_TOOL_PEN ? diff --git a/trunk/drivers/input/tablet/wacom_wac.c b/trunk/drivers/input/tablet/wacom_wac.c index b3252ef1e279..47fd7a041c52 100644 --- a/trunk/drivers/input/tablet/wacom_wac.c +++ b/trunk/drivers/input/tablet/wacom_wac.c @@ -857,134 +857,6 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len) return retval; } -static int wacom_bpt_touch(struct wacom_wac *wacom) -{ - struct wacom_features *features = &wacom->features; - struct input_dev *input = wacom->input; - unsigned char *data = wacom->data; - int sp = 0, sx = 0, sy = 0, count = 0; - int i; - - for (i = 0; i < 2; i++) { - int p = data[9 * i + 2]; - input_mt_slot(input, i); - /* - * Touch events need to be disabled while stylus is - * in proximity because user's hand is resting on touchpad - * and sending unwanted events. User expects tablet buttons - * to continue working though. - */ - if (p && !wacom->shared->stylus_in_proximity) { - int x = get_unaligned_be16(&data[9 * i + 3]) & 0x7ff; - int y = get_unaligned_be16(&data[9 * i + 5]) & 0x7ff; - if (features->quirks & WACOM_QUIRK_BBTOUCH_LOWRES) { - x <<= 5; - y <<= 5; - } - input_report_abs(input, ABS_MT_PRESSURE, p); - input_report_abs(input, ABS_MT_POSITION_X, x); - input_report_abs(input, ABS_MT_POSITION_Y, y); - if (wacom->id[i] < 0) - wacom->id[i] = wacom->trk_id++ & MAX_TRACKING_ID; - if (!count++) - sp = p, sx = x, sy = y; - } else { - wacom->id[i] = -1; - } - input_report_abs(input, ABS_MT_TRACKING_ID, wacom->id[i]); - } - - input_report_key(input, BTN_TOUCH, count > 0); - input_report_key(input, BTN_TOOL_FINGER, count == 1); - input_report_key(input, BTN_TOOL_DOUBLETAP, count == 2); - - input_report_abs(input, ABS_PRESSURE, sp); - input_report_abs(input, ABS_X, sx); - input_report_abs(input, ABS_Y, sy); - - input_report_key(input, BTN_LEFT, (data[1] & 0x08) != 0); - input_report_key(input, BTN_FORWARD, (data[1] & 0x04) != 0); - input_report_key(input, BTN_BACK, (data[1] & 0x02) != 0); - input_report_key(input, BTN_RIGHT, (data[1] & 0x01) != 0); - - input_sync(input); - - return 0; -} - -static int wacom_bpt_pen(struct wacom_wac *wacom) -{ - struct input_dev *input = wacom->input; - unsigned char *data = wacom->data; - int prox = 0, x = 0, y = 0, p = 0, d = 0, pen = 0, btn1 = 0, btn2 = 0; - - /* - * Similar to Graphire protocol, data[1] & 0x20 is proximity and - * data[1] & 0x18 is tool ID. 0x30 is safety check to ignore - * 2 unused tool ID's. - */ - prox = (data[1] & 0x30) == 0x30; - - /* - * All reports shared between PEN and RUBBER tool must be - * forced to a known starting value (zero) when transitioning to - * out-of-prox. - * - * If not reset then, to userspace, it will look like lost events - * if new tool comes in-prox with same values as previous tool sent. - * - * Hardware does report zero in most out-of-prox cases but not all. - */ - if (prox) { - if (!wacom->shared->stylus_in_proximity) { - if (data[1] & 0x08) { - wacom->tool[0] = BTN_TOOL_RUBBER; - wacom->id[0] = ERASER_DEVICE_ID; - } else { - wacom->tool[0] = BTN_TOOL_PEN; - wacom->id[0] = STYLUS_DEVICE_ID; - } - wacom->shared->stylus_in_proximity = true; - } - x = le16_to_cpup((__le16 *)&data[2]); - y = le16_to_cpup((__le16 *)&data[4]); - p = le16_to_cpup((__le16 *)&data[6]); - d = data[8]; - pen = data[1] & 0x01; - btn1 = data[1] & 0x02; - btn2 = data[1] & 0x04; - } - - input_report_key(input, BTN_TOUCH, pen); - input_report_key(input, BTN_STYLUS, btn1); - input_report_key(input, BTN_STYLUS2, btn2); - - input_report_abs(input, ABS_X, x); - input_report_abs(input, ABS_Y, y); - input_report_abs(input, ABS_PRESSURE, p); - input_report_abs(input, ABS_DISTANCE, d); - - if (!prox) { - wacom->id[0] = 0; - wacom->shared->stylus_in_proximity = false; - } - - input_report_key(input, wacom->tool[0], prox); /* PEN or RUBBER */ - input_report_abs(input, ABS_MISC, wacom->id[0]); /* TOOL ID */ - - return 1; -} - -static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len) -{ - if (len == WACOM_PKGLEN_BBTOUCH) - return wacom_bpt_touch(wacom); - else if (len == WACOM_PKGLEN_BBFUN) - return wacom_bpt_pen(wacom); - - return 0; -} - void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) { bool sync; @@ -1030,10 +902,6 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) sync = wacom_tpc_irq(wacom_wac, len); break; - case BAMBOO_PT: - sync = wacom_bpt_irq(wacom_wac, len); - break; - default: sync = false; break; @@ -1043,17 +911,26 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) input_sync(wacom_wac->input); } -static void wacom_setup_cintiq(struct wacom_wac *wacom_wac) +static void wacom_setup_intuos(struct wacom_wac *wacom_wac) { struct input_dev *input_dev = wacom_wac->input; input_set_capability(input_dev, EV_MSC, MSC_SERIAL); + input_set_capability(input_dev, EV_REL, REL_WHEEL); + + __set_bit(BTN_LEFT, input_dev->keybit); + __set_bit(BTN_RIGHT, input_dev->keybit); + __set_bit(BTN_MIDDLE, input_dev->keybit); + __set_bit(BTN_SIDE, input_dev->keybit); + __set_bit(BTN_EXTRA, input_dev->keybit); __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); __set_bit(BTN_TOOL_PEN, input_dev->keybit); + __set_bit(BTN_TOOL_MOUSE, input_dev->keybit); __set_bit(BTN_TOOL_BRUSH, input_dev->keybit); __set_bit(BTN_TOOL_PENCIL, input_dev->keybit); __set_bit(BTN_TOOL_AIRBRUSH, input_dev->keybit); + __set_bit(BTN_TOOL_LENS, input_dev->keybit); __set_bit(BTN_STYLUS, input_dev->keybit); __set_bit(BTN_STYLUS2, input_dev->keybit); @@ -1062,55 +939,10 @@ static void wacom_setup_cintiq(struct wacom_wac *wacom_wac) input_set_abs_params(input_dev, ABS_WHEEL, 0, 1023, 0, 0); input_set_abs_params(input_dev, ABS_TILT_X, 0, 127, 0, 0); input_set_abs_params(input_dev, ABS_TILT_Y, 0, 127, 0, 0); -} - -static void wacom_setup_intuos(struct wacom_wac *wacom_wac) -{ - struct input_dev *input_dev = wacom_wac->input; - - input_set_capability(input_dev, EV_REL, REL_WHEEL); - - wacom_setup_cintiq(wacom_wac); - - __set_bit(BTN_LEFT, input_dev->keybit); - __set_bit(BTN_RIGHT, input_dev->keybit); - __set_bit(BTN_MIDDLE, input_dev->keybit); - __set_bit(BTN_SIDE, input_dev->keybit); - __set_bit(BTN_EXTRA, input_dev->keybit); - __set_bit(BTN_TOOL_MOUSE, input_dev->keybit); - __set_bit(BTN_TOOL_LENS, input_dev->keybit); - input_set_abs_params(input_dev, ABS_RZ, -900, 899, 0, 0); input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0); } -void wacom_setup_device_quirks(struct wacom_features *features) -{ - - /* touch device found but size is not defined. use default */ - if (features->device_type == BTN_TOOL_DOUBLETAP && !features->x_max) { - features->x_max = 1023; - features->y_max = 1023; - } - - /* these device have multiple inputs */ - if (features->type == TABLETPC || features->type == TABLETPC2FG || - features->type == BAMBOO_PT) - features->quirks |= WACOM_QUIRK_MULTI_INPUT; - - /* quirks for bamboo touch */ - if (features->type == BAMBOO_PT && - features->device_type == BTN_TOOL_TRIPLETAP) { - features->x_max <<= 5; - features->y_max <<= 5; - features->x_fuzz <<= 5; - features->y_fuzz <<= 5; - features->pressure_max = 256; - features->pressure_fuzz = 16; - features->quirks |= WACOM_QUIRK_BBTOUCH_LOWRES; - } -} - void wacom_setup_input_capabilities(struct input_dev *input_dev, struct wacom_wac *wacom_wac) { @@ -1121,12 +953,9 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev, __set_bit(BTN_TOUCH, input_dev->keybit); - input_set_abs_params(input_dev, ABS_X, 0, features->x_max, - features->x_fuzz, 0); - input_set_abs_params(input_dev, ABS_Y, 0, features->y_max, - features->y_fuzz, 0); - input_set_abs_params(input_dev, ABS_PRESSURE, 0, features->pressure_max, - features->pressure_fuzz, 0); + input_set_abs_params(input_dev, ABS_X, 0, features->x_max, 4, 0); + input_set_abs_params(input_dev, ABS_Y, 0, features->y_max, 4, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, features->pressure_max, 0, 0); __set_bit(ABS_MISC, input_dev->absbit); @@ -1176,19 +1005,9 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev, __set_bit(BTN_9, input_dev->keybit); /* fall through */ - case CINTIQ: - for (i = 0; i < 8; i++) - __set_bit(BTN_0 + i, input_dev->keybit); - __set_bit(BTN_TOOL_FINGER, input_dev->keybit); - - input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0); - input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0); - input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); - wacom_setup_cintiq(wacom_wac); - break; - case INTUOS3: case INTUOS3L: + case CINTIQ: __set_bit(BTN_4, input_dev->keybit); __set_bit(BTN_5, input_dev->keybit); __set_bit(BTN_6, input_dev->keybit); @@ -1259,38 +1078,6 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev, case PENPARTNER: __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); break; - - case BAMBOO_PT: - __clear_bit(ABS_MISC, input_dev->absbit); - - if (features->device_type == BTN_TOOL_TRIPLETAP) { - __set_bit(BTN_LEFT, input_dev->keybit); - __set_bit(BTN_FORWARD, input_dev->keybit); - __set_bit(BTN_BACK, input_dev->keybit); - __set_bit(BTN_RIGHT, input_dev->keybit); - - __set_bit(BTN_TOOL_FINGER, input_dev->keybit); - __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); - - input_mt_create_slots(input_dev, 2); - input_set_abs_params(input_dev, ABS_MT_POSITION_X, - 0, features->x_max, - features->x_fuzz, 0); - input_set_abs_params(input_dev, ABS_MT_POSITION_Y, - 0, features->y_max, - features->y_fuzz, 0); - input_set_abs_params(input_dev, ABS_MT_PRESSURE, - 0, features->pressure_max, - features->pressure_fuzz, 0); - input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, - MAX_TRACKING_ID, 0, 0); - } else if (features->device_type == BTN_TOOL_PEN) { - __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); - __set_bit(BTN_TOOL_PEN, input_dev->keybit); - __set_bit(BTN_STYLUS, input_dev->keybit); - __set_bit(BTN_STYLUS2, input_dev->keybit); - } - break; } } @@ -1428,14 +1215,6 @@ static const struct wacom_features wacom_features_0xE3 = { "Wacom ISDv4 E3", WACOM_PKGLEN_TPC2FG, 26202, 16325, 255, 0, TABLETPC2FG }; static const struct wacom_features wacom_features_0x47 = { "Wacom Intuos2 6x8", WACOM_PKGLEN_INTUOS, 20320, 16240, 1023, 31, INTUOS }; -static struct wacom_features wacom_features_0xD0 = - { "Wacom Bamboo 2FG", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT }; -static struct wacom_features wacom_features_0xD1 = - { "Wacom Bamboo 2FG 4x5", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT }; -static struct wacom_features wacom_features_0xD2 = - { "Wacom Bamboo Craft", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT }; -static struct wacom_features wacom_features_0xD3 = - { "Wacom Bamboo 2FG 6x8", WACOM_PKGLEN_BBFUN, 21648, 13530, 1023, 63, BAMBOO_PT }; #define USB_DEVICE_WACOM(prod) \ USB_DEVICE(USB_VENDOR_ID_WACOM, prod), \ @@ -1500,10 +1279,6 @@ const struct usb_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0xC6) }, { USB_DEVICE_WACOM(0xC7) }, { USB_DEVICE_WACOM(0xCE) }, - { USB_DEVICE_WACOM(0xD0) }, - { USB_DEVICE_WACOM(0xD1) }, - { USB_DEVICE_WACOM(0xD2) }, - { USB_DEVICE_WACOM(0xD3) }, { USB_DEVICE_WACOM(0xF0) }, { USB_DEVICE_WACOM(0xCC) }, { USB_DEVICE_WACOM(0x90) }, diff --git a/trunk/drivers/input/tablet/wacom_wac.h b/trunk/drivers/input/tablet/wacom_wac.h index 00ca01541d89..99e1a54cd305 100644 --- a/trunk/drivers/input/tablet/wacom_wac.h +++ b/trunk/drivers/input/tablet/wacom_wac.h @@ -21,7 +21,6 @@ #define WACOM_PKGLEN_INTUOS 10 #define WACOM_PKGLEN_TPC1FG 5 #define WACOM_PKGLEN_TPC2FG 14 -#define WACOM_PKGLEN_BBTOUCH 20 /* device IDs */ #define STYLUS_DEVICE_ID 0x02 @@ -38,13 +37,6 @@ #define WACOM_REPORT_TPC1FG 6 #define WACOM_REPORT_TPC2FG 13 -/* device quirks */ -#define WACOM_QUIRK_MULTI_INPUT 0x0001 -#define WACOM_QUIRK_BBTOUCH_LOWRES 0x0002 - -/* largest reported tracking id */ -#define MAX_TRACKING_ID 0xfff - enum { PENPARTNER = 0, GRAPHIRE, @@ -52,7 +44,6 @@ enum { PTU, PL, DTU, - BAMBOO_PT, INTUOS, INTUOS3S, INTUOS3, @@ -82,11 +73,6 @@ struct wacom_features { int y_phy; unsigned char unit; unsigned char unitExpo; - int x_fuzz; - int y_fuzz; - int pressure_fuzz; - int distance_fuzz; - unsigned quirks; }; struct wacom_shared { @@ -100,7 +86,6 @@ struct wacom_wac { int id[3]; __u32 serial[2]; int last_finger; - int trk_id; struct wacom_features features; struct wacom_shared *shared; struct input_dev *input; diff --git a/trunk/drivers/input/touchscreen/Kconfig b/trunk/drivers/input/touchscreen/Kconfig index 0ea361f23fa7..0069d9703fda 100644 --- a/trunk/drivers/input/touchscreen/Kconfig +++ b/trunk/drivers/input/touchscreen/Kconfig @@ -98,18 +98,6 @@ config TOUCHSCREEN_BITSY To compile this driver as a module, choose M here: the module will be called h3600_ts_input. -config TOUCHSCREEN_BU21013 - tristate "BU21013 based touch panel controllers" - depends on I2C - help - Say Y here if you have a bu21013 touchscreen connected to - your system. - - If unsure, say N. - - To compile this driver as a module, choose M here: the - module will be called bu21013_ts. - config TOUCHSCREEN_CY8CTMG110 tristate "cy8ctmg110 touchscreen" depends on I2C @@ -226,16 +214,6 @@ config TOUCHSCREEN_WACOM_W8001 To compile this driver as a module, choose M here: the module will be called wacom_w8001. -config TOUCHSCREEN_LPC32XX - tristate "LPC32XX touchscreen controller" - depends on ARCH_LPC32XX - help - Say Y here if you have a LPC32XX device and want - to support the built-in touchscreen. - - To compile this driver as a module, choose M here: the - module will be called lpc32xx_ts. - config TOUCHSCREEN_MCS5000 tristate "MELFAS MCS-5000 touchscreen" depends on I2C @@ -272,18 +250,6 @@ config TOUCHSCREEN_INEXIO To compile this driver as a module, choose M here: the module will be called inexio. -config TOUCHSCREEN_INTEL_MID - tristate "Intel MID platform resistive touchscreen" - depends on INTEL_SCU_IPC - help - Say Y here if you have a Intel MID based touchscreen in - your system. - - If unsure, say N. - - To compile this driver as a module, choose M here: the - module will be called intel_mid_touch. - config TOUCHSCREEN_MK712 tristate "ICS MicroClock MK712 touchscreen" help diff --git a/trunk/drivers/input/touchscreen/Makefile b/trunk/drivers/input/touchscreen/Makefile index 99b353c4e9ae..28217e1dcafd 100644 --- a/trunk/drivers/input/touchscreen/Makefile +++ b/trunk/drivers/input/touchscreen/Makefile @@ -14,7 +14,6 @@ obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI) += ad7879-spi.o obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o -obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o @@ -24,8 +23,6 @@ obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o -obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o -obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o diff --git a/trunk/drivers/input/touchscreen/ad7877.c b/trunk/drivers/input/touchscreen/ad7877.c index a1952fcc083e..5f0221cffef9 100644 --- a/trunk/drivers/input/touchscreen/ad7877.c +++ b/trunk/drivers/input/touchscreen/ad7877.c @@ -191,12 +191,13 @@ struct ad7877 { struct spi_message msg; struct mutex mutex; - bool disabled; /* P: mutex */ - bool gpio3; /* P: mutex */ - bool gpio4; /* P: mutex */ + unsigned disabled:1; /* P: mutex */ + unsigned gpio3:1; /* P: mutex */ + unsigned gpio4:1; /* P: mutex */ spinlock_t lock; struct timer_list timer; /* P: lock */ + unsigned pending:1; /* P: lock */ /* * DMA (thus cache coherency maintenance) requires the @@ -205,8 +206,8 @@ struct ad7877 { u16 conversion_data[AD7877_NR_SENSE] ____cacheline_aligned; }; -static bool gpio3; -module_param(gpio3, bool, 0); +static int gpio3; +module_param(gpio3, int, 0); MODULE_PARM_DESC(gpio3, "If gpio3 is set to 1 AUX3 acts as GPIO3"); /* @@ -229,7 +230,6 @@ static int ad7877_read(struct spi_device *spi, u16 reg) AD7877_READADD(reg)); req->xfer[0].tx_buf = &req->command; req->xfer[0].len = 2; - req->xfer[0].cs_change = 1; req->xfer[1].rx_buf = &req->sample; req->xfer[1].len = 2; @@ -295,25 +295,20 @@ static int ad7877_read_adc(struct spi_device *spi, unsigned command) req->xfer[0].tx_buf = &req->reset; req->xfer[0].len = 2; - req->xfer[0].cs_change = 1; req->xfer[1].tx_buf = &req->ref_on; req->xfer[1].len = 2; req->xfer[1].delay_usecs = ts->vref_delay_usecs; - req->xfer[1].cs_change = 1; req->xfer[2].tx_buf = &req->command; req->xfer[2].len = 2; req->xfer[2].delay_usecs = ts->vref_delay_usecs; - req->xfer[2].cs_change = 1; req->xfer[3].rx_buf = &req->sample; req->xfer[3].len = 2; - req->xfer[3].cs_change = 1; req->xfer[4].tx_buf = &ts->cmd_crtl2; /*REF OFF*/ req->xfer[4].len = 2; - req->xfer[4].cs_change = 1; req->xfer[5].tx_buf = &ts->cmd_crtl1; /*DEFAULT*/ req->xfer[5].len = 2; @@ -332,7 +327,7 @@ static int ad7877_read_adc(struct spi_device *spi, unsigned command) return status ? : sample; } -static int ad7877_process_data(struct ad7877 *ts) +static void ad7877_rx(struct ad7877 *ts) { struct input_dev *input_dev = ts->input; unsigned Rt; @@ -359,25 +354,11 @@ static int ad7877_process_data(struct ad7877 *ts) Rt /= z1; Rt = (Rt + 2047) >> 12; - /* - * Sample found inconsistent, pressure is beyond - * the maximum. Don't report it to user space. - */ - if (Rt > ts->pressure_max) - return -EINVAL; - - if (!timer_pending(&ts->timer)) - input_report_key(input_dev, BTN_TOUCH, 1); - input_report_abs(input_dev, ABS_X, x); input_report_abs(input_dev, ABS_Y, y); input_report_abs(input_dev, ABS_PRESSURE, Rt); input_sync(input_dev); - - return 0; } - - return -EINVAL; } static inline void ad7877_ts_event_release(struct ad7877 *ts) @@ -385,56 +366,72 @@ static inline void ad7877_ts_event_release(struct ad7877 *ts) struct input_dev *input_dev = ts->input; input_report_abs(input_dev, ABS_PRESSURE, 0); - input_report_key(input_dev, BTN_TOUCH, 0); input_sync(input_dev); } static void ad7877_timer(unsigned long handle) { struct ad7877 *ts = (void *)handle; - unsigned long flags; - spin_lock_irqsave(&ts->lock, flags); ad7877_ts_event_release(ts); - spin_unlock_irqrestore(&ts->lock, flags); } static irqreturn_t ad7877_irq(int irq, void *handle) { struct ad7877 *ts = handle; unsigned long flags; - int error; + int status; - error = spi_sync(ts->spi, &ts->msg); - if (error) { - dev_err(&ts->spi->dev, "spi_sync --> %d\n", error); - goto out; - } + /* + * The repeated conversion sequencer controlled by TMR kicked off + * too fast. We ignore the last and process the sample sequence + * currently in the queue. It can't be older than 9.4ms, and we + * need to avoid that ts->msg doesn't get issued twice while in work. + */ spin_lock_irqsave(&ts->lock, flags); - error = ad7877_process_data(ts); - if (!error) - mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT); + if (!ts->pending) { + ts->pending = 1; + + status = spi_async(ts->spi, &ts->msg); + if (status) + dev_err(&ts->spi->dev, "spi_sync --> %d\n", status); + } spin_unlock_irqrestore(&ts->lock, flags); -out: return IRQ_HANDLED; } +static void ad7877_callback(void *_ts) +{ + struct ad7877 *ts = _ts; + + spin_lock_irq(&ts->lock); + + ad7877_rx(ts); + ts->pending = 0; + mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT); + + spin_unlock_irq(&ts->lock); +} + static void ad7877_disable(struct ad7877 *ts) { mutex_lock(&ts->mutex); if (!ts->disabled) { - ts->disabled = true; + ts->disabled = 1; disable_irq(ts->spi->irq); + /* Wait for spi_async callback */ + while (ts->pending) + msleep(1); + if (del_timer_sync(&ts->timer)) ad7877_ts_event_release(ts); } - /* - * We know the chip's in lowpower mode since we always + /* we know the chip's in lowpower mode since we always * leave it that way after every request */ @@ -446,7 +443,7 @@ static void ad7877_enable(struct ad7877 *ts) mutex_lock(&ts->mutex); if (ts->disabled) { - ts->disabled = false; + ts->disabled = 0; enable_irq(ts->spi->irq); } @@ -456,7 +453,7 @@ static void ad7877_enable(struct ad7877 *ts) #define SHOW(name) static ssize_t \ name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \ { \ - struct ad7877 *ts = dev_get_drvdata(dev); \ + struct ad7877 *ts = dev_get_drvdata(dev); \ ssize_t v = ad7877_read_adc(ts->spi, \ AD7877_READ_CHAN(name)); \ if (v < 0) \ @@ -476,7 +473,7 @@ SHOW(temp2) static ssize_t ad7877_disable_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct ad7877 *ts = dev_get_drvdata(dev); + struct ad7877 *ts = dev_get_drvdata(dev); return sprintf(buf, "%u\n", ts->disabled); } @@ -506,7 +503,7 @@ static DEVICE_ATTR(disable, 0664, ad7877_disable_show, ad7877_disable_store); static ssize_t ad7877_dac_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct ad7877 *ts = dev_get_drvdata(dev); + struct ad7877 *ts = dev_get_drvdata(dev); return sprintf(buf, "%u\n", ts->dac); } @@ -536,7 +533,7 @@ static DEVICE_ATTR(dac, 0664, ad7877_dac_show, ad7877_dac_store); static ssize_t ad7877_gpio3_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct ad7877 *ts = dev_get_drvdata(dev); + struct ad7877 *ts = dev_get_drvdata(dev); return sprintf(buf, "%u\n", ts->gpio3); } @@ -567,7 +564,7 @@ static DEVICE_ATTR(gpio3, 0664, ad7877_gpio3_show, ad7877_gpio3_store); static ssize_t ad7877_gpio4_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct ad7877 *ts = dev_get_drvdata(dev); + struct ad7877 *ts = dev_get_drvdata(dev); return sprintf(buf, "%u\n", ts->gpio4); } @@ -600,35 +597,16 @@ static struct attribute *ad7877_attributes[] = { &dev_attr_temp2.attr, &dev_attr_aux1.attr, &dev_attr_aux2.attr, - &dev_attr_aux3.attr, &dev_attr_bat1.attr, &dev_attr_bat2.attr, &dev_attr_disable.attr, &dev_attr_dac.attr, - &dev_attr_gpio3.attr, &dev_attr_gpio4.attr, NULL }; -static mode_t ad7877_attr_is_visible(struct kobject *kobj, - struct attribute *attr, int n) -{ - mode_t mode = attr->mode; - - if (attr == &dev_attr_aux3.attr) { - if (gpio3) - mode = 0; - } else if (attr == &dev_attr_gpio3.attr) { - if (!gpio3) - mode = 0; - } - - return mode; -} - static const struct attribute_group ad7877_attr_group = { - .is_visible = ad7877_attr_is_visible, - .attrs = ad7877_attributes, + .attrs = ad7877_attributes, }; static void ad7877_setup_ts_def_msg(struct spi_device *spi, struct ad7877 *ts) @@ -657,25 +635,22 @@ static void ad7877_setup_ts_def_msg(struct spi_device *spi, struct ad7877 *ts) spi_message_init(m); + m->complete = ad7877_callback; m->context = ts; ts->xfer[0].tx_buf = &ts->cmd_crtl1; ts->xfer[0].len = 2; - ts->xfer[0].cs_change = 1; spi_message_add_tail(&ts->xfer[0], m); ts->xfer[1].tx_buf = &ts->cmd_dummy; /* Send ZERO */ ts->xfer[1].len = 2; - ts->xfer[1].cs_change = 1; spi_message_add_tail(&ts->xfer[1], m); - for (i = 0; i < AD7877_NR_SENSE; i++) { + for (i = 0; i < 11; i++) { ts->xfer[i + 2].rx_buf = &ts->conversion_data[AD7877_SEQ_YPOS + i]; ts->xfer[i + 2].len = 2; - if (i < (AD7877_NR_SENSE - 1)) - ts->xfer[i + 2].cs_change = 1; spi_message_add_tail(&ts->xfer[i + 2], m); } } @@ -743,8 +718,6 @@ static int __devinit ad7877_probe(struct spi_device *spi) input_dev->phys = ts->phys; input_dev->dev.parent = &spi->dev; - __set_bit(EV_KEY, input_dev->evbit); - __set_bit(BTN_TOUCH, input_dev->keybit); __set_bit(EV_ABS, input_dev->evbit); __set_bit(ABS_X, input_dev->absbit); __set_bit(ABS_Y, input_dev->absbit); @@ -779,9 +752,8 @@ static int __devinit ad7877_probe(struct spi_device *spi) /* Request AD7877 /DAV GPIO interrupt */ - err = request_threaded_irq(spi->irq, NULL, ad7877_irq, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - spi->dev.driver->name, ts); + err = request_irq(spi->irq, ad7877_irq, IRQF_TRIGGER_FALLING, + spi->dev.driver->name, ts); if (err) { dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq); goto err_free_mem; @@ -791,12 +763,20 @@ static int __devinit ad7877_probe(struct spi_device *spi) if (err) goto err_free_irq; - err = input_register_device(input_dev); + err = device_create_file(&spi->dev, + gpio3 ? &dev_attr_gpio3 : &dev_attr_aux3); if (err) goto err_remove_attr_group; + err = input_register_device(input_dev); + if (err) + goto err_remove_attr; + return 0; +err_remove_attr: + device_remove_file(&spi->dev, + gpio3 ? &dev_attr_gpio3 : &dev_attr_aux3); err_remove_attr_group: sysfs_remove_group(&spi->dev.kobj, &ad7877_attr_group); err_free_irq: @@ -810,9 +790,11 @@ static int __devinit ad7877_probe(struct spi_device *spi) static int __devexit ad7877_remove(struct spi_device *spi) { - struct ad7877 *ts = dev_get_drvdata(&spi->dev); + struct ad7877 *ts = dev_get_drvdata(&spi->dev); sysfs_remove_group(&spi->dev.kobj, &ad7877_attr_group); + device_remove_file(&spi->dev, + gpio3 ? &dev_attr_gpio3 : &dev_attr_aux3); ad7877_disable(ts); free_irq(ts->spi->irq, ts); diff --git a/trunk/drivers/input/touchscreen/ads7846.c b/trunk/drivers/input/touchscreen/ads7846.c index 14ea54b78e46..16031933a8f6 100644 --- a/trunk/drivers/input/touchscreen/ads7846.c +++ b/trunk/drivers/input/touchscreen/ads7846.c @@ -17,11 +17,9 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -#include #include #include #include -#include #include #include #include @@ -54,23 +52,22 @@ * files. */ -#define TS_POLL_DELAY 1 /* ms delay before the first sample */ -#define TS_POLL_PERIOD 5 /* ms delay between samples */ +#define TS_POLL_DELAY (1 * 1000000) /* ns delay before the first sample */ +#define TS_POLL_PERIOD (5 * 1000000) /* ns delay between samples */ /* this driver doesn't aim at the peak continuous sample rate */ #define SAMPLE_BITS (8 /*cmd*/ + 16 /*sample*/ + 2 /* before, after */) struct ts_event { - /* - * For portability, we can't read 12 bit values using SPI (which - * would make the controller deliver them as native byte order u16 + /* For portability, we can't read 12 bit values using SPI (which + * would make the controller deliver them as native byteorder u16 * with msbs zeroed). Instead, we read them as two 8-bit values, * *** WHICH NEED BYTESWAPPING *** and range adjustment. */ u16 x; u16 y; u16 z1, z2; - bool ignore; + int ignore; u8 x_buf[3]; u8 y_buf[3]; }; @@ -113,11 +110,8 @@ struct ads7846 { struct spi_transfer xfer[18]; struct spi_message msg[5]; - int msg_count; - wait_queue_head_t wait; - - bool pendown; - + struct spi_message *last_msg; + int msg_idx; int read_cnt; int read_rep; int last_read; @@ -128,10 +122,14 @@ struct ads7846 { u16 penirq_recheck_delay_usecs; - struct mutex lock; - bool stopped; /* P: lock */ - bool disabled; /* P: lock */ - bool suspended; /* P: lock */ + spinlock_t lock; + struct hrtimer timer; + unsigned pendown:1; /* P: lock */ + unsigned pending:1; /* P: lock */ +// FIXME remove "irq_disabled" + unsigned irq_disabled:1; /* P: lock */ + unsigned disabled:1; + unsigned is_suspended:1; int (*filter)(void *data, int data_idx, int *val); void *filter_data; @@ -167,7 +165,7 @@ struct ads7846 { #define ADS_12_BIT (0 << 3) #define ADS_SER (1 << 2) /* non-differential */ #define ADS_DFR (0 << 2) /* differential */ -#define ADS_PD10_PDOWN (0 << 0) /* low power mode + penirq */ +#define ADS_PD10_PDOWN (0 << 0) /* lowpower mode + penirq */ #define ADS_PD10_ADC_ON (1 << 0) /* ADC on */ #define ADS_PD10_REF_ON (2 << 0) /* vREF on + penirq */ #define ADS_PD10_ALL_ON (3 << 0) /* ADC + vREF on */ @@ -195,78 +193,6 @@ struct ads7846 { #define REF_ON (READ_12BIT_DFR(x, 1, 1)) #define REF_OFF (READ_12BIT_DFR(y, 0, 0)) -/* Must be called with ts->lock held */ -static void ads7846_stop(struct ads7846 *ts) -{ - if (!ts->disabled && !ts->suspended) { - /* Signal IRQ thread to stop polling and disable the handler. */ - ts->stopped = true; - mb(); - wake_up(&ts->wait); - disable_irq(ts->spi->irq); - } -} - -/* Must be called with ts->lock held */ -static void ads7846_restart(struct ads7846 *ts) -{ - if (!ts->disabled && !ts->suspended) { - /* Tell IRQ thread that it may poll the device. */ - ts->stopped = false; - mb(); - enable_irq(ts->spi->irq); - } -} - -/* Must be called with ts->lock held */ -static void __ads7846_disable(struct ads7846 *ts) -{ - ads7846_stop(ts); - regulator_disable(ts->reg); - - /* - * We know the chip's in low power mode since we always - * leave it that way after every request - */ -} - -/* Must be called with ts->lock held */ -static void __ads7846_enable(struct ads7846 *ts) -{ - regulator_enable(ts->reg); - ads7846_restart(ts); -} - -static void ads7846_disable(struct ads7846 *ts) -{ - mutex_lock(&ts->lock); - - if (!ts->disabled) { - - if (!ts->suspended) - __ads7846_disable(ts); - - ts->disabled = true; - } - - mutex_unlock(&ts->lock); -} - -static void ads7846_enable(struct ads7846 *ts) -{ - mutex_lock(&ts->lock); - - if (ts->disabled) { - - ts->disabled = false; - - if (!ts->suspended) - __ads7846_enable(ts); - } - - mutex_unlock(&ts->lock); -} - /*--------------------------------------------------------------------------*/ /* @@ -293,15 +219,23 @@ struct ads7845_ser_req { struct spi_transfer xfer[2]; }; -static int ads7846_read12_ser(struct device *dev, unsigned command) +static void ads7846_enable(struct ads7846 *ts); +static void ads7846_disable(struct ads7846 *ts); + +static int device_suspended(struct device *dev) { - struct spi_device *spi = to_spi_device(dev); struct ads7846 *ts = dev_get_drvdata(dev); - struct ser_req *req; - int status; - int use_internal; + return ts->is_suspended || ts->disabled; +} + +static int ads7846_read12_ser(struct device *dev, unsigned command) +{ + struct spi_device *spi = to_spi_device(dev); + struct ads7846 *ts = dev_get_drvdata(dev); + struct ser_req *req = kzalloc(sizeof *req, GFP_KERNEL); + int status; + int use_internal; - req = kzalloc(sizeof *req, GFP_KERNEL); if (!req) return -ENOMEM; @@ -348,11 +282,11 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) CS_CHANGE(req->xfer[5]); spi_message_add_tail(&req->xfer[5], &req->msg); - mutex_lock(&ts->lock); - ads7846_stop(ts); + ts->irq_disabled = 1; + disable_irq(spi->irq); status = spi_sync(spi, &req->msg); - ads7846_restart(ts); - mutex_unlock(&ts->lock); + ts->irq_disabled = 0; + enable_irq(spi->irq); if (status == 0) { /* on-wire is a must-ignore bit, a BE12 value, then padding */ @@ -367,12 +301,11 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) static int ads7845_read12_ser(struct device *dev, unsigned command) { - struct spi_device *spi = to_spi_device(dev); - struct ads7846 *ts = dev_get_drvdata(dev); - struct ads7845_ser_req *req; - int status; + struct spi_device *spi = to_spi_device(dev); + struct ads7846 *ts = dev_get_drvdata(dev); + struct ads7845_ser_req *req = kzalloc(sizeof *req, GFP_KERNEL); + int status; - req = kzalloc(sizeof *req, GFP_KERNEL); if (!req) return -ENOMEM; @@ -384,11 +317,11 @@ static int ads7845_read12_ser(struct device *dev, unsigned command) req->xfer[0].len = 3; spi_message_add_tail(&req->xfer[0], &req->msg); - mutex_lock(&ts->lock); - ads7846_stop(ts); + ts->irq_disabled = 1; + disable_irq(spi->irq); status = spi_sync(spi, &req->msg); - ads7846_restart(ts); - mutex_unlock(&ts->lock); + ts->irq_disabled = 0; + enable_irq(spi->irq); if (status == 0) { /* BE12 value, then padding */ @@ -441,7 +374,6 @@ static inline unsigned vaux_adjust(struct ads7846 *ts, ssize_t v) /* external resistors may scale vAUX into 0..vREF */ retval *= ts->vref_mv; retval = retval >> 12; - return retval; } @@ -452,13 +384,13 @@ static inline unsigned vbatt_adjust(struct ads7846 *ts, ssize_t v) /* ads7846 has a resistor ladder to scale this signal down */ if (ts->model == 7846) retval *= 4; - return retval; } SHOW(in0_input, vaux, vaux_adjust) SHOW(in1_input, vbatt, vbatt_adjust) + static struct attribute *ads7846_attributes[] = { &dev_attr_temp0.attr, &dev_attr_temp1.attr, @@ -566,12 +498,17 @@ static inline void ads784x_hwmon_unregister(struct spi_device *spi, } #endif +static int is_pen_down(struct device *dev) +{ + struct ads7846 *ts = dev_get_drvdata(dev); + + return ts->pendown; +} + static ssize_t ads7846_pen_down_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct ads7846 *ts = dev_get_drvdata(dev); - - return sprintf(buf, "%u\n", ts->pendown); + return sprintf(buf, "%u\n", is_pen_down(dev)); } static DEVICE_ATTR(pen_down, S_IRUGO, ads7846_pen_down_show, NULL); @@ -579,7 +516,7 @@ static DEVICE_ATTR(pen_down, S_IRUGO, ads7846_pen_down_show, NULL); static ssize_t ads7846_disable_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct ads7846 *ts = dev_get_drvdata(dev); + struct ads7846 *ts = dev_get_drvdata(dev); return sprintf(buf, "%u\n", ts->disabled); } @@ -594,11 +531,15 @@ static ssize_t ads7846_disable_store(struct device *dev, if (strict_strtoul(buf, 10, &i)) return -EINVAL; + spin_lock_irq(&ts->lock); + if (i) ads7846_disable(ts); else ads7846_enable(ts); + spin_unlock_irq(&ts->lock); + return count; } @@ -628,141 +569,23 @@ static void null_wait_for_sync(void) { } -static int ads7846_debounce_filter(void *ads, int data_idx, int *val) -{ - struct ads7846 *ts = ads; - - if (!ts->read_cnt || (abs(ts->last_read - *val) > ts->debounce_tol)) { - /* Start over collecting consistent readings. */ - ts->read_rep = 0; - /* - * Repeat it, if this was the first read or the read - * wasn't consistent enough. - */ - if (ts->read_cnt < ts->debounce_max) { - ts->last_read = *val; - ts->read_cnt++; - return ADS7846_FILTER_REPEAT; - } else { - /* - * Maximum number of debouncing reached and still - * not enough number of consistent readings. Abort - * the whole sample, repeat it in the next sampling - * period. - */ - ts->read_cnt = 0; - return ADS7846_FILTER_IGNORE; - } - } else { - if (++ts->read_rep > ts->debounce_rep) { - /* - * Got a good reading for this coordinate, - * go for the next one. - */ - ts->read_cnt = 0; - ts->read_rep = 0; - return ADS7846_FILTER_OK; - } else { - /* Read more values that are consistent. */ - ts->read_cnt++; - return ADS7846_FILTER_REPEAT; - } - } -} - -static int ads7846_no_filter(void *ads, int data_idx, int *val) -{ - return ADS7846_FILTER_OK; -} - -static int ads7846_get_value(struct ads7846 *ts, struct spi_message *m) -{ - struct spi_transfer *t = - list_entry(m->transfers.prev, struct spi_transfer, transfer_list); - - if (ts->model == 7845) { - return be16_to_cpup((__be16 *)&(((char*)t->rx_buf)[1])) >> 3; - } else { - /* - * adjust: on-wire is a must-ignore bit, a BE12 value, then - * padding; built from two 8 bit values written msb-first. - */ - return be16_to_cpup((__be16 *)t->rx_buf) >> 3; - } -} - -static void ads7846_update_value(struct spi_message *m, int val) -{ - struct spi_transfer *t = - list_entry(m->transfers.prev, struct spi_transfer, transfer_list); - - *(u16 *)t->rx_buf = val; -} - -static void ads7846_read_state(struct ads7846 *ts) -{ - struct ads7846_packet *packet = ts->packet; - struct spi_message *m; - int msg_idx = 0; - int val; - int action; - int error; - - while (msg_idx < ts->msg_count) { - - ts->wait_for_sync(); - - m = &ts->msg[msg_idx]; - error = spi_sync(ts->spi, m); - if (error) { - dev_err(&ts->spi->dev, "spi_async --> %d\n", error); - packet->tc.ignore = true; - return; - } - - /* - * Last message is power down request, no need to convert - * or filter the value. - */ - if (msg_idx < ts->msg_count - 1) { - - val = ads7846_get_value(ts, m); - - action = ts->filter(ts->filter_data, msg_idx, &val); - switch (action) { - case ADS7846_FILTER_REPEAT: - continue; - - case ADS7846_FILTER_IGNORE: - packet->tc.ignore = true; - msg_idx = ts->msg_count - 1; - continue; - - case ADS7846_FILTER_OK: - ads7846_update_value(m, val); - packet->tc.ignore = false; - msg_idx++; - break; - - default: - BUG(); - } - } else { - msg_idx++; - } - } -} +/* + * PENIRQ only kicks the timer. The timer only reissues the SPI transfer, + * to retrieve touchscreen status. + * + * The SPI transfer completion callback does the real work. It reports + * touchscreen events and reactivates the timer (or IRQ) as appropriate. + */ -static void ads7846_report_state(struct ads7846 *ts) +static void ads7846_rx(void *ads) { - struct ads7846_packet *packet = ts->packet; - unsigned int Rt; - u16 x, y, z1, z2; + struct ads7846 *ts = ads; + struct ads7846_packet *packet = ts->packet; + unsigned Rt; + u16 x, y, z1, z2; - /* - * ads7846_get_value() does in-place conversion (including byte swap) - * from on-the-wire format as part of debouncing to get stable - * readings. + /* ads7846_rx_val() did in-place conversion (including byteswap) from + * on-the-wire format as part of debouncing to get stable readings. */ if (ts->model == 7845) { x = *(u16 *)packet->tc.x_buf; @@ -800,19 +623,19 @@ static void ads7846_report_state(struct ads7846 *ts) Rt = 0; } - /* - * Sample found inconsistent by debouncing or pressure is beyond + /* Sample found inconsistent by debouncing or pressure is beyond * the maximum. Don't report it to user space, repeat at least * once more the measurement */ if (packet->tc.ignore || Rt > ts->pressure_max) { dev_vdbg(&ts->spi->dev, "ignored %d pressure %d\n", packet->tc.ignore, Rt); + hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD), + HRTIMER_MODE_REL); return; } - /* - * Maybe check the pendown state before reporting. This discards + /* Maybe check the pendown state before reporting. This discards * false readings when the pen is lifted. */ if (ts->penirq_recheck_delay_usecs) { @@ -821,9 +644,8 @@ static void ads7846_report_state(struct ads7846 *ts) Rt = 0; } - /* - * NOTE: We can't rely on the pressure to determine the pen down - * state, even this controller has a pressure sensor. The pressure + /* NOTE: We can't rely on the pressure to determine the pen down + * state, even this controller has a pressure sensor. The pressure * value can fluctuate for quite a while after lifting the pen and * in some cases may not even settle at the expected value. * @@ -833,15 +655,15 @@ static void ads7846_report_state(struct ads7846 *ts) if (Rt) { struct input_dev *input = ts->input; - if (ts->swap_xy) - swap(x, y); - if (!ts->pendown) { input_report_key(input, BTN_TOUCH, 1); - ts->pendown = true; + ts->pendown = 1; dev_vdbg(&ts->spi->dev, "DOWN\n"); } + if (ts->swap_xy) + swap(x, y); + input_report_abs(input, ABS_X, x); input_report_abs(input, ABS_Y, y); input_report_abs(input, ABS_PRESSURE, ts->pressure_max - Rt); @@ -849,94 +671,246 @@ static void ads7846_report_state(struct ads7846 *ts) input_sync(input); dev_vdbg(&ts->spi->dev, "%4d/%4d/%4d\n", x, y, Rt); } + + hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD), + HRTIMER_MODE_REL); } -static irqreturn_t ads7846_hard_irq(int irq, void *handle) +static int ads7846_debounce(void *ads, int data_idx, int *val) { - struct ads7846 *ts = handle; + struct ads7846 *ts = ads; - return get_pendown_state(ts) ? IRQ_WAKE_THREAD : IRQ_HANDLED; + if (!ts->read_cnt || (abs(ts->last_read - *val) > ts->debounce_tol)) { + /* Start over collecting consistent readings. */ + ts->read_rep = 0; + /* Repeat it, if this was the first read or the read + * wasn't consistent enough. */ + if (ts->read_cnt < ts->debounce_max) { + ts->last_read = *val; + ts->read_cnt++; + return ADS7846_FILTER_REPEAT; + } else { + /* Maximum number of debouncing reached and still + * not enough number of consistent readings. Abort + * the whole sample, repeat it in the next sampling + * period. + */ + ts->read_cnt = 0; + return ADS7846_FILTER_IGNORE; + } + } else { + if (++ts->read_rep > ts->debounce_rep) { + /* Got a good reading for this coordinate, + * go for the next one. */ + ts->read_cnt = 0; + ts->read_rep = 0; + return ADS7846_FILTER_OK; + } else { + /* Read more values that are consistent. */ + ts->read_cnt++; + return ADS7846_FILTER_REPEAT; + } + } } +static int ads7846_no_filter(void *ads, int data_idx, int *val) +{ + return ADS7846_FILTER_OK; +} + +static void ads7846_rx_val(void *ads) +{ + struct ads7846 *ts = ads; + struct ads7846_packet *packet = ts->packet; + struct spi_message *m; + struct spi_transfer *t; + int val; + int action; + int status; + + m = &ts->msg[ts->msg_idx]; + t = list_entry(m->transfers.prev, struct spi_transfer, transfer_list); + + if (ts->model == 7845) { + val = be16_to_cpup((__be16 *)&(((char*)t->rx_buf)[1])) >> 3; + } else { + /* adjust: on-wire is a must-ignore bit, a BE12 value, then + * padding; built from two 8 bit values written msb-first. + */ + val = be16_to_cpup((__be16 *)t->rx_buf) >> 3; + } + + action = ts->filter(ts->filter_data, ts->msg_idx, &val); + switch (action) { + case ADS7846_FILTER_REPEAT: + break; + case ADS7846_FILTER_IGNORE: + packet->tc.ignore = 1; + /* Last message will contain ads7846_rx() as the + * completion function. + */ + m = ts->last_msg; + break; + case ADS7846_FILTER_OK: + *(u16 *)t->rx_buf = val; + packet->tc.ignore = 0; + m = &ts->msg[++ts->msg_idx]; + break; + default: + BUG(); + } + ts->wait_for_sync(); + status = spi_async(ts->spi, m); + if (status) + dev_err(&ts->spi->dev, "spi_async --> %d\n", + status); +} + +static enum hrtimer_restart ads7846_timer(struct hrtimer *handle) +{ + struct ads7846 *ts = container_of(handle, struct ads7846, timer); + int status = 0; + + spin_lock(&ts->lock); + + if (unlikely(!get_pendown_state(ts) || + device_suspended(&ts->spi->dev))) { + if (ts->pendown) { + struct input_dev *input = ts->input; + + input_report_key(input, BTN_TOUCH, 0); + input_report_abs(input, ABS_PRESSURE, 0); + input_sync(input); + + ts->pendown = 0; + dev_vdbg(&ts->spi->dev, "UP\n"); + } + + /* measurement cycle ended */ + if (!device_suspended(&ts->spi->dev)) { + ts->irq_disabled = 0; + enable_irq(ts->spi->irq); + } + ts->pending = 0; + } else { + /* pen is still down, continue with the measurement */ + ts->msg_idx = 0; + ts->wait_for_sync(); + status = spi_async(ts->spi, &ts->msg[0]); + if (status) + dev_err(&ts->spi->dev, "spi_async --> %d\n", status); + } + + spin_unlock(&ts->lock); + return HRTIMER_NORESTART; +} static irqreturn_t ads7846_irq(int irq, void *handle) { struct ads7846 *ts = handle; + unsigned long flags; + + spin_lock_irqsave(&ts->lock, flags); + if (likely(get_pendown_state(ts))) { + if (!ts->irq_disabled) { + /* The ARM do_simple_IRQ() dispatcher doesn't act + * like the other dispatchers: it will report IRQs + * even after they've been disabled. We work around + * that here. (The "generic irq" framework may help...) + */ + ts->irq_disabled = 1; + disable_irq_nosync(ts->spi->irq); + ts->pending = 1; + hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_DELAY), + HRTIMER_MODE_REL); + } + } + spin_unlock_irqrestore(&ts->lock, flags); - /* Start with a small delay before checking pendown state */ - msleep(TS_POLL_DELAY); + return IRQ_HANDLED; +} - while (!ts->stopped && get_pendown_state(ts)) { +/*--------------------------------------------------------------------------*/ - /* pen is down, continue with the measurement */ - ads7846_read_state(ts); +/* Must be called with ts->lock held */ +static void ads7846_disable(struct ads7846 *ts) +{ + if (ts->disabled) + return; - if (!ts->stopped) - ads7846_report_state(ts); + ts->disabled = 1; - wait_event_timeout(ts->wait, ts->stopped, - msecs_to_jiffies(TS_POLL_PERIOD)); + /* are we waiting for IRQ, or polling? */ + if (!ts->pending) { + ts->irq_disabled = 1; + disable_irq(ts->spi->irq); + } else { + /* the timer will run at least once more, and + * leave everything in a clean state, IRQ disabled + */ + while (ts->pending) { + spin_unlock_irq(&ts->lock); + msleep(1); + spin_lock_irq(&ts->lock); + } } - if (ts->pendown) { - struct input_dev *input = ts->input; + regulator_disable(ts->reg); - input_report_key(input, BTN_TOUCH, 0); - input_report_abs(input, ABS_PRESSURE, 0); - input_sync(input); + /* we know the chip's in lowpower mode since we always + * leave it that way after every request + */ +} - ts->pendown = false; - dev_vdbg(&ts->spi->dev, "UP\n"); - } +/* Must be called with ts->lock held */ +static void ads7846_enable(struct ads7846 *ts) +{ + if (!ts->disabled) + return; - return IRQ_HANDLED; + regulator_enable(ts->reg); + + ts->disabled = 0; + ts->irq_disabled = 0; + enable_irq(ts->spi->irq); } static int ads7846_suspend(struct spi_device *spi, pm_message_t message) { struct ads7846 *ts = dev_get_drvdata(&spi->dev); - mutex_lock(&ts->lock); - - if (!ts->suspended) { - - if (!ts->disabled) - __ads7846_disable(ts); + spin_lock_irq(&ts->lock); - if (device_may_wakeup(&ts->spi->dev)) - enable_irq_wake(ts->spi->irq); + ts->is_suspended = 1; + ads7846_disable(ts); - ts->suspended = true; - } + spin_unlock_irq(&ts->lock); - mutex_unlock(&ts->lock); + if (device_may_wakeup(&ts->spi->dev)) + enable_irq_wake(ts->spi->irq); return 0; + } static int ads7846_resume(struct spi_device *spi) { struct ads7846 *ts = dev_get_drvdata(&spi->dev); - mutex_lock(&ts->lock); - - if (ts->suspended) { + if (device_may_wakeup(&ts->spi->dev)) + disable_irq_wake(ts->spi->irq); - ts->suspended = false; + spin_lock_irq(&ts->lock); - if (device_may_wakeup(&ts->spi->dev)) - disable_irq_wake(ts->spi->irq); + ts->is_suspended = 0; + ads7846_enable(ts); - if (!ts->disabled) - __ads7846_enable(ts); - } - - mutex_unlock(&ts->lock); + spin_unlock_irq(&ts->lock); return 0; } -static int __devinit ads7846_setup_pendown(struct spi_device *spi, struct ads7846 *ts) +static int __devinit setup_pendown(struct spi_device *spi, struct ads7846 *ts) { struct ads7846_platform_data *pdata = spi->dev.platform_data; int err; @@ -958,40 +932,146 @@ static int __devinit ads7846_setup_pendown(struct spi_device *spi, struct ads784 err = gpio_request(pdata->gpio_pendown, "ads7846_pendown"); if (err) { dev_err(&spi->dev, "failed to request pendown GPIO%d\n", - pdata->gpio_pendown); + pdata->gpio_pendown); return err; } ts->gpio_pendown = pdata->gpio_pendown; - return 0; } -/* - * Set up the transfers to read touchscreen state; this assumes we - * use formula #2 for pressure, not #3. - */ -static void __devinit ads7846_setup_spi_msg(struct ads7846 *ts, - const struct ads7846_platform_data *pdata) +static int __devinit ads7846_probe(struct spi_device *spi) { - struct spi_message *m = &ts->msg[0]; - struct spi_transfer *x = ts->xfer; - struct ads7846_packet *packet = ts->packet; - int vref = pdata->keep_vref_on; + struct ads7846 *ts; + struct ads7846_packet *packet; + struct input_dev *input_dev; + const struct ads7846_platform_data *pdata = spi->dev.platform_data; + struct spi_message *m; + struct spi_transfer *x; + unsigned long irq_flags; + int vref; + int err; + + if (!spi->irq) { + dev_dbg(&spi->dev, "no IRQ?\n"); + return -ENODEV; + } + + if (!pdata) { + dev_dbg(&spi->dev, "no platform data?\n"); + return -ENODEV; + } + + /* don't exceed max specified sample rate */ + if (spi->max_speed_hz > (125000 * SAMPLE_BITS)) { + dev_dbg(&spi->dev, "f(sample) %d KHz?\n", + (spi->max_speed_hz/SAMPLE_BITS)/1000); + return -EINVAL; + } + + /* We'd set TX wordsize 8 bits and RX wordsize to 13 bits ... except + * that even if the hardware can do that, the SPI controller driver + * may not. So we stick to very-portable 8 bit words, both RX and TX. + */ + spi->bits_per_word = 8; + spi->mode = SPI_MODE_0; + err = spi_setup(spi); + if (err < 0) + return err; + + ts = kzalloc(sizeof(struct ads7846), GFP_KERNEL); + packet = kzalloc(sizeof(struct ads7846_packet), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts || !packet || !input_dev) { + err = -ENOMEM; + goto err_free_mem; + } + + dev_set_drvdata(&spi->dev, ts); + + ts->packet = packet; + ts->spi = spi; + ts->input = input_dev; + ts->vref_mv = pdata->vref_mv; + ts->swap_xy = pdata->swap_xy; + + hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ts->timer.function = ads7846_timer; + + spin_lock_init(&ts->lock); + + ts->model = pdata->model ? : 7846; + ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100; + ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; + ts->pressure_max = pdata->pressure_max ? : ~0; + + if (pdata->filter != NULL) { + if (pdata->filter_init != NULL) { + err = pdata->filter_init(pdata, &ts->filter_data); + if (err < 0) + goto err_free_mem; + } + ts->filter = pdata->filter; + ts->filter_cleanup = pdata->filter_cleanup; + } else if (pdata->debounce_max) { + ts->debounce_max = pdata->debounce_max; + if (ts->debounce_max < 2) + ts->debounce_max = 2; + ts->debounce_tol = pdata->debounce_tol; + ts->debounce_rep = pdata->debounce_rep; + ts->filter = ads7846_debounce; + ts->filter_data = ts; + } else + ts->filter = ads7846_no_filter; + + err = setup_pendown(spi, ts); + if (err) + goto err_cleanup_filter; + + if (pdata->penirq_recheck_delay_usecs) + ts->penirq_recheck_delay_usecs = + pdata->penirq_recheck_delay_usecs; + + ts->wait_for_sync = pdata->wait_for_sync ? : null_wait_for_sync; + + snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&spi->dev)); + snprintf(ts->name, sizeof(ts->name), "ADS%d Touchscreen", ts->model); + + input_dev->name = ts->name; + input_dev->phys = ts->phys; + input_dev->dev.parent = &spi->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(input_dev, ABS_X, + pdata->x_min ? : 0, + pdata->x_max ? : MAX_12BIT, + 0, 0); + input_set_abs_params(input_dev, ABS_Y, + pdata->y_min ? : 0, + pdata->y_max ? : MAX_12BIT, + 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, + pdata->pressure_min, pdata->pressure_max, 0, 0); + + vref = pdata->keep_vref_on; if (ts->model == 7873) { - /* - * The AD7873 is almost identical to the ADS7846 + /* The AD7873 is almost identical to the ADS7846 * keep VREF off during differential/ratiometric - * conversion modes. + * conversion modes */ ts->model = 7846; vref = 0; } - ts->msg_count = 1; + /* set up the transfers to read touchscreen state; this assumes we + * use formula #2 for pressure, not #3. + */ + m = &ts->msg[0]; + x = ts->xfer; + spi_message_init(m); - m->context = ts; if (ts->model == 7845) { packet->read_y_cmd[0] = READ_Y(vref); @@ -1014,8 +1094,7 @@ static void __devinit ads7846_setup_spi_msg(struct ads7846 *ts, spi_message_add_tail(x, m); } - /* - * The first sample after switching drivers can be low quality; + /* the first sample after switching drivers can be low quality; * optionally discard it, using a second one after the signals * have had enough time to stabilize. */ @@ -1033,10 +1112,11 @@ static void __devinit ads7846_setup_spi_msg(struct ads7846 *ts, spi_message_add_tail(x, m); } - ts->msg_count++; + m->complete = ads7846_rx_val; + m->context = ts; + m++; spi_message_init(m); - m->context = ts; if (ts->model == 7845) { x++; @@ -1076,12 +1156,13 @@ static void __devinit ads7846_setup_spi_msg(struct ads7846 *ts, spi_message_add_tail(x, m); } + m->complete = ads7846_rx_val; + m->context = ts; + /* turn y+ off, x- on; we'll use formula #2 */ if (ts->model == 7846) { - ts->msg_count++; m++; spi_message_init(m); - m->context = ts; x++; packet->read_z1 = READ_Z1(vref); @@ -1109,10 +1190,11 @@ static void __devinit ads7846_setup_spi_msg(struct ads7846 *ts, spi_message_add_tail(x, m); } - ts->msg_count++; + m->complete = ads7846_rx_val; + m->context = ts; + m++; spi_message_init(m); - m->context = ts; x++; packet->read_z2 = READ_Z2(vref); @@ -1139,13 +1221,14 @@ static void __devinit ads7846_setup_spi_msg(struct ads7846 *ts, x->len = 2; spi_message_add_tail(x, m); } + + m->complete = ads7846_rx_val; + m->context = ts; } /* power down */ - ts->msg_count++; m++; spi_message_init(m); - m->context = ts; if (ts->model == 7845) { x++; @@ -1168,119 +1251,11 @@ static void __devinit ads7846_setup_spi_msg(struct ads7846 *ts, CS_CHANGE(*x); spi_message_add_tail(x, m); -} - -static int __devinit ads7846_probe(struct spi_device *spi) -{ - struct ads7846 *ts; - struct ads7846_packet *packet; - struct input_dev *input_dev; - struct ads7846_platform_data *pdata = spi->dev.platform_data; - unsigned long irq_flags; - int err; - - if (!spi->irq) { - dev_dbg(&spi->dev, "no IRQ?\n"); - return -ENODEV; - } - - if (!pdata) { - dev_dbg(&spi->dev, "no platform data?\n"); - return -ENODEV; - } - - /* don't exceed max specified sample rate */ - if (spi->max_speed_hz > (125000 * SAMPLE_BITS)) { - dev_dbg(&spi->dev, "f(sample) %d KHz?\n", - (spi->max_speed_hz/SAMPLE_BITS)/1000); - return -EINVAL; - } - - /* We'd set TX word size 8 bits and RX word size to 13 bits ... except - * that even if the hardware can do that, the SPI controller driver - * may not. So we stick to very-portable 8 bit words, both RX and TX. - */ - spi->bits_per_word = 8; - spi->mode = SPI_MODE_0; - err = spi_setup(spi); - if (err < 0) - return err; - ts = kzalloc(sizeof(struct ads7846), GFP_KERNEL); - packet = kzalloc(sizeof(struct ads7846_packet), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!ts || !packet || !input_dev) { - err = -ENOMEM; - goto err_free_mem; - } - - dev_set_drvdata(&spi->dev, ts); - - ts->packet = packet; - ts->spi = spi; - ts->input = input_dev; - ts->vref_mv = pdata->vref_mv; - ts->swap_xy = pdata->swap_xy; - - mutex_init(&ts->lock); - init_waitqueue_head(&ts->wait); - - ts->model = pdata->model ? : 7846; - ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100; - ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; - ts->pressure_max = pdata->pressure_max ? : ~0; - - if (pdata->filter != NULL) { - if (pdata->filter_init != NULL) { - err = pdata->filter_init(pdata, &ts->filter_data); - if (err < 0) - goto err_free_mem; - } - ts->filter = pdata->filter; - ts->filter_cleanup = pdata->filter_cleanup; - } else if (pdata->debounce_max) { - ts->debounce_max = pdata->debounce_max; - if (ts->debounce_max < 2) - ts->debounce_max = 2; - ts->debounce_tol = pdata->debounce_tol; - ts->debounce_rep = pdata->debounce_rep; - ts->filter = ads7846_debounce_filter; - ts->filter_data = ts; - } else { - ts->filter = ads7846_no_filter; - } - - err = ads7846_setup_pendown(spi, ts); - if (err) - goto err_cleanup_filter; - - if (pdata->penirq_recheck_delay_usecs) - ts->penirq_recheck_delay_usecs = - pdata->penirq_recheck_delay_usecs; - - ts->wait_for_sync = pdata->wait_for_sync ? : null_wait_for_sync; - - snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&spi->dev)); - snprintf(ts->name, sizeof(ts->name), "ADS%d Touchscreen", ts->model); - - input_dev->name = ts->name; - input_dev->phys = ts->phys; - input_dev->dev.parent = &spi->dev; - - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); - input_set_abs_params(input_dev, ABS_X, - pdata->x_min ? : 0, - pdata->x_max ? : MAX_12BIT, - 0, 0); - input_set_abs_params(input_dev, ABS_Y, - pdata->y_min ? : 0, - pdata->y_max ? : MAX_12BIT, - 0, 0); - input_set_abs_params(input_dev, ABS_PRESSURE, - pdata->pressure_min, pdata->pressure_max, 0, 0); + m->complete = ads7846_rx; + m->context = ts; - ads7846_setup_spi_msg(ts, pdata); + ts->last_msg = m; ts->reg = regulator_get(&spi->dev, "vcc"); if (IS_ERR(ts->reg)) { @@ -1296,17 +1271,16 @@ static int __devinit ads7846_probe(struct spi_device *spi) } irq_flags = pdata->irq_flags ? : IRQF_TRIGGER_FALLING; - irq_flags |= IRQF_ONESHOT; - err = request_threaded_irq(spi->irq, ads7846_hard_irq, ads7846_irq, - irq_flags, spi->dev.driver->name, ts); + err = request_irq(spi->irq, ads7846_irq, irq_flags, + spi->dev.driver->name, ts); + if (err && !pdata->irq_flags) { dev_info(&spi->dev, "trying pin change workaround on irq %d\n", spi->irq); - irq_flags |= IRQF_TRIGGER_RISING; - err = request_threaded_irq(spi->irq, - ads7846_hard_irq, ads7846_irq, - irq_flags, spi->dev.driver->name, ts); + err = request_irq(spi->irq, ads7846_irq, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + spi->dev.driver->name, ts); } if (err) { @@ -1320,8 +1294,7 @@ static int __devinit ads7846_probe(struct spi_device *spi) dev_info(&spi->dev, "touchscreen, irq %d\n", spi->irq); - /* - * Take a first sample, leaving nPENIRQ active and vREF off; avoid + /* take a first sample, leaving nPENIRQ active and vREF off; avoid * the touchscreen, in case it's not connected. */ if (ts->model == 7845) @@ -1367,18 +1340,20 @@ static int __devinit ads7846_probe(struct spi_device *spi) static int __devexit ads7846_remove(struct spi_device *spi) { - struct ads7846 *ts = dev_get_drvdata(&spi->dev); + struct ads7846 *ts = dev_get_drvdata(&spi->dev); device_init_wakeup(&spi->dev, false); - sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group); + ads784x_hwmon_unregister(spi, ts); + input_unregister_device(ts->input); - ads7846_disable(ts); - free_irq(ts->spi->irq, ts); + ads7846_suspend(spi, PMSG_SUSPEND); - input_unregister_device(ts->input); + sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group); - ads784x_hwmon_unregister(spi, ts); + free_irq(ts->spi->irq, ts); + /* suspend left the IRQ disabled */ + enable_irq(ts->spi->irq); regulator_disable(ts->reg); regulator_put(ts->reg); @@ -1393,7 +1368,6 @@ static int __devexit ads7846_remove(struct spi_device *spi) kfree(ts); dev_dbg(&spi->dev, "unregistered touchscreen\n"); - return 0; } diff --git a/trunk/drivers/input/touchscreen/bu21013_ts.c b/trunk/drivers/input/touchscreen/bu21013_ts.c deleted file mode 100644 index ccde58602563..000000000000 --- a/trunk/drivers/input/touchscreen/bu21013_ts.c +++ /dev/null @@ -1,648 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2010 - * Author: Naveen Kumar G for ST-Ericsson - * License terms:GNU General Public License (GPL) version 2 - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define PEN_DOWN_INTR 0 -#define MAX_FINGERS 2 -#define RESET_DELAY 30 -#define PENUP_TIMEOUT (10) -#define DELTA_MIN 16 -#define MASK_BITS 0x03 -#define SHIFT_8 8 -#define SHIFT_2 2 -#define LENGTH_OF_BUFFER 11 -#define I2C_RETRY_COUNT 5 - -#define BU21013_SENSORS_BTN_0_7_REG 0x70 -#define BU21013_SENSORS_BTN_8_15_REG 0x71 -#define BU21013_SENSORS_BTN_16_23_REG 0x72 -#define BU21013_X1_POS_MSB_REG 0x73 -#define BU21013_X1_POS_LSB_REG 0x74 -#define BU21013_Y1_POS_MSB_REG 0x75 -#define BU21013_Y1_POS_LSB_REG 0x76 -#define BU21013_X2_POS_MSB_REG 0x77 -#define BU21013_X2_POS_LSB_REG 0x78 -#define BU21013_Y2_POS_MSB_REG 0x79 -#define BU21013_Y2_POS_LSB_REG 0x7A -#define BU21013_INT_CLR_REG 0xE8 -#define BU21013_INT_MODE_REG 0xE9 -#define BU21013_GAIN_REG 0xEA -#define BU21013_OFFSET_MODE_REG 0xEB -#define BU21013_XY_EDGE_REG 0xEC -#define BU21013_RESET_REG 0xED -#define BU21013_CALIB_REG 0xEE -#define BU21013_DONE_REG 0xEF -#define BU21013_SENSOR_0_7_REG 0xF0 -#define BU21013_SENSOR_8_15_REG 0xF1 -#define BU21013_SENSOR_16_23_REG 0xF2 -#define BU21013_POS_MODE1_REG 0xF3 -#define BU21013_POS_MODE2_REG 0xF4 -#define BU21013_CLK_MODE_REG 0xF5 -#define BU21013_IDLE_REG 0xFA -#define BU21013_FILTER_REG 0xFB -#define BU21013_TH_ON_REG 0xFC -#define BU21013_TH_OFF_REG 0xFD - - -#define BU21013_RESET_ENABLE 0x01 - -#define BU21013_SENSORS_EN_0_7 0x3F -#define BU21013_SENSORS_EN_8_15 0xFC -#define BU21013_SENSORS_EN_16_23 0x1F - -#define BU21013_POS_MODE1_0 0x02 -#define BU21013_POS_MODE1_1 0x04 -#define BU21013_POS_MODE1_2 0x08 - -#define BU21013_POS_MODE2_ZERO 0x01 -#define BU21013_POS_MODE2_AVG1 0x02 -#define BU21013_POS_MODE2_AVG2 0x04 -#define BU21013_POS_MODE2_EN_XY 0x08 -#define BU21013_POS_MODE2_EN_RAW 0x10 -#define BU21013_POS_MODE2_MULTI 0x80 - -#define BU21013_CLK_MODE_DIV 0x01 -#define BU21013_CLK_MODE_EXT 0x02 -#define BU21013_CLK_MODE_CALIB 0x80 - -#define BU21013_IDLET_0 0x01 -#define BU21013_IDLET_1 0x02 -#define BU21013_IDLET_2 0x04 -#define BU21013_IDLET_3 0x08 -#define BU21013_IDLE_INTERMIT_EN 0x10 - -#define BU21013_DELTA_0_6 0x7F -#define BU21013_FILTER_EN 0x80 - -#define BU21013_INT_MODE_LEVEL 0x00 -#define BU21013_INT_MODE_EDGE 0x01 - -#define BU21013_GAIN_0 0x01 -#define BU21013_GAIN_1 0x02 -#define BU21013_GAIN_2 0x04 - -#define BU21013_OFFSET_MODE_DEFAULT 0x00 -#define BU21013_OFFSET_MODE_MOVE 0x01 -#define BU21013_OFFSET_MODE_DISABLE 0x02 - -#define BU21013_TH_ON_0 0x01 -#define BU21013_TH_ON_1 0x02 -#define BU21013_TH_ON_2 0x04 -#define BU21013_TH_ON_3 0x08 -#define BU21013_TH_ON_4 0x10 -#define BU21013_TH_ON_5 0x20 -#define BU21013_TH_ON_6 0x40 -#define BU21013_TH_ON_7 0x80 -#define BU21013_TH_ON_MAX 0xFF - -#define BU21013_TH_OFF_0 0x01 -#define BU21013_TH_OFF_1 0x02 -#define BU21013_TH_OFF_2 0x04 -#define BU21013_TH_OFF_3 0x08 -#define BU21013_TH_OFF_4 0x10 -#define BU21013_TH_OFF_5 0x20 -#define BU21013_TH_OFF_6 0x40 -#define BU21013_TH_OFF_7 0x80 -#define BU21013_TH_OFF_MAX 0xFF - -#define BU21013_X_EDGE_0 0x01 -#define BU21013_X_EDGE_1 0x02 -#define BU21013_X_EDGE_2 0x04 -#define BU21013_X_EDGE_3 0x08 -#define BU21013_Y_EDGE_0 0x10 -#define BU21013_Y_EDGE_1 0x20 -#define BU21013_Y_EDGE_2 0x40 -#define BU21013_Y_EDGE_3 0x80 - -#define BU21013_DONE 0x01 -#define BU21013_NUMBER_OF_X_SENSORS (6) -#define BU21013_NUMBER_OF_Y_SENSORS (11) - -#define DRIVER_TP "bu21013_tp" - -/** - * struct bu21013_ts_data - touch panel data structure - * @client: pointer to the i2c client - * @wait: variable to wait_queue_head_t structure - * @touch_stopped: touch stop flag - * @chip: pointer to the touch panel controller - * @in_dev: pointer to the input device structure - * @intr_pin: interrupt pin value - * - * Touch panel device data structure - */ -struct bu21013_ts_data { - struct i2c_client *client; - wait_queue_head_t wait; - bool touch_stopped; - const struct bu21013_platform_device *chip; - struct input_dev *in_dev; - unsigned int intr_pin; -}; - -/** - * bu21013_read_block_data(): read the touch co-ordinates - * @data: bu21013_ts_data structure pointer - * @buf: byte pointer - * - * Read the touch co-ordinates using i2c read block into buffer - * and returns integer. - */ -static int bu21013_read_block_data(struct bu21013_ts_data *data, u8 *buf) -{ - int ret, i; - - for (i = 0; i < I2C_RETRY_COUNT; i++) { - ret = i2c_smbus_read_i2c_block_data - (data->client, BU21013_SENSORS_BTN_0_7_REG, - LENGTH_OF_BUFFER, buf); - if (ret == LENGTH_OF_BUFFER) - return 0; - } - return -EINVAL; -} - -/** - * bu21013_do_touch_report(): Get the touch co-ordinates - * @data: bu21013_ts_data structure pointer - * - * Get the touch co-ordinates from touch sensor registers and writes - * into device structure and returns integer. - */ -static int bu21013_do_touch_report(struct bu21013_ts_data *data) -{ - u8 buf[LENGTH_OF_BUFFER]; - unsigned int pos_x[2], pos_y[2]; - bool has_x_sensors, has_y_sensors; - int finger_down_count = 0; - int i; - - if (data == NULL) - return -EINVAL; - - if (bu21013_read_block_data(data, buf) < 0) - return -EINVAL; - - has_x_sensors = hweight32(buf[0] & BU21013_SENSORS_EN_0_7); - has_y_sensors = hweight32(((buf[1] & BU21013_SENSORS_EN_8_15) | - ((buf[2] & BU21013_SENSORS_EN_16_23) << SHIFT_8)) >> SHIFT_2); - if (!has_x_sensors || !has_y_sensors) - return 0; - - for (i = 0; i < MAX_FINGERS; i++) { - const u8 *p = &buf[4 * i + 3]; - unsigned int x = p[0] << SHIFT_2 | (p[1] & MASK_BITS); - unsigned int y = p[2] << SHIFT_2 | (p[3] & MASK_BITS); - if (x == 0 || y == 0) - continue; - pos_x[finger_down_count] = x; - pos_y[finger_down_count] = y; - finger_down_count++; - } - - if (finger_down_count) { - if (finger_down_count == 2 && - (abs(pos_x[0] - pos_x[1]) < DELTA_MIN || - abs(pos_y[0] - pos_y[1]) < DELTA_MIN)) { - return 0; - } - - for (i = 0; i < finger_down_count; i++) { - if (data->chip->x_flip) - pos_x[i] = data->chip->touch_x_max - pos_x[i]; - if (data->chip->y_flip) - pos_y[i] = data->chip->touch_y_max - pos_y[i]; - - input_report_abs(data->in_dev, - ABS_MT_POSITION_X, pos_x[i]); - input_report_abs(data->in_dev, - ABS_MT_POSITION_Y, pos_y[i]); - input_mt_sync(data->in_dev); - } - } else - input_mt_sync(data->in_dev); - - input_sync(data->in_dev); - - return 0; -} -/** - * bu21013_gpio_irq() - gpio thread function for touch interrupt - * @irq: irq value - * @device_data: void pointer - * - * This gpio thread function for touch interrupt - * and returns irqreturn_t. - */ -static irqreturn_t bu21013_gpio_irq(int irq, void *device_data) -{ - struct bu21013_ts_data *data = device_data; - struct i2c_client *i2c = data->client; - int retval; - - do { - retval = bu21013_do_touch_report(data); - if (retval < 0) { - dev_err(&i2c->dev, "bu21013_do_touch_report failed\n"); - return IRQ_NONE; - } - - data->intr_pin = data->chip->irq_read_val(); - if (data->intr_pin == PEN_DOWN_INTR) - wait_event_timeout(data->wait, data->touch_stopped, - msecs_to_jiffies(2)); - } while (!data->intr_pin && !data->touch_stopped); - - return IRQ_HANDLED; -} - -/** - * bu21013_init_chip() - power on sequence for the bu21013 controller - * @data: device structure pointer - * - * This function is used to power on - * the bu21013 controller and returns integer. - */ -static int bu21013_init_chip(struct bu21013_ts_data *data) -{ - int retval; - struct i2c_client *i2c = data->client; - - retval = i2c_smbus_write_byte_data(i2c, BU21013_RESET_REG, - BU21013_RESET_ENABLE); - if (retval < 0) { - dev_err(&i2c->dev, "BU21013_RESET reg write failed\n"); - return retval; - } - msleep(RESET_DELAY); - - retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_0_7_REG, - BU21013_SENSORS_EN_0_7); - if (retval < 0) { - dev_err(&i2c->dev, "BU21013_SENSOR_0_7 reg write failed\n"); - return retval; - } - - retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_8_15_REG, - BU21013_SENSORS_EN_8_15); - if (retval < 0) { - dev_err(&i2c->dev, "BU21013_SENSOR_8_15 reg write failed\n"); - return retval; - } - - retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_16_23_REG, - BU21013_SENSORS_EN_16_23); - if (retval < 0) { - dev_err(&i2c->dev, "BU21013_SENSOR_16_23 reg write failed\n"); - return retval; - } - - retval = i2c_smbus_write_byte_data(i2c, BU21013_POS_MODE1_REG, - (BU21013_POS_MODE1_0 | BU21013_POS_MODE1_1)); - if (retval < 0) { - dev_err(&i2c->dev, "BU21013_POS_MODE1 reg write failed\n"); - return retval; - } - - retval = i2c_smbus_write_byte_data(i2c, BU21013_POS_MODE2_REG, - (BU21013_POS_MODE2_ZERO | BU21013_POS_MODE2_AVG1 | - BU21013_POS_MODE2_AVG2 | BU21013_POS_MODE2_EN_RAW | - BU21013_POS_MODE2_MULTI)); - if (retval < 0) { - dev_err(&i2c->dev, "BU21013_POS_MODE2 reg write failed\n"); - return retval; - } - - if (data->chip->ext_clk) - retval = i2c_smbus_write_byte_data(i2c, BU21013_CLK_MODE_REG, - (BU21013_CLK_MODE_EXT | BU21013_CLK_MODE_CALIB)); - else - retval = i2c_smbus_write_byte_data(i2c, BU21013_CLK_MODE_REG, - (BU21013_CLK_MODE_DIV | BU21013_CLK_MODE_CALIB)); - if (retval < 0) { - dev_err(&i2c->dev, "BU21013_CLK_MODE reg write failed\n"); - return retval; - } - - retval = i2c_smbus_write_byte_data(i2c, BU21013_IDLE_REG, - (BU21013_IDLET_0 | BU21013_IDLE_INTERMIT_EN)); - if (retval < 0) { - dev_err(&i2c->dev, "BU21013_IDLE reg write failed\n"); - return retval; - } - - retval = i2c_smbus_write_byte_data(i2c, BU21013_INT_MODE_REG, - BU21013_INT_MODE_LEVEL); - if (retval < 0) { - dev_err(&i2c->dev, "BU21013_INT_MODE reg write failed\n"); - return retval; - } - - retval = i2c_smbus_write_byte_data(i2c, BU21013_FILTER_REG, - (BU21013_DELTA_0_6 | - BU21013_FILTER_EN)); - if (retval < 0) { - dev_err(&i2c->dev, "BU21013_FILTER reg write failed\n"); - return retval; - } - - retval = i2c_smbus_write_byte_data(i2c, BU21013_TH_ON_REG, - BU21013_TH_ON_5); - if (retval < 0) { - dev_err(&i2c->dev, "BU21013_TH_ON reg write failed\n"); - return retval; - } - - retval = i2c_smbus_write_byte_data(i2c, BU21013_TH_OFF_REG, - BU21013_TH_OFF_4 || BU21013_TH_OFF_3); - if (retval < 0) { - dev_err(&i2c->dev, "BU21013_TH_OFF reg write failed\n"); - return retval; - } - - retval = i2c_smbus_write_byte_data(i2c, BU21013_GAIN_REG, - (BU21013_GAIN_0 | BU21013_GAIN_1)); - if (retval < 0) { - dev_err(&i2c->dev, "BU21013_GAIN reg write failed\n"); - return retval; - } - - retval = i2c_smbus_write_byte_data(i2c, BU21013_OFFSET_MODE_REG, - BU21013_OFFSET_MODE_DEFAULT); - if (retval < 0) { - dev_err(&i2c->dev, "BU21013_OFFSET_MODE reg write failed\n"); - return retval; - } - - retval = i2c_smbus_write_byte_data(i2c, BU21013_XY_EDGE_REG, - (BU21013_X_EDGE_0 | BU21013_X_EDGE_2 | - BU21013_Y_EDGE_1 | BU21013_Y_EDGE_3)); - if (retval < 0) { - dev_err(&i2c->dev, "BU21013_XY_EDGE reg write failed\n"); - return retval; - } - - retval = i2c_smbus_write_byte_data(i2c, BU21013_DONE_REG, - BU21013_DONE); - if (retval < 0) { - dev_err(&i2c->dev, "BU21013_REG_DONE reg write failed\n"); - return retval; - } - - return 0; -} - -/** - * bu21013_free_irq() - frees IRQ registered for touchscreen - * @bu21013_data: device structure pointer - * - * This function signals interrupt thread to stop processing and - * frees interrupt. - */ -static void bu21013_free_irq(struct bu21013_ts_data *bu21013_data) -{ - bu21013_data->touch_stopped = true; - wake_up(&bu21013_data->wait); - free_irq(bu21013_data->chip->irq, bu21013_data); -} - -/** - * bu21013_probe() - initializes the i2c-client touchscreen driver - * @client: i2c client structure pointer - * @id: i2c device id pointer - * - * This function used to initializes the i2c-client touchscreen - * driver and returns integer. - */ -static int __devinit bu21013_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct bu21013_ts_data *bu21013_data; - struct input_dev *in_dev; - const struct bu21013_platform_device *pdata = - client->dev.platform_data; - int error; - - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_BYTE_DATA)) { - dev_err(&client->dev, "i2c smbus byte data not supported\n"); - return -EIO; - } - - if (!pdata) { - dev_err(&client->dev, "platform data not defined\n"); - return -EINVAL; - } - - bu21013_data = kzalloc(sizeof(struct bu21013_ts_data), GFP_KERNEL); - in_dev = input_allocate_device(); - if (!bu21013_data || !in_dev) { - dev_err(&client->dev, "device memory alloc failed\n"); - error = -ENOMEM; - goto err_free_mem; - } - - bu21013_data->in_dev = in_dev; - bu21013_data->chip = pdata; - bu21013_data->client = client; - bu21013_data->touch_stopped = false; - init_waitqueue_head(&bu21013_data->wait); - - /* configure the gpio pins */ - if (pdata->cs_en) { - error = pdata->cs_en(pdata->cs_pin); - if (error < 0) { - dev_err(&client->dev, "chip init failed\n"); - goto err_free_mem; - } - } - - /* configure the touch panel controller */ - error = bu21013_init_chip(bu21013_data); - if (error) { - dev_err(&client->dev, "error in bu21013 config\n"); - goto err_cs_disable; - } - - /* register the device to input subsystem */ - in_dev->name = DRIVER_TP; - in_dev->id.bustype = BUS_I2C; - in_dev->dev.parent = &client->dev; - - __set_bit(EV_SYN, in_dev->evbit); - __set_bit(EV_KEY, in_dev->evbit); - __set_bit(EV_ABS, in_dev->evbit); - - input_set_abs_params(in_dev, ABS_MT_POSITION_X, 0, - pdata->x_max_res, 0, 0); - input_set_abs_params(in_dev, ABS_MT_POSITION_Y, 0, - pdata->y_max_res, 0, 0); - input_set_drvdata(in_dev, bu21013_data); - - error = request_threaded_irq(pdata->irq, NULL, bu21013_gpio_irq, - IRQF_TRIGGER_FALLING | IRQF_SHARED, - DRIVER_TP, bu21013_data); - if (error) { - dev_err(&client->dev, "request irq %d failed\n", pdata->irq); - goto err_cs_disable; - } - - error = input_register_device(in_dev); - if (error) { - dev_err(&client->dev, "failed to register input device\n"); - goto err_free_irq; - } - - device_init_wakeup(&client->dev, pdata->wakeup); - i2c_set_clientdata(client, bu21013_data); - - return 0; - -err_free_irq: - bu21013_free_irq(bu21013_data); -err_cs_disable: - pdata->cs_dis(pdata->cs_pin); -err_free_mem: - input_free_device(bu21013_data->in_dev); - kfree(bu21013_data); - - return error; -} -/** - * bu21013_remove() - removes the i2c-client touchscreen driver - * @client: i2c client structure pointer - * - * This function uses to remove the i2c-client - * touchscreen driver and returns integer. - */ -static int __devexit bu21013_remove(struct i2c_client *client) -{ - struct bu21013_ts_data *bu21013_data = i2c_get_clientdata(client); - - bu21013_free_irq(bu21013_data); - - bu21013_data->chip->cs_dis(bu21013_data->chip->cs_pin); - - input_unregister_device(bu21013_data->in_dev); - kfree(bu21013_data); - - device_init_wakeup(&client->dev, false); - - return 0; -} - -#ifdef CONFIG_PM -/** - * bu21013_suspend() - suspend the touch screen controller - * @dev: pointer to device structure - * - * This function is used to suspend the - * touch panel controller and returns integer - */ -static int bu21013_suspend(struct device *dev) -{ - struct bu21013_ts_data *bu21013_data = dev_get_drvdata(dev); - struct i2c_client *client = bu21013_data->client; - - bu21013_data->touch_stopped = true; - if (device_may_wakeup(&client->dev)) - enable_irq_wake(bu21013_data->chip->irq); - else - disable_irq(bu21013_data->chip->irq); - - return 0; -} - -/** - * bu21013_resume() - resume the touch screen controller - * @dev: pointer to device structure - * - * This function is used to resume the touch panel - * controller and returns integer. - */ -static int bu21013_resume(struct device *dev) -{ - struct bu21013_ts_data *bu21013_data = dev_get_drvdata(dev); - struct i2c_client *client = bu21013_data->client; - int retval; - - retval = bu21013_init_chip(bu21013_data); - if (retval < 0) { - dev_err(&client->dev, "bu21013 controller config failed\n"); - return retval; - } - - bu21013_data->touch_stopped = false; - - if (device_may_wakeup(&client->dev)) - disable_irq_wake(bu21013_data->chip->irq); - else - enable_irq(bu21013_data->chip->irq); - - return 0; -} - -static const struct dev_pm_ops bu21013_dev_pm_ops = { - .suspend = bu21013_suspend, - .resume = bu21013_resume, -}; -#endif - -static const struct i2c_device_id bu21013_id[] = { - { DRIVER_TP, 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, bu21013_id); - -static struct i2c_driver bu21013_driver = { - .driver = { - .name = DRIVER_TP, - .owner = THIS_MODULE, -#ifdef CONFIG_PM - .pm = &bu21013_dev_pm_ops, -#endif - }, - .probe = bu21013_probe, - .remove = __devexit_p(bu21013_remove), - .id_table = bu21013_id, -}; - -/** - * bu21013_init() - initializes the bu21013 touchscreen driver - * - * This function used to initializes the bu21013 - * touchscreen driver and returns integer. - */ -static int __init bu21013_init(void) -{ - return i2c_add_driver(&bu21013_driver); -} - -/** - * bu21013_exit() - de-initializes the bu21013 touchscreen driver - * - * This function uses to de-initializes the bu21013 - * touchscreen driver and returns none. - */ -static void __exit bu21013_exit(void) -{ - i2c_del_driver(&bu21013_driver); -} - -module_init(bu21013_init); -module_exit(bu21013_exit); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Naveen Kumar G "); -MODULE_DESCRIPTION("bu21013 touch screen controller driver"); diff --git a/trunk/drivers/input/touchscreen/cy8ctmg110_ts.c b/trunk/drivers/input/touchscreen/cy8ctmg110_ts.c index d0c3a7229adf..5ec0946938fe 100644 --- a/trunk/drivers/input/touchscreen/cy8ctmg110_ts.c +++ b/trunk/drivers/input/touchscreen/cy8ctmg110_ts.c @@ -206,9 +206,9 @@ static int __devinit cy8ctmg110_probe(struct i2c_client *client, input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); input_set_abs_params(input_dev, ABS_X, - CY8CTMG110_X_MIN, CY8CTMG110_X_MAX, 4, 0); + CY8CTMG110_X_MIN, CY8CTMG110_X_MAX, 0, 0); input_set_abs_params(input_dev, ABS_Y, - CY8CTMG110_Y_MIN, CY8CTMG110_Y_MAX, 4, 0); + CY8CTMG110_Y_MIN, CY8CTMG110_Y_MAX, 0, 0); if (ts->reset_pin) { err = gpio_request(ts->reset_pin, NULL); diff --git a/trunk/drivers/input/touchscreen/hp680_ts_input.c b/trunk/drivers/input/touchscreen/hp680_ts_input.c index 498bd62af09a..a89700e7ace4 100644 --- a/trunk/drivers/input/touchscreen/hp680_ts_input.c +++ b/trunk/drivers/input/touchscreen/hp680_ts_input.c @@ -107,7 +107,8 @@ static int __init hp680_ts_init(void) return 0; fail2: free_irq(HP680_TS_IRQ, NULL); - cancel_delayed_work_sync(&work); + cancel_delayed_work(&work); + flush_scheduled_work(); fail1: input_free_device(hp680_ts_dev); return err; } @@ -115,7 +116,8 @@ static int __init hp680_ts_init(void) static void __exit hp680_ts_exit(void) { free_irq(HP680_TS_IRQ, NULL); - cancel_delayed_work_sync(&work); + cancel_delayed_work(&work); + flush_scheduled_work(); input_unregister_device(hp680_ts_dev); } diff --git a/trunk/drivers/input/touchscreen/intel-mid-touch.c b/trunk/drivers/input/touchscreen/intel-mid-touch.c deleted file mode 100644 index c0307b22d86f..000000000000 --- a/trunk/drivers/input/touchscreen/intel-mid-touch.c +++ /dev/null @@ -1,687 +0,0 @@ -/* - * Intel MID Resistive Touch Screen Driver - * - * Copyright (C) 2008 Intel Corp - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * 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. - * - * Questions/Comments/Bug fixes to Sreedhara (sreedhara.ds@intel.com) - * Ramesh Agarwal (ramesh.agarwal@intel.com) - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * TODO: - * review conversion of r/m/w sequences - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* PMIC Interrupt registers */ -#define PMIC_REG_ID1 0x00 /* PMIC ID1 register */ - -/* PMIC Interrupt registers */ -#define PMIC_REG_INT 0x04 /* PMIC interrupt register */ -#define PMIC_REG_MINT 0x05 /* PMIC interrupt mask register */ - -/* ADC Interrupt registers */ -#define PMIC_REG_ADCINT 0x5F /* ADC interrupt register */ -#define PMIC_REG_MADCINT 0x60 /* ADC interrupt mask register */ - -/* ADC Control registers */ -#define PMIC_REG_ADCCNTL1 0x61 /* ADC control register */ - -/* ADC Channel Selection registers */ -#define PMICADDR0 0xA4 -#define END_OF_CHANNEL 0x1F - -/* ADC Result register */ -#define PMIC_REG_ADCSNS0H 0x64 - -/* ADC channels for touch screen */ -#define MRST_TS_CHAN10 0xA /* Touch screen X+ connection */ -#define MRST_TS_CHAN11 0xB /* Touch screen X- connection */ -#define MRST_TS_CHAN12 0xC /* Touch screen Y+ connection */ -#define MRST_TS_CHAN13 0xD /* Touch screen Y- connection */ - -/* Touch screen channel BIAS constants */ -#define MRST_XBIAS 0x20 -#define MRST_YBIAS 0x40 -#define MRST_ZBIAS 0x80 - -/* Touch screen coordinates */ -#define MRST_X_MIN 10 -#define MRST_X_MAX 1024 -#define MRST_X_FUZZ 5 -#define MRST_Y_MIN 10 -#define MRST_Y_MAX 1024 -#define MRST_Y_FUZZ 5 -#define MRST_PRESSURE_MIN 0 -#define MRST_PRESSURE_NOMINAL 50 -#define MRST_PRESSURE_MAX 100 - -#define WAIT_ADC_COMPLETION 10 /* msec */ - -/* PMIC ADC round robin delays */ -#define ADC_LOOP_DELAY0 0x0 /* Continuous loop */ -#define ADC_LOOP_DELAY1 0x1 /* 4.5 ms approximate */ - -/* PMIC Vendor Identifiers */ -#define PMIC_VENDOR_FS 0 /* PMIC vendor FreeScale */ -#define PMIC_VENDOR_MAXIM 1 /* PMIC vendor MAXIM */ -#define PMIC_VENDOR_NEC 2 /* PMIC vendor NEC */ -#define MRSTOUCH_MAX_CHANNELS 32 /* Maximum ADC channels */ - -/* Touch screen device structure */ -struct mrstouch_dev { - struct device *dev; /* device associated with touch screen */ - struct input_dev *input; - char phys[32]; - u16 asr; /* Address selection register */ - int irq; - unsigned int vendor; /* PMIC vendor */ - unsigned int rev; /* PMIC revision */ - - int (*read_prepare)(struct mrstouch_dev *tsdev); - int (*read)(struct mrstouch_dev *tsdev, u16 *x, u16 *y, u16 *z); - int (*read_finish)(struct mrstouch_dev *tsdev); -}; - - -/*************************** NEC and Maxim Interface ************************/ - -static int mrstouch_nec_adc_read_prepare(struct mrstouch_dev *tsdev) -{ - return intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0, 0x20); -} - -/* - * Enables PENDET interrupt. - */ -static int mrstouch_nec_adc_read_finish(struct mrstouch_dev *tsdev) -{ - int err; - - err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x20, 0x20); - if (!err) - err = intel_scu_ipc_update_register(PMIC_REG_ADCCNTL1, 0, 0x05); - - return err; -} - -/* - * Reads PMIC ADC touch screen result - * Reads ADC storage registers for higher 7 and lower 3 bits and - * converts the two readings into a single value and turns off gain bit - */ -static int mrstouch_ts_chan_read(u16 offset, u16 chan, u16 *vp, u16 *vm) -{ - int err; - u16 result; - u32 res; - - result = PMIC_REG_ADCSNS0H + offset; - - if (chan == MRST_TS_CHAN12) - result += 4; - - err = intel_scu_ipc_ioread32(result, &res); - if (err) - return err; - - /* Mash the bits up */ - - *vp = (res & 0xFF) << 3; /* Highest 7 bits */ - *vp |= (res >> 8) & 0x07; /* Lower 3 bits */ - *vp &= 0x3FF; - - res >>= 16; - - *vm = (res & 0xFF) << 3; /* Highest 7 bits */ - *vm |= (res >> 8) & 0x07; /* Lower 3 bits */ - *vm &= 0x3FF; - - return 0; -} - -/* - * Enables X, Y and Z bias values - * Enables YPYM for X channels and XPXM for Y channels - */ -static int mrstouch_ts_bias_set(uint offset, uint bias) -{ - int count; - u16 chan, start; - u16 reg[4]; - u8 data[4]; - - chan = PMICADDR0 + offset; - start = MRST_TS_CHAN10; - - for (count = 0; count <= 3; count++) { - reg[count] = chan++; - data[count] = bias | (start + count); - } - - return intel_scu_ipc_writev(reg, data, 4); -} - -/* To read touch screen channel values */ -static int mrstouch_nec_adc_read(struct mrstouch_dev *tsdev, - u16 *x, u16 *y, u16 *z) -{ - int err; - u16 xm, ym, zm; - - /* configure Y bias for X channels */ - err = mrstouch_ts_bias_set(tsdev->asr, MRST_YBIAS); - if (err) - goto ipc_error; - - msleep(WAIT_ADC_COMPLETION); - - /* read x+ and x- channels */ - err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, x, &xm); - if (err) - goto ipc_error; - - /* configure x bias for y channels */ - err = mrstouch_ts_bias_set(tsdev->asr, MRST_XBIAS); - if (err) - goto ipc_error; - - msleep(WAIT_ADC_COMPLETION); - - /* read y+ and y- channels */ - err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN12, y, &ym); - if (err) - goto ipc_error; - - /* configure z bias for x and y channels */ - err = mrstouch_ts_bias_set(tsdev->asr, MRST_ZBIAS); - if (err) - goto ipc_error; - - msleep(WAIT_ADC_COMPLETION); - - /* read z+ and z- channels */ - err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, z, &zm); - if (err) - goto ipc_error; - - return 0; - -ipc_error: - dev_err(tsdev->dev, "ipc error during adc read\n"); - return err; -} - - -/*************************** Freescale Interface ************************/ - -static int mrstouch_fs_adc_read_prepare(struct mrstouch_dev *tsdev) -{ - int err, count; - u16 chan; - u16 reg[5]; - u8 data[5]; - - /* Stop the ADC */ - err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x00, 0x02); - if (err) - goto ipc_error; - - chan = PMICADDR0 + tsdev->asr; - - /* Set X BIAS */ - for (count = 0; count <= 3; count++) { - reg[count] = chan++; - data[count] = 0x2A; - } - reg[count] = chan++; /* Dummy */ - data[count] = 0; - - err = intel_scu_ipc_writev(reg, data, 5); - if (err) - goto ipc_error; - - msleep(WAIT_ADC_COMPLETION); - - /* Set Y BIAS */ - for (count = 0; count <= 3; count++) { - reg[count] = chan++; - data[count] = 0x4A; - } - reg[count] = chan++; /* Dummy */ - data[count] = 0; - - err = intel_scu_ipc_writev(reg, data, 5); - if (err) - goto ipc_error; - - msleep(WAIT_ADC_COMPLETION); - - /* Set Z BIAS */ - err = intel_scu_ipc_iowrite32(chan + 2, 0x8A8A8A8A); - if (err) - goto ipc_error; - - msleep(WAIT_ADC_COMPLETION); - - return 0; - -ipc_error: - dev_err(tsdev->dev, "ipc error during %s\n", __func__); - return err; -} - -static int mrstouch_fs_adc_read(struct mrstouch_dev *tsdev, - u16 *x, u16 *y, u16 *z) -{ - int err; - u16 result; - u16 reg[4]; - u8 data[4]; - - result = PMIC_REG_ADCSNS0H + tsdev->asr; - - reg[0] = result + 4; - reg[1] = result + 5; - reg[2] = result + 16; - reg[3] = result + 17; - - err = intel_scu_ipc_readv(reg, data, 4); - if (err) - goto ipc_error; - - *x = data[0] << 3; /* Higher 7 bits */ - *x |= data[1] & 0x7; /* Lower 3 bits */ - *x &= 0x3FF; - - *y = data[2] << 3; /* Higher 7 bits */ - *y |= data[3] & 0x7; /* Lower 3 bits */ - *y &= 0x3FF; - - /* Read Z value */ - reg[0] = result + 28; - reg[1] = result + 29; - - err = intel_scu_ipc_readv(reg, data, 4); - if (err) - goto ipc_error; - - *z = data[0] << 3; /* Higher 7 bits */ - *z |= data[1] & 0x7; /* Lower 3 bits */ - *z &= 0x3FF; - - return 0; - -ipc_error: - dev_err(tsdev->dev, "ipc error during %s\n", __func__); - return err; -} - -static int mrstouch_fs_adc_read_finish(struct mrstouch_dev *tsdev) -{ - int err, count; - u16 chan; - u16 reg[5]; - u8 data[5]; - - /* Clear all TS channels */ - chan = PMICADDR0 + tsdev->asr; - for (count = 0; count <= 4; count++) { - reg[count] = chan++; - data[count] = 0; - } - err = intel_scu_ipc_writev(reg, data, 5); - if (err) - goto ipc_error; - - for (count = 0; count <= 4; count++) { - reg[count] = chan++; - data[count] = 0; - } - err = intel_scu_ipc_writev(reg, data, 5); - if (err) - goto ipc_error; - - err = intel_scu_ipc_iowrite32(chan + 2, 0x00000000); - if (err) - goto ipc_error; - - /* Start ADC */ - err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x02, 0x02); - if (err) - goto ipc_error; - - return 0; - -ipc_error: - dev_err(tsdev->dev, "ipc error during %s\n", __func__); - return err; -} - -static void mrstouch_report_event(struct input_dev *input, - unsigned int x, unsigned int y, unsigned int z) -{ - if (z > MRST_PRESSURE_NOMINAL) { - /* Pen touched, report button touch and coordinates */ - input_report_key(input, BTN_TOUCH, 1); - input_report_abs(input, ABS_X, x); - input_report_abs(input, ABS_Y, y); - } else { - input_report_key(input, BTN_TOUCH, 0); - } - - input_report_abs(input, ABS_PRESSURE, z); - input_sync(input); -} - -/* PENDET interrupt handler */ -static irqreturn_t mrstouch_pendet_irq(int irq, void *dev_id) -{ - struct mrstouch_dev *tsdev = dev_id; - u16 x, y, z; - - /* - * Should we lower thread priority? Probably not, since we are - * not spinning but sleeping... - */ - - if (tsdev->read_prepare(tsdev)) - goto out; - - do { - if (tsdev->read(tsdev, &x, &y, &z)) - break; - - mrstouch_report_event(tsdev->input, x, y, z); - } while (z > MRST_PRESSURE_NOMINAL); - - tsdev->read_finish(tsdev); - -out: - return IRQ_HANDLED; -} - -/* Utility to read PMIC ID */ -static int __devinit mrstouch_read_pmic_id(uint *vendor, uint *rev) -{ - int err; - u8 r; - - err = intel_scu_ipc_ioread8(PMIC_REG_ID1, &r); - if (err) - return err; - - *vendor = r & 0x7; - *rev = (r >> 3) & 0x7; - - return 0; -} - -/* - * Parse ADC channels to find end of the channel configured by other ADC user - * NEC and MAXIM requires 4 channels and FreeScale needs 18 channels - */ -static int __devinit mrstouch_chan_parse(struct mrstouch_dev *tsdev) -{ - int err, i, found; - u8 r8; - - found = -1; - - for (i = 0; i < MRSTOUCH_MAX_CHANNELS; i++) { - if (found >= 0) - break; - - err = intel_scu_ipc_ioread8(PMICADDR0 + i, &r8); - if (err) - return err; - - if (r8 == END_OF_CHANNEL) { - found = i; - break; - } - } - if (found < 0) - return 0; - - if (tsdev->vendor == PMIC_VENDOR_FS) { - if (found && found > (MRSTOUCH_MAX_CHANNELS - 18)) - return -ENOSPC; - } else { - if (found && found > (MRSTOUCH_MAX_CHANNELS - 4)) - return -ENOSPC; - } - return found; -} - - -/* - * Writes touch screen channels to ADC address selection registers - */ -static int __devinit mrstouch_ts_chan_set(uint offset) -{ - u16 chan; - - int ret, count; - - chan = PMICADDR0 + offset; - for (count = 0; count <= 3; count++) { - ret = intel_scu_ipc_iowrite8(chan++, MRST_TS_CHAN10 + count); - if (ret) - return ret; - } - return intel_scu_ipc_iowrite8(chan++, END_OF_CHANNEL); -} - -/* Initialize ADC */ -static int __devinit mrstouch_adc_init(struct mrstouch_dev *tsdev) -{ - int err, start; - u8 ra, rm; - - err = mrstouch_read_pmic_id(&tsdev->vendor, &tsdev->rev); - if (err) { - dev_err(tsdev->dev, "Unable to read PMIC id\n"); - return err; - } - - switch (tsdev->vendor) { - case PMIC_VENDOR_NEC: - case PMIC_VENDOR_MAXIM: - tsdev->read_prepare = mrstouch_nec_adc_read_prepare; - tsdev->read = mrstouch_nec_adc_read; - tsdev->read_finish = mrstouch_nec_adc_read_finish; - break; - - case PMIC_VENDOR_FS: - tsdev->read_prepare = mrstouch_fs_adc_read_prepare; - tsdev->read = mrstouch_fs_adc_read; - tsdev->read_finish = mrstouch_fs_adc_read_finish; - break; - - default: - dev_err(tsdev->dev, - "Unsupported touchscreen: %d\n", tsdev->vendor); - return -ENXIO; - } - - start = mrstouch_chan_parse(tsdev); - if (start < 0) { - dev_err(tsdev->dev, "Unable to parse channels\n"); - return start; - } - - tsdev->asr = start; - - /* - * ADC power on, start, enable PENDET and set loop delay - * ADC loop delay is set to 4.5 ms approximately - * Loop delay more than this results in jitter in adc readings - * Setting loop delay to 0 (continous loop) in MAXIM stops PENDET - * interrupt generation sometimes. - */ - - if (tsdev->vendor == PMIC_VENDOR_FS) { - ra = 0xE0 | ADC_LOOP_DELAY0; - rm = 0x5; - } else { - /* NEC and MAXIm not consistent with loop delay 0 */ - ra = 0xE0 | ADC_LOOP_DELAY1; - rm = 0x0; - - /* configure touch screen channels */ - err = mrstouch_ts_chan_set(tsdev->asr); - if (err) - return err; - } - - err = intel_scu_ipc_update_register(PMIC_REG_ADCCNTL1, ra, 0xE7); - if (err) - return err; - - err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, rm, 0x03); - if (err) - return err; - - return 0; -} - - -/* Probe function for touch screen driver */ -static int __devinit mrstouch_probe(struct platform_device *pdev) -{ - struct mrstouch_dev *tsdev; - struct input_dev *input; - int err; - int irq; - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "no interrupt assigned\n"); - return -EINVAL; - } - - tsdev = kzalloc(sizeof(struct mrstouch_dev), GFP_KERNEL); - input = input_allocate_device(); - if (!tsdev || !input) { - dev_err(&pdev->dev, "unable to allocate memory\n"); - err = -ENOMEM; - goto err_free_mem; - } - - tsdev->dev = &pdev->dev; - tsdev->input = input; - tsdev->irq = irq; - - snprintf(tsdev->phys, sizeof(tsdev->phys), - "%s/input0", dev_name(tsdev->dev)); - - err = mrstouch_adc_init(tsdev); - if (err) { - dev_err(&pdev->dev, "ADC initialization failed\n"); - goto err_free_mem; - } - - input->name = "mrst_touchscreen"; - input->phys = tsdev->phys; - input->dev.parent = tsdev->dev; - - input->id.vendor = tsdev->vendor; - input->id.version = tsdev->rev; - - input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); - - input_set_abs_params(tsdev->input, ABS_X, - MRST_X_MIN, MRST_X_MAX, MRST_X_FUZZ, 0); - input_set_abs_params(tsdev->input, ABS_Y, - MRST_Y_MIN, MRST_Y_MAX, MRST_Y_FUZZ, 0); - input_set_abs_params(tsdev->input, ABS_PRESSURE, - MRST_PRESSURE_MIN, MRST_PRESSURE_MAX, 0, 0); - - err = request_threaded_irq(tsdev->irq, NULL, mrstouch_pendet_irq, - 0, "mrstouch", tsdev); - if (err) { - dev_err(tsdev->dev, "unable to allocate irq\n"); - goto err_free_mem; - } - - err = input_register_device(tsdev->input); - if (err) { - dev_err(tsdev->dev, "unable to register input device\n"); - goto err_free_irq; - } - - platform_set_drvdata(pdev, tsdev); - return 0; - -err_free_irq: - free_irq(tsdev->irq, tsdev); -err_free_mem: - input_free_device(input); - kfree(tsdev); - return err; -} - -static int __devexit mrstouch_remove(struct platform_device *pdev) -{ - struct mrstouch_dev *tsdev = platform_get_drvdata(pdev); - - free_irq(tsdev->irq, tsdev); - input_unregister_device(tsdev->input); - kfree(tsdev); - - platform_set_drvdata(pdev, NULL); - - return 0; -} - -static struct platform_driver mrstouch_driver = { - .driver = { - .name = "pmic_touch", - .owner = THIS_MODULE, - }, - .probe = mrstouch_probe, - .remove = __devexit_p(mrstouch_remove), -}; - -static int __init mrstouch_init(void) -{ - return platform_driver_register(&mrstouch_driver); -} -module_init(mrstouch_init); - -static void __exit mrstouch_exit(void) -{ - platform_driver_unregister(&mrstouch_driver); -} -module_exit(mrstouch_exit); - -MODULE_AUTHOR("Sreedhara Murthy. D.S, sreedhara.ds@intel.com"); -MODULE_DESCRIPTION("Intel Moorestown Resistive Touch Screen Driver"); -MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/input/touchscreen/lpc32xx_ts.c b/trunk/drivers/input/touchscreen/lpc32xx_ts.c deleted file mode 100644 index dcf803f5a1f7..000000000000 --- a/trunk/drivers/input/touchscreen/lpc32xx_ts.c +++ /dev/null @@ -1,411 +0,0 @@ -/* - * LPC32xx built-in touchscreen driver - * - * Copyright (C) 2010 NXP Semiconductors - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * Touchscreen controller register offsets - */ -#define LPC32XX_TSC_STAT 0x00 -#define LPC32XX_TSC_SEL 0x04 -#define LPC32XX_TSC_CON 0x08 -#define LPC32XX_TSC_FIFO 0x0C -#define LPC32XX_TSC_DTR 0x10 -#define LPC32XX_TSC_RTR 0x14 -#define LPC32XX_TSC_UTR 0x18 -#define LPC32XX_TSC_TTR 0x1C -#define LPC32XX_TSC_DXP 0x20 -#define LPC32XX_TSC_MIN_X 0x24 -#define LPC32XX_TSC_MAX_X 0x28 -#define LPC32XX_TSC_MIN_Y 0x2C -#define LPC32XX_TSC_MAX_Y 0x30 -#define LPC32XX_TSC_AUX_UTR 0x34 -#define LPC32XX_TSC_AUX_MIN 0x38 -#define LPC32XX_TSC_AUX_MAX 0x3C - -#define LPC32XX_TSC_STAT_FIFO_OVRRN (1 << 8) -#define LPC32XX_TSC_STAT_FIFO_EMPTY (1 << 7) - -#define LPC32XX_TSC_SEL_DEFVAL 0x0284 - -#define LPC32XX_TSC_ADCCON_IRQ_TO_FIFO_4 (0x1 << 11) -#define LPC32XX_TSC_ADCCON_X_SAMPLE_SIZE(s) ((10 - (s)) << 7) -#define LPC32XX_TSC_ADCCON_Y_SAMPLE_SIZE(s) ((10 - (s)) << 4) -#define LPC32XX_TSC_ADCCON_POWER_UP (1 << 2) -#define LPC32XX_TSC_ADCCON_AUTO_EN (1 << 0) - -#define LPC32XX_TSC_FIFO_TS_P_LEVEL (1 << 31) -#define LPC32XX_TSC_FIFO_NORMALIZE_X_VAL(x) (((x) & 0x03FF0000) >> 16) -#define LPC32XX_TSC_FIFO_NORMALIZE_Y_VAL(y) ((y) & 0x000003FF) - -#define LPC32XX_TSC_ADCDAT_VALUE_MASK 0x000003FF - -#define LPC32XX_TSC_MIN_XY_VAL 0x0 -#define LPC32XX_TSC_MAX_XY_VAL 0x3FF - -#define MOD_NAME "ts-lpc32xx" - -#define tsc_readl(dev, reg) \ - __raw_readl((dev)->tsc_base + (reg)) -#define tsc_writel(dev, reg, val) \ - __raw_writel((val), (dev)->tsc_base + (reg)) - -struct lpc32xx_tsc { - struct input_dev *dev; - void __iomem *tsc_base; - int irq; - struct clk *clk; -}; - -static void lpc32xx_fifo_clear(struct lpc32xx_tsc *tsc) -{ - while (!(tsc_readl(tsc, LPC32XX_TSC_STAT) & - LPC32XX_TSC_STAT_FIFO_EMPTY)) - tsc_readl(tsc, LPC32XX_TSC_FIFO); -} - -static irqreturn_t lpc32xx_ts_interrupt(int irq, void *dev_id) -{ - u32 tmp, rv[4], xs[4], ys[4]; - int idx; - struct lpc32xx_tsc *tsc = dev_id; - struct input_dev *input = tsc->dev; - - tmp = tsc_readl(tsc, LPC32XX_TSC_STAT); - - if (tmp & LPC32XX_TSC_STAT_FIFO_OVRRN) { - /* FIFO overflow - throw away samples */ - lpc32xx_fifo_clear(tsc); - return IRQ_HANDLED; - } - - /* - * Gather and normalize 4 samples. Pen-up events may have less - * than 4 samples, but its ok to pop 4 and let the last sample - * pen status check drop the samples. - */ - idx = 0; - while (idx < 4 && - !(tsc_readl(tsc, LPC32XX_TSC_STAT) & - LPC32XX_TSC_STAT_FIFO_EMPTY)) { - tmp = tsc_readl(tsc, LPC32XX_TSC_FIFO); - xs[idx] = LPC32XX_TSC_ADCDAT_VALUE_MASK - - LPC32XX_TSC_FIFO_NORMALIZE_X_VAL(tmp); - ys[idx] = LPC32XX_TSC_ADCDAT_VALUE_MASK - - LPC32XX_TSC_FIFO_NORMALIZE_Y_VAL(tmp); - rv[idx] = tmp; - idx++; - } - - /* Data is only valid if pen is still down in last sample */ - if (!(rv[3] & LPC32XX_TSC_FIFO_TS_P_LEVEL) && idx == 4) { - /* Use average of 2nd and 3rd sample for position */ - input_report_abs(input, ABS_X, (xs[1] + xs[2]) / 2); - input_report_abs(input, ABS_Y, (ys[1] + ys[2]) / 2); - input_report_key(input, BTN_TOUCH, 1); - } else { - input_report_key(input, BTN_TOUCH, 0); - } - - input_sync(input); - - return IRQ_HANDLED; -} - -static void lpc32xx_stop_tsc(struct lpc32xx_tsc *tsc) -{ - /* Disable auto mode */ - tsc_writel(tsc, LPC32XX_TSC_CON, - tsc_readl(tsc, LPC32XX_TSC_CON) & - ~LPC32XX_TSC_ADCCON_AUTO_EN); - - clk_disable(tsc->clk); -} - -static void lpc32xx_setup_tsc(struct lpc32xx_tsc *tsc) -{ - u32 tmp; - - clk_enable(tsc->clk); - - tmp = tsc_readl(tsc, LPC32XX_TSC_CON) & ~LPC32XX_TSC_ADCCON_POWER_UP; - - /* Set the TSC FIFO depth to 4 samples @ 10-bits per sample (max) */ - tmp = LPC32XX_TSC_ADCCON_IRQ_TO_FIFO_4 | - LPC32XX_TSC_ADCCON_X_SAMPLE_SIZE(10) | - LPC32XX_TSC_ADCCON_Y_SAMPLE_SIZE(10); - tsc_writel(tsc, LPC32XX_TSC_CON, tmp); - - /* These values are all preset */ - tsc_writel(tsc, LPC32XX_TSC_SEL, LPC32XX_TSC_SEL_DEFVAL); - tsc_writel(tsc, LPC32XX_TSC_MIN_X, LPC32XX_TSC_MIN_XY_VAL); - tsc_writel(tsc, LPC32XX_TSC_MAX_X, LPC32XX_TSC_MAX_XY_VAL); - tsc_writel(tsc, LPC32XX_TSC_MIN_Y, LPC32XX_TSC_MIN_XY_VAL); - tsc_writel(tsc, LPC32XX_TSC_MAX_Y, LPC32XX_TSC_MAX_XY_VAL); - - /* Aux support is not used */ - tsc_writel(tsc, LPC32XX_TSC_AUX_UTR, 0); - tsc_writel(tsc, LPC32XX_TSC_AUX_MIN, 0); - tsc_writel(tsc, LPC32XX_TSC_AUX_MAX, 0); - - /* - * Set sample rate to about 240Hz per X/Y pair. A single measurement - * consists of 4 pairs which gives about a 60Hz sample rate based on - * a stable 32768Hz clock source. Values are in clocks. - * Rate is (32768 / (RTR + XCONV + RTR + YCONV + DXP + TTR + UTR) / 4 - */ - tsc_writel(tsc, LPC32XX_TSC_RTR, 0x2); - tsc_writel(tsc, LPC32XX_TSC_DTR, 0x2); - tsc_writel(tsc, LPC32XX_TSC_TTR, 0x10); - tsc_writel(tsc, LPC32XX_TSC_DXP, 0x4); - tsc_writel(tsc, LPC32XX_TSC_UTR, 88); - - lpc32xx_fifo_clear(tsc); - - /* Enable automatic ts event capture */ - tsc_writel(tsc, LPC32XX_TSC_CON, tmp | LPC32XX_TSC_ADCCON_AUTO_EN); -} - -static int lpc32xx_ts_open(struct input_dev *dev) -{ - struct lpc32xx_tsc *tsc = input_get_drvdata(dev); - - lpc32xx_setup_tsc(tsc); - - return 0; -} - -static void lpc32xx_ts_close(struct input_dev *dev) -{ - struct lpc32xx_tsc *tsc = input_get_drvdata(dev); - - lpc32xx_stop_tsc(tsc); -} - -static int __devinit lpc32xx_ts_probe(struct platform_device *pdev) -{ - struct lpc32xx_tsc *tsc; - struct input_dev *input; - struct resource *res; - resource_size_t size; - int irq; - int error; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "Can't get memory resource\n"); - return -ENOENT; - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "Can't get interrupt resource\n"); - return irq; - } - - tsc = kzalloc(sizeof(*tsc), GFP_KERNEL); - input = input_allocate_device(); - if (!tsc || !input) { - dev_err(&pdev->dev, "failed allocating memory\n"); - error = -ENOMEM; - goto err_free_mem; - } - - tsc->dev = input; - tsc->irq = irq; - - size = resource_size(res); - - if (!request_mem_region(res->start, size, pdev->name)) { - dev_err(&pdev->dev, "TSC registers are not free\n"); - error = -EBUSY; - goto err_free_mem; - } - - tsc->tsc_base = ioremap(res->start, size); - if (!tsc->tsc_base) { - dev_err(&pdev->dev, "Can't map memory\n"); - error = -ENOMEM; - goto err_release_mem; - } - - tsc->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(tsc->clk)) { - dev_err(&pdev->dev, "failed getting clock\n"); - error = PTR_ERR(tsc->clk); - goto err_unmap; - } - - input->name = MOD_NAME; - input->phys = "lpc32xx/input0"; - input->id.bustype = BUS_HOST; - input->id.vendor = 0x0001; - input->id.product = 0x0002; - input->id.version = 0x0100; - input->dev.parent = &pdev->dev; - input->open = lpc32xx_ts_open; - input->close = lpc32xx_ts_close; - - input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); - input_set_abs_params(input, ABS_X, LPC32XX_TSC_MIN_XY_VAL, - LPC32XX_TSC_MAX_XY_VAL, 0, 0); - input_set_abs_params(input, ABS_Y, LPC32XX_TSC_MIN_XY_VAL, - LPC32XX_TSC_MAX_XY_VAL, 0, 0); - - input_set_drvdata(input, tsc); - - error = request_irq(tsc->irq, lpc32xx_ts_interrupt, - IRQF_DISABLED, pdev->name, tsc); - if (error) { - dev_err(&pdev->dev, "failed requesting interrupt\n"); - goto err_put_clock; - } - - error = input_register_device(input); - if (error) { - dev_err(&pdev->dev, "failed registering input device\n"); - goto err_free_irq; - } - - platform_set_drvdata(pdev, tsc); - device_init_wakeup(&pdev->dev, 1); - - return 0; - -err_free_irq: - free_irq(tsc->irq, tsc); -err_put_clock: - clk_put(tsc->clk); -err_unmap: - iounmap(tsc->tsc_base); -err_release_mem: - release_mem_region(res->start, size); -err_free_mem: - input_free_device(input); - kfree(tsc); - - return error; -} - -static int __devexit lpc32xx_ts_remove(struct platform_device *pdev) -{ - struct lpc32xx_tsc *tsc = platform_get_drvdata(pdev); - struct resource *res; - - device_init_wakeup(&pdev->dev, 0); - free_irq(tsc->irq, tsc); - - input_unregister_device(tsc->dev); - - clk_put(tsc->clk); - - iounmap(tsc->tsc_base); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, resource_size(res)); - - kfree(tsc); - - return 0; -} - -#ifdef CONFIG_PM -static int lpc32xx_ts_suspend(struct device *dev) -{ - struct lpc32xx_tsc *tsc = dev_get_drvdata(dev); - struct input_dev *input = tsc->dev; - - /* - * Suspend and resume can be called when the device hasn't been - * enabled. If there are no users that have the device open, then - * avoid calling the TSC stop and start functions as the TSC - * isn't yet clocked. - */ - mutex_lock(&input->mutex); - - if (input->users) { - if (device_may_wakeup(dev)) - enable_irq_wake(tsc->irq); - else - lpc32xx_stop_tsc(tsc); - } - - mutex_unlock(&input->mutex); - - return 0; -} - -static int lpc32xx_ts_resume(struct device *dev) -{ - struct lpc32xx_tsc *tsc = dev_get_drvdata(dev); - struct input_dev *input = tsc->dev; - - mutex_lock(&input->mutex); - - if (input->users) { - if (device_may_wakeup(dev)) - disable_irq_wake(tsc->irq); - else - lpc32xx_setup_tsc(tsc); - } - - mutex_unlock(&input->mutex); - - return 0; -} - -static const struct dev_pm_ops lpc32xx_ts_pm_ops = { - .suspend = lpc32xx_ts_suspend, - .resume = lpc32xx_ts_resume, -}; -#define LPC32XX_TS_PM_OPS (&lpc32xx_ts_pm_ops) -#else -#define LPC32XX_TS_PM_OPS NULL -#endif - -static struct platform_driver lpc32xx_ts_driver = { - .probe = lpc32xx_ts_probe, - .remove = __devexit_p(lpc32xx_ts_remove), - .driver = { - .name = MOD_NAME, - .owner = THIS_MODULE, - .pm = LPC32XX_TS_PM_OPS, - }, -}; - -static int __init lpc32xx_ts_init(void) -{ - return platform_driver_register(&lpc32xx_ts_driver); -} -module_init(lpc32xx_ts_init); - -static void __exit lpc32xx_ts_exit(void) -{ - platform_driver_unregister(&lpc32xx_ts_driver); -} -module_exit(lpc32xx_ts_exit); - -MODULE_AUTHOR("Kevin Wells stmpe = stmpe; @@ -365,6 +361,7 @@ static int __devexit stmpe_ts_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); input_unregister_device(ts->idev); + input_free_device(ts->idev); kfree(ts); diff --git a/trunk/drivers/input/touchscreen/tps6507x-ts.c b/trunk/drivers/input/touchscreen/tps6507x-ts.c index c8c136cf7bbc..a644d18c04dc 100644 --- a/trunk/drivers/input/touchscreen/tps6507x-ts.c +++ b/trunk/drivers/input/touchscreen/tps6507x-ts.c @@ -335,7 +335,6 @@ static int tps6507x_ts_probe(struct platform_device *pdev) dev_err(tsc->dev, "schedule failed"); goto err2; } - platform_set_drvdata(pdev, tps6507x_dev); return 0; @@ -359,7 +358,7 @@ static int __devexit tps6507x_ts_remove(struct platform_device *pdev) cancel_delayed_work_sync(&tsc->work); destroy_workqueue(tsc->wq); - input_unregister_device(input_dev); + input_free_device(input_dev); tps6507x_dev->ts = NULL; kfree(tsc); diff --git a/trunk/drivers/input/touchscreen/tsc2007.c b/trunk/drivers/input/touchscreen/tsc2007.c index 80467f262331..be23780e8a3e 100644 --- a/trunk/drivers/input/touchscreen/tsc2007.c +++ b/trunk/drivers/input/touchscreen/tsc2007.c @@ -265,7 +265,7 @@ static int __devinit tsc2007_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct tsc2007 *ts; - struct tsc2007_platform_data *pdata = client->dev.platform_data; + struct tsc2007_platform_data *pdata = pdata = client->dev.platform_data; struct input_dev *input_dev; int err; diff --git a/trunk/drivers/input/touchscreen/wacom_w8001.c b/trunk/drivers/input/touchscreen/wacom_w8001.c index 9ae4c7b16ba7..56dc35c94bb1 100644 --- a/trunk/drivers/input/touchscreen/wacom_w8001.c +++ b/trunk/drivers/input/touchscreen/wacom_w8001.c @@ -2,7 +2,6 @@ * Wacom W8001 penabled serial touchscreen driver * * Copyright (c) 2008 Jaya Kumar - * Copyright (c) 2010 Red Hat, Inc. * * 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 @@ -31,24 +30,11 @@ MODULE_LICENSE("GPL"); #define W8001_LEAD_BYTE 0x80 #define W8001_TAB_MASK 0x40 #define W8001_TAB_BYTE 0x40 -/* set in first byte of touch data packets */ -#define W8001_TOUCH_MASK (0x10 | W8001_LEAD_MASK) -#define W8001_TOUCH_BYTE (0x10 | W8001_LEAD_BYTE) #define W8001_QUERY_PACKET 0x20 #define W8001_CMD_START '1' #define W8001_CMD_QUERY '*' -#define W8001_CMD_TOUCHQUERY '%' - -/* length of data packets in bytes, depends on device. */ -#define W8001_PKTLEN_TOUCH93 5 -#define W8001_PKTLEN_TOUCH9A 7 -#define W8001_PKTLEN_TPCPEN 9 -#define W8001_PKTLEN_TPCCTL 11 /* control packet */ -#define W8001_PKTLEN_TOUCH2FG 13 - -#define MAX_TRACKING_ID 0xFF /* arbitrarily chosen */ struct w8001_coord { u8 rdy; @@ -62,15 +48,6 @@ struct w8001_coord { u8 tilt_y; }; -/* touch query reply packet */ -struct w8001_touch_query { - u8 panel_res; - u8 capacity_res; - u8 sensor_id; - u16 x; - u16 y; -}; - /* * Per-touchscreen data. */ @@ -85,9 +62,6 @@ struct w8001 { unsigned char response[W8001_MAX_LENGTH]; unsigned char data[W8001_MAX_LENGTH]; char phys[32]; - int type; - unsigned int pktlen; - int trkid[2]; }; static void parse_data(u8 *data, struct w8001_coord *coord) @@ -114,98 +88,11 @@ static void parse_data(u8 *data, struct w8001_coord *coord) coord->tilt_y = data[8] & 0x7F; } -static void parse_touch(struct w8001 *w8001) -{ - static int trkid; - struct input_dev *dev = w8001->dev; - unsigned char *data = w8001->data; - int i; - - for (i = 0; i < 2; i++) { - input_mt_slot(dev, i); - - if (data[0] & (1 << i)) { - int x = (data[6 * i + 1] << 7) | (data[6 * i + 2]); - int y = (data[6 * i + 3] << 7) | (data[6 * i + 4]); - /* data[5,6] and [11,12] is finger capacity */ - - input_report_abs(dev, ABS_MT_POSITION_X, x); - input_report_abs(dev, ABS_MT_POSITION_Y, y); - input_report_abs(dev, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER); - if (w8001->trkid[i] < 0) - w8001->trkid[i] = trkid++ & MAX_TRACKING_ID; - } else { - w8001->trkid[i] = -1; - } - input_report_abs(dev, ABS_MT_TRACKING_ID, w8001->trkid[i]); - } - - input_sync(dev); -} - -static void parse_touchquery(u8 *data, struct w8001_touch_query *query) -{ - memset(query, 0, sizeof(*query)); - - query->panel_res = data[1]; - query->sensor_id = data[2] & 0x7; - query->capacity_res = data[7]; - - query->x = data[3] << 9; - query->x |= data[4] << 2; - query->x |= (data[2] >> 5) & 0x3; - - query->y = data[5] << 9; - query->y |= data[6] << 2; - query->y |= (data[2] >> 3) & 0x3; -} - -static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord) -{ - struct input_dev *dev = w8001->dev; - - /* - * We have 1 bit for proximity (rdy) and 3 bits for tip, side, - * side2/eraser. If rdy && f2 are set, this can be either pen + side2, - * or eraser. assume - * - if dev is already in proximity and f2 is toggled → pen + side2 - * - if dev comes into proximity with f2 set → eraser - * If f2 disappears after assuming eraser, fake proximity out for - * eraser and in for pen. - */ - - if (!w8001->type) { - w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; - } else if (w8001->type == BTN_TOOL_RUBBER) { - if (!coord->f2) { - input_report_abs(dev, ABS_PRESSURE, 0); - input_report_key(dev, BTN_TOUCH, 0); - input_report_key(dev, BTN_STYLUS, 0); - input_report_key(dev, BTN_STYLUS2, 0); - input_report_key(dev, BTN_TOOL_RUBBER, 0); - input_sync(dev); - w8001->type = BTN_TOOL_PEN; - } - } else { - input_report_key(dev, BTN_STYLUS2, coord->f2); - } - - input_report_abs(dev, ABS_X, coord->x); - input_report_abs(dev, ABS_Y, coord->y); - input_report_abs(dev, ABS_PRESSURE, coord->pen_pressure); - input_report_key(dev, BTN_TOUCH, coord->tsw); - input_report_key(dev, BTN_STYLUS, coord->f1); - input_report_key(dev, w8001->type, coord->rdy); - input_sync(dev); - - if (!coord->rdy) - w8001->type = 0; -} - static irqreturn_t w8001_interrupt(struct serio *serio, unsigned char data, unsigned int flags) { struct w8001 *w8001 = serio_get_drvdata(serio); + struct input_dev *dev = w8001->dev; struct w8001_coord coord; unsigned char tmp; @@ -218,45 +105,26 @@ static irqreturn_t w8001_interrupt(struct serio *serio, } break; - case W8001_PKTLEN_TOUCH93 - 1: - case W8001_PKTLEN_TOUCH9A - 1: - /* ignore one-finger touch packet. */ - if (w8001->pktlen == w8001->idx) - w8001->idx = 0; - break; - - /* Pen coordinates packet */ - case W8001_PKTLEN_TPCPEN - 1: + case 8: tmp = w8001->data[0] & W8001_TAB_MASK; if (unlikely(tmp == W8001_TAB_BYTE)) break; - tmp = (w8001->data[0] & W8001_TOUCH_BYTE); - if (tmp == W8001_TOUCH_BYTE) - break; - w8001->idx = 0; parse_data(w8001->data, &coord); - report_pen_events(w8001, &coord); + input_report_abs(dev, ABS_X, coord.x); + input_report_abs(dev, ABS_Y, coord.y); + input_report_abs(dev, ABS_PRESSURE, coord.pen_pressure); + input_report_key(dev, BTN_TOUCH, coord.tsw); + input_sync(dev); break; - /* control packet */ - case W8001_PKTLEN_TPCCTL - 1: - tmp = (w8001->data[0] & W8001_TOUCH_MASK); - if (tmp == W8001_TOUCH_BYTE) - break; - + case 10: w8001->idx = 0; memcpy(w8001->response, w8001->data, W8001_MAX_LENGTH); w8001->response_type = W8001_QUERY_PACKET; complete(&w8001->cmd_done); break; - - /* 2 finger touch packet */ - case W8001_PKTLEN_TOUCH2FG - 1: - w8001->idx = 0; - parse_touch(w8001); - break; } return IRQ_HANDLED; @@ -299,38 +167,6 @@ static int w8001_setup(struct w8001 *w8001) input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0); input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0); - error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true); - if (!error) { - struct w8001_touch_query touch; - - parse_touchquery(w8001->response, &touch); - - switch (touch.sensor_id) { - case 0: - case 2: - w8001->pktlen = W8001_PKTLEN_TOUCH93; - break; - case 1: - case 3: - case 4: - w8001->pktlen = W8001_PKTLEN_TOUCH9A; - break; - case 5: - w8001->pktlen = W8001_PKTLEN_TOUCH2FG; - - input_mt_create_slots(dev, 2); - input_set_abs_params(dev, ABS_MT_TRACKING_ID, - 0, MAX_TRACKING_ID, 0, 0); - input_set_abs_params(dev, ABS_MT_POSITION_X, - 0, touch.x, 0, 0); - input_set_abs_params(dev, ABS_MT_POSITION_Y, - 0, touch.y, 0, 0); - input_set_abs_params(dev, ABS_MT_TOOL_TYPE, - 0, 0, 0, 0); - break; - } - } - return w8001_command(w8001, W8001_CMD_START, false); } @@ -372,7 +208,6 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv) w8001->serio = serio; w8001->id = serio->id.id; w8001->dev = input_dev; - w8001->trkid[0] = w8001->trkid[1] = -1; init_completion(&w8001->cmd_done); snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys); @@ -386,10 +221,6 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv) input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); - input_dev->keybit[BIT_WORD(BTN_TOOL_PEN)] |= BIT_MASK(BTN_TOOL_PEN); - input_dev->keybit[BIT_WORD(BTN_TOOL_RUBBER)] |= BIT_MASK(BTN_TOOL_RUBBER); - input_dev->keybit[BIT_WORD(BTN_STYLUS)] |= BIT_MASK(BTN_STYLUS); - input_dev->keybit[BIT_WORD(BTN_STYLUS2)] |= BIT_MASK(BTN_STYLUS2); serio_set_drvdata(serio, w8001); err = serio_open(serio, drv); diff --git a/trunk/drivers/input/touchscreen/wm97xx-core.c b/trunk/drivers/input/touchscreen/wm97xx-core.c index 6b75c9f660ae..cbfef1ea7e30 100644 --- a/trunk/drivers/input/touchscreen/wm97xx-core.c +++ b/trunk/drivers/input/touchscreen/wm97xx-core.c @@ -125,8 +125,6 @@ int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel) { int power_adc = 0, auxval; u16 power = 0; - int rc = 0; - int timeout = 0; /* get codec */ mutex_lock(&wm->codec_mutex); @@ -145,9 +143,7 @@ int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel) /* Turn polling mode on to read AUX ADC */ wm->pen_probably_down = 1; - - while (rc != RC_VALID && timeout++ < 5) - rc = wm->codec->poll_sample(wm, adcsel, &auxval); + wm->codec->poll_sample(wm, adcsel, &auxval); if (power_adc) wm97xx_reg_write(wm, AC97_EXTENDED_MID, power | 0x8000); @@ -156,15 +152,8 @@ int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel) wm->pen_probably_down = 0; - if (timeout >= 5) { - dev_err(wm->dev, - "timeout reading auxadc %d, disabling digitiser\n", - adcsel); - wm->codec->dig_enable(wm, false); - } - mutex_unlock(&wm->codec_mutex); - return (rc == RC_VALID ? auxval & 0xfff : -EBUSY); + return auxval & 0xfff; } EXPORT_SYMBOL_GPL(wm97xx_read_aux_adc); @@ -695,7 +684,8 @@ static int wm97xx_probe(struct device *dev) touch_reg_err: platform_device_put(wm->touch_dev); touch_err: - platform_device_del(wm->battery_dev); + platform_device_unregister(wm->battery_dev); + wm->battery_dev = NULL; batt_reg_err: platform_device_put(wm->battery_dev); batt_err: diff --git a/trunk/drivers/media/IR/ir-keytable.c b/trunk/drivers/media/IR/ir-keytable.c index c06b4d50a3dc..7961d59f5cac 100644 --- a/trunk/drivers/media/IR/ir-keytable.c +++ b/trunk/drivers/media/IR/ir-keytable.c @@ -24,57 +24,15 @@ /* FIXME: IR_KEYPRESS_TIMEOUT should be protocol specific */ #define IR_KEYPRESS_TIMEOUT 250 -/** - * ir_create_table() - initializes a scancode table - * @rc_tab: the ir_scancode_table to initialize - * @name: name to assign to the table - * @ir_type: ir type to assign to the new table - * @size: initial size of the table - * @return: zero on success or a negative error code - * - * This routine will initialize the ir_scancode_table and will allocate - * memory to hold at least the specified number elements. - */ -static int ir_create_table(struct ir_scancode_table *rc_tab, - const char *name, u64 ir_type, size_t size) -{ - rc_tab->name = name; - rc_tab->ir_type = ir_type; - rc_tab->alloc = roundup_pow_of_two(size * sizeof(struct ir_scancode)); - rc_tab->size = rc_tab->alloc / sizeof(struct ir_scancode); - rc_tab->scan = kmalloc(rc_tab->alloc, GFP_KERNEL); - if (!rc_tab->scan) - return -ENOMEM; - - IR_dprintk(1, "Allocated space for %u keycode entries (%u bytes)\n", - rc_tab->size, rc_tab->alloc); - return 0; -} - -/** - * ir_free_table() - frees memory allocated by a scancode table - * @rc_tab: the table whose mappings need to be freed - * - * This routine will free memory alloctaed for key mappings used by given - * scancode table. - */ -static void ir_free_table(struct ir_scancode_table *rc_tab) -{ - rc_tab->size = 0; - kfree(rc_tab->scan); - rc_tab->scan = NULL; -} - /** * ir_resize_table() - resizes a scancode table if necessary * @rc_tab: the ir_scancode_table to resize - * @gfp_flags: gfp flags to use when allocating memory * @return: zero on success or a negative error code * * This routine will shrink the ir_scancode_table if it has lots of * unused entries and grow it if it is full. */ -static int ir_resize_table(struct ir_scancode_table *rc_tab, gfp_t gfp_flags) +static int ir_resize_table(struct ir_scancode_table *rc_tab) { unsigned int oldalloc = rc_tab->alloc; unsigned int newalloc = oldalloc; @@ -99,7 +57,7 @@ static int ir_resize_table(struct ir_scancode_table *rc_tab, gfp_t gfp_flags) if (newalloc == oldalloc) return 0; - newscan = kmalloc(newalloc, gfp_flags); + newscan = kmalloc(newalloc, GFP_ATOMIC); if (!newscan) { IR_dprintk(1, "Failed to kmalloc %u bytes\n", newalloc); return -ENOMEM; @@ -114,78 +72,26 @@ static int ir_resize_table(struct ir_scancode_table *rc_tab, gfp_t gfp_flags) } /** - * ir_update_mapping() - set a keycode in the scancode->keycode table + * ir_do_setkeycode() - internal function to set a keycode in the + * scancode->keycode table * @dev: the struct input_dev device descriptor - * @rc_tab: scancode table to be adjusted - * @index: index of the mapping that needs to be updated - * @keycode: the desired keycode - * @return: previous keycode assigned to the mapping - * - * This routine is used to update scancode->keycopde mapping at given - * position. - */ -static unsigned int ir_update_mapping(struct input_dev *dev, - struct ir_scancode_table *rc_tab, - unsigned int index, - unsigned int new_keycode) -{ - int old_keycode = rc_tab->scan[index].keycode; - int i; - - /* Did the user wish to remove the mapping? */ - if (new_keycode == KEY_RESERVED || new_keycode == KEY_UNKNOWN) { - IR_dprintk(1, "#%d: Deleting scan 0x%04x\n", - index, rc_tab->scan[index].scancode); - rc_tab->len--; - memmove(&rc_tab->scan[index], &rc_tab->scan[index+ 1], - (rc_tab->len - index) * sizeof(struct ir_scancode)); - } else { - IR_dprintk(1, "#%d: %s scan 0x%04x with key 0x%04x\n", - index, - old_keycode == KEY_RESERVED ? "New" : "Replacing", - rc_tab->scan[index].scancode, new_keycode); - rc_tab->scan[index].keycode = new_keycode; - __set_bit(new_keycode, dev->keybit); - } - - if (old_keycode != KEY_RESERVED) { - /* A previous mapping was updated... */ - __clear_bit(old_keycode, dev->keybit); - /* ... but another scancode might use the same keycode */ - for (i = 0; i < rc_tab->len; i++) { - if (rc_tab->scan[i].keycode == old_keycode) { - __set_bit(old_keycode, dev->keybit); - break; - } - } - - /* Possibly shrink the keytable, failure is not a problem */ - ir_resize_table(rc_tab, GFP_ATOMIC); - } - - return old_keycode; -} - -/** - * ir_locate_scancode() - set a keycode in the scancode->keycode table - * @ir_dev: the struct ir_input_dev device descriptor - * @rc_tab: scancode table to be searched - * @scancode: the desired scancode - * @resize: controls whether we allowed to resize the table to - * accomodate not yet present scancodes - * @return: index of the mapping containing scancode in question - * or -1U in case of failure. + * @rc_tab: the struct ir_scancode_table to set the keycode in + * @scancode: the scancode for the ir command + * @keycode: the keycode for the ir command + * @resize: whether the keytable may be shrunk + * @return: -EINVAL if the keycode could not be inserted, otherwise zero. * - * This routine is used to locate given scancode in ir_scancode_table. - * If scancode is not yet present the routine will allocate a new slot - * for it. + * This routine is used internally to manipulate the scancode->keycode table. + * The caller has to hold @rc_tab->lock. */ -static unsigned int ir_establish_scancode(struct ir_input_dev *ir_dev, - struct ir_scancode_table *rc_tab, - unsigned int scancode, - bool resize) +static int ir_do_setkeycode(struct input_dev *dev, + struct ir_scancode_table *rc_tab, + unsigned scancode, unsigned keycode, + bool resize) { unsigned int i; + int old_keycode = KEY_RESERVED; + struct ir_input_dev *ir_dev = input_get_drvdata(dev); /* * Unfortunately, some hardware-based IR decoders don't provide @@ -194,34 +100,65 @@ static unsigned int ir_establish_scancode(struct ir_input_dev *ir_dev, * the provided IR with another one, it is needed to allow loading * IR tables from other remotes. So, */ - if (ir_dev->props && ir_dev->props->scanmask) + if (ir_dev->props && ir_dev->props->scanmask) { scancode &= ir_dev->props->scanmask; + } /* First check if we already have a mapping for this ir command */ for (i = 0; i < rc_tab->len; i++) { - if (rc_tab->scan[i].scancode == scancode) - return i; - /* Keytable is sorted from lowest to highest scancode */ - if (rc_tab->scan[i].scancode >= scancode) + if (rc_tab->scan[i].scancode > scancode) break; - } + else if (rc_tab->scan[i].scancode < scancode) + continue; + + old_keycode = rc_tab->scan[i].keycode; + rc_tab->scan[i].keycode = keycode; + + /* Did the user wish to remove the mapping? */ + if (keycode == KEY_RESERVED || keycode == KEY_UNKNOWN) { + IR_dprintk(1, "#%d: Deleting scan 0x%04x\n", + i, scancode); + rc_tab->len--; + memmove(&rc_tab->scan[i], &rc_tab->scan[i + 1], + (rc_tab->len - i) * sizeof(struct ir_scancode)); + } - /* No previous mapping found, we might need to grow the table */ - if (rc_tab->size == rc_tab->len) { - if (!resize || ir_resize_table(rc_tab, GFP_ATOMIC)) - return -1U; + /* Possibly shrink the keytable, failure is not a problem */ + ir_resize_table(rc_tab); + break; } - /* i is the proper index to insert our new keycode */ - if (i < rc_tab->len) + if (old_keycode == KEY_RESERVED && keycode != KEY_RESERVED) { + /* No previous mapping found, we might need to grow the table */ + if (resize && ir_resize_table(rc_tab)) + return -ENOMEM; + + IR_dprintk(1, "#%d: New scan 0x%04x with key 0x%04x\n", + i, scancode, keycode); + + /* i is the proper index to insert our new keycode */ memmove(&rc_tab->scan[i + 1], &rc_tab->scan[i], (rc_tab->len - i) * sizeof(struct ir_scancode)); - rc_tab->scan[i].scancode = scancode; - rc_tab->scan[i].keycode = KEY_RESERVED; - rc_tab->len++; + rc_tab->scan[i].scancode = scancode; + rc_tab->scan[i].keycode = keycode; + rc_tab->len++; + set_bit(keycode, dev->keybit); + } else { + IR_dprintk(1, "#%d: Replacing scan 0x%04x with key 0x%04x\n", + i, scancode, keycode); + /* A previous mapping was updated... */ + clear_bit(old_keycode, dev->keybit); + /* ...but another scancode might use the same keycode */ + for (i = 0; i < rc_tab->len; i++) { + if (rc_tab->scan[i].keycode == old_keycode) { + set_bit(old_keycode, dev->keybit); + break; + } + } + } - return i; + return 0; } /** @@ -234,41 +171,17 @@ static unsigned int ir_establish_scancode(struct ir_input_dev *ir_dev, * This routine is used to handle evdev EVIOCSKEY ioctl. */ static int ir_setkeycode(struct input_dev *dev, - const struct input_keymap_entry *ke, - unsigned int *old_keycode) + unsigned int scancode, unsigned int keycode) { + int rc; + unsigned long flags; struct ir_input_dev *ir_dev = input_get_drvdata(dev); struct ir_scancode_table *rc_tab = &ir_dev->rc_tab; - unsigned int index; - unsigned int scancode; - int retval; - unsigned long flags; spin_lock_irqsave(&rc_tab->lock, flags); - - if (ke->flags & INPUT_KEYMAP_BY_INDEX) { - index = ke->index; - if (index >= rc_tab->len) { - retval = -EINVAL; - goto out; - } - } else { - retval = input_scancode_to_scalar(ke, &scancode); - if (retval) - goto out; - - index = ir_establish_scancode(ir_dev, rc_tab, scancode, true); - if (index >= rc_tab->len) { - retval = -ENOMEM; - goto out; - } - } - - *old_keycode = ir_update_mapping(dev, rc_tab, index, ke->keycode); - -out: + rc = ir_do_setkeycode(dev, rc_tab, scancode, keycode, true); spin_unlock_irqrestore(&rc_tab->lock, flags); - return retval; + return rc; } /** @@ -276,72 +189,31 @@ static int ir_setkeycode(struct input_dev *dev, * @dev: the struct input_dev device descriptor * @to: the struct ir_scancode_table to copy entries to * @from: the struct ir_scancode_table to copy entries from - * @return: -ENOMEM if all keycodes could not be inserted, otherwise zero. + * @return: -EINVAL if all keycodes could not be inserted, otherwise zero. * * This routine is used to handle table initialization. */ -static int ir_setkeytable(struct ir_input_dev *ir_dev, +static int ir_setkeytable(struct input_dev *dev, + struct ir_scancode_table *to, const struct ir_scancode_table *from) { + struct ir_input_dev *ir_dev = input_get_drvdata(dev); struct ir_scancode_table *rc_tab = &ir_dev->rc_tab; - unsigned int i, index; - int rc; - - rc = ir_create_table(&ir_dev->rc_tab, - from->name, from->ir_type, from->size); - if (rc) - return rc; - - IR_dprintk(1, "Allocated space for %u keycode entries (%u bytes)\n", - rc_tab->size, rc_tab->alloc); + unsigned long flags; + unsigned int i; + int rc = 0; + spin_lock_irqsave(&rc_tab->lock, flags); for (i = 0; i < from->size; i++) { - index = ir_establish_scancode(ir_dev, rc_tab, - from->scan[i].scancode, false); - if (index >= rc_tab->len) { - rc = -ENOMEM; + rc = ir_do_setkeycode(dev, to, from->scan[i].scancode, + from->scan[i].keycode, false); + if (rc) break; - } - - ir_update_mapping(ir_dev->input_dev, rc_tab, index, - from->scan[i].keycode); } - - if (rc) - ir_free_table(rc_tab); - + spin_unlock_irqrestore(&rc_tab->lock, flags); return rc; } -/** - * ir_lookup_by_scancode() - locate mapping by scancode - * @rc_tab: the &struct ir_scancode_table to search - * @scancode: scancode to look for in the table - * @return: index in the table, -1U if not found - * - * This routine performs binary search in RC keykeymap table for - * given scancode. - */ -static unsigned int ir_lookup_by_scancode(const struct ir_scancode_table *rc_tab, - unsigned int scancode) -{ - unsigned int start = 0; - unsigned int end = rc_tab->len - 1; - unsigned int mid; - - while (start <= end) { - mid = (start + end) / 2; - if (rc_tab->scan[mid].scancode < scancode) - start = mid + 1; - else if (rc_tab->scan[mid].scancode > scancode) - end = mid - 1; - else - return mid; - } - - return -1U; -} - /** * ir_getkeycode() - get a keycode from the scancode->keycode table * @dev: the struct input_dev device descriptor @@ -352,46 +224,36 @@ static unsigned int ir_lookup_by_scancode(const struct ir_scancode_table *rc_tab * This routine is used to handle evdev EVIOCGKEY ioctl. */ static int ir_getkeycode(struct input_dev *dev, - struct input_keymap_entry *ke) + unsigned int scancode, unsigned int *keycode) { + int start, end, mid; + unsigned long flags; + int key = KEY_RESERVED; struct ir_input_dev *ir_dev = input_get_drvdata(dev); struct ir_scancode_table *rc_tab = &ir_dev->rc_tab; - struct ir_scancode *entry; - unsigned long flags; - unsigned int index; - unsigned int scancode; - int retval; spin_lock_irqsave(&rc_tab->lock, flags); - - if (ke->flags & INPUT_KEYMAP_BY_INDEX) { - index = ke->index; - } else { - retval = input_scancode_to_scalar(ke, &scancode); - if (retval) - goto out; - - index = ir_lookup_by_scancode(rc_tab, scancode); - } - - if (index >= rc_tab->len) { - if (!(ke->flags & INPUT_KEYMAP_BY_INDEX)) - IR_dprintk(1, "unknown key for scancode 0x%04x\n", - scancode); - retval = -EINVAL; - goto out; + start = 0; + end = rc_tab->len - 1; + while (start <= end) { + mid = (start + end) / 2; + if (rc_tab->scan[mid].scancode < scancode) + start = mid + 1; + else if (rc_tab->scan[mid].scancode > scancode) + end = mid - 1; + else { + key = rc_tab->scan[mid].keycode; + break; + } } + spin_unlock_irqrestore(&rc_tab->lock, flags); - entry = &rc_tab->scan[index]; - - ke->index = index; - ke->keycode = entry->keycode; - ke->len = sizeof(entry->scancode); - memcpy(ke->scancode, &entry->scancode, sizeof(entry->scancode)); + if (key == KEY_RESERVED) + IR_dprintk(1, "unknown key for scancode 0x%04x\n", + scancode); -out: - spin_unlock_irqrestore(&rc_tab->lock, flags); - return retval; + *keycode = key; + return 0; } /** @@ -406,24 +268,12 @@ static int ir_getkeycode(struct input_dev *dev, */ u32 ir_g_keycode_from_table(struct input_dev *dev, u32 scancode) { - struct ir_input_dev *ir_dev = input_get_drvdata(dev); - struct ir_scancode_table *rc_tab = &ir_dev->rc_tab; - unsigned int keycode; - unsigned int index; - unsigned long flags; - - spin_lock_irqsave(&rc_tab->lock, flags); - - index = ir_lookup_by_scancode(rc_tab, scancode); - keycode = index < rc_tab->len ? - rc_tab->scan[index].keycode : KEY_RESERVED; - - spin_unlock_irqrestore(&rc_tab->lock, flags); + int keycode; + ir_getkeycode(dev, scancode, &keycode); if (keycode != KEY_RESERVED) IR_dprintk(1, "%s: scancode 0x%04x keycode 0x%02x\n", dev->name, scancode, keycode); - return keycode; } EXPORT_SYMBOL_GPL(ir_g_keycode_from_table); @@ -603,8 +453,8 @@ int __ir_input_register(struct input_dev *input_dev, goto out_dev; } - input_dev->getkeycode_new = ir_getkeycode; - input_dev->setkeycode_new = ir_setkeycode; + input_dev->getkeycode = ir_getkeycode; + input_dev->setkeycode = ir_setkeycode; input_set_drvdata(input_dev, ir_dev); ir_dev->input_dev = input_dev; @@ -612,6 +462,12 @@ int __ir_input_register(struct input_dev *input_dev, spin_lock_init(&ir_dev->keylock); setup_timer(&ir_dev->timer_keyup, ir_timer_keyup, (unsigned long)ir_dev); + ir_dev->rc_tab.name = rc_tab->name; + ir_dev->rc_tab.ir_type = rc_tab->ir_type; + ir_dev->rc_tab.alloc = roundup_pow_of_two(rc_tab->size * + sizeof(struct ir_scancode)); + ir_dev->rc_tab.scan = kmalloc(ir_dev->rc_tab.alloc, GFP_KERNEL); + ir_dev->rc_tab.size = ir_dev->rc_tab.alloc / sizeof(struct ir_scancode); if (props) { ir_dev->props = props; if (props->open) @@ -620,14 +476,23 @@ int __ir_input_register(struct input_dev *input_dev, input_dev->close = ir_close; } + if (!ir_dev->rc_tab.scan) { + rc = -ENOMEM; + goto out_name; + } + + IR_dprintk(1, "Allocated space for %u keycode entries (%u bytes)\n", + ir_dev->rc_tab.size, ir_dev->rc_tab.alloc); + set_bit(EV_KEY, input_dev->evbit); set_bit(EV_REP, input_dev->evbit); set_bit(EV_MSC, input_dev->evbit); set_bit(MSC_SCAN, input_dev->mscbit); - rc = ir_setkeytable(ir_dev, rc_tab); - if (rc) - goto out_name; + if (ir_setkeytable(input_dev, &ir_dev->rc_tab, rc_tab)) { + rc = -ENOMEM; + goto out_table; + } rc = ir_register_class(input_dev); if (rc < 0) @@ -657,7 +522,7 @@ int __ir_input_register(struct input_dev *input_dev, out_event: ir_unregister_class(input_dev); out_table: - ir_free_table(&ir_dev->rc_tab); + kfree(ir_dev->rc_tab.scan); out_name: kfree(ir_dev->driver_name); out_dev: @@ -675,6 +540,7 @@ EXPORT_SYMBOL_GPL(__ir_input_register); void ir_input_unregister(struct input_dev *input_dev) { struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); + struct ir_scancode_table *rc_tab; if (!ir_dev) return; @@ -686,7 +552,10 @@ void ir_input_unregister(struct input_dev *input_dev) if (ir_dev->props->driver_type == RC_DRIVER_IR_RAW) ir_raw_event_unregister(input_dev); - ir_free_table(&ir_dev->rc_tab); + rc_tab = &ir_dev->rc_tab; + rc_tab->size = 0; + kfree(rc_tab->scan); + rc_tab->scan = NULL; ir_unregister_class(input_dev); diff --git a/trunk/drivers/mfd/ab8500-core.c b/trunk/drivers/mfd/ab8500-core.c index b8c4b80e4c43..defa786dee34 100644 --- a/trunk/drivers/mfd/ab8500-core.c +++ b/trunk/drivers/mfd/ab8500-core.c @@ -338,21 +338,6 @@ static struct resource ab8500_rtc_resources[] = { }, }; -static struct resource ab8500_poweronkey_db_resources[] = { - { - .name = "ONKEY_DBF", - .start = AB8500_INT_PON_KEY1DB_F, - .end = AB8500_INT_PON_KEY1DB_F, - .flags = IORESOURCE_IRQ, - }, - { - .name = "ONKEY_DBR", - .start = AB8500_INT_PON_KEY1DB_R, - .end = AB8500_INT_PON_KEY1DB_R, - .flags = IORESOURCE_IRQ, - }, -}; - static struct mfd_cell ab8500_devs[] = { { .name = "ab8500-gpadc", @@ -369,11 +354,6 @@ static struct mfd_cell ab8500_devs[] = { { .name = "ab8500-usb", }, { .name = "ab8500-pwm", }, { .name = "ab8500-regulator", }, - { - .name = "ab8500-poweron-key", - .num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources), - .resources = ab8500_poweronkey_db_resources, - }, }; int __devinit ab8500_init(struct ab8500 *ab8500) diff --git a/trunk/drivers/mfd/sh_mobile_sdhi.c b/trunk/drivers/mfd/sh_mobile_sdhi.c index 49b4d069cbf9..cd164595f08a 100644 --- a/trunk/drivers/mfd/sh_mobile_sdhi.c +++ b/trunk/drivers/mfd/sh_mobile_sdhi.c @@ -65,7 +65,7 @@ static void sh_mobile_sdhi_set_pwr(struct platform_device *tmio, int state) p->set_pwr(pdev, state); } -static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) +static int __init sh_mobile_sdhi_probe(struct platform_device *pdev) { struct sh_mobile_sdhi *priv; struct tmio_mmc_data *mmc_data; diff --git a/trunk/drivers/mfd/twl-core.c b/trunk/drivers/mfd/twl-core.c index 5d0fb60a4c14..720e099e506d 100644 --- a/trunk/drivers/mfd/twl-core.c +++ b/trunk/drivers/mfd/twl-core.c @@ -698,17 +698,17 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) if (twl_has_codec() && pdata->codec && twl_class_is_4030()) { sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid; - child = add_child(sub_chip_id, "twl4030-audio", + child = add_child(sub_chip_id, "twl4030_codec", pdata->codec, sizeof(*pdata->codec), false, 0, 0); if (IS_ERR(child)) return PTR_ERR(child); } - /* Phoenix codec driver is probed directly atm */ + /* Phoenix*/ if (twl_has_codec() && pdata->codec && twl_class_is_6030()) { sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid; - child = add_child(sub_chip_id, "twl6040-codec", + child = add_child(sub_chip_id, "twl6040_codec", pdata->codec, sizeof(*pdata->codec), false, 0, 0); if (IS_ERR(child)) diff --git a/trunk/drivers/mfd/twl4030-codec.c b/trunk/drivers/mfd/twl4030-codec.c index 9a4b196d6deb..add6f67d8032 100644 --- a/trunk/drivers/mfd/twl4030-codec.c +++ b/trunk/drivers/mfd/twl4030-codec.c @@ -207,14 +207,14 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) if (pdata->audio) { cell = &codec->cells[childs]; - cell->name = "twl4030-codec"; + cell->name = "twl4030_codec_audio"; cell->platform_data = pdata->audio; cell->data_size = sizeof(*pdata->audio); childs++; } if (pdata->vibra) { cell = &codec->cells[childs]; - cell->name = "twl4030-vibra"; + cell->name = "twl4030_codec_vibra"; cell->platform_data = pdata->vibra; cell->data_size = sizeof(*pdata->vibra); childs++; @@ -249,14 +249,14 @@ static int __devexit twl4030_codec_remove(struct platform_device *pdev) return 0; } -MODULE_ALIAS("platform:twl4030-audio"); +MODULE_ALIAS("platform:twl4030_codec"); static struct platform_driver twl4030_codec_driver = { .probe = twl4030_codec_probe, .remove = __devexit_p(twl4030_codec_remove), .driver = { .owner = THIS_MODULE, - .name = "twl4030-audio", + .name = "twl4030_codec", }, }; diff --git a/trunk/drivers/mtd/devices/m25p80.c b/trunk/drivers/mtd/devices/m25p80.c index ea22520c0406..6f512b5c117b 100644 --- a/trunk/drivers/mtd/devices/m25p80.c +++ b/trunk/drivers/mtd/devices/m25p80.c @@ -924,13 +924,6 @@ static int __devinit m25p_probe(struct spi_device *spi) nr_parts = data->nr_parts; } -#ifdef CONFIG_OF - if (nr_parts <= 0 && spi->dev.of_node) { - nr_parts = of_mtd_parse_partitions(&spi->dev, - spi->dev.of_node, &parts); - } -#endif - if (nr_parts > 0) { for (i = 0; i < nr_parts; i++) { DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = " diff --git a/trunk/drivers/mtd/maps/physmap_of.c b/trunk/drivers/mtd/maps/physmap_of.c index ec3edf6e68b4..fe63f6bd663c 100644 --- a/trunk/drivers/mtd/maps/physmap_of.c +++ b/trunk/drivers/mtd/maps/physmap_of.c @@ -294,7 +294,7 @@ static int __devinit of_flash_probe(struct platform_device *dev, info->list[i].map.name = dev_name(&dev->dev); info->list[i].map.phys = res.start; info->list[i].map.size = res_size; - info->list[i].map.bankwidth = be32_to_cpup(width); + info->list[i].map.bankwidth = *width; err = -ENOMEM; info->list[i].map.virt = ioremap(info->list[i].map.phys, diff --git a/trunk/drivers/mtd/ofpart.c b/trunk/drivers/mtd/ofpart.c index 7bd171eefd21..8bf7dc6d1ce6 100644 --- a/trunk/drivers/mtd/ofpart.c +++ b/trunk/drivers/mtd/ofpart.c @@ -53,8 +53,8 @@ int __devinit of_mtd_parse_partitions(struct device *dev, continue; } - (*pparts)[i].offset = be32_to_cpu(reg[0]); - (*pparts)[i].size = be32_to_cpu(reg[1]); + (*pparts)[i].offset = reg[0]; + (*pparts)[i].size = reg[1]; partname = of_get_property(pp, "label", &len); if (!partname) diff --git a/trunk/drivers/of/Kconfig b/trunk/drivers/of/Kconfig index aa675ebd8eb3..6acbff389ab6 100644 --- a/trunk/drivers/of/Kconfig +++ b/trunk/drivers/of/Kconfig @@ -4,7 +4,7 @@ config DTC config OF bool -menu "Device Tree and Open Firmware support" +menu "Flattened Device Tree and Open Firmware support" depends on OF config PROC_DEVICETREE @@ -19,9 +19,6 @@ config OF_FLATTREE bool select DTC -config OF_PROMTREE - bool - config OF_DYNAMIC def_bool y depends on PPC_OF diff --git a/trunk/drivers/of/Makefile b/trunk/drivers/of/Makefile index 7888155bea08..0052c405463a 100644 --- a/trunk/drivers/of/Makefile +++ b/trunk/drivers/of/Makefile @@ -1,6 +1,5 @@ obj-y = base.o obj-$(CONFIG_OF_FLATTREE) += fdt.o -obj-$(CONFIG_OF_PROMTREE) += pdt.o obj-$(CONFIG_OF_ADDRESS) += address.o obj-$(CONFIG_OF_IRQ) += irq.o obj-$(CONFIG_OF_DEVICE) += device.o platform.o diff --git a/trunk/drivers/of/address.c b/trunk/drivers/of/address.c index 3a1c7e70b192..fcadb726d4f9 100644 --- a/trunk/drivers/of/address.c +++ b/trunk/drivers/of/address.c @@ -163,7 +163,7 @@ static int of_bus_pci_translate(u32 *addr, u64 offset, int na) const u32 *of_get_pci_address(struct device_node *dev, int bar_no, u64 *size, unsigned int *flags) { - const __be32 *prop; + const u32 *prop; unsigned int psize; struct device_node *parent; struct of_bus *bus; diff --git a/trunk/drivers/of/base.c b/trunk/drivers/of/base.c index 710b53bfac6d..aa805250de76 100644 --- a/trunk/drivers/of/base.c +++ b/trunk/drivers/of/base.c @@ -33,7 +33,7 @@ DEFINE_RWLOCK(devtree_lock); int of_n_addr_cells(struct device_node *np) { - const __be32 *ip; + const int *ip; do { if (np->parent) @@ -49,7 +49,7 @@ EXPORT_SYMBOL(of_n_addr_cells); int of_n_size_cells(struct device_node *np) { - const __be32 *ip; + const int *ip; do { if (np->parent) diff --git a/trunk/drivers/of/device.c b/trunk/drivers/of/device.c index 45d86530799f..92de0eb74aea 100644 --- a/trunk/drivers/of/device.c +++ b/trunk/drivers/of/device.c @@ -81,10 +81,29 @@ struct device_attribute of_platform_device_attrs[] = { __ATTR_NULL }; -int of_device_add(struct platform_device *ofdev) +/** + * of_release_dev - free an of device structure when all users of it are finished. + * @dev: device that's been disconnected + * + * Will be called only by the device core when all users of this of device are + * done. + */ +void of_release_dev(struct device *dev) +{ + struct platform_device *ofdev; + + ofdev = to_platform_device(dev); + of_node_put(ofdev->dev.of_node); + kfree(ofdev); +} +EXPORT_SYMBOL(of_release_dev); + +int of_device_register(struct platform_device *ofdev) { BUG_ON(ofdev->dev.of_node == NULL); + device_initialize(&ofdev->dev); + /* name and id have to be set so that the platform bus doesn't get * confused on matching */ ofdev->name = dev_name(&ofdev->dev); @@ -98,12 +117,6 @@ int of_device_add(struct platform_device *ofdev) return device_add(&ofdev->dev); } - -int of_device_register(struct platform_device *pdev) -{ - device_initialize(&pdev->dev); - return of_device_add(pdev); -} EXPORT_SYMBOL(of_device_register); void of_device_unregister(struct platform_device *ofdev) diff --git a/trunk/drivers/of/fdt.c b/trunk/drivers/of/fdt.c index c1360e02f921..65da5aec7552 100644 --- a/trunk/drivers/of/fdt.c +++ b/trunk/drivers/of/fdt.c @@ -533,6 +533,8 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname, strlcpy(cmd_line, CONFIG_CMDLINE, COMMAND_LINE_SIZE); #endif /* CONFIG_CMDLINE */ + early_init_dt_scan_chosen_arch(node); + pr_debug("Command line is: %s\n", cmd_line); /* break now */ diff --git a/trunk/drivers/of/irq.c b/trunk/drivers/of/irq.c index 75b0d3cb7676..6e595e5a3977 100644 --- a/trunk/drivers/of/irq.c +++ b/trunk/drivers/of/irq.c @@ -24,11 +24,6 @@ #include #include -/* For archs that don't support NO_IRQ (such as x86), provide a dummy value */ -#ifndef NO_IRQ -#define NO_IRQ 0 -#endif - /** * irq_of_parse_and_map - Parse and map an interrupt into linux virq space * @device: Device node of the device whose interrupt is to be mapped @@ -352,37 +347,3 @@ int of_irq_to_resource(struct device_node *dev, int index, struct resource *r) return irq; } EXPORT_SYMBOL_GPL(of_irq_to_resource); - -/** - * of_irq_count - Count the number of IRQs a node uses - * @dev: pointer to device tree node - */ -int of_irq_count(struct device_node *dev) -{ - int nr = 0; - - while (of_irq_to_resource(dev, nr, NULL) != NO_IRQ) - nr++; - - return nr; -} - -/** - * of_irq_to_resource_table - Fill in resource table with node's IRQ info - * @dev: pointer to device tree node - * @res: array of resources to fill in - * @nr_irqs: the number of IRQs (and upper bound for num of @res elements) - * - * Returns the size of the filled in table (up to @nr_irqs). - */ -int of_irq_to_resource_table(struct device_node *dev, struct resource *res, - int nr_irqs) -{ - int i; - - for (i = 0; i < nr_irqs; i++, res++) - if (of_irq_to_resource(dev, i, res) == NO_IRQ) - break; - - return i; -} diff --git a/trunk/drivers/of/of_i2c.c b/trunk/drivers/of/of_i2c.c index c85d3c7421fc..0a694debd226 100644 --- a/trunk/drivers/of/of_i2c.c +++ b/trunk/drivers/of/of_i2c.c @@ -12,7 +12,6 @@ */ #include -#include #include #include #include diff --git a/trunk/drivers/of/pdt.c b/trunk/drivers/of/pdt.c deleted file mode 100644 index 28295d0a50f6..000000000000 --- a/trunk/drivers/of/pdt.c +++ /dev/null @@ -1,276 +0,0 @@ -/* pdt.c: OF PROM device tree support code. - * - * Paul Mackerras August 1996. - * Copyright (C) 1996-2005 Paul Mackerras. - * - * Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner. - * {engebret|bergner}@us.ibm.com - * - * Adapted for sparc by David S. Miller davem@davemloft.net - * Adapted for multiple architectures by Andres Salomon - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -static struct of_pdt_ops *of_pdt_prom_ops __initdata; - -void __initdata (*of_pdt_build_more)(struct device_node *dp, - struct device_node ***nextp); - -#if defined(CONFIG_SPARC) -unsigned int of_pdt_unique_id __initdata; - -#define of_pdt_incr_unique_id(p) do { \ - (p)->unique_id = of_pdt_unique_id++; \ -} while (0) - -static inline const char *of_pdt_node_name(struct device_node *dp) -{ - return dp->path_component_name; -} - -#else - -static inline void of_pdt_incr_unique_id(void *p) { } -static inline void irq_trans_init(struct device_node *dp) { } - -static inline const char *of_pdt_node_name(struct device_node *dp) -{ - return dp->name; -} - -#endif /* !CONFIG_SPARC */ - -static struct property * __init of_pdt_build_one_prop(phandle node, char *prev, - char *special_name, - void *special_val, - int special_len) -{ - static struct property *tmp = NULL; - struct property *p; - int err; - - if (tmp) { - p = tmp; - memset(p, 0, sizeof(*p) + 32); - tmp = NULL; - } else { - p = prom_early_alloc(sizeof(struct property) + 32); - of_pdt_incr_unique_id(p); - } - - p->name = (char *) (p + 1); - if (special_name) { - strcpy(p->name, special_name); - p->length = special_len; - p->value = prom_early_alloc(special_len); - memcpy(p->value, special_val, special_len); - } else { - err = of_pdt_prom_ops->nextprop(node, prev, p->name); - if (err) { - tmp = p; - return NULL; - } - p->length = of_pdt_prom_ops->getproplen(node, p->name); - if (p->length <= 0) { - p->length = 0; - } else { - int len; - - p->value = prom_early_alloc(p->length + 1); - len = of_pdt_prom_ops->getproperty(node, p->name, - p->value, p->length); - if (len <= 0) - p->length = 0; - ((unsigned char *)p->value)[p->length] = '\0'; - } - } - return p; -} - -static struct property * __init of_pdt_build_prop_list(phandle node) -{ - struct property *head, *tail; - - head = tail = of_pdt_build_one_prop(node, NULL, - ".node", &node, sizeof(node)); - - tail->next = of_pdt_build_one_prop(node, NULL, NULL, NULL, 0); - tail = tail->next; - while(tail) { - tail->next = of_pdt_build_one_prop(node, tail->name, - NULL, NULL, 0); - tail = tail->next; - } - - return head; -} - -static char * __init of_pdt_get_one_property(phandle node, const char *name) -{ - char *buf = ""; - int len; - - len = of_pdt_prom_ops->getproplen(node, name); - if (len > 0) { - buf = prom_early_alloc(len); - len = of_pdt_prom_ops->getproperty(node, name, buf, len); - } - - return buf; -} - -static char * __init of_pdt_try_pkg2path(phandle node) -{ - char *res, *buf = NULL; - int len; - - if (!of_pdt_prom_ops->pkg2path) - return NULL; - - if (of_pdt_prom_ops->pkg2path(node, buf, 0, &len)) - return NULL; - buf = prom_early_alloc(len + 1); - if (of_pdt_prom_ops->pkg2path(node, buf, len, &len)) { - pr_err("%s: package-to-path failed\n", __func__); - return NULL; - } - - res = strrchr(buf, '/'); - if (!res) { - pr_err("%s: couldn't find / in %s\n", __func__, buf); - return NULL; - } - return res+1; -} - -/* - * When fetching the node's name, first try using package-to-path; if - * that fails (either because the arch hasn't supplied a PROM callback, - * or some other random failure), fall back to just looking at the node's - * 'name' property. - */ -static char * __init of_pdt_build_name(phandle node) -{ - char *buf; - - buf = of_pdt_try_pkg2path(node); - if (!buf) - buf = of_pdt_get_one_property(node, "name"); - - return buf; -} - -static struct device_node * __init of_pdt_create_node(phandle node, - struct device_node *parent) -{ - struct device_node *dp; - - if (!node) - return NULL; - - dp = prom_early_alloc(sizeof(*dp)); - of_pdt_incr_unique_id(dp); - dp->parent = parent; - - kref_init(&dp->kref); - - dp->name = of_pdt_build_name(node); - dp->type = of_pdt_get_one_property(node, "device_type"); - dp->phandle = node; - - dp->properties = of_pdt_build_prop_list(node); - - irq_trans_init(dp); - - return dp; -} - -static char * __init of_pdt_build_full_name(struct device_node *dp) -{ - int len, ourlen, plen; - char *n; - - plen = strlen(dp->parent->full_name); - ourlen = strlen(of_pdt_node_name(dp)); - len = ourlen + plen + 2; - - n = prom_early_alloc(len); - strcpy(n, dp->parent->full_name); - if (!of_node_is_root(dp->parent)) { - strcpy(n + plen, "/"); - plen++; - } - strcpy(n + plen, of_pdt_node_name(dp)); - - return n; -} - -static struct device_node * __init of_pdt_build_tree(struct device_node *parent, - phandle node, - struct device_node ***nextp) -{ - struct device_node *ret = NULL, *prev_sibling = NULL; - struct device_node *dp; - - while (1) { - dp = of_pdt_create_node(node, parent); - if (!dp) - break; - - if (prev_sibling) - prev_sibling->sibling = dp; - - if (!ret) - ret = dp; - prev_sibling = dp; - - *(*nextp) = dp; - *nextp = &dp->allnext; - -#if defined(CONFIG_SPARC) - dp->path_component_name = build_path_component(dp); -#endif - dp->full_name = of_pdt_build_full_name(dp); - - dp->child = of_pdt_build_tree(dp, - of_pdt_prom_ops->getchild(node), nextp); - - if (of_pdt_build_more) - of_pdt_build_more(dp, nextp); - - node = of_pdt_prom_ops->getsibling(node); - } - - return ret; -} - -void __init of_pdt_build_devicetree(phandle root_node, struct of_pdt_ops *ops) -{ - struct device_node **nextp; - - BUG_ON(!ops); - of_pdt_prom_ops = ops; - - allnodes = of_pdt_create_node(root_node, NULL); -#if defined(CONFIG_SPARC) - allnodes->path_component_name = ""; -#endif - allnodes->full_name = "/"; - - nextp = &allnodes->allnext; - allnodes->child = of_pdt_build_tree(allnodes, - of_pdt_prom_ops->getchild(allnodes->phandle), &nextp); -} diff --git a/trunk/drivers/of/platform.c b/trunk/drivers/of/platform.c index 5b4a07f1220e..bb72223c22ae 100644 --- a/trunk/drivers/of/platform.c +++ b/trunk/drivers/of/platform.c @@ -584,33 +584,34 @@ struct platform_device *of_device_alloc(struct device_node *np, struct device *parent) { struct platform_device *dev; - int rc, i, num_reg = 0, num_irq; + int rc, i, num_reg = 0, num_irq = 0; struct resource *res, temp_res; - dev = platform_device_alloc("", -1); - if (!dev) - return NULL; - - /* count the io and irq resources */ + /* First count how many resources are needed */ while (of_address_to_resource(np, num_reg, &temp_res) == 0) num_reg++; - num_irq = of_irq_count(np); + while (of_irq_to_resource(np, num_irq, &temp_res) != NO_IRQ) + num_irq++; + + /* Allocate memory for both the struct device and the resource table */ + dev = kzalloc(sizeof(*dev) + (sizeof(*res) * (num_reg + num_irq)), + GFP_KERNEL); + if (!dev) + return NULL; + res = (struct resource *) &dev[1]; /* Populate the resource table */ if (num_irq || num_reg) { - res = kzalloc(sizeof(*res) * (num_irq + num_reg), GFP_KERNEL); - if (!res) { - platform_device_put(dev); - return NULL; - } - dev->num_resources = num_reg + num_irq; dev->resource = res; for (i = 0; i < num_reg; i++, res++) { rc = of_address_to_resource(np, i, res); WARN_ON(rc); } - WARN_ON(of_irq_to_resource_table(np, res, num_irq) != num_irq); + for (i = 0; i < num_irq; i++, res++) { + rc = of_irq_to_resource(np, i, res); + WARN_ON(rc == NO_IRQ); + } } dev->dev.of_node = of_node_get(np); @@ -618,6 +619,7 @@ struct platform_device *of_device_alloc(struct device_node *np, dev->dev.dma_mask = &dev->archdata.dma_mask; #endif dev->dev.parent = parent; + dev->dev.release = of_release_dev; if (bus_id) dev_set_name(&dev->dev, "%s", bus_id); @@ -655,8 +657,8 @@ struct platform_device *of_platform_device_create(struct device_node *np, * to do such, possibly using a device notifier */ - if (of_device_add(dev) != 0) { - platform_device_put(dev); + if (of_device_register(dev) != 0) { + of_device_free(dev); return NULL; } diff --git a/trunk/drivers/platform/x86/Kconfig b/trunk/drivers/platform/x86/Kconfig index faec777b1ed4..cff7cc2c1f02 100644 --- a/trunk/drivers/platform/x86/Kconfig +++ b/trunk/drivers/platform/x86/Kconfig @@ -92,7 +92,6 @@ config DELL_WMI tristate "Dell WMI extras" depends on ACPI_WMI depends on INPUT - select INPUT_SPARSEKMAP ---help--- Say Y here if you want to support WMI-based hotkeys on Dell laptops. @@ -141,7 +140,6 @@ config HP_WMI depends on ACPI_WMI depends on INPUT depends on RFKILL || RFKILL = n - select INPUT_SPARSEKMAP help Say Y here if you want to support WMI-based hotkeys on HP laptops and to read data from WMI such as docking or ambient light sensor state. @@ -173,7 +171,6 @@ config PANASONIC_LAPTOP tristate "Panasonic Laptop Extras" depends on INPUT && ACPI depends on BACKLIGHT_CLASS_DEVICE - select INPUT_SPARSEKMAP ---help--- This driver adds support for access to backlight control and hotkeys on Panasonic Let's Note laptops. @@ -222,8 +219,8 @@ config SONYPI_COMPAT ---help--- Build the sonypi driver compatibility code into the sony-laptop driver. -config IDEAPAD_LAPTOP - tristate "Lenovo IdeaPad Laptop Extras" +config IDEAPAD_ACPI + tristate "Lenovo IdeaPad ACPI Laptop Extras" depends on ACPI depends on RFKILL help @@ -368,26 +365,6 @@ config THINKPAD_ACPI_HOTKEY_POLL If you are not sure, say Y here. The driver enables polling only if it is strictly necessary to do so. -config SENSORS_HDAPS - tristate "Thinkpad Hard Drive Active Protection System (hdaps)" - depends on INPUT && X86 - select INPUT_POLLDEV - default n - help - This driver provides support for the IBM Hard Drive Active Protection - System (hdaps), which provides an accelerometer and other misc. data. - ThinkPads starting with the R50, T41, and X40 are supported. The - accelerometer data is readable via sysfs. - - This driver also provides an absolute input class device, allowing - the laptop to act as a pinball machine-esque joystick. - - If your ThinkPad is not recognized by the driver, please update to latest - BIOS. This is especially the case for some R52 ThinkPads. - - Say Y here if you have an applicable laptop and want to experience - the awesome power of hdaps. - config INTEL_MENLOW tristate "Thermal Management driver for Intel menlow platform" depends on ACPI_THERMAL @@ -501,7 +478,6 @@ config TOPSTAR_LAPTOP tristate "Topstar Laptop Extras" depends on ACPI depends on INPUT - select INPUT_SPARSEKMAP ---help--- This driver adds support for hotkeys found on Topstar laptops. @@ -516,7 +492,6 @@ config ACPI_TOSHIBA depends on INPUT depends on RFKILL || RFKILL = n select INPUT_POLLDEV - select INPUT_SPARSEKMAP ---help--- This driver adds support for access to certain system settings on "legacy free" Toshiba laptops. These laptops can be recognized by @@ -615,28 +590,4 @@ config INTEL_IPS functionality. If in doubt, say Y here; it will only load on supported platforms. -config IBM_RTL - tristate "Device driver to enable PRTL support" - depends on X86 && PCI - ---help--- - Enable support for IBM Premium Real Time Mode (PRTM). - This module will allow you the enter and exit PRTM in the BIOS via - sysfs on platforms that support this feature. System in PRTM will - not receive CPU-generated SMIs for recoverable errors. Use of this - feature without proper support may void your hardware warranty. - - If the proper BIOS support is found the driver will load and create - /sys/devices/system/ibm_rtl/. The "state" variable will indicate - whether or not the BIOS is in PRTM. - state = 0 (BIOS SMIs on) - state = 1 (BIOS SMIs off) - -config XO1_RFKILL - tristate "OLPC XO-1 software RF kill switch" - depends on OLPC - depends on RFKILL - ---help--- - Support for enabling/disabling the WLAN interface on the OLPC XO-1 - laptop. - endif # X86_PLATFORM_DEVICES diff --git a/trunk/drivers/platform/x86/Makefile b/trunk/drivers/platform/x86/Makefile index 9950ccc940b5..85fb2b84f57e 100644 --- a/trunk/drivers/platform/x86/Makefile +++ b/trunk/drivers/platform/x86/Makefile @@ -15,9 +15,8 @@ obj-$(CONFIG_ACERHDF) += acerhdf.o obj-$(CONFIG_HP_WMI) += hp-wmi.o obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o -obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o +obj-$(CONFIG_IDEAPAD_ACPI) += ideapad_acpi.o obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o -obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o @@ -31,5 +30,4 @@ obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o obj-$(CONFIG_RAR_REGISTER) += intel_rar_register.o obj-$(CONFIG_INTEL_IPS) += intel_ips.o obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o -obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o -obj-$(CONFIG_IBM_RTL) += ibm_rtl.o + diff --git a/trunk/drivers/platform/x86/acer-wmi.c b/trunk/drivers/platform/x86/acer-wmi.c index c8c65375bfe2..2badee2fdeed 100644 --- a/trunk/drivers/platform/x86/acer-wmi.c +++ b/trunk/drivers/platform/x86/acer-wmi.c @@ -1314,7 +1314,7 @@ static int __init acer_wmi_init(void) AMW0_find_mailled(); if (!interface) { - printk(ACER_INFO "No or unsupported WMI interface, unable to " + printk(ACER_ERR "No or unsupported WMI interface, unable to " "load\n"); return -ENODEV; } diff --git a/trunk/drivers/platform/x86/asus-laptop.c b/trunk/drivers/platform/x86/asus-laptop.c index 60a5a5c6b50a..b756e07d41b4 100644 --- a/trunk/drivers/platform/x86/asus-laptop.c +++ b/trunk/drivers/platform/x86/asus-laptop.c @@ -236,6 +236,7 @@ struct asus_laptop { u8 light_level; /* light sensor level */ u8 light_switch; /* light sensor switch value */ u16 event_count[128]; /* count for each event TODO make this better */ + u16 *keycode_map; }; static const struct key_entry asus_keymap[] = { @@ -277,7 +278,6 @@ static const struct key_entry asus_keymap[] = { {KE_KEY, 0x99, { KEY_PHONE } }, {KE_KEY, 0xc4, { KEY_KBDILLUMUP } }, {KE_KEY, 0xc5, { KEY_KBDILLUMDOWN } }, - {KE_KEY, 0xb5, { KEY_CALC } }, {KE_END, 0}, }; @@ -639,29 +639,29 @@ static int asus_backlight_notify(struct asus_laptop *asus) static int asus_backlight_init(struct asus_laptop *asus) { struct backlight_device *bd; + struct device *dev = &asus->platform_device->dev; struct backlight_properties props; - if (acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_GET, NULL) || - acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL) || - !lcd_switch_handle) - return 0; + if (!acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_GET, NULL) && + !acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL) && + lcd_switch_handle) { + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = 15; + + bd = backlight_device_register(ASUS_LAPTOP_FILE, dev, + asus, &asusbl_ops, &props); + if (IS_ERR(bd)) { + pr_err("Could not register asus backlight device\n"); + asus->backlight_device = NULL; + return PTR_ERR(bd); + } - memset(&props, 0, sizeof(struct backlight_properties)); - props.max_brightness = 15; + asus->backlight_device = bd; - bd = backlight_device_register(ASUS_LAPTOP_FILE, - &asus->platform_device->dev, asus, - &asusbl_ops, &props); - if (IS_ERR(bd)) { - pr_err("Could not register asus backlight device\n"); - asus->backlight_device = NULL; - return PTR_ERR(bd); + bd->props.power = FB_BLANK_UNBLANK; + bd->props.brightness = asus_read_brightness(bd); + backlight_update_status(bd); } - - asus->backlight_device = bd; - bd->props.brightness = asus_read_brightness(bd); - bd->props.power = FB_BLANK_UNBLANK; - backlight_update_status(bd); return 0; } @@ -1065,9 +1065,9 @@ static ssize_t store_gps(struct device *dev, struct device_attribute *attr, */ static int asus_gps_rfkill_set(void *data, bool blocked) { - struct asus_laptop *asus = data; + acpi_handle handle = data; - return asus_gps_switch(asus, !blocked); + return asus_gps_switch(handle, !blocked); } static const struct rfkill_ops asus_gps_rfkill_ops = { @@ -1094,7 +1094,7 @@ static int asus_rfkill_init(struct asus_laptop *asus) asus->gps_rfkill = rfkill_alloc("asus-gps", &asus->platform_device->dev, RFKILL_TYPE_GPS, - &asus_gps_rfkill_ops, asus); + &asus_gps_rfkill_ops, NULL); if (!asus->gps_rfkill) return -EINVAL; @@ -1130,6 +1130,7 @@ static int asus_input_init(struct asus_laptop *asus) input->phys = ASUS_LAPTOP_FILE "/input0"; input->id.bustype = BUS_HOST; input->dev.parent = &asus->platform_device->dev; + input_set_drvdata(input, asus); error = sparse_keymap_setup(input, asus_keymap, NULL); if (error) { @@ -1158,7 +1159,6 @@ static void asus_input_exit(struct asus_laptop *asus) sparse_keymap_free(asus->inputdev); input_unregister_device(asus->inputdev); } - asus->inputdev = NULL; } /* @@ -1200,100 +1200,111 @@ static void asus_acpi_notify(struct acpi_device *device, u32 event) static DEVICE_ATTR(infos, S_IRUGO, show_infos, NULL); static DEVICE_ATTR(wlan, S_IRUGO | S_IWUSR, show_wlan, store_wlan); -static DEVICE_ATTR(bluetooth, S_IRUGO | S_IWUSR, - show_bluetooth, store_bluetooth); +static DEVICE_ATTR(bluetooth, S_IRUGO | S_IWUSR, show_bluetooth, + store_bluetooth); static DEVICE_ATTR(display, S_IRUGO | S_IWUSR, show_disp, store_disp); static DEVICE_ATTR(ledd, S_IRUGO | S_IWUSR, show_ledd, store_ledd); static DEVICE_ATTR(ls_level, S_IRUGO | S_IWUSR, show_lslvl, store_lslvl); static DEVICE_ATTR(ls_switch, S_IRUGO | S_IWUSR, show_lssw, store_lssw); static DEVICE_ATTR(gps, S_IRUGO | S_IWUSR, show_gps, store_gps); -static struct attribute *asus_attributes[] = { - &dev_attr_infos.attr, - &dev_attr_wlan.attr, - &dev_attr_bluetooth.attr, - &dev_attr_display.attr, - &dev_attr_ledd.attr, - &dev_attr_ls_level.attr, - &dev_attr_ls_switch.attr, - &dev_attr_gps.attr, - NULL -}; - -static mode_t asus_sysfs_is_visible(struct kobject *kobj, - struct attribute *attr, - int idx) +static void asus_sysfs_exit(struct asus_laptop *asus) { - struct device *dev = container_of(kobj, struct device, kobj); - struct platform_device *pdev = to_platform_device(dev); - struct asus_laptop *asus = platform_get_drvdata(pdev); - acpi_handle handle = asus->handle; - bool supported; + struct platform_device *device = asus->platform_device; + + device_remove_file(&device->dev, &dev_attr_infos); + device_remove_file(&device->dev, &dev_attr_wlan); + device_remove_file(&device->dev, &dev_attr_bluetooth); + device_remove_file(&device->dev, &dev_attr_display); + device_remove_file(&device->dev, &dev_attr_ledd); + device_remove_file(&device->dev, &dev_attr_ls_switch); + device_remove_file(&device->dev, &dev_attr_ls_level); + device_remove_file(&device->dev, &dev_attr_gps); +} - if (attr == &dev_attr_wlan.attr) { - supported = !acpi_check_handle(handle, METHOD_WLAN, NULL); +static int asus_sysfs_init(struct asus_laptop *asus) +{ + struct platform_device *device = asus->platform_device; + int err; - } else if (attr == &dev_attr_bluetooth.attr) { - supported = !acpi_check_handle(handle, METHOD_BLUETOOTH, NULL); + err = device_create_file(&device->dev, &dev_attr_infos); + if (err) + return err; - } else if (attr == &dev_attr_display.attr) { - supported = !acpi_check_handle(handle, METHOD_SWITCH_DISPLAY, NULL); + if (!acpi_check_handle(asus->handle, METHOD_WLAN, NULL)) { + err = device_create_file(&device->dev, &dev_attr_wlan); + if (err) + return err; + } - } else if (attr == &dev_attr_ledd.attr) { - supported = !acpi_check_handle(handle, METHOD_LEDD, NULL); + if (!acpi_check_handle(asus->handle, METHOD_BLUETOOTH, NULL)) { + err = device_create_file(&device->dev, &dev_attr_bluetooth); + if (err) + return err; + } - } else if (attr == &dev_attr_ls_switch.attr || - attr == &dev_attr_ls_level.attr) { - supported = !acpi_check_handle(handle, METHOD_ALS_CONTROL, NULL) && - !acpi_check_handle(handle, METHOD_ALS_LEVEL, NULL); + if (!acpi_check_handle(asus->handle, METHOD_SWITCH_DISPLAY, NULL)) { + err = device_create_file(&device->dev, &dev_attr_display); + if (err) + return err; + } - } else if (attr == &dev_attr_gps.attr) { - supported = !acpi_check_handle(handle, METHOD_GPS_ON, NULL) && - !acpi_check_handle(handle, METHOD_GPS_OFF, NULL) && - !acpi_check_handle(handle, METHOD_GPS_STATUS, NULL); - } else { - supported = true; + if (!acpi_check_handle(asus->handle, METHOD_LEDD, NULL)) { + err = device_create_file(&device->dev, &dev_attr_ledd); + if (err) + return err; } - return supported ? attr->mode : 0; -} + if (!acpi_check_handle(asus->handle, METHOD_ALS_CONTROL, NULL) && + !acpi_check_handle(asus->handle, METHOD_ALS_LEVEL, NULL)) { + err = device_create_file(&device->dev, &dev_attr_ls_switch); + if (err) + return err; + err = device_create_file(&device->dev, &dev_attr_ls_level); + if (err) + return err; + } + if (!acpi_check_handle(asus->handle, METHOD_GPS_ON, NULL) && + !acpi_check_handle(asus->handle, METHOD_GPS_OFF, NULL) && + !acpi_check_handle(asus->handle, METHOD_GPS_STATUS, NULL)) { + err = device_create_file(&device->dev, &dev_attr_gps); + if (err) + return err; + } -static const struct attribute_group asus_attr_group = { - .is_visible = asus_sysfs_is_visible, - .attrs = asus_attributes, -}; + return err; +} static int asus_platform_init(struct asus_laptop *asus) { - int result; + int err; asus->platform_device = platform_device_alloc(ASUS_LAPTOP_FILE, -1); if (!asus->platform_device) return -ENOMEM; platform_set_drvdata(asus->platform_device, asus); - result = platform_device_add(asus->platform_device); - if (result) + err = platform_device_add(asus->platform_device); + if (err) goto fail_platform_device; - result = sysfs_create_group(&asus->platform_device->dev.kobj, - &asus_attr_group); - if (result) + err = asus_sysfs_init(asus); + if (err) goto fail_sysfs; - return 0; fail_sysfs: + asus_sysfs_exit(asus); platform_device_del(asus->platform_device); fail_platform_device: platform_device_put(asus->platform_device); - return result; + return err; } static void asus_platform_exit(struct asus_laptop *asus) { - sysfs_remove_group(&asus->platform_device->dev.kobj, &asus_attr_group); + asus_sysfs_exit(asus); platform_device_unregister(asus->platform_device); } @@ -1417,6 +1428,8 @@ static int asus_laptop_get_info(struct asus_laptop *asus) return AE_OK; } +static bool asus_device_present; + static int __devinit asus_acpi_init(struct asus_laptop *asus) { int result = 0; @@ -1461,8 +1474,6 @@ static int __devinit asus_acpi_init(struct asus_laptop *asus) return result; } -static bool asus_device_present; - static int __devinit asus_acpi_add(struct acpi_device *device) { struct asus_laptop *asus; diff --git a/trunk/drivers/platform/x86/dell-laptop.c b/trunk/drivers/platform/x86/dell-laptop.c index cf8a89a0d8f5..4413975912e0 100644 --- a/trunk/drivers/platform/x86/dell-laptop.c +++ b/trunk/drivers/platform/x86/dell-laptop.c @@ -25,8 +25,6 @@ #include #include #include -#include -#include #include "../../firmware/dcdbas.h" #define BRIGHTNESS_TOKEN 0x7d @@ -327,75 +325,6 @@ static const struct rfkill_ops dell_rfkill_ops = { .query = dell_rfkill_query, }; -static struct dentry *dell_laptop_dir; - -static int dell_debugfs_show(struct seq_file *s, void *data) -{ - int status; - - get_buffer(); - dell_send_request(buffer, 17, 11); - status = buffer->output[1]; - release_buffer(); - - seq_printf(s, "status:\t0x%X\n", status); - seq_printf(s, "Bit 0 : Hardware switch supported: %lu\n", - status & BIT(0)); - seq_printf(s, "Bit 1 : Wifi locator supported: %lu\n", - (status & BIT(1)) >> 1); - seq_printf(s, "Bit 2 : Wifi is supported: %lu\n", - (status & BIT(2)) >> 2); - seq_printf(s, "Bit 3 : Bluetooth is supported: %lu\n", - (status & BIT(3)) >> 3); - seq_printf(s, "Bit 4 : WWAN is supported: %lu\n", - (status & BIT(4)) >> 4); - seq_printf(s, "Bit 5 : Wireless keyboard supported: %lu\n", - (status & BIT(5)) >> 5); - seq_printf(s, "Bit 8 : Wifi is installed: %lu\n", - (status & BIT(8)) >> 8); - seq_printf(s, "Bit 9 : Bluetooth is installed: %lu\n", - (status & BIT(9)) >> 9); - seq_printf(s, "Bit 10: WWAN is installed: %lu\n", - (status & BIT(10)) >> 10); - seq_printf(s, "Bit 16: Hardware switch is on: %lu\n", - (status & BIT(16)) >> 16); - seq_printf(s, "Bit 17: Wifi is blocked: %lu\n", - (status & BIT(17)) >> 17); - seq_printf(s, "Bit 18: Bluetooth is blocked: %lu\n", - (status & BIT(18)) >> 18); - seq_printf(s, "Bit 19: WWAN is blocked: %lu\n", - (status & BIT(19)) >> 19); - - seq_printf(s, "\nhwswitch_state:\t0x%X\n", hwswitch_state); - seq_printf(s, "Bit 0 : Wifi controlled by switch: %lu\n", - hwswitch_state & BIT(0)); - seq_printf(s, "Bit 1 : Bluetooth controlled by switch: %lu\n", - (hwswitch_state & BIT(1)) >> 1); - seq_printf(s, "Bit 2 : WWAN controlled by switch: %lu\n", - (hwswitch_state & BIT(2)) >> 2); - seq_printf(s, "Bit 7 : Wireless switch config locked: %lu\n", - (hwswitch_state & BIT(7)) >> 7); - seq_printf(s, "Bit 8 : Wifi locator enabled: %lu\n", - (hwswitch_state & BIT(8)) >> 8); - seq_printf(s, "Bit 15: Wifi locator setting locked: %lu\n", - (hwswitch_state & BIT(15)) >> 15); - - return 0; -} - -static int dell_debugfs_open(struct inode *inode, struct file *file) -{ - return single_open(file, dell_debugfs_show, inode->i_private); -} - -static const struct file_operations dell_debugfs_fops = { - .owner = THIS_MODULE, - .open = dell_debugfs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - static void dell_update_rfkill(struct work_struct *ignored) { if (wifi_rfkill) @@ -627,11 +556,6 @@ static int __init dell_init(void) goto fail_filter; } - dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL); - if (dell_laptop_dir != NULL) - debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL, - &dell_debugfs_fops); - #ifdef CONFIG_ACPI /* In the event of an ACPI backlight being available, don't * register the platform controller. @@ -691,7 +615,6 @@ static int __init dell_init(void) static void __exit dell_exit(void) { - debugfs_remove_recursive(dell_laptop_dir); i8042_remove_filter(dell_laptop_i8042_filter); cancel_delayed_work_sync(&dell_rfkill_work); backlight_device_unregister(dell_backlight_device); diff --git a/trunk/drivers/platform/x86/dell-wmi.c b/trunk/drivers/platform/x86/dell-wmi.c index 77f1d55414c6..08fb70f6d9bf 100644 --- a/trunk/drivers/platform/x86/dell-wmi.c +++ b/trunk/drivers/platform/x86/dell-wmi.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -45,70 +44,78 @@ static int acpi_video; MODULE_ALIAS("wmi:"DELL_EVENT_GUID); +struct key_entry { + char type; /* See KE_* below */ + u16 code; + u16 keycode; +}; + +enum { KE_KEY, KE_SW, KE_IGNORE, KE_END }; + /* * Certain keys are flagged as KE_IGNORE. All of these are either * notifications (rather than requests for change) or are also sent * via the keyboard controller so should not be sent again. */ -static const struct key_entry dell_wmi_legacy_keymap[] __initconst = { - { KE_KEY, 0xe045, { KEY_PROG1 } }, - { KE_KEY, 0xe009, { KEY_EJECTCD } }, +static struct key_entry dell_legacy_wmi_keymap[] = { + {KE_KEY, 0xe045, KEY_PROG1}, + {KE_KEY, 0xe009, KEY_EJECTCD}, /* These also contain the brightness level at offset 6 */ - { KE_KEY, 0xe006, { KEY_BRIGHTNESSUP } }, - { KE_KEY, 0xe005, { KEY_BRIGHTNESSDOWN } }, + {KE_KEY, 0xe006, KEY_BRIGHTNESSUP}, + {KE_KEY, 0xe005, KEY_BRIGHTNESSDOWN}, /* Battery health status button */ - { KE_KEY, 0xe007, { KEY_BATTERY } }, + {KE_KEY, 0xe007, KEY_BATTERY}, /* This is actually for all radios. Although physically a * switch, the notification does not provide an indication of * state and so it should be reported as a key */ - { KE_KEY, 0xe008, { KEY_WLAN } }, + {KE_KEY, 0xe008, KEY_WLAN}, /* The next device is at offset 6, the active devices are at offset 8 and the attached devices at offset 10 */ - { KE_KEY, 0xe00b, { KEY_SWITCHVIDEOMODE } }, + {KE_KEY, 0xe00b, KEY_SWITCHVIDEOMODE}, - { KE_IGNORE, 0xe00c, { KEY_KBDILLUMTOGGLE } }, + {KE_IGNORE, 0xe00c, KEY_KBDILLUMTOGGLE}, /* BIOS error detected */ - { KE_IGNORE, 0xe00d, { KEY_RESERVED } }, + {KE_IGNORE, 0xe00d, KEY_RESERVED}, /* Wifi Catcher */ - { KE_KEY, 0xe011, {KEY_PROG2 } }, + {KE_KEY, 0xe011, KEY_PROG2}, /* Ambient light sensor toggle */ - { KE_IGNORE, 0xe013, { KEY_RESERVED } }, - - { KE_IGNORE, 0xe020, { KEY_MUTE } }, - { KE_IGNORE, 0xe02e, { KEY_VOLUMEDOWN } }, - { KE_IGNORE, 0xe030, { KEY_VOLUMEUP } }, - { KE_IGNORE, 0xe033, { KEY_KBDILLUMUP } }, - { KE_IGNORE, 0xe034, { KEY_KBDILLUMDOWN } }, - { KE_IGNORE, 0xe03a, { KEY_CAPSLOCK } }, - { KE_IGNORE, 0xe045, { KEY_NUMLOCK } }, - { KE_IGNORE, 0xe046, { KEY_SCROLLLOCK } }, - { KE_END, 0 } + {KE_IGNORE, 0xe013, KEY_RESERVED}, + + {KE_IGNORE, 0xe020, KEY_MUTE}, + {KE_IGNORE, 0xe02e, KEY_VOLUMEDOWN}, + {KE_IGNORE, 0xe030, KEY_VOLUMEUP}, + {KE_IGNORE, 0xe033, KEY_KBDILLUMUP}, + {KE_IGNORE, 0xe034, KEY_KBDILLUMDOWN}, + {KE_IGNORE, 0xe03a, KEY_CAPSLOCK}, + {KE_IGNORE, 0xe045, KEY_NUMLOCK}, + {KE_IGNORE, 0xe046, KEY_SCROLLLOCK}, + {KE_END, 0} }; static bool dell_new_hk_type; -struct dell_bios_keymap_entry { +struct dell_new_keymap_entry { u16 scancode; u16 keycode; }; -struct dell_bios_hotkey_table { +struct dell_hotkey_table { struct dmi_header header; - struct dell_bios_keymap_entry keymap[]; + struct dell_new_keymap_entry keymap[]; }; -static const struct dell_bios_hotkey_table *dell_bios_hotkey_table; +static struct key_entry *dell_new_wmi_keymap; -static const u16 bios_to_linux_keycode[256] __initconst = { +static u16 bios_to_linux_keycode[256] = { KEY_MEDIA, KEY_NEXTSONG, KEY_PLAYPAUSE, KEY_PREVIOUSSONG, KEY_STOPCD, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, @@ -131,11 +138,68 @@ static const u16 bios_to_linux_keycode[256] __initconst = { KEY_PROG3 }; + +static struct key_entry *dell_wmi_keymap = dell_legacy_wmi_keymap; + static struct input_dev *dell_wmi_input_dev; +static struct key_entry *dell_wmi_get_entry_by_scancode(unsigned int code) +{ + struct key_entry *key; + + for (key = dell_wmi_keymap; key->type != KE_END; key++) + if (code == key->code) + return key; + + return NULL; +} + +static struct key_entry *dell_wmi_get_entry_by_keycode(unsigned int keycode) +{ + struct key_entry *key; + + for (key = dell_wmi_keymap; key->type != KE_END; key++) + if (key->type == KE_KEY && keycode == key->keycode) + return key; + + return NULL; +} + +static int dell_wmi_getkeycode(struct input_dev *dev, + unsigned int scancode, unsigned int *keycode) +{ + struct key_entry *key = dell_wmi_get_entry_by_scancode(scancode); + + if (key && key->type == KE_KEY) { + *keycode = key->keycode; + return 0; + } + + return -EINVAL; +} + +static int dell_wmi_setkeycode(struct input_dev *dev, + unsigned int scancode, unsigned int keycode) +{ + struct key_entry *key; + unsigned int old_keycode; + + key = dell_wmi_get_entry_by_scancode(scancode); + if (key && key->type == KE_KEY) { + old_keycode = key->keycode; + key->keycode = keycode; + set_bit(keycode, dev->keybit); + if (!dell_wmi_get_entry_by_keycode(old_keycode)) + clear_bit(old_keycode, dev->keybit); + return 0; + } + return -EINVAL; +} + static void dell_wmi_notify(u32 value, void *context) { struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; + static struct key_entry *key; union acpi_object *obj; acpi_status status; @@ -148,10 +212,8 @@ static void dell_wmi_notify(u32 value, void *context) obj = (union acpi_object *)response.pointer; if (obj && obj->type == ACPI_TYPE_BUFFER) { - const struct key_entry *key; int reported_key; u16 *buffer_entry = (u16 *)obj->buffer.pointer; - if (dell_new_hk_type && (buffer_entry[1] != 0x10)) { printk(KERN_INFO "dell-wmi: Received unknown WMI event" " (0x%x)\n", buffer_entry[1]); @@ -164,8 +226,8 @@ static void dell_wmi_notify(u32 value, void *context) else reported_key = (int)buffer_entry[1] & 0xffff; - key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev, - reported_key); + key = dell_wmi_get_entry_by_scancode(reported_key); + if (!key) { printk(KERN_INFO "dell-wmi: Unknown key %x pressed\n", reported_key); @@ -175,98 +237,92 @@ static void dell_wmi_notify(u32 value, void *context) * come via ACPI */ ; } else { - sparse_keymap_report_entry(dell_wmi_input_dev, key, - 1, true); + input_report_key(dell_wmi_input_dev, key->keycode, 1); + input_sync(dell_wmi_input_dev); + input_report_key(dell_wmi_input_dev, key->keycode, 0); + input_sync(dell_wmi_input_dev); } } kfree(obj); } -static const struct key_entry * __init dell_wmi_prepare_new_keymap(void) + +static void setup_new_hk_map(const struct dmi_header *dm) { - int hotkey_num = (dell_bios_hotkey_table->header.length - 4) / - sizeof(struct dell_bios_keymap_entry); - struct key_entry *keymap; + int i; + int hotkey_num = (dm->length-4)/sizeof(struct dell_new_keymap_entry); + struct dell_hotkey_table *table = + container_of(dm, struct dell_hotkey_table, header); - keymap = kcalloc(hotkey_num + 1, sizeof(struct key_entry), GFP_KERNEL); - if (!keymap) - return NULL; + dell_new_wmi_keymap = kzalloc((hotkey_num+1) * + sizeof(struct key_entry), GFP_KERNEL); for (i = 0; i < hotkey_num; i++) { - const struct dell_bios_keymap_entry *bios_entry = - &dell_bios_hotkey_table->keymap[i]; - keymap[i].type = KE_KEY; - keymap[i].code = bios_entry->scancode; - keymap[i].keycode = bios_entry->keycode < 256 ? - bios_to_linux_keycode[bios_entry->keycode] : - KEY_RESERVED; + dell_new_wmi_keymap[i].type = KE_KEY; + dell_new_wmi_keymap[i].code = table->keymap[i].scancode; + dell_new_wmi_keymap[i].keycode = + (table->keymap[i].keycode > 255) ? 0 : + bios_to_linux_keycode[table->keymap[i].keycode]; } - keymap[hotkey_num].type = KE_END; + dell_new_wmi_keymap[i].type = KE_END; + dell_new_wmi_keymap[i].code = 0; + dell_new_wmi_keymap[i].keycode = 0; + + dell_wmi_keymap = dell_new_wmi_keymap; - return keymap; } + +static void find_hk_type(const struct dmi_header *dm, void *dummy) +{ + + if ((dm->type == 0xb2) && (dm->length > 6)) { + dell_new_hk_type = true; + setup_new_hk_map(dm); + } + +} + + static int __init dell_wmi_input_setup(void) { + struct key_entry *key; int err; dell_wmi_input_dev = input_allocate_device(); + if (!dell_wmi_input_dev) return -ENOMEM; dell_wmi_input_dev->name = "Dell WMI hotkeys"; dell_wmi_input_dev->phys = "wmi/input0"; dell_wmi_input_dev->id.bustype = BUS_HOST; - - if (dell_new_hk_type) { - const struct key_entry *keymap = dell_wmi_prepare_new_keymap(); - if (!keymap) { - err = -ENOMEM; - goto err_free_dev; + dell_wmi_input_dev->getkeycode = dell_wmi_getkeycode; + dell_wmi_input_dev->setkeycode = dell_wmi_setkeycode; + + for (key = dell_wmi_keymap; key->type != KE_END; key++) { + switch (key->type) { + case KE_KEY: + set_bit(EV_KEY, dell_wmi_input_dev->evbit); + set_bit(key->keycode, dell_wmi_input_dev->keybit); + break; + case KE_SW: + set_bit(EV_SW, dell_wmi_input_dev->evbit); + set_bit(key->keycode, dell_wmi_input_dev->swbit); + break; } - - err = sparse_keymap_setup(dell_wmi_input_dev, keymap, NULL); - - /* - * Sparse keymap library makes a copy of keymap so we - * don't need the original one that was allocated. - */ - kfree(keymap); - } else { - err = sparse_keymap_setup(dell_wmi_input_dev, - dell_wmi_legacy_keymap, NULL); } - if (err) - goto err_free_dev; err = input_register_device(dell_wmi_input_dev); - if (err) - goto err_free_keymap; - - return 0; - err_free_keymap: - sparse_keymap_free(dell_wmi_input_dev); - err_free_dev: - input_free_device(dell_wmi_input_dev); - return err; -} - -static void dell_wmi_input_destroy(void) -{ - sparse_keymap_free(dell_wmi_input_dev); - input_unregister_device(dell_wmi_input_dev); -} - -static void __init find_hk_type(const struct dmi_header *dm, void *dummy) -{ - if (dm->type == 0xb2 && dm->length > 6) { - dell_new_hk_type = true; - dell_bios_hotkey_table = - container_of(dm, struct dell_bios_hotkey_table, header); + if (err) { + input_free_device(dell_wmi_input_dev); + return err; } + + return 0; } static int __init dell_wmi_init(void) @@ -283,13 +339,18 @@ static int __init dell_wmi_init(void) acpi_video = acpi_video_backlight_support(); err = dell_wmi_input_setup(); - if (err) + if (err) { + if (dell_new_hk_type) + kfree(dell_wmi_keymap); return err; + } status = wmi_install_notify_handler(DELL_EVENT_GUID, dell_wmi_notify, NULL); if (ACPI_FAILURE(status)) { - dell_wmi_input_destroy(); + input_unregister_device(dell_wmi_input_dev); + if (dell_new_hk_type) + kfree(dell_wmi_keymap); printk(KERN_ERR "dell-wmi: Unable to register notify handler - %d\n", status); @@ -298,11 +359,14 @@ static int __init dell_wmi_init(void) return 0; } -module_init(dell_wmi_init); static void __exit dell_wmi_exit(void) { wmi_remove_notify_handler(DELL_EVENT_GUID); - dell_wmi_input_destroy(); + input_unregister_device(dell_wmi_input_dev); + if (dell_new_hk_type) + kfree(dell_wmi_keymap); } + +module_init(dell_wmi_init); module_exit(dell_wmi_exit); diff --git a/trunk/drivers/platform/x86/eeepc-laptop.c b/trunk/drivers/platform/x86/eeepc-laptop.c index b2edfdcdcb84..6b8e06206c46 100644 --- a/trunk/drivers/platform/x86/eeepc-laptop.c +++ b/trunk/drivers/platform/x86/eeepc-laptop.c @@ -165,7 +165,6 @@ struct eeepc_laptop { u16 event_count[128]; /* count for each event */ struct platform_device *platform_device; - struct acpi_device *device; /* the device we are in */ struct device *hwmon_device; struct backlight_device *backlight_device; @@ -1194,9 +1193,9 @@ static int eeepc_input_init(struct eeepc_laptop *eeepc) eeepc->inputdev = input; return 0; -err_free_keymap: + err_free_keymap: sparse_keymap_free(input); -err_free_dev: + err_free_dev: input_free_device(input); return error; } @@ -1207,7 +1206,6 @@ static void eeepc_input_exit(struct eeepc_laptop *eeepc) sparse_keymap_free(eeepc->inputdev); input_unregister_device(eeepc->inputdev); } - eeepc->inputdev = NULL; } /* @@ -1328,15 +1326,16 @@ static void cmsg_quirks(struct eeepc_laptop *eeepc) cmsg_quirk(eeepc, CM_ASL_TPD, "TPD"); } -static int __devinit eeepc_acpi_init(struct eeepc_laptop *eeepc) +static int eeepc_acpi_init(struct eeepc_laptop *eeepc, + struct acpi_device *device) { unsigned int init_flags; int result; - result = acpi_bus_get_status(eeepc->device); + result = acpi_bus_get_status(device); if (result) return result; - if (!eeepc->device->status.present) { + if (!device->status.present) { pr_err("Hotkey device not present, aborting\n"); return -ENODEV; } @@ -1385,13 +1384,12 @@ static int __devinit eeepc_acpi_add(struct acpi_device *device) strcpy(acpi_device_name(device), EEEPC_ACPI_DEVICE_NAME); strcpy(acpi_device_class(device), EEEPC_ACPI_CLASS); device->driver_data = eeepc; - eeepc->device = device; eeepc->hotplug_disabled = hotplug_disabled; eeepc_dmi_check(eeepc); - result = eeepc_acpi_init(eeepc); + result = eeepc_acpi_init(eeepc, device); if (result) goto fail_platform; eeepc_enable_camera(eeepc); diff --git a/trunk/drivers/platform/x86/eeepc-wmi.c b/trunk/drivers/platform/x86/eeepc-wmi.c index 462ceab93f87..9dc50fbf3d0b 100644 --- a/trunk/drivers/platform/x86/eeepc-wmi.c +++ b/trunk/drivers/platform/x86/eeepc-wmi.c @@ -57,7 +57,6 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); #define EEEPC_WMI_METHODID_DEVS 0x53564544 #define EEEPC_WMI_METHODID_DSTS 0x53544344 -#define EEEPC_WMI_METHODID_CFVS 0x53564643 #define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012 @@ -70,11 +69,6 @@ static const struct key_entry eeepc_wmi_keymap[] = { { KE_IGNORE, NOTIFY_BRNDOWN_MIN, { KEY_BRIGHTNESSDOWN } }, { KE_IGNORE, NOTIFY_BRNUP_MIN, { KEY_BRIGHTNESSUP } }, { KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } }, - { KE_KEY, 0x6b, { KEY_F13 } }, /* Disable Touchpad */ - { KE_KEY, 0xe1, { KEY_F14 } }, - { KE_KEY, 0xe9, { KEY_DISPLAY_OFF } }, - { KE_KEY, 0xe0, { KEY_PROG1 } }, - { KE_KEY, 0x5c, { KEY_F15 } }, { KE_END, 0}, }; @@ -298,49 +292,6 @@ static void eeepc_wmi_notify(u32 value, void *context) kfree(obj); } -static int store_cpufv(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - int value; - struct acpi_buffer input = { (acpi_size)sizeof(value), &value }; - acpi_status status; - - if (!count || sscanf(buf, "%i", &value) != 1) - return -EINVAL; - if (value < 0 || value > 2) - return -EINVAL; - - status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, - 1, EEEPC_WMI_METHODID_CFVS, &input, NULL); - - if (ACPI_FAILURE(status)) - return -EIO; - else - return count; -} - -static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv); - -static void eeepc_wmi_sysfs_exit(struct platform_device *device) -{ - device_remove_file(&device->dev, &dev_attr_cpufv); -} - -static int eeepc_wmi_sysfs_init(struct platform_device *device) -{ - int retval = -ENOMEM; - - retval = device_create_file(&device->dev, &dev_attr_cpufv); - if (retval) - goto error_sysfs; - - return 0; - -error_sysfs: - eeepc_wmi_sysfs_exit(platform_device); - return retval; -} - static int __devinit eeepc_wmi_platform_probe(struct platform_device *device) { struct eeepc_wmi *eeepc; @@ -436,14 +387,8 @@ static int __init eeepc_wmi_init(void) goto del_dev; } - err = eeepc_wmi_sysfs_init(platform_device); - if (err) - goto del_sysfs; - return 0; -del_sysfs: - eeepc_wmi_sysfs_exit(platform_device); del_dev: platform_device_del(platform_device); put_dev: @@ -458,7 +403,6 @@ static void __exit eeepc_wmi_exit(void) { struct eeepc_wmi *eeepc; - eeepc_wmi_sysfs_exit(platform_device); eeepc = platform_get_drvdata(platform_device); platform_driver_unregister(&platform_driver); platform_device_unregister(platform_device); diff --git a/trunk/drivers/platform/x86/hp-wmi.c b/trunk/drivers/platform/x86/hp-wmi.c index 1dac659b5e0c..c1741142a4cb 100644 --- a/trunk/drivers/platform/x86/hp-wmi.c +++ b/trunk/drivers/platform/x86/hp-wmi.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -89,16 +88,24 @@ struct bios_return { u32 value; }; -static const struct key_entry hp_wmi_keymap[] = { - { KE_KEY, 0x02, { KEY_BRIGHTNESSUP } }, - { KE_KEY, 0x03, { KEY_BRIGHTNESSDOWN } }, - { KE_KEY, 0x20e6, { KEY_PROG1 } }, - { KE_KEY, 0x20e8, { KEY_MEDIA } }, - { KE_KEY, 0x2142, { KEY_MEDIA } }, - { KE_KEY, 0x213b, { KEY_INFO } }, - { KE_KEY, 0x2169, { KEY_DIRECTION } }, - { KE_KEY, 0x231b, { KEY_HELP } }, - { KE_END, 0 } +struct key_entry { + char type; /* See KE_* below */ + u16 code; + u16 keycode; +}; + +enum { KE_KEY, KE_END }; + +static struct key_entry hp_wmi_keymap[] = { + {KE_KEY, 0x02, KEY_BRIGHTNESSUP}, + {KE_KEY, 0x03, KEY_BRIGHTNESSDOWN}, + {KE_KEY, 0x20e6, KEY_PROG1}, + {KE_KEY, 0x20e8, KEY_MEDIA}, + {KE_KEY, 0x2142, KEY_MEDIA}, + {KE_KEY, 0x213b, KEY_INFO}, + {KE_KEY, 0x2169, KEY_DIRECTION}, + {KE_KEY, 0x231b, KEY_HELP}, + {KE_END, 0} }; static struct input_dev *hp_wmi_input_dev; @@ -340,9 +347,64 @@ static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als); static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL); static DEVICE_ATTR(tablet, S_IRUGO, show_tablet, NULL); +static struct key_entry *hp_wmi_get_entry_by_scancode(unsigned int code) +{ + struct key_entry *key; + + for (key = hp_wmi_keymap; key->type != KE_END; key++) + if (code == key->code) + return key; + + return NULL; +} + +static struct key_entry *hp_wmi_get_entry_by_keycode(unsigned int keycode) +{ + struct key_entry *key; + + for (key = hp_wmi_keymap; key->type != KE_END; key++) + if (key->type == KE_KEY && keycode == key->keycode) + return key; + + return NULL; +} + +static int hp_wmi_getkeycode(struct input_dev *dev, + unsigned int scancode, unsigned int *keycode) +{ + struct key_entry *key = hp_wmi_get_entry_by_scancode(scancode); + + if (key && key->type == KE_KEY) { + *keycode = key->keycode; + return 0; + } + + return -EINVAL; +} + +static int hp_wmi_setkeycode(struct input_dev *dev, + unsigned int scancode, unsigned int keycode) +{ + struct key_entry *key; + unsigned int old_keycode; + + key = hp_wmi_get_entry_by_scancode(scancode); + if (key && key->type == KE_KEY) { + old_keycode = key->keycode; + key->keycode = keycode; + set_bit(keycode, dev->keybit); + if (!hp_wmi_get_entry_by_keycode(old_keycode)) + clear_bit(old_keycode, dev->keybit); + return 0; + } + + return -EINVAL; +} + static void hp_wmi_notify(u32 value, void *context) { struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; + static struct key_entry *key; union acpi_object *obj; u32 event_id, event_data; int key_code = 0, ret; @@ -403,9 +465,19 @@ static void hp_wmi_notify(u32 value, void *context) sizeof(key_code)); if (ret) break; - - if (!sparse_keymap_report_event(hp_wmi_input_dev, - key_code, 1, true)) + key = hp_wmi_get_entry_by_scancode(key_code); + if (key) { + switch (key->type) { + case KE_KEY: + input_report_key(hp_wmi_input_dev, + key->keycode, 1); + input_sync(hp_wmi_input_dev); + input_report_key(hp_wmi_input_dev, + key->keycode, 0); + input_sync(hp_wmi_input_dev); + break; + } + } else printk(KERN_INFO PREFIX "Unknown key code - 0x%x\n", key_code); break; @@ -438,7 +510,7 @@ static void hp_wmi_notify(u32 value, void *context) static int __init hp_wmi_input_setup(void) { - acpi_status status; + struct key_entry *key; int err; hp_wmi_input_dev = input_allocate_device(); @@ -448,14 +520,21 @@ static int __init hp_wmi_input_setup(void) hp_wmi_input_dev->name = "HP WMI hotkeys"; hp_wmi_input_dev->phys = "wmi/input0"; hp_wmi_input_dev->id.bustype = BUS_HOST; + hp_wmi_input_dev->getkeycode = hp_wmi_getkeycode; + hp_wmi_input_dev->setkeycode = hp_wmi_setkeycode; + + for (key = hp_wmi_keymap; key->type != KE_END; key++) { + switch (key->type) { + case KE_KEY: + set_bit(EV_KEY, hp_wmi_input_dev->evbit); + set_bit(key->keycode, hp_wmi_input_dev->keybit); + break; + } + } - __set_bit(EV_SW, hp_wmi_input_dev->evbit); - __set_bit(SW_DOCK, hp_wmi_input_dev->swbit); - __set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit); - - err = sparse_keymap_setup(hp_wmi_input_dev, hp_wmi_keymap, NULL); - if (err) - goto err_free_dev; + set_bit(EV_SW, hp_wmi_input_dev->evbit); + set_bit(SW_DOCK, hp_wmi_input_dev->swbit); + set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit); /* Set initial hardware state */ input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state()); @@ -463,32 +542,14 @@ static int __init hp_wmi_input_setup(void) hp_wmi_tablet_state()); input_sync(hp_wmi_input_dev); - status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL); - if (ACPI_FAILURE(status)) { - err = -EIO; - goto err_free_keymap; - } - err = input_register_device(hp_wmi_input_dev); - if (err) - goto err_uninstall_notifier; - - return 0; - err_uninstall_notifier: - wmi_remove_notify_handler(HPWMI_EVENT_GUID); - err_free_keymap: - sparse_keymap_free(hp_wmi_input_dev); - err_free_dev: - input_free_device(hp_wmi_input_dev); - return err; -} + if (err) { + input_free_device(hp_wmi_input_dev); + return err; + } -static void hp_wmi_input_destroy(void) -{ - wmi_remove_notify_handler(HPWMI_EVENT_GUID); - sparse_keymap_free(hp_wmi_input_dev); - input_unregister_device(hp_wmi_input_dev); + return 0; } static void cleanup_sysfs(struct platform_device *device) @@ -643,9 +704,15 @@ static int __init hp_wmi_init(void) int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID); if (event_capable) { + err = wmi_install_notify_handler(HPWMI_EVENT_GUID, + hp_wmi_notify, NULL); + if (ACPI_FAILURE(err)) + return -EINVAL; err = hp_wmi_input_setup(); - if (err) + if (err) { + wmi_remove_notify_handler(HPWMI_EVENT_GUID); return err; + } } if (bios_capable) { @@ -672,17 +739,20 @@ static int __init hp_wmi_init(void) err_device_alloc: platform_driver_unregister(&hp_wmi_driver); err_driver_reg: - if (event_capable) - hp_wmi_input_destroy(); + if (wmi_has_guid(HPWMI_EVENT_GUID)) { + input_unregister_device(hp_wmi_input_dev); + wmi_remove_notify_handler(HPWMI_EVENT_GUID); + } return err; } static void __exit hp_wmi_exit(void) { - if (wmi_has_guid(HPWMI_EVENT_GUID)) - hp_wmi_input_destroy(); - + if (wmi_has_guid(HPWMI_EVENT_GUID)) { + wmi_remove_notify_handler(HPWMI_EVENT_GUID); + input_unregister_device(hp_wmi_input_dev); + } if (hp_wmi_platform_dev) { platform_device_unregister(hp_wmi_platform_dev); platform_driver_unregister(&hp_wmi_driver); diff --git a/trunk/drivers/platform/x86/ibm_rtl.c b/trunk/drivers/platform/x86/ibm_rtl.c deleted file mode 100644 index 3c2c6b91ecb3..000000000000 --- a/trunk/drivers/platform/x86/ibm_rtl.c +++ /dev/null @@ -1,341 +0,0 @@ -/* - * IBM Real-Time Linux driver - * - * 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. - * - * Copyright (C) IBM Corporation, 2010 - * - * Author: Keith Mannthey - * Vernon Mauery - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -static bool force; -module_param(force, bool, 0); -MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); - -static bool debug; -module_param(debug, bool, 0644); -MODULE_PARM_DESC(debug, "Show debug output"); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Keith Mannthey "); -MODULE_AUTHOR("Vernon Mauery "); - -#define RTL_ADDR_TYPE_IO 1 -#define RTL_ADDR_TYPE_MMIO 2 - -#define RTL_CMD_ENTER_PRTM 1 -#define RTL_CMD_EXIT_PRTM 2 - -/* The RTL table as presented by the EBDA: */ -struct ibm_rtl_table { - char signature[5]; /* signature should be "_RTL_" */ - u8 version; - u8 rt_status; - u8 command; - u8 command_status; - u8 cmd_address_type; - u8 cmd_granularity; - u8 cmd_offset; - u16 reserve1; - u32 cmd_port_address; /* platform dependent address */ - u32 cmd_port_value; /* platform dependent value */ -} __attribute__((packed)); - -/* to locate "_RTL_" signature do a masked 5-byte integer compare */ -#define RTL_SIGNATURE 0x0000005f4c54525fULL -#define RTL_MASK 0x000000ffffffffffULL - -#define RTL_DEBUG(A, ...) do { \ - if (debug) \ - pr_info("ibm-rtl: " A, ##__VA_ARGS__ ); \ -} while (0) - -static DEFINE_MUTEX(rtl_lock); -static struct ibm_rtl_table __iomem *rtl_table; -static void __iomem *ebda_map; -static void __iomem *rtl_cmd_addr; -static u8 rtl_cmd_type; -static u8 rtl_cmd_width; - -static void __iomem *rtl_port_map(phys_addr_t addr, unsigned long len) -{ - if (rtl_cmd_type == RTL_ADDR_TYPE_MMIO) - return ioremap(addr, len); - return ioport_map(addr, len); -} - -static void rtl_port_unmap(void __iomem *addr) -{ - if (addr && rtl_cmd_type == RTL_ADDR_TYPE_MMIO) - iounmap(addr); - else - ioport_unmap(addr); -} - -static int ibm_rtl_write(u8 value) -{ - int ret = 0, count = 0; - static u32 cmd_port_val; - - RTL_DEBUG("%s(%d)\n", __FUNCTION__, value); - - value = value == 1 ? RTL_CMD_ENTER_PRTM : RTL_CMD_EXIT_PRTM; - - mutex_lock(&rtl_lock); - - if (ioread8(&rtl_table->rt_status) != value) { - iowrite8(value, &rtl_table->command); - - switch (rtl_cmd_width) { - case 8: - cmd_port_val = ioread8(&rtl_table->cmd_port_value); - RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val); - iowrite8((u8)cmd_port_val, rtl_cmd_addr); - break; - case 16: - cmd_port_val = ioread16(&rtl_table->cmd_port_value); - RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val); - iowrite16((u16)cmd_port_val, rtl_cmd_addr); - break; - case 32: - cmd_port_val = ioread32(&rtl_table->cmd_port_value); - RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val); - iowrite32(cmd_port_val, rtl_cmd_addr); - break; - } - - while (ioread8(&rtl_table->command)) { - msleep(10); - if (count++ > 500) { - pr_err("ibm-rtl: Hardware not responding to " - "mode switch request\n"); - ret = -EIO; - break; - } - - } - - if (ioread8(&rtl_table->command_status)) { - RTL_DEBUG("command_status reports failed command\n"); - ret = -EIO; - } - } - - mutex_unlock(&rtl_lock); - return ret; -} - -static ssize_t rtl_show_version(struct sysdev_class * dev, - struct sysdev_class_attribute *attr, - char *buf) -{ - return sprintf(buf, "%d\n", (int)ioread8(&rtl_table->version)); -} - -static ssize_t rtl_show_state(struct sysdev_class *dev, - struct sysdev_class_attribute *attr, - char *buf) -{ - return sprintf(buf, "%d\n", ioread8(&rtl_table->rt_status)); -} - -static ssize_t rtl_set_state(struct sysdev_class *dev, - struct sysdev_class_attribute *attr, - const char *buf, - size_t count) -{ - ssize_t ret; - - if (count < 1 || count > 2) - return -EINVAL; - - switch (buf[0]) { - case '0': - ret = ibm_rtl_write(0); - break; - case '1': - ret = ibm_rtl_write(1); - break; - default: - ret = -EINVAL; - } - if (ret >= 0) - ret = count; - - return ret; -} - -static struct sysdev_class class_rtl = { - .name = "ibm_rtl", -}; - -static SYSDEV_CLASS_ATTR(version, S_IRUGO, rtl_show_version, NULL); -static SYSDEV_CLASS_ATTR(state, 0600, rtl_show_state, rtl_set_state); - -static struct sysdev_class_attribute *rtl_attributes[] = { - &attr_version, - &attr_state, - NULL -}; - - -static int rtl_setup_sysfs(void) { - int ret, i; - ret = sysdev_class_register(&class_rtl); - - if (!ret) { - for (i = 0; rtl_attributes[i]; i ++) - sysdev_class_create_file(&class_rtl, rtl_attributes[i]); - } - return ret; -} - -static void rtl_teardown_sysfs(void) { - int i; - for (i = 0; rtl_attributes[i]; i ++) - sysdev_class_remove_file(&class_rtl, rtl_attributes[i]); - sysdev_class_unregister(&class_rtl); -} - -static int dmi_check_cb(const struct dmi_system_id *id) -{ - RTL_DEBUG("found IBM server '%s'\n", id->ident); - return 0; -} - -#define ibm_dmi_entry(NAME, TYPE) \ -{ \ - .ident = NAME, \ - .matches = { \ - DMI_MATCH(DMI_SYS_VENDOR, "IBM"), \ - DMI_MATCH(DMI_PRODUCT_NAME, TYPE), \ - }, \ - .callback = dmi_check_cb \ -} - -static struct dmi_system_id __initdata ibm_rtl_dmi_table[] = { - ibm_dmi_entry("BladeCenter LS21", "7971"), - ibm_dmi_entry("BladeCenter LS22", "7901"), - ibm_dmi_entry("BladeCenter HS21 XM", "7995"), - ibm_dmi_entry("BladeCenter HS22", "7870"), - ibm_dmi_entry("BladeCenter HS22V", "7871"), - ibm_dmi_entry("System x3550 M2", "7946"), - ibm_dmi_entry("System x3650 M2", "7947"), - ibm_dmi_entry("System x3550 M3", "7944"), - ibm_dmi_entry("System x3650 M3", "7945"), - { } -}; - -static int __init ibm_rtl_init(void) { - unsigned long ebda_addr, ebda_size; - unsigned int ebda_kb; - int ret = -ENODEV, i; - - if (force) - pr_warning("ibm-rtl: module loaded by force\n"); - /* first ensure that we are running on IBM HW */ - else if (!dmi_check_system(ibm_rtl_dmi_table)) - return -ENODEV; - - /* Get the address for the Extended BIOS Data Area */ - ebda_addr = get_bios_ebda(); - if (!ebda_addr) { - RTL_DEBUG("no BIOS EBDA found\n"); - return -ENODEV; - } - - ebda_map = ioremap(ebda_addr, 4); - if (!ebda_map) - return -ENOMEM; - - /* First word in the EDBA is the Size in KB */ - ebda_kb = ioread16(ebda_map); - RTL_DEBUG("EBDA is %d kB\n", ebda_kb); - - if (ebda_kb == 0) - goto out; - - iounmap(ebda_map); - ebda_size = ebda_kb*1024; - - /* Remap the whole table */ - ebda_map = ioremap(ebda_addr, ebda_size); - if (!ebda_map) - return -ENOMEM; - - /* search for the _RTL_ signature at the start of the table */ - for (i = 0 ; i < ebda_size/sizeof(unsigned int); i++) { - struct ibm_rtl_table __iomem * tmp; - tmp = (struct ibm_rtl_table __iomem *) (ebda_map+i); - if ((readq(&tmp->signature) & RTL_MASK) == RTL_SIGNATURE) { - phys_addr_t addr; - unsigned int plen; - RTL_DEBUG("found RTL_SIGNATURE at %#llx\n", (u64)tmp); - rtl_table = tmp; - /* The address, value, width and offset are platform - * dependent and found in the ibm_rtl_table */ - rtl_cmd_width = ioread8(&rtl_table->cmd_granularity); - rtl_cmd_type = ioread8(&rtl_table->cmd_address_type); - RTL_DEBUG("rtl_cmd_width = %u, rtl_cmd_type = %u\n", - rtl_cmd_width, rtl_cmd_type); - addr = ioread32(&rtl_table->cmd_port_address); - RTL_DEBUG("addr = %#llx\n", addr); - plen = rtl_cmd_width/sizeof(char); - rtl_cmd_addr = rtl_port_map(addr, plen); - RTL_DEBUG("rtl_cmd_addr = %#llx\n", (u64)rtl_cmd_addr); - if (!rtl_cmd_addr) { - ret = -ENOMEM; - break; - } - ret = rtl_setup_sysfs(); - break; - } - } - -out: - if (ret) { - iounmap(ebda_map); - rtl_port_unmap(rtl_cmd_addr); - } - - return ret; -} - -static void __exit ibm_rtl_exit(void) -{ - if (rtl_table) { - RTL_DEBUG("cleaning up"); - /* do not leave the machine in SMI-free mode */ - ibm_rtl_write(0); - /* unmap, unlink and remove all traces */ - rtl_teardown_sysfs(); - iounmap(ebda_map); - rtl_port_unmap(rtl_cmd_addr); - } -} - -module_init(ibm_rtl_init); -module_exit(ibm_rtl_exit); diff --git a/trunk/drivers/platform/x86/ideapad-laptop.c b/trunk/drivers/platform/x86/ideapad_acpi.c similarity index 53% rename from trunk/drivers/platform/x86/ideapad-laptop.c rename to trunk/drivers/platform/x86/ideapad_acpi.c index 5ff12205aa6b..798496353e8c 100644 --- a/trunk/drivers/platform/x86/ideapad-laptop.c +++ b/trunk/drivers/platform/x86/ideapad_acpi.c @@ -35,162 +35,112 @@ #define IDEAPAD_DEV_KILLSW 4 struct ideapad_private { - acpi_handle handle; struct rfkill *rfk[5]; -} *ideapad_priv; +}; static struct { char *name; - int cfgbit; - int opcode; int type; } ideapad_rfk_data[] = { - { "ideapad_camera", 19, 0x1E, NUM_RFKILL_TYPES }, - { "ideapad_wlan", 18, 0x15, RFKILL_TYPE_WLAN }, - { "ideapad_bluetooth", 16, 0x17, RFKILL_TYPE_BLUETOOTH }, - { "ideapad_3g", 17, 0x20, RFKILL_TYPE_WWAN }, - { "ideapad_killsw", 0, 0, RFKILL_TYPE_WLAN } + /* camera has no rfkill */ + { "ideapad_wlan", RFKILL_TYPE_WLAN }, + { "ideapad_bluetooth", RFKILL_TYPE_BLUETOOTH }, + { "ideapad_3g", RFKILL_TYPE_WWAN }, + { "ideapad_killsw", RFKILL_TYPE_WLAN } }; -static bool no_bt_rfkill; -module_param(no_bt_rfkill, bool, 0444); -MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth."); - -/* - * ACPI Helpers - */ -#define IDEAPAD_EC_TIMEOUT (100) /* in ms */ - -static int read_method_int(acpi_handle handle, const char *method, int *val) +static int ideapad_dev_exists(int device) { acpi_status status; - unsigned long long result; + union acpi_object in_param; + struct acpi_object_list input = { 1, &in_param }; + struct acpi_buffer output; + union acpi_object out_obj; + + output.length = sizeof(out_obj); + output.pointer = &out_obj; - status = acpi_evaluate_integer(handle, (char *)method, NULL, &result); + in_param.type = ACPI_TYPE_INTEGER; + in_param.integer.value = device + 1; + + status = acpi_evaluate_object(NULL, "\\_SB_.DECN", &input, &output); if (ACPI_FAILURE(status)) { - *val = -1; - return -1; - } else { - *val = result; - return 0; + printk(KERN_WARNING "IdeaPAD \\_SB_.DECN method failed %d. Is this an IdeaPAD?\n", status); + return -ENODEV; } + if (out_obj.type != ACPI_TYPE_INTEGER) { + printk(KERN_WARNING "IdeaPAD \\_SB_.DECN method returned unexpected type\n"); + return -ENODEV; + } + return out_obj.integer.value; } -static int method_vpcr(acpi_handle handle, int cmd, int *ret) +static int ideapad_dev_get_state(int device) { acpi_status status; - unsigned long long result; - struct acpi_object_list params; - union acpi_object in_obj; + union acpi_object in_param; + struct acpi_object_list input = { 1, &in_param }; + struct acpi_buffer output; + union acpi_object out_obj; - params.count = 1; - params.pointer = &in_obj; - in_obj.type = ACPI_TYPE_INTEGER; - in_obj.integer.value = cmd; + output.length = sizeof(out_obj); + output.pointer = &out_obj; - status = acpi_evaluate_integer(handle, "VPCR", ¶ms, &result); + in_param.type = ACPI_TYPE_INTEGER; + in_param.integer.value = device + 1; + status = acpi_evaluate_object(NULL, "\\_SB_.GECN", &input, &output); if (ACPI_FAILURE(status)) { - *ret = -1; - return -1; - } else { - *ret = result; - return 0; + printk(KERN_WARNING "IdeaPAD \\_SB_.GECN method failed %d\n", status); + return -ENODEV; + } + if (out_obj.type != ACPI_TYPE_INTEGER) { + printk(KERN_WARNING "IdeaPAD \\_SB_.GECN method returned unexpected type\n"); + return -ENODEV; } + return out_obj.integer.value; } -static int method_vpcw(acpi_handle handle, int cmd, int data) +static int ideapad_dev_set_state(int device, int state) { - struct acpi_object_list params; - union acpi_object in_obj[2]; acpi_status status; + union acpi_object in_params[2]; + struct acpi_object_list input = { 2, in_params }; - params.count = 2; - params.pointer = in_obj; - in_obj[0].type = ACPI_TYPE_INTEGER; - in_obj[0].integer.value = cmd; - in_obj[1].type = ACPI_TYPE_INTEGER; - in_obj[1].integer.value = data; - - status = acpi_evaluate_object(handle, "VPCW", ¶ms, NULL); - if (status != AE_OK) - return -1; - return 0; -} - -static int read_ec_data(acpi_handle handle, int cmd, unsigned long *data) -{ - int val; - unsigned long int end_jiffies; - - if (method_vpcw(handle, 1, cmd)) - return -1; - - for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1; - time_before(jiffies, end_jiffies);) { - schedule(); - if (method_vpcr(handle, 1, &val)) - return -1; - if (val == 0) { - if (method_vpcr(handle, 0, &val)) - return -1; - *data = val; - return 0; - } - } - pr_err("timeout in read_ec_cmd\n"); - return -1; -} + in_params[0].type = ACPI_TYPE_INTEGER; + in_params[0].integer.value = device + 1; + in_params[1].type = ACPI_TYPE_INTEGER; + in_params[1].integer.value = state; -static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data) -{ - int val; - unsigned long int end_jiffies; - - if (method_vpcw(handle, 0, data)) - return -1; - if (method_vpcw(handle, 1, cmd)) - return -1; - - for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1; - time_before(jiffies, end_jiffies);) { - schedule(); - if (method_vpcr(handle, 1, &val)) - return -1; - if (val == 0) - return 0; + status = acpi_evaluate_object(NULL, "\\_SB_.SECN", &input, NULL); + if (ACPI_FAILURE(status)) { + printk(KERN_WARNING "IdeaPAD \\_SB_.SECN method failed %d\n", status); + return -ENODEV; } - pr_err("timeout in write_ec_cmd\n"); - return -1; + return 0; } -/* the above is ACPI helpers */ - static ssize_t show_ideapad_cam(struct device *dev, struct device_attribute *attr, char *buf) { - struct ideapad_private *priv = dev_get_drvdata(dev); - acpi_handle handle = priv->handle; - unsigned long result; + int state = ideapad_dev_get_state(IDEAPAD_DEV_CAMERA); + if (state < 0) + return state; - if (read_ec_data(handle, 0x1D, &result)) - return sprintf(buf, "-1\n"); - return sprintf(buf, "%lu\n", result); + return sprintf(buf, "%d\n", state); } static ssize_t store_ideapad_cam(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct ideapad_private *priv = dev_get_drvdata(dev); - acpi_handle handle = priv->handle; int ret, state; if (!count) return 0; if (sscanf(buf, "%i", &state) != 1) return -EINVAL; - ret = write_ec_cmd(handle, 0x1E, state); + ret = ideapad_dev_set_state(IDEAPAD_DEV_CAMERA, !!state); if (ret < 0) return ret; return count; @@ -204,10 +154,7 @@ static int ideapad_rfk_set(void *data, bool blocked) if (device == IDEAPAD_DEV_KILLSW) return -EINVAL; - - return write_ec_cmd(ideapad_priv->handle, - ideapad_rfk_data[device].opcode, - !blocked); + return ideapad_dev_set_state(device, !blocked); } static struct rfkill_ops ideapad_rfk_ops = { @@ -217,47 +164,32 @@ static struct rfkill_ops ideapad_rfk_ops = { static void ideapad_sync_rfk_state(struct acpi_device *adevice) { struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); - acpi_handle handle = priv->handle; - unsigned long hw_blocked; + int hw_blocked = !ideapad_dev_get_state(IDEAPAD_DEV_KILLSW); int i; - if (read_ec_data(handle, 0x23, &hw_blocked)) + rfkill_set_hw_state(priv->rfk[IDEAPAD_DEV_KILLSW], hw_blocked); + for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++) + if (priv->rfk[i]) + rfkill_set_hw_state(priv->rfk[i], hw_blocked); + if (hw_blocked) return; - hw_blocked = !hw_blocked; - for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) + for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++) if (priv->rfk[i]) - rfkill_set_hw_state(priv->rfk[i], hw_blocked); + rfkill_set_sw_state(priv->rfk[i], !ideapad_dev_get_state(i)); } static int ideapad_register_rfkill(struct acpi_device *adevice, int dev) { struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); int ret; - unsigned long sw_blocked; - - if (no_bt_rfkill && - (ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) { - /* Force to enable bluetooth when no_bt_rfkill=1 */ - write_ec_cmd(ideapad_priv->handle, - ideapad_rfk_data[dev].opcode, 1); - return 0; - } - priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name, &adevice->dev, - ideapad_rfk_data[dev].type, &ideapad_rfk_ops, + priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev-1].name, &adevice->dev, + ideapad_rfk_data[dev-1].type, &ideapad_rfk_ops, (void *)(long)dev); if (!priv->rfk[dev]) return -ENOMEM; - if (read_ec_data(ideapad_priv->handle, ideapad_rfk_data[dev].opcode-1, - &sw_blocked)) { - rfkill_init_sw_state(priv->rfk[dev], 0); - } else { - sw_blocked = !sw_blocked; - rfkill_init_sw_state(priv->rfk[dev], sw_blocked); - } - ret = rfkill_register(priv->rfk[dev]); if (ret) { rfkill_destroy(priv->rfk[dev]); @@ -285,18 +217,14 @@ MODULE_DEVICE_TABLE(acpi, ideapad_device_ids); static int ideapad_acpi_add(struct acpi_device *adevice) { - int i, cfg; + int i; int devs_present[5]; struct ideapad_private *priv; - if (read_method_int(adevice->handle, "_CFG", &cfg)) - return -ENODEV; - for (i = IDEAPAD_DEV_CAMERA; i < IDEAPAD_DEV_KILLSW; i++) { - if (test_bit(ideapad_rfk_data[i].cfgbit, (unsigned long *)&cfg)) - devs_present[i] = 1; - else - devs_present[i] = 0; + devs_present[i] = ideapad_dev_exists(i); + if (devs_present[i] < 0) + return devs_present[i]; } /* The hardware switch is always present */ @@ -314,9 +242,7 @@ static int ideapad_acpi_add(struct acpi_device *adevice) } } - priv->handle = adevice->handle; dev_set_drvdata(&adevice->dev, priv); - ideapad_priv = priv; for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) { if (!devs_present[i]) continue; @@ -344,21 +270,7 @@ static int ideapad_acpi_remove(struct acpi_device *adevice, int type) static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) { - acpi_handle handle = adevice->handle; - unsigned long vpc1, vpc2, vpc_bit; - - if (read_ec_data(handle, 0x10, &vpc1)) - return; - if (read_ec_data(handle, 0x1A, &vpc2)) - return; - - vpc1 = (vpc2 << 8) | vpc1; - for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) { - if (test_bit(vpc_bit, &vpc1)) { - if (vpc_bit == 9) - ideapad_sync_rfk_state(adevice); - } - } + ideapad_sync_rfk_state(adevice); } static struct acpi_driver ideapad_acpi_driver = { diff --git a/trunk/drivers/platform/x86/intel_pmic_gpio.c b/trunk/drivers/platform/x86/intel_pmic_gpio.c index f540ff96c53f..5cdcff653918 100644 --- a/trunk/drivers/platform/x86/intel_pmic_gpio.c +++ b/trunk/drivers/platform/x86/intel_pmic_gpio.c @@ -142,16 +142,16 @@ static int pmic_gpio_direction_output(struct gpio_chip *chip, if (offset < 8)/* it is GPIO */ rc = intel_scu_ipc_update_register(GPIO0 + offset, - GPIO_DRV | (value ? GPIO_DOU : 0), - GPIO_DRV | GPIO_DOU | GPIO_DIR); + GPIO_DRV | GPIO_DOU | GPIO_DIR, + GPIO_DRV | (value ? GPIO_DOU : 0)); else if (offset < 16)/* it is GPOSW */ rc = intel_scu_ipc_update_register(GPOSWCTL0 + offset - 8, - GPOSW_DRV | (value ? GPOSW_DOU : 0), - GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV); + GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV, + GPOSW_DRV | (value ? GPOSW_DOU : 0)); else if (offset > 15 && offset < 24)/* it is GPO */ rc = intel_scu_ipc_update_register(GPO, - value ? 1 << (offset - 16) : 0, - 1 << (offset - 16)); + 1 << (offset - 16), + value ? 1 << (offset - 16) : 0); else { printk(KERN_ERR "%s: invalid PMIC GPIO pin %d!\n", __func__, offset); @@ -179,16 +179,16 @@ static void pmic_gpio_set(struct gpio_chip *chip, unsigned offset, int value) { if (offset < 8)/* it is GPIO */ intel_scu_ipc_update_register(GPIO0 + offset, - GPIO_DRV | (value ? GPIO_DOU : 0), - GPIO_DRV | GPIO_DOU); + GPIO_DRV | GPIO_DOU, + GPIO_DRV | (value ? GPIO_DOU : 0)); else if (offset < 16)/* it is GPOSW */ intel_scu_ipc_update_register(GPOSWCTL0 + offset - 8, - GPOSW_DRV | (value ? GPOSW_DOU : 0), - GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV); + GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV, + GPOSW_DRV | (value ? GPOSW_DOU : 0)); else if (offset > 15 && offset < 24) /* it is GPO */ intel_scu_ipc_update_register(GPO, - value ? 1 << (offset - 16) : 0, - 1 << (offset - 16)); + 1 << (offset - 16), + value ? 1 << (offset - 16) : 0); } static int pmic_irq_type(unsigned irq, unsigned type) @@ -197,7 +197,7 @@ static int pmic_irq_type(unsigned irq, unsigned type) u32 gpio = irq - pg->irq_base; unsigned long flags; - if (gpio >= pg->chip.ngpio) + if (gpio > pg->chip.ngpio) return -EINVAL; spin_lock_irqsave(&pg->irqtypes.lock, flags); diff --git a/trunk/drivers/platform/x86/intel_scu_ipc.c b/trunk/drivers/platform/x86/intel_scu_ipc.c index 41a9e34899ac..6abe18e638e9 100644 --- a/trunk/drivers/platform/x86/intel_scu_ipc.c +++ b/trunk/drivers/platform/x86/intel_scu_ipc.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include diff --git a/trunk/drivers/platform/x86/panasonic-laptop.c b/trunk/drivers/platform/x86/panasonic-laptop.c index cc1e0ba104d7..ec01c3d8fc5a 100644 --- a/trunk/drivers/platform/x86/panasonic-laptop.c +++ b/trunk/drivers/platform/x86/panasonic-laptop.c @@ -128,7 +128,6 @@ #include #include #include -#include #ifndef ACPI_HOTKEY_COMPONENT @@ -201,29 +200,30 @@ static struct acpi_driver acpi_pcc_driver = { }, }; -static const struct key_entry panasonic_keymap[] = { - { KE_KEY, 0, { KEY_RESERVED } }, - { KE_KEY, 1, { KEY_BRIGHTNESSDOWN } }, - { KE_KEY, 2, { KEY_BRIGHTNESSUP } }, - { KE_KEY, 3, { KEY_DISPLAYTOGGLE } }, - { KE_KEY, 4, { KEY_MUTE } }, - { KE_KEY, 5, { KEY_VOLUMEDOWN } }, - { KE_KEY, 6, { KEY_VOLUMEUP } }, - { KE_KEY, 7, { KEY_SLEEP } }, - { KE_KEY, 8, { KEY_PROG1 } }, /* Change CPU boost */ - { KE_KEY, 9, { KEY_BATTERY } }, - { KE_KEY, 10, { KEY_SUSPEND } }, - { KE_END, 0 } +#define KEYMAP_SIZE 11 +static const unsigned int initial_keymap[KEYMAP_SIZE] = { + /* 0 */ KEY_RESERVED, + /* 1 */ KEY_BRIGHTNESSDOWN, + /* 2 */ KEY_BRIGHTNESSUP, + /* 3 */ KEY_DISPLAYTOGGLE, + /* 4 */ KEY_MUTE, + /* 5 */ KEY_VOLUMEDOWN, + /* 6 */ KEY_VOLUMEUP, + /* 7 */ KEY_SLEEP, + /* 8 */ KEY_PROG1, /* Change CPU boost */ + /* 9 */ KEY_BATTERY, + /* 10 */ KEY_SUSPEND, }; struct pcc_acpi { acpi_handle handle; unsigned long num_sifr; int sticky_mode; - u32 *sinf; + u32 *sinf; struct acpi_device *device; struct input_dev *input_dev; struct backlight_device *backlight; + unsigned int keymap[KEYMAP_SIZE]; }; struct pcc_keyinput { @@ -267,7 +267,7 @@ static inline int acpi_pcc_get_sqty(struct acpi_device *device) } } -static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc) +static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf) { acpi_status status; struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; @@ -285,7 +285,6 @@ static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc) hkey = buffer.pointer; if (!hkey || (hkey->type != ACPI_TYPE_PACKAGE)) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF\n")); - status = AE_ERROR; goto end; } @@ -299,12 +298,12 @@ static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc) for (i = 0; i < hkey->package.count; i++) { union acpi_object *element = &(hkey->package.elements[i]); if (likely(element->type == ACPI_TYPE_INTEGER)) { - pcc->sinf[i] = element->integer.value; + sinf[i] = element->integer.value; } else ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF data\n")); } - pcc->sinf[hkey->package.count] = -1; + sinf[hkey->package.count] = -1; end: kfree(buffer.pointer); @@ -322,7 +321,7 @@ static int bl_get(struct backlight_device *bd) { struct pcc_acpi *pcc = bl_get_data(bd); - if (!acpi_pcc_retrieve_biosdata(pcc)) + if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) return -EIO; return pcc->sinf[SINF_AC_CUR_BRIGHT]; @@ -334,7 +333,7 @@ static int bl_set_status(struct backlight_device *bd) int bright = bd->props.brightness; int rc; - if (!acpi_pcc_retrieve_biosdata(pcc)) + if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) return -EIO; if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT]) @@ -368,7 +367,7 @@ static ssize_t show_numbatt(struct device *dev, struct device_attribute *attr, struct acpi_device *acpi = to_acpi_device(dev); struct pcc_acpi *pcc = acpi_driver_data(acpi); - if (!acpi_pcc_retrieve_biosdata(pcc)) + if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) return -EIO; return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]); @@ -380,7 +379,7 @@ static ssize_t show_lcdtype(struct device *dev, struct device_attribute *attr, struct acpi_device *acpi = to_acpi_device(dev); struct pcc_acpi *pcc = acpi_driver_data(acpi); - if (!acpi_pcc_retrieve_biosdata(pcc)) + if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) return -EIO; return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_LCD_TYPE]); @@ -392,7 +391,7 @@ static ssize_t show_mute(struct device *dev, struct device_attribute *attr, struct acpi_device *acpi = to_acpi_device(dev); struct pcc_acpi *pcc = acpi_driver_data(acpi); - if (!acpi_pcc_retrieve_biosdata(pcc)) + if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) return -EIO; return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_MUTE]); @@ -404,7 +403,7 @@ static ssize_t show_sticky(struct device *dev, struct device_attribute *attr, struct acpi_device *acpi = to_acpi_device(dev); struct pcc_acpi *pcc = acpi_driver_data(acpi); - if (!acpi_pcc_retrieve_biosdata(pcc)) + if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) return -EIO; return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_STICKY_KEY]); @@ -447,10 +446,56 @@ static struct attribute_group pcc_attr_group = { /* hotkey input device driver */ +static int pcc_getkeycode(struct input_dev *dev, + unsigned int scancode, unsigned int *keycode) +{ + struct pcc_acpi *pcc = input_get_drvdata(dev); + + if (scancode >= ARRAY_SIZE(pcc->keymap)) + return -EINVAL; + + *keycode = pcc->keymap[scancode]; + + return 0; +} + +static int keymap_get_by_keycode(struct pcc_acpi *pcc, unsigned int keycode) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(pcc->keymap); i++) { + if (pcc->keymap[i] == keycode) + return i+1; + } + + return 0; +} + +static int pcc_setkeycode(struct input_dev *dev, + unsigned int scancode, unsigned int keycode) +{ + struct pcc_acpi *pcc = input_get_drvdata(dev); + int oldkeycode; + + if (scancode >= ARRAY_SIZE(pcc->keymap)) + return -EINVAL; + + oldkeycode = pcc->keymap[scancode]; + pcc->keymap[scancode] = keycode; + + set_bit(keycode, dev->keybit); + + if (!keymap_get_by_keycode(pcc, oldkeycode)) + clear_bit(oldkeycode, dev->keybit); + + return 0; +} + static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc) { struct input_dev *hotk_input_dev = pcc->input_dev; int rc; + int key_code, hkey_num; unsigned long long result; rc = acpi_evaluate_integer(pcc->handle, METHOD_HKEY_QUERY, @@ -463,10 +508,25 @@ static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc) acpi_bus_generate_proc_event(pcc->device, HKEY_NOTIFY, result); - if (!sparse_keymap_report_event(hotk_input_dev, - result & 0xf, result & 0x80, false)) + hkey_num = result & 0xf; + + if (hkey_num < 0 || hkey_num >= ARRAY_SIZE(pcc->keymap)) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "Unknown hotkey event: %d\n", result)); + "hotkey number out of range: %d\n", + hkey_num)); + return; + } + + key_code = pcc->keymap[hkey_num]; + + if (key_code != KEY_RESERVED) { + int pushed = (result & 0x80) ? TRUE : FALSE; + + input_report_key(hotk_input_dev, key_code, pushed); + input_sync(hotk_input_dev); + } + + return; } static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event) @@ -485,55 +545,40 @@ static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event) static int acpi_pcc_init_input(struct pcc_acpi *pcc) { - struct input_dev *input_dev; - int error; + int i, rc; - input_dev = input_allocate_device(); - if (!input_dev) { + pcc->input_dev = input_allocate_device(); + if (!pcc->input_dev) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Couldn't allocate input device for hotkey")); return -ENOMEM; } - input_dev->name = ACPI_PCC_DRIVER_NAME; - input_dev->phys = ACPI_PCC_INPUT_PHYS; - input_dev->id.bustype = BUS_HOST; - input_dev->id.vendor = 0x0001; - input_dev->id.product = 0x0001; - input_dev->id.version = 0x0100; + pcc->input_dev->evbit[0] = BIT(EV_KEY); - error = sparse_keymap_setup(input_dev, panasonic_keymap, NULL); - if (error) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "Unable to setup input device keymap\n")); - goto err_free_dev; - } + pcc->input_dev->name = ACPI_PCC_DRIVER_NAME; + pcc->input_dev->phys = ACPI_PCC_INPUT_PHYS; + pcc->input_dev->id.bustype = BUS_HOST; + pcc->input_dev->id.vendor = 0x0001; + pcc->input_dev->id.product = 0x0001; + pcc->input_dev->id.version = 0x0100; + pcc->input_dev->getkeycode = pcc_getkeycode; + pcc->input_dev->setkeycode = pcc_setkeycode; - error = input_register_device(input_dev); - if (error) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "Unable to register input device\n")); - goto err_free_keymap; - } + /* load initial keymap */ + memcpy(pcc->keymap, initial_keymap, sizeof(pcc->keymap)); - pcc->input_dev = input_dev; - return 0; + for (i = 0; i < ARRAY_SIZE(pcc->keymap); i++) + __set_bit(pcc->keymap[i], pcc->input_dev->keybit); + __clear_bit(KEY_RESERVED, pcc->input_dev->keybit); - err_free_keymap: - sparse_keymap_free(input_dev); - err_free_dev: - input_free_device(input_dev); - return error; -} + input_set_drvdata(pcc->input_dev, pcc); -static void acpi_pcc_destroy_input(struct pcc_acpi *pcc) -{ - sparse_keymap_free(pcc->input_dev); - input_unregister_device(pcc->input_dev); - /* - * No need to input_free_device() since core input API refcounts - * and free()s the device. - */ + rc = input_register_device(pcc->input_dev); + if (rc < 0) + input_free_device(pcc->input_dev); + + return rc; } /* kernel module interface */ @@ -591,13 +636,12 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) if (result) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error installing keyinput handler\n")); - goto out_sinf; + goto out_hotkey; } - if (!acpi_pcc_retrieve_biosdata(pcc)) { + if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Couldn't retrieve BIOS data\n")); - result = -EIO; goto out_input; } /* initialize backlight */ @@ -607,7 +651,7 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) &pcc_backlight_ops, &props); if (IS_ERR(pcc->backlight)) { result = PTR_ERR(pcc->backlight); - goto out_input; + goto out_sinf; } /* read the initial brightness setting from the hardware */ @@ -625,10 +669,12 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) out_backlight: backlight_device_unregister(pcc->backlight); -out_input: - acpi_pcc_destroy_input(pcc); out_sinf: kfree(pcc->sinf); +out_input: + input_unregister_device(pcc->input_dev); + /* no need to input_free_device() since core input API refcount and + * free()s the device */ out_hotkey: kfree(pcc); @@ -663,7 +709,9 @@ static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type) backlight_device_unregister(pcc->backlight); - acpi_pcc_destroy_input(pcc); + input_unregister_device(pcc->input_dev); + /* no need to input_free_device() since core input API refcount and + * free()s the device */ kfree(pcc->sinf); kfree(pcc); diff --git a/trunk/drivers/platform/x86/topstar-laptop.c b/trunk/drivers/platform/x86/topstar-laptop.c index 1d07d6d09f27..ff4b476f1950 100644 --- a/trunk/drivers/platform/x86/topstar-laptop.c +++ b/trunk/drivers/platform/x86/topstar-laptop.c @@ -19,7 +19,6 @@ #include #include #include -#include #define ACPI_TOPSTAR_CLASS "topstar" @@ -27,37 +26,52 @@ struct topstar_hkey { struct input_dev *inputdev; }; -static const struct key_entry topstar_keymap[] = { - { KE_KEY, 0x80, { KEY_BRIGHTNESSUP } }, - { KE_KEY, 0x81, { KEY_BRIGHTNESSDOWN } }, - { KE_KEY, 0x83, { KEY_VOLUMEUP } }, - { KE_KEY, 0x84, { KEY_VOLUMEDOWN } }, - { KE_KEY, 0x85, { KEY_MUTE } }, - { KE_KEY, 0x86, { KEY_SWITCHVIDEOMODE } }, - { KE_KEY, 0x87, { KEY_F13 } }, /* touchpad enable/disable key */ - { KE_KEY, 0x88, { KEY_WLAN } }, - { KE_KEY, 0x8a, { KEY_WWW } }, - { KE_KEY, 0x8b, { KEY_MAIL } }, - { KE_KEY, 0x8c, { KEY_MEDIA } }, +struct tps_key_entry { + u8 code; + u16 keycode; +}; - /* Known non hotkey events don't handled or that we don't care yet */ - { KE_IGNORE, 0x8e, }, - { KE_IGNORE, 0x8f, }, - { KE_IGNORE, 0x90, }, +static struct tps_key_entry topstar_keymap[] = { + { 0x80, KEY_BRIGHTNESSUP }, + { 0x81, KEY_BRIGHTNESSDOWN }, + { 0x83, KEY_VOLUMEUP }, + { 0x84, KEY_VOLUMEDOWN }, + { 0x85, KEY_MUTE }, + { 0x86, KEY_SWITCHVIDEOMODE }, + { 0x87, KEY_F13 }, /* touchpad enable/disable key */ + { 0x88, KEY_WLAN }, + { 0x8a, KEY_WWW }, + { 0x8b, KEY_MAIL }, + { 0x8c, KEY_MEDIA }, + { 0x96, KEY_F14 }, /* G key? */ + { } +}; - /* - * 'G key' generate two event codes, convert to only - * one event/key code for now, consider replacing by - * a switch (3G switch - SW_3G?) - */ - { KE_KEY, 0x96, { KEY_F14 } }, - { KE_KEY, 0x97, { KEY_F14 } }, +static struct tps_key_entry *tps_get_key_by_scancode(unsigned int code) +{ + struct tps_key_entry *key; - { KE_END, 0 } -}; + for (key = topstar_keymap; key->code; key++) + if (code == key->code) + return key; + + return NULL; +} + +static struct tps_key_entry *tps_get_key_by_keycode(unsigned int code) +{ + struct tps_key_entry *key; + + for (key = topstar_keymap; key->code; key++) + if (code == key->keycode) + return key; + + return NULL; +} static void acpi_topstar_notify(struct acpi_device *device, u32 event) { + struct tps_key_entry *key; static bool dup_evnt[2]; bool *dup; struct topstar_hkey *hkey = acpi_driver_data(device); @@ -72,8 +86,27 @@ static void acpi_topstar_notify(struct acpi_device *device, u32 event) *dup = true; } - if (!sparse_keymap_report_event(hkey->inputdev, event, 1, true)) - pr_info("unknown event = 0x%02x\n", event); + /* + * 'G key' generate two event codes, convert to only + * one event/key code for now (3G switch?) + */ + if (event == 0x97) + event = 0x96; + + key = tps_get_key_by_scancode(event); + if (key) { + input_report_key(hkey->inputdev, key->keycode, 1); + input_sync(hkey->inputdev); + input_report_key(hkey->inputdev, key->keycode, 0); + input_sync(hkey->inputdev); + return; + } + + /* Known non hotkey events don't handled or that we don't care yet */ + if (event == 0x8e || event == 0x8f || event == 0x90) + return; + + pr_info("unknown event = 0x%02x\n", event); } static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state) @@ -94,41 +127,62 @@ static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state) return 0; } +static int topstar_getkeycode(struct input_dev *dev, + unsigned int scancode, unsigned int *keycode) +{ + struct tps_key_entry *key = tps_get_key_by_scancode(scancode); + + if (!key) + return -EINVAL; + + *keycode = key->keycode; + return 0; +} + +static int topstar_setkeycode(struct input_dev *dev, + unsigned int scancode, unsigned int keycode) +{ + struct tps_key_entry *key; + int old_keycode; + + key = tps_get_key_by_scancode(scancode); + + if (!key) + return -EINVAL; + + old_keycode = key->keycode; + key->keycode = keycode; + set_bit(keycode, dev->keybit); + if (!tps_get_key_by_keycode(old_keycode)) + clear_bit(old_keycode, dev->keybit); + return 0; +} + static int acpi_topstar_init_hkey(struct topstar_hkey *hkey) { - struct input_dev *input; - int error; + struct tps_key_entry *key; - input = input_allocate_device(); - if (!input) { + hkey->inputdev = input_allocate_device(); + if (!hkey->inputdev) { pr_err("Unable to allocate input device\n"); - return -ENOMEM; + return -ENODEV; } - - input->name = "Topstar Laptop extra buttons"; - input->phys = "topstar/input0"; - input->id.bustype = BUS_HOST; - - error = sparse_keymap_setup(input, topstar_keymap, NULL); - if (error) { - pr_err("Unable to setup input device keymap\n"); - goto err_free_dev; + hkey->inputdev->name = "Topstar Laptop extra buttons"; + hkey->inputdev->phys = "topstar/input0"; + hkey->inputdev->id.bustype = BUS_HOST; + hkey->inputdev->getkeycode = topstar_getkeycode; + hkey->inputdev->setkeycode = topstar_setkeycode; + for (key = topstar_keymap; key->code; key++) { + set_bit(EV_KEY, hkey->inputdev->evbit); + set_bit(key->keycode, hkey->inputdev->keybit); } - - error = input_register_device(input); - if (error) { + if (input_register_device(hkey->inputdev)) { pr_err("Unable to register input device\n"); - goto err_free_keymap; + input_free_device(hkey->inputdev); + return -ENODEV; } - hkey->inputdev = input; return 0; - - err_free_keymap: - sparse_keymap_free(input); - err_free_dev: - input_free_device(input); - return error; } static int acpi_topstar_add(struct acpi_device *device) @@ -162,7 +216,6 @@ static int acpi_topstar_remove(struct acpi_device *device, int type) acpi_topstar_fncx_switch(device, false); - sparse_keymap_free(tps_hkey->inputdev); input_unregister_device(tps_hkey->inputdev); kfree(tps_hkey); diff --git a/trunk/drivers/platform/x86/toshiba_acpi.c b/trunk/drivers/platform/x86/toshiba_acpi.c index 06f304f46e02..7d67a45bb2b0 100644 --- a/trunk/drivers/platform/x86/toshiba_acpi.c +++ b/trunk/drivers/platform/x86/toshiba_acpi.c @@ -48,7 +48,6 @@ #include #include #include -#include #include #include @@ -122,28 +121,36 @@ static const struct acpi_device_id toshiba_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, toshiba_device_ids); -static const struct key_entry toshiba_acpi_keymap[] __initconst = { - { KE_KEY, 0x101, { KEY_MUTE } }, - { KE_KEY, 0x102, { KEY_ZOOMOUT } }, - { KE_KEY, 0x103, { KEY_ZOOMIN } }, - { KE_KEY, 0x13b, { KEY_COFFEE } }, - { KE_KEY, 0x13c, { KEY_BATTERY } }, - { KE_KEY, 0x13d, { KEY_SLEEP } }, - { KE_KEY, 0x13e, { KEY_SUSPEND } }, - { KE_KEY, 0x13f, { KEY_SWITCHVIDEOMODE } }, - { KE_KEY, 0x140, { KEY_BRIGHTNESSDOWN } }, - { KE_KEY, 0x141, { KEY_BRIGHTNESSUP } }, - { KE_KEY, 0x142, { KEY_WLAN } }, - { KE_KEY, 0x143, { KEY_PROG1 } }, - { KE_KEY, 0xb05, { KEY_PROG2 } }, - { KE_KEY, 0xb06, { KEY_WWW } }, - { KE_KEY, 0xb07, { KEY_MAIL } }, - { KE_KEY, 0xb30, { KEY_STOP } }, - { KE_KEY, 0xb31, { KEY_PREVIOUSSONG } }, - { KE_KEY, 0xb32, { KEY_NEXTSONG } }, - { KE_KEY, 0xb33, { KEY_PLAYPAUSE } }, - { KE_KEY, 0xb5a, { KEY_MEDIA } }, - { KE_END, 0 }, +struct key_entry { + char type; + u16 code; + u16 keycode; +}; + +enum {KE_KEY, KE_END}; + +static struct key_entry toshiba_acpi_keymap[] = { + {KE_KEY, 0x101, KEY_MUTE}, + {KE_KEY, 0x102, KEY_ZOOMOUT}, + {KE_KEY, 0x103, KEY_ZOOMIN}, + {KE_KEY, 0x13b, KEY_COFFEE}, + {KE_KEY, 0x13c, KEY_BATTERY}, + {KE_KEY, 0x13d, KEY_SLEEP}, + {KE_KEY, 0x13e, KEY_SUSPEND}, + {KE_KEY, 0x13f, KEY_SWITCHVIDEOMODE}, + {KE_KEY, 0x140, KEY_BRIGHTNESSDOWN}, + {KE_KEY, 0x141, KEY_BRIGHTNESSUP}, + {KE_KEY, 0x142, KEY_WLAN}, + {KE_KEY, 0x143, KEY_PROG1}, + {KE_KEY, 0xb05, KEY_PROG2}, + {KE_KEY, 0xb06, KEY_WWW}, + {KE_KEY, 0xb07, KEY_MAIL}, + {KE_KEY, 0xb30, KEY_STOP}, + {KE_KEY, 0xb31, KEY_PREVIOUSSONG}, + {KE_KEY, 0xb32, KEY_NEXTSONG}, + {KE_KEY, 0xb33, KEY_PLAYPAUSE}, + {KE_KEY, 0xb5a, KEY_MEDIA}, + {KE_END, 0, 0}, }; /* utility @@ -845,9 +852,64 @@ static struct backlight_ops toshiba_backlight_data = { .update_status = set_lcd_status, }; +static struct key_entry *toshiba_acpi_get_entry_by_scancode(unsigned int code) +{ + struct key_entry *key; + + for (key = toshiba_acpi_keymap; key->type != KE_END; key++) + if (code == key->code) + return key; + + return NULL; +} + +static struct key_entry *toshiba_acpi_get_entry_by_keycode(unsigned int code) +{ + struct key_entry *key; + + for (key = toshiba_acpi_keymap; key->type != KE_END; key++) + if (code == key->keycode && key->type == KE_KEY) + return key; + + return NULL; +} + +static int toshiba_acpi_getkeycode(struct input_dev *dev, + unsigned int scancode, unsigned int *keycode) +{ + struct key_entry *key = toshiba_acpi_get_entry_by_scancode(scancode); + + if (key && key->type == KE_KEY) { + *keycode = key->keycode; + return 0; + } + + return -EINVAL; +} + +static int toshiba_acpi_setkeycode(struct input_dev *dev, + unsigned int scancode, unsigned int keycode) +{ + struct key_entry *key; + unsigned int old_keycode; + + key = toshiba_acpi_get_entry_by_scancode(scancode); + if (key && key->type == KE_KEY) { + old_keycode = key->keycode; + key->keycode = keycode; + set_bit(keycode, dev->keybit); + if (!toshiba_acpi_get_entry_by_keycode(old_keycode)) + clear_bit(old_keycode, dev->keybit); + return 0; + } + + return -EINVAL; +} + static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context) { u32 hci_result, value; + struct key_entry *key; if (event != 0x80) return; @@ -860,11 +922,19 @@ static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context) if (value & 0x80) continue; - if (!sparse_keymap_report_event(toshiba_acpi.hotkey_dev, - value, 1, true)) { + key = toshiba_acpi_get_entry_by_scancode + (value); + if (!key) { printk(MY_INFO "Unknown key %x\n", value); + continue; } + input_report_key(toshiba_acpi.hotkey_dev, + key->keycode, 1); + input_sync(toshiba_acpi.hotkey_dev); + input_report_key(toshiba_acpi.hotkey_dev, + key->keycode, 0); + input_sync(toshiba_acpi.hotkey_dev); } else if (hci_result == HCI_NOT_SUPPORTED) { /* This is a workaround for an unresolved issue on * some machines where system events sporadically @@ -875,17 +945,34 @@ static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context) } while (hci_result != HCI_EMPTY); } -static int __init toshiba_acpi_setup_keyboard(char *device) +static int toshiba_acpi_setup_keyboard(char *device) { acpi_status status; - int error; + acpi_handle handle; + int result; + const struct key_entry *key; - status = acpi_get_handle(NULL, device, &toshiba_acpi.handle); + status = acpi_get_handle(NULL, device, &handle); if (ACPI_FAILURE(status)) { printk(MY_INFO "Unable to get notification device\n"); return -ENODEV; } + toshiba_acpi.handle = handle; + + status = acpi_evaluate_object(handle, "ENAB", NULL, NULL); + if (ACPI_FAILURE(status)) { + printk(MY_INFO "Unable to enable hotkeys\n"); + return -ENODEV; + } + + status = acpi_install_notify_handler(handle, ACPI_DEVICE_NOTIFY, + toshiba_acpi_notify, NULL); + if (ACPI_FAILURE(status)) { + printk(MY_INFO "Unable to install hotkey notification\n"); + return -ENODEV; + } + toshiba_acpi.hotkey_dev = input_allocate_device(); if (!toshiba_acpi.hotkey_dev) { printk(MY_INFO "Unable to register input device\n"); @@ -895,54 +982,27 @@ static int __init toshiba_acpi_setup_keyboard(char *device) toshiba_acpi.hotkey_dev->name = "Toshiba input device"; toshiba_acpi.hotkey_dev->phys = device; toshiba_acpi.hotkey_dev->id.bustype = BUS_HOST; + toshiba_acpi.hotkey_dev->getkeycode = toshiba_acpi_getkeycode; + toshiba_acpi.hotkey_dev->setkeycode = toshiba_acpi_setkeycode; - error = sparse_keymap_setup(toshiba_acpi.hotkey_dev, - toshiba_acpi_keymap, NULL); - if (error) - goto err_free_dev; - - status = acpi_install_notify_handler(toshiba_acpi.handle, - ACPI_DEVICE_NOTIFY, toshiba_acpi_notify, NULL); - if (ACPI_FAILURE(status)) { - printk(MY_INFO "Unable to install hotkey notification\n"); - error = -ENODEV; - goto err_free_keymap; - } - - status = acpi_evaluate_object(toshiba_acpi.handle, "ENAB", NULL, NULL); - if (ACPI_FAILURE(status)) { - printk(MY_INFO "Unable to enable hotkeys\n"); - error = -ENODEV; - goto err_remove_notify; + for (key = toshiba_acpi_keymap; key->type != KE_END; key++) { + set_bit(EV_KEY, toshiba_acpi.hotkey_dev->evbit); + set_bit(key->keycode, toshiba_acpi.hotkey_dev->keybit); } - error = input_register_device(toshiba_acpi.hotkey_dev); - if (error) { + result = input_register_device(toshiba_acpi.hotkey_dev); + if (result) { printk(MY_INFO "Unable to register input device\n"); - goto err_remove_notify; + return result; } return 0; - - err_remove_notify: - acpi_remove_notify_handler(toshiba_acpi.handle, - ACPI_DEVICE_NOTIFY, toshiba_acpi_notify); - err_free_keymap: - sparse_keymap_free(toshiba_acpi.hotkey_dev); - err_free_dev: - input_free_device(toshiba_acpi.hotkey_dev); - toshiba_acpi.hotkey_dev = NULL; - return error; } static void toshiba_acpi_exit(void) { - if (toshiba_acpi.hotkey_dev) { - acpi_remove_notify_handler(toshiba_acpi.handle, - ACPI_DEVICE_NOTIFY, toshiba_acpi_notify); - sparse_keymap_free(toshiba_acpi.hotkey_dev); + if (toshiba_acpi.hotkey_dev) input_unregister_device(toshiba_acpi.hotkey_dev); - } if (toshiba_acpi.bt_rfk) { rfkill_unregister(toshiba_acpi.bt_rfk); @@ -957,6 +1017,9 @@ static void toshiba_acpi_exit(void) if (toshiba_proc_dir) remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); + acpi_remove_notify_handler(toshiba_acpi.handle, ACPI_DEVICE_NOTIFY, + toshiba_acpi_notify); + if (toshiba_acpi.illumination_installed) led_classdev_unregister(&toshiba_led); diff --git a/trunk/drivers/platform/x86/wmi.c b/trunk/drivers/platform/x86/wmi.c index 104b77c87ef5..b2978a04317f 100644 --- a/trunk/drivers/platform/x86/wmi.c +++ b/trunk/drivers/platform/x86/wmi.c @@ -27,8 +27,6 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include #include #include @@ -46,8 +44,9 @@ MODULE_LICENSE("GPL"); #define ACPI_WMI_CLASS "wmi" +#define PREFIX "ACPI: WMI: " + static DEFINE_MUTEX(wmi_data_lock); -static LIST_HEAD(wmi_block_list); struct guid_block { char guid[16]; @@ -68,9 +67,10 @@ struct wmi_block { acpi_handle handle; wmi_notify_handler handler; void *handler_data; - struct device dev; + struct device *dev; }; +static struct wmi_block wmi_blocks; /* * If the GUID data block is marked as expensive, we must enable and @@ -110,7 +110,7 @@ static struct acpi_driver acpi_wmi_driver = { .add = acpi_wmi_add, .remove = acpi_wmi_remove, .notify = acpi_wmi_notify, - }, + }, }; /* @@ -128,18 +128,30 @@ static struct acpi_driver acpi_wmi_driver = { */ static int wmi_parse_hexbyte(const u8 *src) { + unsigned int x; /* For correct wrapping */ int h; - int value; /* high part */ - h = value = hex_to_bin(src[0]); - if (value < 0) + x = src[0]; + if (x - '0' <= '9' - '0') { + h = x - '0'; + } else if (x - 'a' <= 'f' - 'a') { + h = x - 'a' + 10; + } else if (x - 'A' <= 'F' - 'A') { + h = x - 'A' + 10; + } else { return -1; + } + h <<= 4; /* low part */ - value = hex_to_bin(src[1]); - if (value >= 0) - return (h << 4) | value; + x = src[1]; + if (x - '0' <= '9' - '0') + return h | (x - '0'); + if (x - 'a' <= 'f' - 'a') + return h | (x - 'a' + 10); + if (x - 'A' <= 'F' - 'A') + return h | (x - 'A' + 10); return -1; } @@ -220,7 +232,7 @@ static int wmi_gtoa(const char *in, char *out) for (i = 10; i <= 15; i++) out += sprintf(out, "%02X", in[i] & 0xFF); - *out = '\0'; + out = '\0'; return 0; } @@ -234,7 +246,7 @@ static bool find_guid(const char *guid_string, struct wmi_block **out) wmi_parse_guid(guid_string, tmp); wmi_swap_bytes(tmp, guid_input); - list_for_each(p, &wmi_block_list) { + list_for_each(p, &wmi_blocks.list) { wblock = list_entry(p, struct wmi_block, list); block = &wblock->gblock; @@ -475,29 +487,30 @@ const struct acpi_buffer *in) } EXPORT_SYMBOL_GPL(wmi_set_block); -static void wmi_dump_wdg(const struct guid_block *g) +static void wmi_dump_wdg(struct guid_block *g) { char guid_string[37]; wmi_gtoa(g->guid, guid_string); - - pr_info("%s:\n", guid_string); - pr_info("\tobject_id: %c%c\n", g->object_id[0], g->object_id[1]); - pr_info("\tnotify_id: %02X\n", g->notify_id); - pr_info("\treserved: %02X\n", g->reserved); - pr_info("\tinstance_count: %d\n", g->instance_count); - pr_info("\tflags: %#x ", g->flags); + printk(KERN_INFO PREFIX "%s:\n", guid_string); + printk(KERN_INFO PREFIX "\tobject_id: %c%c\n", + g->object_id[0], g->object_id[1]); + printk(KERN_INFO PREFIX "\tnotify_id: %02X\n", g->notify_id); + printk(KERN_INFO PREFIX "\treserved: %02X\n", g->reserved); + printk(KERN_INFO PREFIX "\tinstance_count: %d\n", g->instance_count); + printk(KERN_INFO PREFIX "\tflags: %#x", g->flags); if (g->flags) { + printk(" "); if (g->flags & ACPI_WMI_EXPENSIVE) - pr_cont("ACPI_WMI_EXPENSIVE "); + printk("ACPI_WMI_EXPENSIVE "); if (g->flags & ACPI_WMI_METHOD) - pr_cont("ACPI_WMI_METHOD "); + printk("ACPI_WMI_METHOD "); if (g->flags & ACPI_WMI_STRING) - pr_cont("ACPI_WMI_STRING "); + printk("ACPI_WMI_STRING "); if (g->flags & ACPI_WMI_EVENT) - pr_cont("ACPI_WMI_EVENT "); + printk("ACPI_WMI_EVENT "); } - pr_cont("\n"); + printk("\n"); } @@ -509,7 +522,7 @@ static void wmi_notify_debug(u32 value, void *context) status = wmi_get_event_data(value, &response); if (status != AE_OK) { - pr_info("bad event status 0x%x\n", status); + printk(KERN_INFO "wmi: bad event status 0x%x\n", status); return; } @@ -518,22 +531,22 @@ static void wmi_notify_debug(u32 value, void *context) if (!obj) return; - pr_info("DEBUG Event "); + printk(KERN_INFO PREFIX "DEBUG Event "); switch(obj->type) { case ACPI_TYPE_BUFFER: - pr_cont("BUFFER_TYPE - length %d\n", obj->buffer.length); + printk("BUFFER_TYPE - length %d\n", obj->buffer.length); break; case ACPI_TYPE_STRING: - pr_cont("STRING_TYPE - %s\n", obj->string.pointer); + printk("STRING_TYPE - %s\n", obj->string.pointer); break; case ACPI_TYPE_INTEGER: - pr_cont("INTEGER_TYPE - %llu\n", obj->integer.value); + printk("INTEGER_TYPE - %llu\n", obj->integer.value); break; case ACPI_TYPE_PACKAGE: - pr_cont("PACKAGE_TYPE - %d elements\n", obj->package.count); + printk("PACKAGE_TYPE - %d elements\n", obj->package.count); break; default: - pr_cont("object type 0x%X\n", obj->type); + printk("object type 0x%X\n", obj->type); } kfree(obj); } @@ -620,7 +633,7 @@ acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out) params[0].type = ACPI_TYPE_INTEGER; params[0].integer.value = event; - list_for_each(p, &wmi_block_list) { + list_for_each(p, &wmi_blocks.list) { wblock = list_entry(p, struct wmi_block, list); gblock = &wblock->gblock; @@ -649,7 +662,7 @@ EXPORT_SYMBOL_GPL(wmi_has_guid); /* * sysfs interface */ -static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, +static ssize_t show_modalias(struct device *dev, struct device_attribute *attr, char *buf) { char guid_string[37]; @@ -663,11 +676,7 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, return sprintf(buf, "wmi:%s\n", guid_string); } - -static struct device_attribute wmi_dev_attrs[] = { - __ATTR_RO(modalias), - __ATTR_NULL -}; +static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) { @@ -693,71 +702,108 @@ static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) static void wmi_dev_free(struct device *dev) { - struct wmi_block *wmi_block = container_of(dev, struct wmi_block, dev); - - kfree(wmi_block); + kfree(dev); } static struct class wmi_class = { .name = "wmi", .dev_release = wmi_dev_free, .dev_uevent = wmi_dev_uevent, - .dev_attrs = wmi_dev_attrs, }; -static struct wmi_block *wmi_create_device(const struct guid_block *gblock, - acpi_handle handle) +static int wmi_create_devs(void) { - struct wmi_block *wblock; - int error; + int result; char guid_string[37]; + struct guid_block *gblock; + struct wmi_block *wblock; + struct list_head *p; + struct device *guid_dev; - wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); - if (!wblock) { - error = -ENOMEM; - goto err_out; - } + /* Create devices for all the GUIDs */ + list_for_each(p, &wmi_blocks.list) { + wblock = list_entry(p, struct wmi_block, list); + + guid_dev = kzalloc(sizeof(struct device), GFP_KERNEL); + if (!guid_dev) + return -ENOMEM; - wblock->handle = handle; - wblock->gblock = *gblock; + wblock->dev = guid_dev; - wblock->dev.class = &wmi_class; + guid_dev->class = &wmi_class; + dev_set_drvdata(guid_dev, wblock); - wmi_gtoa(gblock->guid, guid_string); - dev_set_name(&wblock->dev, guid_string); + gblock = &wblock->gblock; - dev_set_drvdata(&wblock->dev, wblock); + wmi_gtoa(gblock->guid, guid_string); + dev_set_name(guid_dev, guid_string); - error = device_register(&wblock->dev); - if (error) - goto err_free; + result = device_register(guid_dev); + if (result) + return result; - list_add_tail(&wblock->list, &wmi_block_list); - return wblock; + result = device_create_file(guid_dev, &dev_attr_modalias); + if (result) + return result; + } -err_free: - kfree(wblock); -err_out: - return ERR_PTR(error); + return 0; } -static void wmi_free_devices(void) +static void wmi_remove_devs(void) { - struct wmi_block *wblock, *next; + struct guid_block *gblock; + struct wmi_block *wblock; + struct list_head *p; + struct device *guid_dev; /* Delete devices for all the GUIDs */ - list_for_each_entry_safe(wblock, next, &wmi_block_list, list) - device_unregister(&wblock->dev); + list_for_each(p, &wmi_blocks.list) { + wblock = list_entry(p, struct wmi_block, list); + + guid_dev = wblock->dev; + gblock = &wblock->gblock; + + device_remove_file(guid_dev, &dev_attr_modalias); + + device_unregister(guid_dev); + } +} + +static void wmi_class_exit(void) +{ + wmi_remove_devs(); + class_unregister(&wmi_class); +} + +static int wmi_class_init(void) +{ + int ret; + + ret = class_register(&wmi_class); + if (ret) + return ret; + + ret = wmi_create_devs(); + if (ret) + wmi_class_exit(); + + return ret; } static bool guid_already_parsed(const char *guid_string) { + struct guid_block *gblock; struct wmi_block *wblock; + struct list_head *p; - list_for_each_entry(wblock, &wmi_block_list, list) - if (strncmp(wblock->gblock.guid, guid_string, 16) == 0) - return true; + list_for_each(p, &wmi_blocks.list) { + wblock = list_entry(p, struct wmi_block, list); + gblock = &wblock->gblock; + if (strncmp(gblock->guid, guid_string, 16) == 0) + return true; + } return false; } @@ -768,29 +814,30 @@ static acpi_status parse_wdg(acpi_handle handle) { struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; union acpi_object *obj; - const struct guid_block *gblock; + struct guid_block *gblock; struct wmi_block *wblock; char guid_string[37]; acpi_status status; - int retval; u32 i, total; status = acpi_evaluate_object(handle, "_WDG", NULL, &out); + if (ACPI_FAILURE(status)) - return -ENXIO; + return status; obj = (union acpi_object *) out.pointer; - if (!obj) - return -ENXIO; - if (obj->type != ACPI_TYPE_BUFFER) { - retval = -ENXIO; - goto out_free_pointer; - } + if (obj->type != ACPI_TYPE_BUFFER) + return AE_ERROR; - gblock = (const struct guid_block *)obj->buffer.pointer; total = obj->buffer.length / sizeof(struct guid_block); + gblock = kmemdup(obj->buffer.pointer, obj->buffer.length, GFP_KERNEL); + if (!gblock) { + status = AE_NO_MEMORY; + goto out_free_pointer; + } + for (i = 0; i < total; i++) { /* Some WMI devices, like those for nVidia hooks, have a @@ -801,32 +848,34 @@ static acpi_status parse_wdg(acpi_handle handle) */ if (guid_already_parsed(gblock[i].guid) == true) { wmi_gtoa(gblock[i].guid, guid_string); - pr_info("Skipping duplicate GUID %s\n", guid_string); + printk(KERN_INFO PREFIX "Skipping duplicate GUID %s\n", + guid_string); continue; } - if (debug_dump_wdg) wmi_dump_wdg(&gblock[i]); - wblock = wmi_create_device(&gblock[i], handle); - if (IS_ERR(wblock)) { - retval = PTR_ERR(wblock); - wmi_free_devices(); - break; + wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); + if (!wblock) { + status = AE_NO_MEMORY; + goto out_free_gblock; } + wblock->gblock = gblock[i]; + wblock->handle = handle; if (debug_event) { wblock->handler = wmi_notify_debug; - wmi_method_enable(wblock, 1); + status = wmi_method_enable(wblock, 1); } + list_add_tail(&wblock->list, &wmi_blocks.list); } - retval = 0; - +out_free_gblock: + kfree(gblock); out_free_pointer: kfree(out.pointer); - return retval; + return status; } /* @@ -880,7 +929,7 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event) struct list_head *p; char guid_string[37]; - list_for_each(p, &wmi_block_list) { + list_for_each(p, &wmi_blocks.list) { wblock = list_entry(p, struct wmi_block, list); block = &wblock->gblock; @@ -890,7 +939,8 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event) wblock->handler(event, wblock->handler_data); if (debug_event) { wmi_gtoa(wblock->gblock.guid, guid_string); - pr_info("DEBUG Event GUID: %s\n", guid_string); + printk(KERN_INFO PREFIX "DEBUG Event GUID:" + " %s\n", guid_string); } acpi_bus_generate_netlink_event( @@ -905,7 +955,6 @@ static int acpi_wmi_remove(struct acpi_device *device, int type) { acpi_remove_address_space_handler(device->handle, ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); - wmi_free_devices(); return 0; } @@ -913,57 +962,68 @@ static int acpi_wmi_remove(struct acpi_device *device, int type) static int acpi_wmi_add(struct acpi_device *device) { acpi_status status; - int error; + int result = 0; status = acpi_install_address_space_handler(device->handle, ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler, NULL, NULL); - if (ACPI_FAILURE(status)) { - pr_err("Error installing EC region handler\n"); + if (ACPI_FAILURE(status)) return -ENODEV; - } - error = parse_wdg(device->handle); - if (error) { - acpi_remove_address_space_handler(device->handle, - ACPI_ADR_SPACE_EC, - &acpi_wmi_ec_space_handler); - pr_err("Failed to parse WDG method\n"); - return error; + status = parse_wdg(device->handle); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PREFIX "Error installing EC region handler\n"); + return -ENODEV; } - return 0; + return result; } static int __init acpi_wmi_init(void) { - int error; + int result; + + INIT_LIST_HEAD(&wmi_blocks.list); if (acpi_disabled) return -ENODEV; - error = class_register(&wmi_class); - if (error) - return error; + result = acpi_bus_register_driver(&acpi_wmi_driver); - error = acpi_bus_register_driver(&acpi_wmi_driver); - if (error) { - pr_err("Error loading mapper\n"); - class_unregister(&wmi_class); - return error; + if (result < 0) { + printk(KERN_INFO PREFIX "Error loading mapper\n"); + return -ENODEV; } - pr_info("Mapper loaded\n"); - return 0; + result = wmi_class_init(); + if (result) { + acpi_bus_unregister_driver(&acpi_wmi_driver); + return result; + } + + printk(KERN_INFO PREFIX "Mapper loaded\n"); + + return result; } static void __exit acpi_wmi_exit(void) { + struct list_head *p, *tmp; + struct wmi_block *wblock; + + wmi_class_exit(); + acpi_bus_unregister_driver(&acpi_wmi_driver); - class_unregister(&wmi_class); - pr_info("Mapper unloaded\n"); + list_for_each_safe(p, tmp, &wmi_blocks.list) { + wblock = list_entry(p, struct wmi_block, list); + + list_del(p); + kfree(wblock); + } + + printk(KERN_INFO PREFIX "Mapper unloaded\n"); } subsys_initcall(acpi_wmi_init); diff --git a/trunk/drivers/platform/x86/xo1-rfkill.c b/trunk/drivers/platform/x86/xo1-rfkill.c deleted file mode 100644 index e549eeeda121..000000000000 --- a/trunk/drivers/platform/x86/xo1-rfkill.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Support for rfkill through the OLPC XO-1 laptop embedded controller - * - * Copyright (C) 2010 One Laptop per Child - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include -#include -#include - -#include - -static int rfkill_set_block(void *data, bool blocked) -{ - unsigned char cmd; - if (blocked) - cmd = EC_WLAN_ENTER_RESET; - else - cmd = EC_WLAN_LEAVE_RESET; - - return olpc_ec_cmd(cmd, NULL, 0, NULL, 0); -} - -static const struct rfkill_ops rfkill_ops = { - .set_block = rfkill_set_block, -}; - -static int __devinit xo1_rfkill_probe(struct platform_device *pdev) -{ - struct rfkill *rfk; - int r; - - rfk = rfkill_alloc(pdev->name, &pdev->dev, RFKILL_TYPE_WLAN, - &rfkill_ops, NULL); - if (!rfk) - return -ENOMEM; - - r = rfkill_register(rfk); - if (r) { - rfkill_destroy(rfk); - return r; - } - - platform_set_drvdata(pdev, rfk); - return 0; -} - -static int __devexit xo1_rfkill_remove(struct platform_device *pdev) -{ - struct rfkill *rfk = platform_get_drvdata(pdev); - rfkill_unregister(rfk); - rfkill_destroy(rfk); - return 0; -} - -static struct platform_driver xo1_rfkill_driver = { - .driver = { - .name = "xo1-rfkill", - .owner = THIS_MODULE, - }, - .probe = xo1_rfkill_probe, - .remove = __devexit_p(xo1_rfkill_remove), -}; - -static int __init xo1_rfkill_init(void) -{ - return platform_driver_register(&xo1_rfkill_driver); -} - -static void __exit xo1_rfkill_exit(void) -{ - platform_driver_unregister(&xo1_rfkill_driver); -} - -MODULE_AUTHOR("Daniel Drake "); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:xo1-rfkill"); - -module_init(xo1_rfkill_init); -module_exit(xo1_rfkill_exit); diff --git a/trunk/drivers/sbus/char/jsflash.c b/trunk/drivers/sbus/char/jsflash.c index a624f5af4320..13f48e28a1e1 100644 --- a/trunk/drivers/sbus/char/jsflash.c +++ b/trunk/drivers/sbus/char/jsflash.c @@ -461,7 +461,7 @@ static int jsflash_init(void) { int rc; struct jsflash *jsf; - phandle node; + int node; char banner[128]; struct linux_prom_registers reg0; diff --git a/trunk/drivers/serial/68328serial.h b/trunk/drivers/serial/68328serial.h index 664ceb0a158c..58aa2154655b 100644 --- a/trunk/drivers/serial/68328serial.h +++ b/trunk/drivers/serial/68328serial.h @@ -181,8 +181,13 @@ struct m68k_serial { /* * Define the number of ports supported and their irqs. */ +#ifndef CONFIG_68328_SERIAL_UART2 #define NR_PORTS 1 #define UART_IRQ_DEFNS {UART_IRQ_NUM} +#else +#define NR_PORTS 2 +#define UART_IRQ_DEFNS {UART1_IRQ_NUM, UART2_IRQ_NUM} +#endif #endif /* __KERNEL__ */ #endif /* !(_MC683XX_SERIAL_H) */ diff --git a/trunk/drivers/serial/of_serial.c b/trunk/drivers/serial/of_serial.c index 17849dcb9adc..2af8fd113123 100644 --- a/trunk/drivers/serial/of_serial.c +++ b/trunk/drivers/serial/of_serial.c @@ -31,8 +31,8 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev, { struct resource resource; struct device_node *np = ofdev->dev.of_node; - const __be32 *clk, *spd; - const __be32 *prop; + const unsigned int *clk, *spd; + const u32 *prop; int ret, prop_size; memset(port, 0, sizeof *port); @@ -55,23 +55,23 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev, /* Check for shifted address mapping */ prop = of_get_property(np, "reg-offset", &prop_size); if (prop && (prop_size == sizeof(u32))) - port->mapbase += be32_to_cpup(prop); + port->mapbase += *prop; /* Check for registers offset within the devices address range */ prop = of_get_property(np, "reg-shift", &prop_size); if (prop && (prop_size == sizeof(u32))) - port->regshift = be32_to_cpup(prop); + port->regshift = *prop; port->irq = irq_of_parse_and_map(np, 0); port->iotype = UPIO_MEM; port->type = type; - port->uartclk = be32_to_cpup(clk); + port->uartclk = *clk; port->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP | UPF_FIXED_PORT | UPF_FIXED_TYPE; port->dev = &ofdev->dev; /* If current-speed was set, then try not to change it. */ if (spd) - port->custom_divisor = be32_to_cpup(clk) / (16 * (be32_to_cpup(spd))); + port->custom_divisor = *clk / (16 * (*spd)); return 0; } diff --git a/trunk/drivers/serial/sh-sci.h b/trunk/drivers/serial/sh-sci.h index d2352ac437c5..9b52f77a9305 100644 --- a/trunk/drivers/serial/sh-sci.h +++ b/trunk/drivers/serial/sh-sci.h @@ -140,15 +140,7 @@ # define SCSPTR0 0xffe00024 /* 16 bit SCIF */ # define SCSPTR1 0xffe10024 /* 16 bit SCIF */ # define SCIF_ORER 0x0001 /* Overrun error bit */ - -#if defined(CONFIG_SH_SH2007) -/* TIE=0,RIE=0,TE=1,RE=1,REIE=1,CKE1=0 */ -# define SCSCR_INIT(port) 0x38 -#else -/* TIE=0,RIE=0,TE=1,RE=1,REIE=1,CKE1=1 */ -# define SCSCR_INIT(port) 0x3a -#endif - +# define SCSCR_INIT(port) 0x3a /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ #elif defined(CONFIG_CPU_SUBTYPE_SH7785) || \ defined(CONFIG_CPU_SUBTYPE_SH7786) # define SCSPTR0 0xffea0024 /* 16 bit SCIF */ @@ -624,10 +616,9 @@ static inline int sci_rxd_in(struct uart_port *port) * -- Mitch Davis - 15 Jul 2000 */ -#if (defined(CONFIG_CPU_SUBTYPE_SH7780) || \ - defined(CONFIG_CPU_SUBTYPE_SH7785) || \ - defined(CONFIG_CPU_SUBTYPE_SH7786)) && \ - !defined(CONFIG_SH_SH2007) +#if defined(CONFIG_CPU_SUBTYPE_SH7780) || \ + defined(CONFIG_CPU_SUBTYPE_SH7785) || \ + defined(CONFIG_CPU_SUBTYPE_SH7786) #define SCBRR_VALUE(bps, clk) ((clk+16*bps)/(16*bps)-1) #elif defined(CONFIG_CPU_SUBTYPE_SH7705) || \ defined(CONFIG_CPU_SUBTYPE_SH7720) || \ diff --git a/trunk/drivers/sh/Kconfig b/trunk/drivers/sh/Kconfig index f168a6159961..a54de0b9b3df 100644 --- a/trunk/drivers/sh/Kconfig +++ b/trunk/drivers/sh/Kconfig @@ -1,5 +1,24 @@ -menu "SuperH / SH-Mobile Driver Options" +config INTC_USERIMASK + bool "Userspace interrupt masking support" + depends on ARCH_SHMOBILE || (SUPERH && CPU_SH4A) + help + This enables support for hardware-assisted userspace hardirq + masking. -source "drivers/sh/intc/Kconfig" + SH-4A and newer interrupt blocks all support a special shadowed + page with all non-masking registers obscured when mapped in to + userspace. This is primarily for use by userspace device + drivers that are using special priority levels. -endmenu + If in doubt, say N. + +config INTC_BALANCING + bool "Hardware IRQ balancing support" + depends on SMP && SUPERH && CPU_SUBTYPE_SH7786 + help + This enables support for IRQ auto-distribution mode on SH-X3 + SMP parts. All of the balancing and CPU wakeup decisions are + taken care of automatically by hardware for distributed + vectors. + + If in doubt, say N. diff --git a/trunk/drivers/sh/Makefile b/trunk/drivers/sh/Makefile index 24e6cec0ae8d..08fc653a825c 100644 --- a/trunk/drivers/sh/Makefile +++ b/trunk/drivers/sh/Makefile @@ -1,9 +1,10 @@ # # Makefile for the SuperH specific drivers. # -obj-y := intc/ +obj-y := clk.o intc.o -obj-$(CONFIG_HAVE_CLK) += clk/ -obj-$(CONFIG_MAPLE) += maple/ obj-$(CONFIG_SUPERHYWAY) += superhyway/ +obj-$(CONFIG_MAPLE) += maple/ + obj-$(CONFIG_GENERIC_GPIO) += pfc.o +obj-$(CONFIG_SH_CLK_CPG) += clk-cpg.o diff --git a/trunk/drivers/sh/clk/cpg.c b/trunk/drivers/sh/clk-cpg.c similarity index 96% rename from trunk/drivers/sh/clk/cpg.c rename to trunk/drivers/sh/clk-cpg.c index 3aea5f0ceb09..8c024b984ed8 100644 --- a/trunk/drivers/sh/clk/cpg.c +++ b/trunk/drivers/sh/clk-cpg.c @@ -1,12 +1,3 @@ -/* - * Helper routines for SuperH Clock Pulse Generator blocks (CPG). - * - * Copyright (C) 2010 Magnus Damm - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - */ #include #include #include @@ -189,6 +180,7 @@ static int __init sh_clk_div6_register_ops(struct clk *clks, int nr, clkp = clks + k; clkp->ops = ops; + clkp->id = -1; clkp->freq_table = freq_table + (k * freq_table_size); clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END; @@ -327,6 +319,7 @@ static int __init sh_clk_div4_register_ops(struct clk *clks, int nr, clkp = clks + k; clkp->ops = ops; + clkp->id = -1; clkp->priv = table; clkp->freq_table = freq_table + (k * freq_table_size); diff --git a/trunk/drivers/sh/clk/core.c b/trunk/drivers/sh/clk.c similarity index 74% rename from trunk/drivers/sh/clk/core.c rename to trunk/drivers/sh/clk.c index fd0d1b98901c..5d84adac9ec4 100644 --- a/trunk/drivers/sh/clk/core.c +++ b/trunk/drivers/sh/clk.c @@ -1,7 +1,7 @@ /* - * SuperH clock framework + * drivers/sh/clk.c - SuperH clock framework * - * Copyright (C) 2005 - 2010 Paul Mundt + * Copyright (C) 2005 - 2009 Paul Mundt * * This clock framework is derived from the OMAP version by: * @@ -14,8 +14,6 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. */ -#define pr_fmt(fmt) "clock: " fmt - #include #include #include @@ -25,7 +23,7 @@ #include #include #include -#include +#include #include #include #include @@ -45,8 +43,6 @@ void clk_rate_table_build(struct clk *clk, unsigned long freq; int i; - clk->nr_freqs = nr_freqs; - for (i = 0; i < nr_freqs; i++) { div = 1; mult = 1; @@ -71,39 +67,29 @@ void clk_rate_table_build(struct clk *clk, freq_table[i].frequency = CPUFREQ_TABLE_END; } -struct clk_rate_round_data; - -struct clk_rate_round_data { - unsigned long rate; - unsigned int min, max; - long (*func)(unsigned int, struct clk_rate_round_data *); - void *arg; -}; - -#define for_each_frequency(pos, r, freq) \ - for (pos = r->min, freq = r->func(pos, r); \ - pos <= r->max; pos++, freq = r->func(pos, r)) \ - if (unlikely(freq == 0)) \ - ; \ - else - -static long clk_rate_round_helper(struct clk_rate_round_data *rounder) +long clk_rate_table_round(struct clk *clk, + struct cpufreq_frequency_table *freq_table, + unsigned long rate) { unsigned long rate_error, rate_error_prev = ~0UL; - unsigned long rate_best_fit = rounder->rate; - unsigned long highest, lowest, freq; + unsigned long rate_best_fit = rate; + unsigned long highest, lowest; int i; - highest = 0; - lowest = ~0UL; + highest = lowest = 0; + + for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { + unsigned long freq = freq_table[i].frequency; + + if (freq == CPUFREQ_ENTRY_INVALID) + continue; - for_each_frequency(i, rounder, freq) { if (freq > highest) highest = freq; if (freq < lowest) lowest = freq; - rate_error = abs(freq - rounder->rate); + rate_error = abs(freq - rate); if (rate_error < rate_error_prev) { rate_best_fit = freq; rate_error_prev = rate_error; @@ -113,64 +99,14 @@ static long clk_rate_round_helper(struct clk_rate_round_data *rounder) break; } - if (rounder->rate >= highest) + if (rate >= highest) rate_best_fit = highest; - if (rounder->rate <= lowest) + if (rate <= lowest) rate_best_fit = lowest; return rate_best_fit; } -static long clk_rate_table_iter(unsigned int pos, - struct clk_rate_round_data *rounder) -{ - struct cpufreq_frequency_table *freq_table = rounder->arg; - unsigned long freq = freq_table[pos].frequency; - - if (freq == CPUFREQ_ENTRY_INVALID) - freq = 0; - - return freq; -} - -long clk_rate_table_round(struct clk *clk, - struct cpufreq_frequency_table *freq_table, - unsigned long rate) -{ - struct clk_rate_round_data table_round = { - .min = 0, - .max = clk->nr_freqs - 1, - .func = clk_rate_table_iter, - .arg = freq_table, - .rate = rate, - }; - - if (clk->nr_freqs < 1) - return 0; - - return clk_rate_round_helper(&table_round); -} - -static long clk_rate_div_range_iter(unsigned int pos, - struct clk_rate_round_data *rounder) -{ - return clk_get_rate(rounder->arg) / pos; -} - -long clk_rate_div_range_round(struct clk *clk, unsigned int div_min, - unsigned int div_max, unsigned long rate) -{ - struct clk_rate_round_data div_range_round = { - .min = div_min, - .max = div_max, - .func = clk_rate_div_range_iter, - .arg = clk_get_parent(clk), - .rate = rate, - }; - - return clk_rate_round_helper(&div_range_round); -} - int clk_rate_table_find(struct clk *clk, struct cpufreq_frequency_table *freq_table, unsigned long rate) @@ -224,8 +160,8 @@ void propagate_rate(struct clk *tclk) static void __clk_disable(struct clk *clk) { - if (WARN(!clk->usecount, "Trying to disable clock %p with 0 usecount\n", - clk)) + if (WARN(!clk->usecount, "Trying to disable clock %s with 0 usecount\n", + clk->name)) return; if (!(--clk->usecount)) { @@ -312,88 +248,8 @@ void recalculate_root_clocks(void) } } -static struct clk_mapping dummy_mapping; - -static struct clk *lookup_root_clock(struct clk *clk) -{ - while (clk->parent) - clk = clk->parent; - - return clk; -} - -static int clk_establish_mapping(struct clk *clk) -{ - struct clk_mapping *mapping = clk->mapping; - - /* - * Propagate mappings. - */ - if (!mapping) { - struct clk *clkp; - - /* - * dummy mapping for root clocks with no specified ranges - */ - if (!clk->parent) { - clk->mapping = &dummy_mapping; - return 0; - } - - /* - * If we're on a child clock and it provides no mapping of its - * own, inherit the mapping from its root clock. - */ - clkp = lookup_root_clock(clk); - mapping = clkp->mapping; - BUG_ON(!mapping); - } - - /* - * Establish initial mapping. - */ - if (!mapping->base && mapping->phys) { - kref_init(&mapping->ref); - - mapping->base = ioremap_nocache(mapping->phys, mapping->len); - if (unlikely(!mapping->base)) - return -ENXIO; - } else if (mapping->base) { - /* - * Bump the refcount for an existing mapping - */ - kref_get(&mapping->ref); - } - - clk->mapping = mapping; - return 0; -} - -static void clk_destroy_mapping(struct kref *kref) -{ - struct clk_mapping *mapping; - - mapping = container_of(kref, struct clk_mapping, ref); - - iounmap(mapping->base); -} - -static void clk_teardown_mapping(struct clk *clk) -{ - struct clk_mapping *mapping = clk->mapping; - - /* Nothing to do */ - if (mapping == &dummy_mapping) - return; - - kref_put(&mapping->ref, clk_destroy_mapping); - clk->mapping = NULL; -} - int clk_register(struct clk *clk) { - int ret; - if (clk == NULL || IS_ERR(clk)) return -EINVAL; @@ -408,10 +264,6 @@ int clk_register(struct clk *clk) INIT_LIST_HEAD(&clk->children); clk->usecount = 0; - ret = clk_establish_mapping(clk); - if (unlikely(ret)) - goto out_unlock; - if (clk->parent) list_add(&clk->sibling, &clk->parent->children); else @@ -420,11 +272,9 @@ int clk_register(struct clk *clk) list_add(&clk->node, &clock_list); if (clk->ops && clk->ops->init) clk->ops->init(clk); - -out_unlock: mutex_unlock(&clock_list_sem); - return ret; + return 0; } EXPORT_SYMBOL_GPL(clk_register); @@ -433,7 +283,6 @@ void clk_unregister(struct clk *clk) mutex_lock(&clock_list_sem); list_del(&clk->sibling); list_del(&clk->node); - clk_teardown_mapping(clk); mutex_unlock(&clock_list_sem); } EXPORT_SYMBOL_GPL(clk_unregister); @@ -505,10 +354,10 @@ int clk_set_parent(struct clk *clk, struct clk *parent) ret = clk_reparent(clk, parent); if (ret == 0) { + pr_debug("clock: set parent of %s to %s (new rate %ld)\n", + clk->name, clk->parent->name, clk->rate); if (clk->ops->recalc) clk->rate = clk->ops->recalc(clk); - pr_debug("set parent of %p to %p (new rate %ld)\n", - clk, clk->parent, clk->rate); propagate_rate(clk); } } else @@ -620,7 +469,9 @@ static int clk_debugfs_register_one(struct clk *c) char s[255]; char *p = s; - p += sprintf(p, "%p", c); + p += sprintf(p, "%s", c->name); + if (c->id >= 0) + sprintf(p, ":%d", c->id); d = debugfs_create_dir(s, pa ? pa->dentry : clk_debugfs_root); if (!d) return -ENOMEM; @@ -662,7 +513,7 @@ static int clk_debugfs_register(struct clk *c) return err; } - if (!c->dentry) { + if (!c->dentry && c->name) { err = clk_debugfs_register_one(c); if (err) return err; diff --git a/trunk/drivers/sh/clk/Makefile b/trunk/drivers/sh/clk/Makefile deleted file mode 100644 index 5d15ebfaa074..000000000000 --- a/trunk/drivers/sh/clk/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -obj-y := core.o - -obj-$(CONFIG_SH_CLK_CPG) += cpg.o diff --git a/trunk/drivers/sh/intc.c b/trunk/drivers/sh/intc.c new file mode 100644 index 000000000000..e91a23e5ffd8 --- /dev/null +++ b/trunk/drivers/sh/intc.c @@ -0,0 +1,1390 @@ +/* + * Shared interrupt handling code for IPR and INTC2 types of IRQs. + * + * Copyright (C) 2007, 2008 Magnus Damm + * Copyright (C) 2009, 2010 Paul Mundt + * + * Based on intc2.c and ipr.c + * + * Copyright (C) 1999 Niibe Yutaka & Takeshi Yaegashi + * Copyright (C) 2000 Kazumoto Kojima + * Copyright (C) 2001 David J. Mckay (david.mckay@st.com) + * Copyright (C) 2003 Takashi Kusuda + * Copyright (C) 2005, 2006 Paul Mundt + * + * 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. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \ + ((shift) | ((width) << 5) | ((fn) << 9) | ((mode) << 13) | \ + ((addr_e) << 16) | ((addr_d << 24))) + +#define _INTC_SHIFT(h) (h & 0x1f) +#define _INTC_WIDTH(h) ((h >> 5) & 0xf) +#define _INTC_FN(h) ((h >> 9) & 0xf) +#define _INTC_MODE(h) ((h >> 13) & 0x7) +#define _INTC_ADDR_E(h) ((h >> 16) & 0xff) +#define _INTC_ADDR_D(h) ((h >> 24) & 0xff) + +struct intc_handle_int { + unsigned int irq; + unsigned long handle; +}; + +struct intc_window { + phys_addr_t phys; + void __iomem *virt; + unsigned long size; +}; + +struct intc_desc_int { + struct list_head list; + struct sys_device sysdev; + pm_message_t state; + unsigned long *reg; +#ifdef CONFIG_SMP + unsigned long *smp; +#endif + unsigned int nr_reg; + struct intc_handle_int *prio; + unsigned int nr_prio; + struct intc_handle_int *sense; + unsigned int nr_sense; + struct intc_window *window; + unsigned int nr_windows; + struct irq_chip chip; +}; + +static LIST_HEAD(intc_list); + +/* + * The intc_irq_map provides a global map of bound IRQ vectors for a + * given platform. Allocation of IRQs are either static through the CPU + * vector map, or dynamic in the case of board mux vectors or MSI. + * + * As this is a central point for all IRQ controllers on the system, + * each of the available sources are mapped out here. This combined with + * sparseirq makes it quite trivial to keep the vector map tightly packed + * when dynamically creating IRQs, as well as tying in to otherwise + * unused irq_desc positions in the sparse array. + */ +static DECLARE_BITMAP(intc_irq_map, NR_IRQS); +static DEFINE_SPINLOCK(vector_lock); + +#ifdef CONFIG_SMP +#define IS_SMP(x) x.smp +#define INTC_REG(d, x, c) (d->reg[(x)] + ((d->smp[(x)] & 0xff) * c)) +#define SMP_NR(d, x) ((d->smp[(x)] >> 8) ? (d->smp[(x)] >> 8) : 1) +#else +#define IS_SMP(x) 0 +#define INTC_REG(d, x, c) (d->reg[(x)]) +#define SMP_NR(d, x) 1 +#endif + +static unsigned int intc_prio_level[NR_IRQS]; /* for now */ +static unsigned int default_prio_level = 2; /* 2 - 16 */ +static unsigned long ack_handle[NR_IRQS]; +#ifdef CONFIG_INTC_BALANCING +static unsigned long dist_handle[NR_IRQS]; +#endif + +static inline struct intc_desc_int *get_intc_desc(unsigned int irq) +{ + struct irq_chip *chip = get_irq_chip(irq); + return container_of(chip, struct intc_desc_int, chip); +} + +static unsigned long intc_phys_to_virt(struct intc_desc_int *d, + unsigned long address) +{ + struct intc_window *window; + int k; + + /* scan through physical windows and convert address */ + for (k = 0; k < d->nr_windows; k++) { + window = d->window + k; + + if (address < window->phys) + continue; + + if (address >= (window->phys + window->size)) + continue; + + address -= window->phys; + address += (unsigned long)window->virt; + + return address; + } + + /* no windows defined, register must be 1:1 mapped virt:phys */ + return address; +} + +static unsigned int intc_get_reg(struct intc_desc_int *d, unsigned long address) +{ + unsigned int k; + + address = intc_phys_to_virt(d, address); + + for (k = 0; k < d->nr_reg; k++) { + if (d->reg[k] == address) + return k; + } + + BUG(); + return 0; +} + +static inline unsigned int set_field(unsigned int value, + unsigned int field_value, + unsigned int handle) +{ + unsigned int width = _INTC_WIDTH(handle); + unsigned int shift = _INTC_SHIFT(handle); + + value &= ~(((1 << width) - 1) << shift); + value |= field_value << shift; + return value; +} + +static void write_8(unsigned long addr, unsigned long h, unsigned long data) +{ + __raw_writeb(set_field(0, data, h), addr); + (void)__raw_readb(addr); /* Defeat write posting */ +} + +static void write_16(unsigned long addr, unsigned long h, unsigned long data) +{ + __raw_writew(set_field(0, data, h), addr); + (void)__raw_readw(addr); /* Defeat write posting */ +} + +static void write_32(unsigned long addr, unsigned long h, unsigned long data) +{ + __raw_writel(set_field(0, data, h), addr); + (void)__raw_readl(addr); /* Defeat write posting */ +} + +static void modify_8(unsigned long addr, unsigned long h, unsigned long data) +{ + unsigned long flags; + local_irq_save(flags); + __raw_writeb(set_field(__raw_readb(addr), data, h), addr); + (void)__raw_readb(addr); /* Defeat write posting */ + local_irq_restore(flags); +} + +static void modify_16(unsigned long addr, unsigned long h, unsigned long data) +{ + unsigned long flags; + local_irq_save(flags); + __raw_writew(set_field(__raw_readw(addr), data, h), addr); + (void)__raw_readw(addr); /* Defeat write posting */ + local_irq_restore(flags); +} + +static void modify_32(unsigned long addr, unsigned long h, unsigned long data) +{ + unsigned long flags; + local_irq_save(flags); + __raw_writel(set_field(__raw_readl(addr), data, h), addr); + (void)__raw_readl(addr); /* Defeat write posting */ + local_irq_restore(flags); +} + +enum { REG_FN_ERR = 0, REG_FN_WRITE_BASE = 1, REG_FN_MODIFY_BASE = 5 }; + +static void (*intc_reg_fns[])(unsigned long addr, + unsigned long h, + unsigned long data) = { + [REG_FN_WRITE_BASE + 0] = write_8, + [REG_FN_WRITE_BASE + 1] = write_16, + [REG_FN_WRITE_BASE + 3] = write_32, + [REG_FN_MODIFY_BASE + 0] = modify_8, + [REG_FN_MODIFY_BASE + 1] = modify_16, + [REG_FN_MODIFY_BASE + 3] = modify_32, +}; + +enum { MODE_ENABLE_REG = 0, /* Bit(s) set -> interrupt enabled */ + MODE_MASK_REG, /* Bit(s) set -> interrupt disabled */ + MODE_DUAL_REG, /* Two registers, set bit to enable / disable */ + MODE_PRIO_REG, /* Priority value written to enable interrupt */ + MODE_PCLR_REG, /* Above plus all bits set to disable interrupt */ +}; + +static void intc_mode_field(unsigned long addr, + unsigned long handle, + void (*fn)(unsigned long, + unsigned long, + unsigned long), + unsigned int irq) +{ + fn(addr, handle, ((1 << _INTC_WIDTH(handle)) - 1)); +} + +static void intc_mode_zero(unsigned long addr, + unsigned long handle, + void (*fn)(unsigned long, + unsigned long, + unsigned long), + unsigned int irq) +{ + fn(addr, handle, 0); +} + +static void intc_mode_prio(unsigned long addr, + unsigned long handle, + void (*fn)(unsigned long, + unsigned long, + unsigned long), + unsigned int irq) +{ + fn(addr, handle, intc_prio_level[irq]); +} + +static void (*intc_enable_fns[])(unsigned long addr, + unsigned long handle, + void (*fn)(unsigned long, + unsigned long, + unsigned long), + unsigned int irq) = { + [MODE_ENABLE_REG] = intc_mode_field, + [MODE_MASK_REG] = intc_mode_zero, + [MODE_DUAL_REG] = intc_mode_field, + [MODE_PRIO_REG] = intc_mode_prio, + [MODE_PCLR_REG] = intc_mode_prio, +}; + +static void (*intc_disable_fns[])(unsigned long addr, + unsigned long handle, + void (*fn)(unsigned long, + unsigned long, + unsigned long), + unsigned int irq) = { + [MODE_ENABLE_REG] = intc_mode_zero, + [MODE_MASK_REG] = intc_mode_field, + [MODE_DUAL_REG] = intc_mode_field, + [MODE_PRIO_REG] = intc_mode_zero, + [MODE_PCLR_REG] = intc_mode_field, +}; + +#ifdef CONFIG_INTC_BALANCING +static inline void intc_balancing_enable(unsigned int irq) +{ + struct intc_desc_int *d = get_intc_desc(irq); + unsigned long handle = dist_handle[irq]; + unsigned long addr; + + if (irq_balancing_disabled(irq) || !handle) + return; + + addr = INTC_REG(d, _INTC_ADDR_D(handle), 0); + intc_reg_fns[_INTC_FN(handle)](addr, handle, 1); +} + +static inline void intc_balancing_disable(unsigned int irq) +{ + struct intc_desc_int *d = get_intc_desc(irq); + unsigned long handle = dist_handle[irq]; + unsigned long addr; + + if (irq_balancing_disabled(irq) || !handle) + return; + + addr = INTC_REG(d, _INTC_ADDR_D(handle), 0); + intc_reg_fns[_INTC_FN(handle)](addr, handle, 0); +} + +static unsigned int intc_dist_data(struct intc_desc *desc, + struct intc_desc_int *d, + intc_enum enum_id) +{ + struct intc_mask_reg *mr = desc->hw.mask_regs; + unsigned int i, j, fn, mode; + unsigned long reg_e, reg_d; + + for (i = 0; mr && enum_id && i < desc->hw.nr_mask_regs; i++) { + mr = desc->hw.mask_regs + i; + + /* + * Skip this entry if there's no auto-distribution + * register associated with it. + */ + if (!mr->dist_reg) + continue; + + for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) { + if (mr->enum_ids[j] != enum_id) + continue; + + fn = REG_FN_MODIFY_BASE; + mode = MODE_ENABLE_REG; + reg_e = mr->dist_reg; + reg_d = mr->dist_reg; + + fn += (mr->reg_width >> 3) - 1; + return _INTC_MK(fn, mode, + intc_get_reg(d, reg_e), + intc_get_reg(d, reg_d), + 1, + (mr->reg_width - 1) - j); + } + } + + /* + * It's possible we've gotten here with no distribution options + * available for the IRQ in question, so we just skip over those. + */ + return 0; +} +#else +static inline void intc_balancing_enable(unsigned int irq) +{ +} + +static inline void intc_balancing_disable(unsigned int irq) +{ +} +#endif + +static inline void _intc_enable(unsigned int irq, unsigned long handle) +{ + struct intc_desc_int *d = get_intc_desc(irq); + unsigned long addr; + unsigned int cpu; + + for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_E(handle)); cpu++) { +#ifdef CONFIG_SMP + if (!cpumask_test_cpu(cpu, irq_to_desc(irq)->affinity)) + continue; +#endif + addr = INTC_REG(d, _INTC_ADDR_E(handle), cpu); + intc_enable_fns[_INTC_MODE(handle)](addr, handle, intc_reg_fns\ + [_INTC_FN(handle)], irq); + } + + intc_balancing_enable(irq); +} + +static void intc_enable(unsigned int irq) +{ + _intc_enable(irq, (unsigned long)get_irq_chip_data(irq)); +} + +static void intc_disable(unsigned int irq) +{ + struct intc_desc_int *d = get_intc_desc(irq); + unsigned long handle = (unsigned long)get_irq_chip_data(irq); + unsigned long addr; + unsigned int cpu; + + intc_balancing_disable(irq); + + for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_D(handle)); cpu++) { +#ifdef CONFIG_SMP + if (!cpumask_test_cpu(cpu, irq_to_desc(irq)->affinity)) + continue; +#endif + addr = INTC_REG(d, _INTC_ADDR_D(handle), cpu); + intc_disable_fns[_INTC_MODE(handle)](addr, handle,intc_reg_fns\ + [_INTC_FN(handle)], irq); + } +} + +static void (*intc_enable_noprio_fns[])(unsigned long addr, + unsigned long handle, + void (*fn)(unsigned long, + unsigned long, + unsigned long), + unsigned int irq) = { + [MODE_ENABLE_REG] = intc_mode_field, + [MODE_MASK_REG] = intc_mode_zero, + [MODE_DUAL_REG] = intc_mode_field, + [MODE_PRIO_REG] = intc_mode_field, + [MODE_PCLR_REG] = intc_mode_field, +}; + +static void intc_enable_disable(struct intc_desc_int *d, + unsigned long handle, int do_enable) +{ + unsigned long addr; + unsigned int cpu; + void (*fn)(unsigned long, unsigned long, + void (*)(unsigned long, unsigned long, unsigned long), + unsigned int); + + if (do_enable) { + for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_E(handle)); cpu++) { + addr = INTC_REG(d, _INTC_ADDR_E(handle), cpu); + fn = intc_enable_noprio_fns[_INTC_MODE(handle)]; + fn(addr, handle, intc_reg_fns[_INTC_FN(handle)], 0); + } + } else { + for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_D(handle)); cpu++) { + addr = INTC_REG(d, _INTC_ADDR_D(handle), cpu); + fn = intc_disable_fns[_INTC_MODE(handle)]; + fn(addr, handle, intc_reg_fns[_INTC_FN(handle)], 0); + } + } +} + +static int intc_set_wake(unsigned int irq, unsigned int on) +{ + return 0; /* allow wakeup, but setup hardware in intc_suspend() */ +} + +#ifdef CONFIG_SMP +/* + * This is held with the irq desc lock held, so we don't require any + * additional locking here at the intc desc level. The affinity mask is + * later tested in the enable/disable paths. + */ +static int intc_set_affinity(unsigned int irq, const struct cpumask *cpumask) +{ + if (!cpumask_intersects(cpumask, cpu_online_mask)) + return -1; + + cpumask_copy(irq_to_desc(irq)->affinity, cpumask); + + return 0; +} +#endif + +static void intc_mask_ack(unsigned int irq) +{ + struct intc_desc_int *d = get_intc_desc(irq); + unsigned long handle = ack_handle[irq]; + unsigned long addr; + + intc_disable(irq); + + /* read register and write zero only to the associated bit */ + if (handle) { + addr = INTC_REG(d, _INTC_ADDR_D(handle), 0); + switch (_INTC_FN(handle)) { + case REG_FN_MODIFY_BASE + 0: /* 8bit */ + __raw_readb(addr); + __raw_writeb(0xff ^ set_field(0, 1, handle), addr); + break; + case REG_FN_MODIFY_BASE + 1: /* 16bit */ + __raw_readw(addr); + __raw_writew(0xffff ^ set_field(0, 1, handle), addr); + break; + case REG_FN_MODIFY_BASE + 3: /* 32bit */ + __raw_readl(addr); + __raw_writel(0xffffffff ^ set_field(0, 1, handle), addr); + break; + default: + BUG(); + break; + } + } +} + +static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp, + unsigned int nr_hp, + unsigned int irq) +{ + int i; + + /* + * this doesn't scale well, but... + * + * this function should only be used for cerain uncommon + * operations such as intc_set_priority() and intc_set_sense() + * and in those rare cases performance doesn't matter that much. + * keeping the memory footprint low is more important. + * + * one rather simple way to speed this up and still keep the + * memory footprint down is to make sure the array is sorted + * and then perform a bisect to lookup the irq. + */ + for (i = 0; i < nr_hp; i++) { + if ((hp + i)->irq != irq) + continue; + + return hp + i; + } + + return NULL; +} + +int intc_set_priority(unsigned int irq, unsigned int prio) +{ + struct intc_desc_int *d = get_intc_desc(irq); + struct intc_handle_int *ihp; + + if (!intc_prio_level[irq] || prio <= 1) + return -EINVAL; + + ihp = intc_find_irq(d->prio, d->nr_prio, irq); + if (ihp) { + if (prio >= (1 << _INTC_WIDTH(ihp->handle))) + return -EINVAL; + + intc_prio_level[irq] = prio; + + /* + * only set secondary masking method directly + * primary masking method is using intc_prio_level[irq] + * priority level will be set during next enable() + */ + if (_INTC_FN(ihp->handle) != REG_FN_ERR) + _intc_enable(irq, ihp->handle); + } + return 0; +} + +#define VALID(x) (x | 0x80) + +static unsigned char intc_irq_sense_table[IRQ_TYPE_SENSE_MASK + 1] = { + [IRQ_TYPE_EDGE_FALLING] = VALID(0), + [IRQ_TYPE_EDGE_RISING] = VALID(1), + [IRQ_TYPE_LEVEL_LOW] = VALID(2), + /* SH7706, SH7707 and SH7709 do not support high level triggered */ +#if !defined(CONFIG_CPU_SUBTYPE_SH7706) && \ + !defined(CONFIG_CPU_SUBTYPE_SH7707) && \ + !defined(CONFIG_CPU_SUBTYPE_SH7709) + [IRQ_TYPE_LEVEL_HIGH] = VALID(3), +#endif +}; + +static int intc_set_sense(unsigned int irq, unsigned int type) +{ + struct intc_desc_int *d = get_intc_desc(irq); + unsigned char value = intc_irq_sense_table[type & IRQ_TYPE_SENSE_MASK]; + struct intc_handle_int *ihp; + unsigned long addr; + + if (!value) + return -EINVAL; + + ihp = intc_find_irq(d->sense, d->nr_sense, irq); + if (ihp) { + addr = INTC_REG(d, _INTC_ADDR_E(ihp->handle), 0); + intc_reg_fns[_INTC_FN(ihp->handle)](addr, ihp->handle, value); + } + return 0; +} + +static intc_enum __init intc_grp_id(struct intc_desc *desc, + intc_enum enum_id) +{ + struct intc_group *g = desc->hw.groups; + unsigned int i, j; + + for (i = 0; g && enum_id && i < desc->hw.nr_groups; i++) { + g = desc->hw.groups + i; + + for (j = 0; g->enum_ids[j]; j++) { + if (g->enum_ids[j] != enum_id) + continue; + + return g->enum_id; + } + } + + return 0; +} + +static unsigned int __init _intc_mask_data(struct intc_desc *desc, + struct intc_desc_int *d, + intc_enum enum_id, + unsigned int *reg_idx, + unsigned int *fld_idx) +{ + struct intc_mask_reg *mr = desc->hw.mask_regs; + unsigned int fn, mode; + unsigned long reg_e, reg_d; + + while (mr && enum_id && *reg_idx < desc->hw.nr_mask_regs) { + mr = desc->hw.mask_regs + *reg_idx; + + for (; *fld_idx < ARRAY_SIZE(mr->enum_ids); (*fld_idx)++) { + if (mr->enum_ids[*fld_idx] != enum_id) + continue; + + if (mr->set_reg && mr->clr_reg) { + fn = REG_FN_WRITE_BASE; + mode = MODE_DUAL_REG; + reg_e = mr->clr_reg; + reg_d = mr->set_reg; + } else { + fn = REG_FN_MODIFY_BASE; + if (mr->set_reg) { + mode = MODE_ENABLE_REG; + reg_e = mr->set_reg; + reg_d = mr->set_reg; + } else { + mode = MODE_MASK_REG; + reg_e = mr->clr_reg; + reg_d = mr->clr_reg; + } + } + + fn += (mr->reg_width >> 3) - 1; + return _INTC_MK(fn, mode, + intc_get_reg(d, reg_e), + intc_get_reg(d, reg_d), + 1, + (mr->reg_width - 1) - *fld_idx); + } + + *fld_idx = 0; + (*reg_idx)++; + } + + return 0; +} + +static unsigned int __init intc_mask_data(struct intc_desc *desc, + struct intc_desc_int *d, + intc_enum enum_id, int do_grps) +{ + unsigned int i = 0; + unsigned int j = 0; + unsigned int ret; + + ret = _intc_mask_data(desc, d, enum_id, &i, &j); + if (ret) + return ret; + + if (do_grps) + return intc_mask_data(desc, d, intc_grp_id(desc, enum_id), 0); + + return 0; +} + +static unsigned int __init _intc_prio_data(struct intc_desc *desc, + struct intc_desc_int *d, + intc_enum enum_id, + unsigned int *reg_idx, + unsigned int *fld_idx) +{ + struct intc_prio_reg *pr = desc->hw.prio_regs; + unsigned int fn, n, mode, bit; + unsigned long reg_e, reg_d; + + while (pr && enum_id && *reg_idx < desc->hw.nr_prio_regs) { + pr = desc->hw.prio_regs + *reg_idx; + + for (; *fld_idx < ARRAY_SIZE(pr->enum_ids); (*fld_idx)++) { + if (pr->enum_ids[*fld_idx] != enum_id) + continue; + + if (pr->set_reg && pr->clr_reg) { + fn = REG_FN_WRITE_BASE; + mode = MODE_PCLR_REG; + reg_e = pr->set_reg; + reg_d = pr->clr_reg; + } else { + fn = REG_FN_MODIFY_BASE; + mode = MODE_PRIO_REG; + if (!pr->set_reg) + BUG(); + reg_e = pr->set_reg; + reg_d = pr->set_reg; + } + + fn += (pr->reg_width >> 3) - 1; + n = *fld_idx + 1; + + BUG_ON(n * pr->field_width > pr->reg_width); + + bit = pr->reg_width - (n * pr->field_width); + + return _INTC_MK(fn, mode, + intc_get_reg(d, reg_e), + intc_get_reg(d, reg_d), + pr->field_width, bit); + } + + *fld_idx = 0; + (*reg_idx)++; + } + + return 0; +} + +static unsigned int __init intc_prio_data(struct intc_desc *desc, + struct intc_desc_int *d, + intc_enum enum_id, int do_grps) +{ + unsigned int i = 0; + unsigned int j = 0; + unsigned int ret; + + ret = _intc_prio_data(desc, d, enum_id, &i, &j); + if (ret) + return ret; + + if (do_grps) + return intc_prio_data(desc, d, intc_grp_id(desc, enum_id), 0); + + return 0; +} + +static void __init intc_enable_disable_enum(struct intc_desc *desc, + struct intc_desc_int *d, + intc_enum enum_id, int enable) +{ + unsigned int i, j, data; + + /* go through and enable/disable all mask bits */ + i = j = 0; + do { + data = _intc_mask_data(desc, d, enum_id, &i, &j); + if (data) + intc_enable_disable(d, data, enable); + j++; + } while (data); + + /* go through and enable/disable all priority fields */ + i = j = 0; + do { + data = _intc_prio_data(desc, d, enum_id, &i, &j); + if (data) + intc_enable_disable(d, data, enable); + + j++; + } while (data); +} + +static unsigned int __init intc_ack_data(struct intc_desc *desc, + struct intc_desc_int *d, + intc_enum enum_id) +{ + struct intc_mask_reg *mr = desc->hw.ack_regs; + unsigned int i, j, fn, mode; + unsigned long reg_e, reg_d; + + for (i = 0; mr && enum_id && i < desc->hw.nr_ack_regs; i++) { + mr = desc->hw.ack_regs + i; + + for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) { + if (mr->enum_ids[j] != enum_id) + continue; + + fn = REG_FN_MODIFY_BASE; + mode = MODE_ENABLE_REG; + reg_e = mr->set_reg; + reg_d = mr->set_reg; + + fn += (mr->reg_width >> 3) - 1; + return _INTC_MK(fn, mode, + intc_get_reg(d, reg_e), + intc_get_reg(d, reg_d), + 1, + (mr->reg_width - 1) - j); + } + } + + return 0; +} + +static unsigned int __init intc_sense_data(struct intc_desc *desc, + struct intc_desc_int *d, + intc_enum enum_id) +{ + struct intc_sense_reg *sr = desc->hw.sense_regs; + unsigned int i, j, fn, bit; + + for (i = 0; sr && enum_id && i < desc->hw.nr_sense_regs; i++) { + sr = desc->hw.sense_regs + i; + + for (j = 0; j < ARRAY_SIZE(sr->enum_ids); j++) { + if (sr->enum_ids[j] != enum_id) + continue; + + fn = REG_FN_MODIFY_BASE; + fn += (sr->reg_width >> 3) - 1; + + BUG_ON((j + 1) * sr->field_width > sr->reg_width); + + bit = sr->reg_width - ((j + 1) * sr->field_width); + + return _INTC_MK(fn, 0, intc_get_reg(d, sr->reg), + 0, sr->field_width, bit); + } + } + + return 0; +} + +static void __init intc_register_irq(struct intc_desc *desc, + struct intc_desc_int *d, + intc_enum enum_id, + unsigned int irq) +{ + struct intc_handle_int *hp; + unsigned int data[2], primary; + + /* + * Register the IRQ position with the global IRQ map + */ + set_bit(irq, intc_irq_map); + + /* + * Prefer single interrupt source bitmap over other combinations: + * + * 1. bitmap, single interrupt source + * 2. priority, single interrupt source + * 3. bitmap, multiple interrupt sources (groups) + * 4. priority, multiple interrupt sources (groups) + */ + data[0] = intc_mask_data(desc, d, enum_id, 0); + data[1] = intc_prio_data(desc, d, enum_id, 0); + + primary = 0; + if (!data[0] && data[1]) + primary = 1; + + if (!data[0] && !data[1]) + pr_warning("missing unique irq mask for irq %d (vect 0x%04x)\n", + irq, irq2evt(irq)); + + data[0] = data[0] ? data[0] : intc_mask_data(desc, d, enum_id, 1); + data[1] = data[1] ? data[1] : intc_prio_data(desc, d, enum_id, 1); + + if (!data[primary]) + primary ^= 1; + + BUG_ON(!data[primary]); /* must have primary masking method */ + + disable_irq_nosync(irq); + set_irq_chip_and_handler_name(irq, &d->chip, + handle_level_irq, "level"); + set_irq_chip_data(irq, (void *)data[primary]); + + /* + * set priority level + * - this needs to be at least 2 for 5-bit priorities on 7780 + */ + intc_prio_level[irq] = default_prio_level; + + /* enable secondary masking method if present */ + if (data[!primary]) + _intc_enable(irq, data[!primary]); + + /* add irq to d->prio list if priority is available */ + if (data[1]) { + hp = d->prio + d->nr_prio; + hp->irq = irq; + hp->handle = data[1]; + + if (primary) { + /* + * only secondary priority should access registers, so + * set _INTC_FN(h) = REG_FN_ERR for intc_set_priority() + */ + hp->handle &= ~_INTC_MK(0x0f, 0, 0, 0, 0, 0); + hp->handle |= _INTC_MK(REG_FN_ERR, 0, 0, 0, 0, 0); + } + d->nr_prio++; + } + + /* add irq to d->sense list if sense is available */ + data[0] = intc_sense_data(desc, d, enum_id); + if (data[0]) { + (d->sense + d->nr_sense)->irq = irq; + (d->sense + d->nr_sense)->handle = data[0]; + d->nr_sense++; + } + + /* irq should be disabled by default */ + d->chip.mask(irq); + + if (desc->hw.ack_regs) + ack_handle[irq] = intc_ack_data(desc, d, enum_id); + +#ifdef CONFIG_INTC_BALANCING + if (desc->hw.mask_regs) + dist_handle[irq] = intc_dist_data(desc, d, enum_id); +#endif + +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); /* Enable IRQ on ARM systems */ +#endif +} + +static unsigned int __init save_reg(struct intc_desc_int *d, + unsigned int cnt, + unsigned long value, + unsigned int smp) +{ + if (value) { + value = intc_phys_to_virt(d, value); + + d->reg[cnt] = value; +#ifdef CONFIG_SMP + d->smp[cnt] = smp; +#endif + return 1; + } + + return 0; +} + +static void intc_redirect_irq(unsigned int irq, struct irq_desc *desc) +{ + generic_handle_irq((unsigned int)get_irq_data(irq)); +} + +int __init register_intc_controller(struct intc_desc *desc) +{ + unsigned int i, k, smp; + struct intc_hw_desc *hw = &desc->hw; + struct intc_desc_int *d; + struct resource *res; + + pr_info("Registered controller '%s' with %u IRQs\n", + desc->name, hw->nr_vectors); + + d = kzalloc(sizeof(*d), GFP_NOWAIT); + if (!d) + goto err0; + + INIT_LIST_HEAD(&d->list); + list_add(&d->list, &intc_list); + + if (desc->num_resources) { + d->nr_windows = desc->num_resources; + d->window = kzalloc(d->nr_windows * sizeof(*d->window), + GFP_NOWAIT); + if (!d->window) + goto err1; + + for (k = 0; k < d->nr_windows; k++) { + res = desc->resource + k; + WARN_ON(resource_type(res) != IORESOURCE_MEM); + d->window[k].phys = res->start; + d->window[k].size = resource_size(res); + d->window[k].virt = ioremap_nocache(res->start, + resource_size(res)); + if (!d->window[k].virt) + goto err2; + } + } + + d->nr_reg = hw->mask_regs ? hw->nr_mask_regs * 2 : 0; +#ifdef CONFIG_INTC_BALANCING + if (d->nr_reg) + d->nr_reg += hw->nr_mask_regs; +#endif + d->nr_reg += hw->prio_regs ? hw->nr_prio_regs * 2 : 0; + d->nr_reg += hw->sense_regs ? hw->nr_sense_regs : 0; + d->nr_reg += hw->ack_regs ? hw->nr_ack_regs : 0; + + d->reg = kzalloc(d->nr_reg * sizeof(*d->reg), GFP_NOWAIT); + if (!d->reg) + goto err2; + +#ifdef CONFIG_SMP + d->smp = kzalloc(d->nr_reg * sizeof(*d->smp), GFP_NOWAIT); + if (!d->smp) + goto err3; +#endif + k = 0; + + if (hw->mask_regs) { + for (i = 0; i < hw->nr_mask_regs; i++) { + smp = IS_SMP(hw->mask_regs[i]); + k += save_reg(d, k, hw->mask_regs[i].set_reg, smp); + k += save_reg(d, k, hw->mask_regs[i].clr_reg, smp); +#ifdef CONFIG_INTC_BALANCING + k += save_reg(d, k, hw->mask_regs[i].dist_reg, 0); +#endif + } + } + + if (hw->prio_regs) { + d->prio = kzalloc(hw->nr_vectors * sizeof(*d->prio), + GFP_NOWAIT); + if (!d->prio) + goto err4; + + for (i = 0; i < hw->nr_prio_regs; i++) { + smp = IS_SMP(hw->prio_regs[i]); + k += save_reg(d, k, hw->prio_regs[i].set_reg, smp); + k += save_reg(d, k, hw->prio_regs[i].clr_reg, smp); + } + } + + if (hw->sense_regs) { + d->sense = kzalloc(hw->nr_vectors * sizeof(*d->sense), + GFP_NOWAIT); + if (!d->sense) + goto err5; + + for (i = 0; i < hw->nr_sense_regs; i++) + k += save_reg(d, k, hw->sense_regs[i].reg, 0); + } + + d->chip.name = desc->name; + d->chip.mask = intc_disable; + d->chip.unmask = intc_enable; + d->chip.mask_ack = intc_disable; + d->chip.enable = intc_enable; + d->chip.disable = intc_disable; + d->chip.shutdown = intc_disable; + d->chip.set_type = intc_set_sense; + d->chip.set_wake = intc_set_wake; +#ifdef CONFIG_SMP + d->chip.set_affinity = intc_set_affinity; +#endif + + if (hw->ack_regs) { + for (i = 0; i < hw->nr_ack_regs; i++) + k += save_reg(d, k, hw->ack_regs[i].set_reg, 0); + + d->chip.mask_ack = intc_mask_ack; + } + + /* disable bits matching force_disable before registering irqs */ + if (desc->force_disable) + intc_enable_disable_enum(desc, d, desc->force_disable, 0); + + /* disable bits matching force_enable before registering irqs */ + if (desc->force_enable) + intc_enable_disable_enum(desc, d, desc->force_enable, 0); + + BUG_ON(k > 256); /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */ + + /* register the vectors one by one */ + for (i = 0; i < hw->nr_vectors; i++) { + struct intc_vect *vect = hw->vectors + i; + unsigned int irq = evt2irq(vect->vect); + struct irq_desc *irq_desc; + + if (!vect->enum_id) + continue; + + irq_desc = irq_to_desc_alloc_node(irq, numa_node_id()); + if (unlikely(!irq_desc)) { + pr_err("can't get irq_desc for %d\n", irq); + continue; + } + + intc_register_irq(desc, d, vect->enum_id, irq); + + for (k = i + 1; k < hw->nr_vectors; k++) { + struct intc_vect *vect2 = hw->vectors + k; + unsigned int irq2 = evt2irq(vect2->vect); + + if (vect->enum_id != vect2->enum_id) + continue; + + /* + * In the case of multi-evt handling and sparse + * IRQ support, each vector still needs to have + * its own backing irq_desc. + */ + irq_desc = irq_to_desc_alloc_node(irq2, numa_node_id()); + if (unlikely(!irq_desc)) { + pr_err("can't get irq_desc for %d\n", irq2); + continue; + } + + vect2->enum_id = 0; + + /* redirect this interrupts to the first one */ + set_irq_chip(irq2, &dummy_irq_chip); + set_irq_chained_handler(irq2, intc_redirect_irq); + set_irq_data(irq2, (void *)irq); + } + } + + /* enable bits matching force_enable after registering irqs */ + if (desc->force_enable) + intc_enable_disable_enum(desc, d, desc->force_enable, 1); + + return 0; +err5: + kfree(d->prio); +err4: +#ifdef CONFIG_SMP + kfree(d->smp); +err3: +#endif + kfree(d->reg); +err2: + for (k = 0; k < d->nr_windows; k++) + if (d->window[k].virt) + iounmap(d->window[k].virt); + + kfree(d->window); +err1: + kfree(d); +err0: + pr_err("unable to allocate INTC memory\n"); + + return -ENOMEM; +} + +#ifdef CONFIG_INTC_USERIMASK +static void __iomem *uimask; + +int register_intc_userimask(unsigned long addr) +{ + if (unlikely(uimask)) + return -EBUSY; + + uimask = ioremap_nocache(addr, SZ_4K); + if (unlikely(!uimask)) + return -ENOMEM; + + pr_info("userimask support registered for levels 0 -> %d\n", + default_prio_level - 1); + + return 0; +} + +static ssize_t +show_intc_userimask(struct sysdev_class *cls, + struct sysdev_class_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", (__raw_readl(uimask) >> 4) & 0xf); +} + +static ssize_t +store_intc_userimask(struct sysdev_class *cls, + struct sysdev_class_attribute *attr, + const char *buf, size_t count) +{ + unsigned long level; + + level = simple_strtoul(buf, NULL, 10); + + /* + * Minimal acceptable IRQ levels are in the 2 - 16 range, but + * these are chomped so as to not interfere with normal IRQs. + * + * Level 1 is a special case on some CPUs in that it's not + * directly settable, but given that USERIMASK cuts off below a + * certain level, we don't care about this limitation here. + * Level 0 on the other hand equates to user masking disabled. + * + * We use default_prio_level as a cut off so that only special + * case opt-in IRQs can be mangled. + */ + if (level >= default_prio_level) + return -EINVAL; + + __raw_writel(0xa5 << 24 | level << 4, uimask); + + return count; +} + +static SYSDEV_CLASS_ATTR(userimask, S_IRUSR | S_IWUSR, + show_intc_userimask, store_intc_userimask); +#endif + +static ssize_t +show_intc_name(struct sys_device *dev, struct sysdev_attribute *attr, char *buf) +{ + struct intc_desc_int *d; + + d = container_of(dev, struct intc_desc_int, sysdev); + + return sprintf(buf, "%s\n", d->chip.name); +} + +static SYSDEV_ATTR(name, S_IRUGO, show_intc_name, NULL); + +static int intc_suspend(struct sys_device *dev, pm_message_t state) +{ + struct intc_desc_int *d; + struct irq_desc *desc; + int irq; + + /* get intc controller associated with this sysdev */ + d = container_of(dev, struct intc_desc_int, sysdev); + + switch (state.event) { + case PM_EVENT_ON: + if (d->state.event != PM_EVENT_FREEZE) + break; + for_each_irq_desc(irq, desc) { + if (desc->handle_irq == intc_redirect_irq) + continue; + if (desc->chip != &d->chip) + continue; + if (desc->status & IRQ_DISABLED) + intc_disable(irq); + else + intc_enable(irq); + } + break; + case PM_EVENT_FREEZE: + /* nothing has to be done */ + break; + case PM_EVENT_SUSPEND: + /* enable wakeup irqs belonging to this intc controller */ + for_each_irq_desc(irq, desc) { + if ((desc->status & IRQ_WAKEUP) && (desc->chip == &d->chip)) + intc_enable(irq); + } + break; + } + d->state = state; + + return 0; +} + +static int intc_resume(struct sys_device *dev) +{ + return intc_suspend(dev, PMSG_ON); +} + +static struct sysdev_class intc_sysdev_class = { + .name = "intc", + .suspend = intc_suspend, + .resume = intc_resume, +}; + +/* register this intc as sysdev to allow suspend/resume */ +static int __init register_intc_sysdevs(void) +{ + struct intc_desc_int *d; + int error; + int id = 0; + + error = sysdev_class_register(&intc_sysdev_class); +#ifdef CONFIG_INTC_USERIMASK + if (!error && uimask) + error = sysdev_class_create_file(&intc_sysdev_class, + &attr_userimask); +#endif + if (!error) { + list_for_each_entry(d, &intc_list, list) { + d->sysdev.id = id; + d->sysdev.cls = &intc_sysdev_class; + error = sysdev_register(&d->sysdev); + if (error == 0) + error = sysdev_create_file(&d->sysdev, + &attr_name); + if (error) + break; + + id++; + } + } + + if (error) + pr_err("sysdev registration error\n"); + + return error; +} +device_initcall(register_intc_sysdevs); + +/* + * Dynamic IRQ allocation and deallocation + */ +unsigned int create_irq_nr(unsigned int irq_want, int node) +{ + unsigned int irq = 0, new; + unsigned long flags; + struct irq_desc *desc; + + spin_lock_irqsave(&vector_lock, flags); + + /* + * First try the wanted IRQ + */ + if (test_and_set_bit(irq_want, intc_irq_map) == 0) { + new = irq_want; + } else { + /* .. then fall back to scanning. */ + new = find_first_zero_bit(intc_irq_map, nr_irqs); + if (unlikely(new == nr_irqs)) + goto out_unlock; + + __set_bit(new, intc_irq_map); + } + + desc = irq_to_desc_alloc_node(new, node); + if (unlikely(!desc)) { + pr_err("can't get irq_desc for %d\n", new); + goto out_unlock; + } + + desc = move_irq_desc(desc, node); + irq = new; + +out_unlock: + spin_unlock_irqrestore(&vector_lock, flags); + + if (irq > 0) { + dynamic_irq_init(irq); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); /* Enable IRQ on ARM systems */ +#endif + } + + return irq; +} + +int create_irq(void) +{ + int nid = cpu_to_node(smp_processor_id()); + int irq; + + irq = create_irq_nr(NR_IRQS_LEGACY, nid); + if (irq == 0) + irq = -1; + + return irq; +} + +void destroy_irq(unsigned int irq) +{ + unsigned long flags; + + dynamic_irq_cleanup(irq); + + spin_lock_irqsave(&vector_lock, flags); + __clear_bit(irq, intc_irq_map); + spin_unlock_irqrestore(&vector_lock, flags); +} + +int reserve_irq_vector(unsigned int irq) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&vector_lock, flags); + if (test_and_set_bit(irq, intc_irq_map)) + ret = -EBUSY; + spin_unlock_irqrestore(&vector_lock, flags); + + return ret; +} + +void reserve_irq_legacy(void) +{ + unsigned long flags; + int i, j; + + spin_lock_irqsave(&vector_lock, flags); + j = find_first_bit(intc_irq_map, nr_irqs); + for (i = 0; i < j; i++) + __set_bit(i, intc_irq_map); + spin_unlock_irqrestore(&vector_lock, flags); +} diff --git a/trunk/drivers/sh/intc/Kconfig b/trunk/drivers/sh/intc/Kconfig deleted file mode 100644 index c88cbccc62b0..000000000000 --- a/trunk/drivers/sh/intc/Kconfig +++ /dev/null @@ -1,35 +0,0 @@ -comment "Interrupt controller options" - -config INTC_USERIMASK - bool "Userspace interrupt masking support" - depends on ARCH_SHMOBILE || (SUPERH && CPU_SH4A) - help - This enables support for hardware-assisted userspace hardirq - masking. - - SH-4A and newer interrupt blocks all support a special shadowed - page with all non-masking registers obscured when mapped in to - userspace. This is primarily for use by userspace device - drivers that are using special priority levels. - - If in doubt, say N. - -config INTC_BALANCING - bool "Hardware IRQ balancing support" - depends on SMP && SUPERH && CPU_SHX3 - help - This enables support for IRQ auto-distribution mode on SH-X3 - SMP parts. All of the balancing and CPU wakeup decisions are - taken care of automatically by hardware for distributed - vectors. - - If in doubt, say N. - -config INTC_MAPPING_DEBUG - bool "Expose IRQ to per-controller id mapping via debugfs" - depends on DEBUG_FS - help - This will create a debugfs entry for showing the relationship - between system IRQs and the per-controller id tables. - - If in doubt, say N. diff --git a/trunk/drivers/sh/intc/Makefile b/trunk/drivers/sh/intc/Makefile deleted file mode 100644 index bb5df868d77a..000000000000 --- a/trunk/drivers/sh/intc/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -obj-y := access.o chip.o core.o dynamic.o handle.o virq.o - -obj-$(CONFIG_INTC_BALANCING) += balancing.o -obj-$(CONFIG_INTC_USERIMASK) += userimask.o -obj-$(CONFIG_INTC_MAPPING_DEBUG) += virq-debugfs.o diff --git a/trunk/drivers/sh/intc/access.c b/trunk/drivers/sh/intc/access.c deleted file mode 100644 index f892ae1d212a..000000000000 --- a/trunk/drivers/sh/intc/access.c +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Common INTC2 register accessors - * - * Copyright (C) 2007, 2008 Magnus Damm - * Copyright (C) 2009, 2010 Paul Mundt - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - */ -#include -#include "internals.h" - -unsigned long intc_phys_to_virt(struct intc_desc_int *d, unsigned long address) -{ - struct intc_window *window; - int k; - - /* scan through physical windows and convert address */ - for (k = 0; k < d->nr_windows; k++) { - window = d->window + k; - - if (address < window->phys) - continue; - - if (address >= (window->phys + window->size)) - continue; - - address -= window->phys; - address += (unsigned long)window->virt; - - return address; - } - - /* no windows defined, register must be 1:1 mapped virt:phys */ - return address; -} - -unsigned int intc_get_reg(struct intc_desc_int *d, unsigned long address) -{ - unsigned int k; - - address = intc_phys_to_virt(d, address); - - for (k = 0; k < d->nr_reg; k++) { - if (d->reg[k] == address) - return k; - } - - BUG(); - return 0; -} - -unsigned int intc_set_field_from_handle(unsigned int value, - unsigned int field_value, - unsigned int handle) -{ - unsigned int width = _INTC_WIDTH(handle); - unsigned int shift = _INTC_SHIFT(handle); - - value &= ~(((1 << width) - 1) << shift); - value |= field_value << shift; - return value; -} - -unsigned long intc_get_field_from_handle(unsigned int value, unsigned int handle) -{ - unsigned int width = _INTC_WIDTH(handle); - unsigned int shift = _INTC_SHIFT(handle); - unsigned int mask = ((1 << width) - 1) << shift; - - return (value & mask) >> shift; -} - -static unsigned long test_8(unsigned long addr, unsigned long h, - unsigned long ignore) -{ - return intc_get_field_from_handle(__raw_readb(addr), h); -} - -static unsigned long test_16(unsigned long addr, unsigned long h, - unsigned long ignore) -{ - return intc_get_field_from_handle(__raw_readw(addr), h); -} - -static unsigned long test_32(unsigned long addr, unsigned long h, - unsigned long ignore) -{ - return intc_get_field_from_handle(__raw_readl(addr), h); -} - -static unsigned long write_8(unsigned long addr, unsigned long h, - unsigned long data) -{ - __raw_writeb(intc_set_field_from_handle(0, data, h), addr); - (void)__raw_readb(addr); /* Defeat write posting */ - return 0; -} - -static unsigned long write_16(unsigned long addr, unsigned long h, - unsigned long data) -{ - __raw_writew(intc_set_field_from_handle(0, data, h), addr); - (void)__raw_readw(addr); /* Defeat write posting */ - return 0; -} - -static unsigned long write_32(unsigned long addr, unsigned long h, - unsigned long data) -{ - __raw_writel(intc_set_field_from_handle(0, data, h), addr); - (void)__raw_readl(addr); /* Defeat write posting */ - return 0; -} - -static unsigned long modify_8(unsigned long addr, unsigned long h, - unsigned long data) -{ - unsigned long flags; - unsigned int value; - local_irq_save(flags); - value = intc_set_field_from_handle(__raw_readb(addr), data, h); - __raw_writeb(value, addr); - (void)__raw_readb(addr); /* Defeat write posting */ - local_irq_restore(flags); - return 0; -} - -static unsigned long modify_16(unsigned long addr, unsigned long h, - unsigned long data) -{ - unsigned long flags; - unsigned int value; - local_irq_save(flags); - value = intc_set_field_from_handle(__raw_readw(addr), data, h); - __raw_writew(value, addr); - (void)__raw_readw(addr); /* Defeat write posting */ - local_irq_restore(flags); - return 0; -} - -static unsigned long modify_32(unsigned long addr, unsigned long h, - unsigned long data) -{ - unsigned long flags; - unsigned int value; - local_irq_save(flags); - value = intc_set_field_from_handle(__raw_readl(addr), data, h); - __raw_writel(value, addr); - (void)__raw_readl(addr); /* Defeat write posting */ - local_irq_restore(flags); - return 0; -} - -static unsigned long intc_mode_field(unsigned long addr, - unsigned long handle, - unsigned long (*fn)(unsigned long, - unsigned long, - unsigned long), - unsigned int irq) -{ - return fn(addr, handle, ((1 << _INTC_WIDTH(handle)) - 1)); -} - -static unsigned long intc_mode_zero(unsigned long addr, - unsigned long handle, - unsigned long (*fn)(unsigned long, - unsigned long, - unsigned long), - unsigned int irq) -{ - return fn(addr, handle, 0); -} - -static unsigned long intc_mode_prio(unsigned long addr, - unsigned long handle, - unsigned long (*fn)(unsigned long, - unsigned long, - unsigned long), - unsigned int irq) -{ - return fn(addr, handle, intc_get_prio_level(irq)); -} - -unsigned long (*intc_reg_fns[])(unsigned long addr, - unsigned long h, - unsigned long data) = { - [REG_FN_TEST_BASE + 0] = test_8, - [REG_FN_TEST_BASE + 1] = test_16, - [REG_FN_TEST_BASE + 3] = test_32, - [REG_FN_WRITE_BASE + 0] = write_8, - [REG_FN_WRITE_BASE + 1] = write_16, - [REG_FN_WRITE_BASE + 3] = write_32, - [REG_FN_MODIFY_BASE + 0] = modify_8, - [REG_FN_MODIFY_BASE + 1] = modify_16, - [REG_FN_MODIFY_BASE + 3] = modify_32, -}; - -unsigned long (*intc_enable_fns[])(unsigned long addr, - unsigned long handle, - unsigned long (*fn)(unsigned long, - unsigned long, - unsigned long), - unsigned int irq) = { - [MODE_ENABLE_REG] = intc_mode_field, - [MODE_MASK_REG] = intc_mode_zero, - [MODE_DUAL_REG] = intc_mode_field, - [MODE_PRIO_REG] = intc_mode_prio, - [MODE_PCLR_REG] = intc_mode_prio, -}; - -unsigned long (*intc_disable_fns[])(unsigned long addr, - unsigned long handle, - unsigned long (*fn)(unsigned long, - unsigned long, - unsigned long), - unsigned int irq) = { - [MODE_ENABLE_REG] = intc_mode_zero, - [MODE_MASK_REG] = intc_mode_field, - [MODE_DUAL_REG] = intc_mode_field, - [MODE_PRIO_REG] = intc_mode_zero, - [MODE_PCLR_REG] = intc_mode_field, -}; - -unsigned long (*intc_enable_noprio_fns[])(unsigned long addr, - unsigned long handle, - unsigned long (*fn)(unsigned long, - unsigned long, - unsigned long), - unsigned int irq) = { - [MODE_ENABLE_REG] = intc_mode_field, - [MODE_MASK_REG] = intc_mode_zero, - [MODE_DUAL_REG] = intc_mode_field, - [MODE_PRIO_REG] = intc_mode_field, - [MODE_PCLR_REG] = intc_mode_field, -}; diff --git a/trunk/drivers/sh/intc/balancing.c b/trunk/drivers/sh/intc/balancing.c deleted file mode 100644 index cec7a96f2c09..000000000000 --- a/trunk/drivers/sh/intc/balancing.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Support for hardware-managed IRQ auto-distribution. - * - * Copyright (C) 2010 Paul Mundt - * - * 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 "internals.h" - -static unsigned long dist_handle[NR_IRQS]; - -void intc_balancing_enable(unsigned int irq) -{ - struct intc_desc_int *d = get_intc_desc(irq); - unsigned long handle = dist_handle[irq]; - unsigned long addr; - - if (irq_balancing_disabled(irq) || !handle) - return; - - addr = INTC_REG(d, _INTC_ADDR_D(handle), 0); - intc_reg_fns[_INTC_FN(handle)](addr, handle, 1); -} - -void intc_balancing_disable(unsigned int irq) -{ - struct intc_desc_int *d = get_intc_desc(irq); - unsigned long handle = dist_handle[irq]; - unsigned long addr; - - if (irq_balancing_disabled(irq) || !handle) - return; - - addr = INTC_REG(d, _INTC_ADDR_D(handle), 0); - intc_reg_fns[_INTC_FN(handle)](addr, handle, 0); -} - -static unsigned int intc_dist_data(struct intc_desc *desc, - struct intc_desc_int *d, - intc_enum enum_id) -{ - struct intc_mask_reg *mr = desc->hw.mask_regs; - unsigned int i, j, fn, mode; - unsigned long reg_e, reg_d; - - for (i = 0; mr && enum_id && i < desc->hw.nr_mask_regs; i++) { - mr = desc->hw.mask_regs + i; - - /* - * Skip this entry if there's no auto-distribution - * register associated with it. - */ - if (!mr->dist_reg) - continue; - - for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) { - if (mr->enum_ids[j] != enum_id) - continue; - - fn = REG_FN_MODIFY_BASE; - mode = MODE_ENABLE_REG; - reg_e = mr->dist_reg; - reg_d = mr->dist_reg; - - fn += (mr->reg_width >> 3) - 1; - return _INTC_MK(fn, mode, - intc_get_reg(d, reg_e), - intc_get_reg(d, reg_d), - 1, - (mr->reg_width - 1) - j); - } - } - - /* - * It's possible we've gotten here with no distribution options - * available for the IRQ in question, so we just skip over those. - */ - return 0; -} - -void intc_set_dist_handle(unsigned int irq, struct intc_desc *desc, - struct intc_desc_int *d, intc_enum id) -{ - unsigned long flags; - - /* - * Nothing to do for this IRQ. - */ - if (!desc->hw.mask_regs) - return; - - raw_spin_lock_irqsave(&intc_big_lock, flags); - dist_handle[irq] = intc_dist_data(desc, d, id); - raw_spin_unlock_irqrestore(&intc_big_lock, flags); -} diff --git a/trunk/drivers/sh/intc/chip.c b/trunk/drivers/sh/intc/chip.c deleted file mode 100644 index 35c03706cc21..000000000000 --- a/trunk/drivers/sh/intc/chip.c +++ /dev/null @@ -1,215 +0,0 @@ -/* - * IRQ chip definitions for INTC IRQs. - * - * Copyright (C) 2007, 2008 Magnus Damm - * Copyright (C) 2009, 2010 Paul Mundt - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - */ -#include -#include -#include "internals.h" - -void _intc_enable(unsigned int irq, unsigned long handle) -{ - struct intc_desc_int *d = get_intc_desc(irq); - unsigned long addr; - unsigned int cpu; - - for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_E(handle)); cpu++) { -#ifdef CONFIG_SMP - if (!cpumask_test_cpu(cpu, irq_to_desc(irq)->affinity)) - continue; -#endif - addr = INTC_REG(d, _INTC_ADDR_E(handle), cpu); - intc_enable_fns[_INTC_MODE(handle)](addr, handle, intc_reg_fns\ - [_INTC_FN(handle)], irq); - } - - intc_balancing_enable(irq); -} - -static void intc_enable(unsigned int irq) -{ - _intc_enable(irq, (unsigned long)get_irq_chip_data(irq)); -} - -static void intc_disable(unsigned int irq) -{ - struct intc_desc_int *d = get_intc_desc(irq); - unsigned long handle = (unsigned long)get_irq_chip_data(irq); - unsigned long addr; - unsigned int cpu; - - intc_balancing_disable(irq); - - for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_D(handle)); cpu++) { -#ifdef CONFIG_SMP - if (!cpumask_test_cpu(cpu, irq_to_desc(irq)->affinity)) - continue; -#endif - addr = INTC_REG(d, _INTC_ADDR_D(handle), cpu); - intc_disable_fns[_INTC_MODE(handle)](addr, handle,intc_reg_fns\ - [_INTC_FN(handle)], irq); - } -} - -static int intc_set_wake(unsigned int irq, unsigned int on) -{ - return 0; /* allow wakeup, but setup hardware in intc_suspend() */ -} - -#ifdef CONFIG_SMP -/* - * This is held with the irq desc lock held, so we don't require any - * additional locking here at the intc desc level. The affinity mask is - * later tested in the enable/disable paths. - */ -static int intc_set_affinity(unsigned int irq, const struct cpumask *cpumask) -{ - if (!cpumask_intersects(cpumask, cpu_online_mask)) - return -1; - - cpumask_copy(irq_to_desc(irq)->affinity, cpumask); - - return 0; -} -#endif - -static void intc_mask_ack(unsigned int irq) -{ - struct intc_desc_int *d = get_intc_desc(irq); - unsigned long handle = intc_get_ack_handle(irq); - unsigned long addr; - - intc_disable(irq); - - /* read register and write zero only to the associated bit */ - if (handle) { - unsigned int value; - - addr = INTC_REG(d, _INTC_ADDR_D(handle), 0); - value = intc_set_field_from_handle(0, 1, handle); - - switch (_INTC_FN(handle)) { - case REG_FN_MODIFY_BASE + 0: /* 8bit */ - __raw_readb(addr); - __raw_writeb(0xff ^ value, addr); - break; - case REG_FN_MODIFY_BASE + 1: /* 16bit */ - __raw_readw(addr); - __raw_writew(0xffff ^ value, addr); - break; - case REG_FN_MODIFY_BASE + 3: /* 32bit */ - __raw_readl(addr); - __raw_writel(0xffffffff ^ value, addr); - break; - default: - BUG(); - break; - } - } -} - -static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp, - unsigned int nr_hp, - unsigned int irq) -{ - int i; - - /* - * this doesn't scale well, but... - * - * this function should only be used for cerain uncommon - * operations such as intc_set_priority() and intc_set_type() - * and in those rare cases performance doesn't matter that much. - * keeping the memory footprint low is more important. - * - * one rather simple way to speed this up and still keep the - * memory footprint down is to make sure the array is sorted - * and then perform a bisect to lookup the irq. - */ - for (i = 0; i < nr_hp; i++) { - if ((hp + i)->irq != irq) - continue; - - return hp + i; - } - - return NULL; -} - -int intc_set_priority(unsigned int irq, unsigned int prio) -{ - struct intc_desc_int *d = get_intc_desc(irq); - struct intc_handle_int *ihp; - - if (!intc_get_prio_level(irq) || prio <= 1) - return -EINVAL; - - ihp = intc_find_irq(d->prio, d->nr_prio, irq); - if (ihp) { - if (prio >= (1 << _INTC_WIDTH(ihp->handle))) - return -EINVAL; - - intc_set_prio_level(irq, prio); - - /* - * only set secondary masking method directly - * primary masking method is using intc_prio_level[irq] - * priority level will be set during next enable() - */ - if (_INTC_FN(ihp->handle) != REG_FN_ERR) - _intc_enable(irq, ihp->handle); - } - return 0; -} - -#define VALID(x) (x | 0x80) - -static unsigned char intc_irq_sense_table[IRQ_TYPE_SENSE_MASK + 1] = { - [IRQ_TYPE_EDGE_FALLING] = VALID(0), - [IRQ_TYPE_EDGE_RISING] = VALID(1), - [IRQ_TYPE_LEVEL_LOW] = VALID(2), - /* SH7706, SH7707 and SH7709 do not support high level triggered */ -#if !defined(CONFIG_CPU_SUBTYPE_SH7706) && \ - !defined(CONFIG_CPU_SUBTYPE_SH7707) && \ - !defined(CONFIG_CPU_SUBTYPE_SH7709) - [IRQ_TYPE_LEVEL_HIGH] = VALID(3), -#endif -}; - -static int intc_set_type(unsigned int irq, unsigned int type) -{ - struct intc_desc_int *d = get_intc_desc(irq); - unsigned char value = intc_irq_sense_table[type & IRQ_TYPE_SENSE_MASK]; - struct intc_handle_int *ihp; - unsigned long addr; - - if (!value) - return -EINVAL; - - ihp = intc_find_irq(d->sense, d->nr_sense, irq); - if (ihp) { - addr = INTC_REG(d, _INTC_ADDR_E(ihp->handle), 0); - intc_reg_fns[_INTC_FN(ihp->handle)](addr, ihp->handle, value); - } - - return 0; -} - -struct irq_chip intc_irq_chip = { - .mask = intc_disable, - .unmask = intc_enable, - .mask_ack = intc_mask_ack, - .enable = intc_enable, - .disable = intc_disable, - .shutdown = intc_disable, - .set_type = intc_set_type, - .set_wake = intc_set_wake, -#ifdef CONFIG_SMP - .set_affinity = intc_set_affinity, -#endif -}; diff --git a/trunk/drivers/sh/intc/core.c b/trunk/drivers/sh/intc/core.c deleted file mode 100644 index 306ed287077a..000000000000 --- a/trunk/drivers/sh/intc/core.c +++ /dev/null @@ -1,469 +0,0 @@ -/* - * Shared interrupt handling code for IPR and INTC2 types of IRQs. - * - * Copyright (C) 2007, 2008 Magnus Damm - * Copyright (C) 2009, 2010 Paul Mundt - * - * Based on intc2.c and ipr.c - * - * Copyright (C) 1999 Niibe Yutaka & Takeshi Yaegashi - * Copyright (C) 2000 Kazumoto Kojima - * Copyright (C) 2001 David J. Mckay (david.mckay@st.com) - * Copyright (C) 2003 Takashi Kusuda - * Copyright (C) 2005, 2006 Paul Mundt - * - * 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. - */ -#define pr_fmt(fmt) "intc: " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "internals.h" - -LIST_HEAD(intc_list); -DEFINE_RAW_SPINLOCK(intc_big_lock); -unsigned int nr_intc_controllers; - -/* - * Default priority level - * - this needs to be at least 2 for 5-bit priorities on 7780 - */ -static unsigned int default_prio_level = 2; /* 2 - 16 */ -static unsigned int intc_prio_level[NR_IRQS]; /* for now */ - -unsigned int intc_get_dfl_prio_level(void) -{ - return default_prio_level; -} - -unsigned int intc_get_prio_level(unsigned int irq) -{ - return intc_prio_level[irq]; -} - -void intc_set_prio_level(unsigned int irq, unsigned int level) -{ - unsigned long flags; - - raw_spin_lock_irqsave(&intc_big_lock, flags); - intc_prio_level[irq] = level; - raw_spin_unlock_irqrestore(&intc_big_lock, flags); -} - -static void intc_redirect_irq(unsigned int irq, struct irq_desc *desc) -{ - generic_handle_irq((unsigned int)get_irq_data(irq)); -} - -static void __init intc_register_irq(struct intc_desc *desc, - struct intc_desc_int *d, - intc_enum enum_id, - unsigned int irq) -{ - struct intc_handle_int *hp; - unsigned int data[2], primary; - unsigned long flags; - - /* - * Register the IRQ position with the global IRQ map, then insert - * it in to the radix tree. - */ - reserve_irq_vector(irq); - - raw_spin_lock_irqsave(&intc_big_lock, flags); - radix_tree_insert(&d->tree, enum_id, intc_irq_xlate_get(irq)); - raw_spin_unlock_irqrestore(&intc_big_lock, flags); - - /* - * Prefer single interrupt source bitmap over other combinations: - * - * 1. bitmap, single interrupt source - * 2. priority, single interrupt source - * 3. bitmap, multiple interrupt sources (groups) - * 4. priority, multiple interrupt sources (groups) - */ - data[0] = intc_get_mask_handle(desc, d, enum_id, 0); - data[1] = intc_get_prio_handle(desc, d, enum_id, 0); - - primary = 0; - if (!data[0] && data[1]) - primary = 1; - - if (!data[0] && !data[1]) - pr_warning("missing unique irq mask for irq %d (vect 0x%04x)\n", - irq, irq2evt(irq)); - - data[0] = data[0] ? data[0] : intc_get_mask_handle(desc, d, enum_id, 1); - data[1] = data[1] ? data[1] : intc_get_prio_handle(desc, d, enum_id, 1); - - if (!data[primary]) - primary ^= 1; - - BUG_ON(!data[primary]); /* must have primary masking method */ - - disable_irq_nosync(irq); - set_irq_chip_and_handler_name(irq, &d->chip, - handle_level_irq, "level"); - set_irq_chip_data(irq, (void *)data[primary]); - - /* - * set priority level - */ - intc_set_prio_level(irq, intc_get_dfl_prio_level()); - - /* enable secondary masking method if present */ - if (data[!primary]) - _intc_enable(irq, data[!primary]); - - /* add irq to d->prio list if priority is available */ - if (data[1]) { - hp = d->prio + d->nr_prio; - hp->irq = irq; - hp->handle = data[1]; - - if (primary) { - /* - * only secondary priority should access registers, so - * set _INTC_FN(h) = REG_FN_ERR for intc_set_priority() - */ - hp->handle &= ~_INTC_MK(0x0f, 0, 0, 0, 0, 0); - hp->handle |= _INTC_MK(REG_FN_ERR, 0, 0, 0, 0, 0); - } - d->nr_prio++; - } - - /* add irq to d->sense list if sense is available */ - data[0] = intc_get_sense_handle(desc, d, enum_id); - if (data[0]) { - (d->sense + d->nr_sense)->irq = irq; - (d->sense + d->nr_sense)->handle = data[0]; - d->nr_sense++; - } - - /* irq should be disabled by default */ - d->chip.mask(irq); - - intc_set_ack_handle(irq, desc, d, enum_id); - intc_set_dist_handle(irq, desc, d, enum_id); - - activate_irq(irq); -} - -static unsigned int __init save_reg(struct intc_desc_int *d, - unsigned int cnt, - unsigned long value, - unsigned int smp) -{ - if (value) { - value = intc_phys_to_virt(d, value); - - d->reg[cnt] = value; -#ifdef CONFIG_SMP - d->smp[cnt] = smp; -#endif - return 1; - } - - return 0; -} - -int __init register_intc_controller(struct intc_desc *desc) -{ - unsigned int i, k, smp; - struct intc_hw_desc *hw = &desc->hw; - struct intc_desc_int *d; - struct resource *res; - - pr_info("Registered controller '%s' with %u IRQs\n", - desc->name, hw->nr_vectors); - - d = kzalloc(sizeof(*d), GFP_NOWAIT); - if (!d) - goto err0; - - INIT_LIST_HEAD(&d->list); - list_add_tail(&d->list, &intc_list); - - raw_spin_lock_init(&d->lock); - - d->index = nr_intc_controllers; - - if (desc->num_resources) { - d->nr_windows = desc->num_resources; - d->window = kzalloc(d->nr_windows * sizeof(*d->window), - GFP_NOWAIT); - if (!d->window) - goto err1; - - for (k = 0; k < d->nr_windows; k++) { - res = desc->resource + k; - WARN_ON(resource_type(res) != IORESOURCE_MEM); - d->window[k].phys = res->start; - d->window[k].size = resource_size(res); - d->window[k].virt = ioremap_nocache(res->start, - resource_size(res)); - if (!d->window[k].virt) - goto err2; - } - } - - d->nr_reg = hw->mask_regs ? hw->nr_mask_regs * 2 : 0; -#ifdef CONFIG_INTC_BALANCING - if (d->nr_reg) - d->nr_reg += hw->nr_mask_regs; -#endif - d->nr_reg += hw->prio_regs ? hw->nr_prio_regs * 2 : 0; - d->nr_reg += hw->sense_regs ? hw->nr_sense_regs : 0; - d->nr_reg += hw->ack_regs ? hw->nr_ack_regs : 0; - d->nr_reg += hw->subgroups ? hw->nr_subgroups : 0; - - d->reg = kzalloc(d->nr_reg * sizeof(*d->reg), GFP_NOWAIT); - if (!d->reg) - goto err2; - -#ifdef CONFIG_SMP - d->smp = kzalloc(d->nr_reg * sizeof(*d->smp), GFP_NOWAIT); - if (!d->smp) - goto err3; -#endif - k = 0; - - if (hw->mask_regs) { - for (i = 0; i < hw->nr_mask_regs; i++) { - smp = IS_SMP(hw->mask_regs[i]); - k += save_reg(d, k, hw->mask_regs[i].set_reg, smp); - k += save_reg(d, k, hw->mask_regs[i].clr_reg, smp); -#ifdef CONFIG_INTC_BALANCING - k += save_reg(d, k, hw->mask_regs[i].dist_reg, 0); -#endif - } - } - - if (hw->prio_regs) { - d->prio = kzalloc(hw->nr_vectors * sizeof(*d->prio), - GFP_NOWAIT); - if (!d->prio) - goto err4; - - for (i = 0; i < hw->nr_prio_regs; i++) { - smp = IS_SMP(hw->prio_regs[i]); - k += save_reg(d, k, hw->prio_regs[i].set_reg, smp); - k += save_reg(d, k, hw->prio_regs[i].clr_reg, smp); - } - } - - if (hw->sense_regs) { - d->sense = kzalloc(hw->nr_vectors * sizeof(*d->sense), - GFP_NOWAIT); - if (!d->sense) - goto err5; - - for (i = 0; i < hw->nr_sense_regs; i++) - k += save_reg(d, k, hw->sense_regs[i].reg, 0); - } - - if (hw->subgroups) - for (i = 0; i < hw->nr_subgroups; i++) - if (hw->subgroups[i].reg) - k+= save_reg(d, k, hw->subgroups[i].reg, 0); - - memcpy(&d->chip, &intc_irq_chip, sizeof(struct irq_chip)); - d->chip.name = desc->name; - - if (hw->ack_regs) - for (i = 0; i < hw->nr_ack_regs; i++) - k += save_reg(d, k, hw->ack_regs[i].set_reg, 0); - else - d->chip.mask_ack = d->chip.disable; - - /* disable bits matching force_disable before registering irqs */ - if (desc->force_disable) - intc_enable_disable_enum(desc, d, desc->force_disable, 0); - - /* disable bits matching force_enable before registering irqs */ - if (desc->force_enable) - intc_enable_disable_enum(desc, d, desc->force_enable, 0); - - BUG_ON(k > 256); /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */ - - /* register the vectors one by one */ - for (i = 0; i < hw->nr_vectors; i++) { - struct intc_vect *vect = hw->vectors + i; - unsigned int irq = evt2irq(vect->vect); - struct irq_desc *irq_desc; - - if (!vect->enum_id) - continue; - - irq_desc = irq_to_desc_alloc_node(irq, numa_node_id()); - if (unlikely(!irq_desc)) { - pr_err("can't get irq_desc for %d\n", irq); - continue; - } - - intc_irq_xlate_set(irq, vect->enum_id, d); - intc_register_irq(desc, d, vect->enum_id, irq); - - for (k = i + 1; k < hw->nr_vectors; k++) { - struct intc_vect *vect2 = hw->vectors + k; - unsigned int irq2 = evt2irq(vect2->vect); - - if (vect->enum_id != vect2->enum_id) - continue; - - /* - * In the case of multi-evt handling and sparse - * IRQ support, each vector still needs to have - * its own backing irq_desc. - */ - irq_desc = irq_to_desc_alloc_node(irq2, numa_node_id()); - if (unlikely(!irq_desc)) { - pr_err("can't get irq_desc for %d\n", irq2); - continue; - } - - vect2->enum_id = 0; - - /* redirect this interrupts to the first one */ - set_irq_chip(irq2, &dummy_irq_chip); - set_irq_chained_handler(irq2, intc_redirect_irq); - set_irq_data(irq2, (void *)irq); - } - } - - intc_subgroup_init(desc, d); - - /* enable bits matching force_enable after registering irqs */ - if (desc->force_enable) - intc_enable_disable_enum(desc, d, desc->force_enable, 1); - - nr_intc_controllers++; - - return 0; -err5: - kfree(d->prio); -err4: -#ifdef CONFIG_SMP - kfree(d->smp); -err3: -#endif - kfree(d->reg); -err2: - for (k = 0; k < d->nr_windows; k++) - if (d->window[k].virt) - iounmap(d->window[k].virt); - - kfree(d->window); -err1: - kfree(d); -err0: - pr_err("unable to allocate INTC memory\n"); - - return -ENOMEM; -} - -static ssize_t -show_intc_name(struct sys_device *dev, struct sysdev_attribute *attr, char *buf) -{ - struct intc_desc_int *d; - - d = container_of(dev, struct intc_desc_int, sysdev); - - return sprintf(buf, "%s\n", d->chip.name); -} - -static SYSDEV_ATTR(name, S_IRUGO, show_intc_name, NULL); - -static int intc_suspend(struct sys_device *dev, pm_message_t state) -{ - struct intc_desc_int *d; - struct irq_desc *desc; - int irq; - - /* get intc controller associated with this sysdev */ - d = container_of(dev, struct intc_desc_int, sysdev); - - switch (state.event) { - case PM_EVENT_ON: - if (d->state.event != PM_EVENT_FREEZE) - break; - - for_each_irq_desc(irq, desc) { - /* - * This will catch the redirect and VIRQ cases - * due to the dummy_irq_chip being inserted. - */ - if (desc->chip != &d->chip) - continue; - if (desc->status & IRQ_DISABLED) - desc->chip->disable(irq); - else - desc->chip->enable(irq); - } - break; - case PM_EVENT_FREEZE: - /* nothing has to be done */ - break; - case PM_EVENT_SUSPEND: - /* enable wakeup irqs belonging to this intc controller */ - for_each_irq_desc(irq, desc) { - if (desc->chip != &d->chip) - continue; - if ((desc->status & IRQ_WAKEUP)) - desc->chip->enable(irq); - } - break; - } - - d->state = state; - - return 0; -} - -static int intc_resume(struct sys_device *dev) -{ - return intc_suspend(dev, PMSG_ON); -} - -struct sysdev_class intc_sysdev_class = { - .name = "intc", - .suspend = intc_suspend, - .resume = intc_resume, -}; - -/* register this intc as sysdev to allow suspend/resume */ -static int __init register_intc_sysdevs(void) -{ - struct intc_desc_int *d; - int error; - - error = sysdev_class_register(&intc_sysdev_class); - if (!error) { - list_for_each_entry(d, &intc_list, list) { - d->sysdev.id = d->index; - d->sysdev.cls = &intc_sysdev_class; - error = sysdev_register(&d->sysdev); - if (error == 0) - error = sysdev_create_file(&d->sysdev, - &attr_name); - if (error) - break; - } - } - - if (error) - pr_err("sysdev registration error\n"); - - return error; -} -device_initcall(register_intc_sysdevs); diff --git a/trunk/drivers/sh/intc/dynamic.c b/trunk/drivers/sh/intc/dynamic.c deleted file mode 100644 index 6caecdffe201..000000000000 --- a/trunk/drivers/sh/intc/dynamic.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Dynamic IRQ management - * - * Copyright (C) 2010 Paul Mundt - * - * Modelled after arch/x86/kernel/apic/io_apic.c - * - * 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. - */ -#define pr_fmt(fmt) "intc: " fmt - -#include -#include -#include -#include "internals.h" /* only for activate_irq() damage.. */ - -/* - * The intc_irq_map provides a global map of bound IRQ vectors for a - * given platform. Allocation of IRQs are either static through the CPU - * vector map, or dynamic in the case of board mux vectors or MSI. - * - * As this is a central point for all IRQ controllers on the system, - * each of the available sources are mapped out here. This combined with - * sparseirq makes it quite trivial to keep the vector map tightly packed - * when dynamically creating IRQs, as well as tying in to otherwise - * unused irq_desc positions in the sparse array. - */ -static DECLARE_BITMAP(intc_irq_map, NR_IRQS); -static DEFINE_RAW_SPINLOCK(vector_lock); - -/* - * Dynamic IRQ allocation and deallocation - */ -unsigned int create_irq_nr(unsigned int irq_want, int node) -{ - unsigned int irq = 0, new; - unsigned long flags; - struct irq_desc *desc; - - raw_spin_lock_irqsave(&vector_lock, flags); - - /* - * First try the wanted IRQ - */ - if (test_and_set_bit(irq_want, intc_irq_map) == 0) { - new = irq_want; - } else { - /* .. then fall back to scanning. */ - new = find_first_zero_bit(intc_irq_map, nr_irqs); - if (unlikely(new == nr_irqs)) - goto out_unlock; - - __set_bit(new, intc_irq_map); - } - - desc = irq_to_desc_alloc_node(new, node); - if (unlikely(!desc)) { - pr_err("can't get irq_desc for %d\n", new); - goto out_unlock; - } - - desc = move_irq_desc(desc, node); - irq = new; - -out_unlock: - raw_spin_unlock_irqrestore(&vector_lock, flags); - - if (irq > 0) { - dynamic_irq_init(irq); - activate_irq(irq); - } - - return irq; -} - -int create_irq(void) -{ - int nid = cpu_to_node(smp_processor_id()); - int irq; - - irq = create_irq_nr(NR_IRQS_LEGACY, nid); - if (irq == 0) - irq = -1; - - return irq; -} - -void destroy_irq(unsigned int irq) -{ - unsigned long flags; - - dynamic_irq_cleanup(irq); - - raw_spin_lock_irqsave(&vector_lock, flags); - __clear_bit(irq, intc_irq_map); - raw_spin_unlock_irqrestore(&vector_lock, flags); -} - -int reserve_irq_vector(unsigned int irq) -{ - unsigned long flags; - int ret = 0; - - raw_spin_lock_irqsave(&vector_lock, flags); - if (test_and_set_bit(irq, intc_irq_map)) - ret = -EBUSY; - raw_spin_unlock_irqrestore(&vector_lock, flags); - - return ret; -} - -void reserve_intc_vectors(struct intc_vect *vectors, unsigned int nr_vecs) -{ - unsigned long flags; - int i; - - raw_spin_lock_irqsave(&vector_lock, flags); - for (i = 0; i < nr_vecs; i++) - __set_bit(evt2irq(vectors[i].vect), intc_irq_map); - raw_spin_unlock_irqrestore(&vector_lock, flags); -} - -void reserve_irq_legacy(void) -{ - unsigned long flags; - int i, j; - - raw_spin_lock_irqsave(&vector_lock, flags); - j = find_first_bit(intc_irq_map, nr_irqs); - for (i = 0; i < j; i++) - __set_bit(i, intc_irq_map); - raw_spin_unlock_irqrestore(&vector_lock, flags); -} diff --git a/trunk/drivers/sh/intc/handle.c b/trunk/drivers/sh/intc/handle.c deleted file mode 100644 index 057ce56829bf..000000000000 --- a/trunk/drivers/sh/intc/handle.c +++ /dev/null @@ -1,307 +0,0 @@ -/* - * Shared interrupt handling code for IPR and INTC2 types of IRQs. - * - * Copyright (C) 2007, 2008 Magnus Damm - * Copyright (C) 2009, 2010 Paul Mundt - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - */ -#include -#include -#include -#include "internals.h" - -static unsigned long ack_handle[NR_IRQS]; - -static intc_enum __init intc_grp_id(struct intc_desc *desc, - intc_enum enum_id) -{ - struct intc_group *g = desc->hw.groups; - unsigned int i, j; - - for (i = 0; g && enum_id && i < desc->hw.nr_groups; i++) { - g = desc->hw.groups + i; - - for (j = 0; g->enum_ids[j]; j++) { - if (g->enum_ids[j] != enum_id) - continue; - - return g->enum_id; - } - } - - return 0; -} - -static unsigned int __init _intc_mask_data(struct intc_desc *desc, - struct intc_desc_int *d, - intc_enum enum_id, - unsigned int *reg_idx, - unsigned int *fld_idx) -{ - struct intc_mask_reg *mr = desc->hw.mask_regs; - unsigned int fn, mode; - unsigned long reg_e, reg_d; - - while (mr && enum_id && *reg_idx < desc->hw.nr_mask_regs) { - mr = desc->hw.mask_regs + *reg_idx; - - for (; *fld_idx < ARRAY_SIZE(mr->enum_ids); (*fld_idx)++) { - if (mr->enum_ids[*fld_idx] != enum_id) - continue; - - if (mr->set_reg && mr->clr_reg) { - fn = REG_FN_WRITE_BASE; - mode = MODE_DUAL_REG; - reg_e = mr->clr_reg; - reg_d = mr->set_reg; - } else { - fn = REG_FN_MODIFY_BASE; - if (mr->set_reg) { - mode = MODE_ENABLE_REG; - reg_e = mr->set_reg; - reg_d = mr->set_reg; - } else { - mode = MODE_MASK_REG; - reg_e = mr->clr_reg; - reg_d = mr->clr_reg; - } - } - - fn += (mr->reg_width >> 3) - 1; - return _INTC_MK(fn, mode, - intc_get_reg(d, reg_e), - intc_get_reg(d, reg_d), - 1, - (mr->reg_width - 1) - *fld_idx); - } - - *fld_idx = 0; - (*reg_idx)++; - } - - return 0; -} - -unsigned int __init -intc_get_mask_handle(struct intc_desc *desc, struct intc_desc_int *d, - intc_enum enum_id, int do_grps) -{ - unsigned int i = 0; - unsigned int j = 0; - unsigned int ret; - - ret = _intc_mask_data(desc, d, enum_id, &i, &j); - if (ret) - return ret; - - if (do_grps) - return intc_get_mask_handle(desc, d, intc_grp_id(desc, enum_id), 0); - - return 0; -} - -static unsigned int __init _intc_prio_data(struct intc_desc *desc, - struct intc_desc_int *d, - intc_enum enum_id, - unsigned int *reg_idx, - unsigned int *fld_idx) -{ - struct intc_prio_reg *pr = desc->hw.prio_regs; - unsigned int fn, n, mode, bit; - unsigned long reg_e, reg_d; - - while (pr && enum_id && *reg_idx < desc->hw.nr_prio_regs) { - pr = desc->hw.prio_regs + *reg_idx; - - for (; *fld_idx < ARRAY_SIZE(pr->enum_ids); (*fld_idx)++) { - if (pr->enum_ids[*fld_idx] != enum_id) - continue; - - if (pr->set_reg && pr->clr_reg) { - fn = REG_FN_WRITE_BASE; - mode = MODE_PCLR_REG; - reg_e = pr->set_reg; - reg_d = pr->clr_reg; - } else { - fn = REG_FN_MODIFY_BASE; - mode = MODE_PRIO_REG; - if (!pr->set_reg) - BUG(); - reg_e = pr->set_reg; - reg_d = pr->set_reg; - } - - fn += (pr->reg_width >> 3) - 1; - n = *fld_idx + 1; - - BUG_ON(n * pr->field_width > pr->reg_width); - - bit = pr->reg_width - (n * pr->field_width); - - return _INTC_MK(fn, mode, - intc_get_reg(d, reg_e), - intc_get_reg(d, reg_d), - pr->field_width, bit); - } - - *fld_idx = 0; - (*reg_idx)++; - } - - return 0; -} - -unsigned int __init -intc_get_prio_handle(struct intc_desc *desc, struct intc_desc_int *d, - intc_enum enum_id, int do_grps) -{ - unsigned int i = 0; - unsigned int j = 0; - unsigned int ret; - - ret = _intc_prio_data(desc, d, enum_id, &i, &j); - if (ret) - return ret; - - if (do_grps) - return intc_get_prio_handle(desc, d, intc_grp_id(desc, enum_id), 0); - - return 0; -} - -static unsigned int __init intc_ack_data(struct intc_desc *desc, - struct intc_desc_int *d, - intc_enum enum_id) -{ - struct intc_mask_reg *mr = desc->hw.ack_regs; - unsigned int i, j, fn, mode; - unsigned long reg_e, reg_d; - - for (i = 0; mr && enum_id && i < desc->hw.nr_ack_regs; i++) { - mr = desc->hw.ack_regs + i; - - for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) { - if (mr->enum_ids[j] != enum_id) - continue; - - fn = REG_FN_MODIFY_BASE; - mode = MODE_ENABLE_REG; - reg_e = mr->set_reg; - reg_d = mr->set_reg; - - fn += (mr->reg_width >> 3) - 1; - return _INTC_MK(fn, mode, - intc_get_reg(d, reg_e), - intc_get_reg(d, reg_d), - 1, - (mr->reg_width - 1) - j); - } - } - - return 0; -} - -static void intc_enable_disable(struct intc_desc_int *d, - unsigned long handle, int do_enable) -{ - unsigned long addr; - unsigned int cpu; - unsigned long (*fn)(unsigned long, unsigned long, - unsigned long (*)(unsigned long, unsigned long, - unsigned long), - unsigned int); - - if (do_enable) { - for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_E(handle)); cpu++) { - addr = INTC_REG(d, _INTC_ADDR_E(handle), cpu); - fn = intc_enable_noprio_fns[_INTC_MODE(handle)]; - fn(addr, handle, intc_reg_fns[_INTC_FN(handle)], 0); - } - } else { - for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_D(handle)); cpu++) { - addr = INTC_REG(d, _INTC_ADDR_D(handle), cpu); - fn = intc_disable_fns[_INTC_MODE(handle)]; - fn(addr, handle, intc_reg_fns[_INTC_FN(handle)], 0); - } - } -} - -void __init intc_enable_disable_enum(struct intc_desc *desc, - struct intc_desc_int *d, - intc_enum enum_id, int enable) -{ - unsigned int i, j, data; - - /* go through and enable/disable all mask bits */ - i = j = 0; - do { - data = _intc_mask_data(desc, d, enum_id, &i, &j); - if (data) - intc_enable_disable(d, data, enable); - j++; - } while (data); - - /* go through and enable/disable all priority fields */ - i = j = 0; - do { - data = _intc_prio_data(desc, d, enum_id, &i, &j); - if (data) - intc_enable_disable(d, data, enable); - - j++; - } while (data); -} - -unsigned int __init -intc_get_sense_handle(struct intc_desc *desc, struct intc_desc_int *d, - intc_enum enum_id) -{ - struct intc_sense_reg *sr = desc->hw.sense_regs; - unsigned int i, j, fn, bit; - - for (i = 0; sr && enum_id && i < desc->hw.nr_sense_regs; i++) { - sr = desc->hw.sense_regs + i; - - for (j = 0; j < ARRAY_SIZE(sr->enum_ids); j++) { - if (sr->enum_ids[j] != enum_id) - continue; - - fn = REG_FN_MODIFY_BASE; - fn += (sr->reg_width >> 3) - 1; - - BUG_ON((j + 1) * sr->field_width > sr->reg_width); - - bit = sr->reg_width - ((j + 1) * sr->field_width); - - return _INTC_MK(fn, 0, intc_get_reg(d, sr->reg), - 0, sr->field_width, bit); - } - } - - return 0; -} - - -void intc_set_ack_handle(unsigned int irq, struct intc_desc *desc, - struct intc_desc_int *d, intc_enum id) -{ - unsigned long flags; - - /* - * Nothing to do for this IRQ. - */ - if (!desc->hw.ack_regs) - return; - - raw_spin_lock_irqsave(&intc_big_lock, flags); - ack_handle[irq] = intc_ack_data(desc, d, id); - raw_spin_unlock_irqrestore(&intc_big_lock, flags); -} - -unsigned long intc_get_ack_handle(unsigned int irq) -{ - return ack_handle[irq]; -} diff --git a/trunk/drivers/sh/intc/internals.h b/trunk/drivers/sh/intc/internals.h deleted file mode 100644 index d49482c623fa..000000000000 --- a/trunk/drivers/sh/intc/internals.h +++ /dev/null @@ -1,186 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \ - ((shift) | ((width) << 5) | ((fn) << 9) | ((mode) << 13) | \ - ((addr_e) << 16) | ((addr_d << 24))) - -#define _INTC_SHIFT(h) (h & 0x1f) -#define _INTC_WIDTH(h) ((h >> 5) & 0xf) -#define _INTC_FN(h) ((h >> 9) & 0xf) -#define _INTC_MODE(h) ((h >> 13) & 0x7) -#define _INTC_ADDR_E(h) ((h >> 16) & 0xff) -#define _INTC_ADDR_D(h) ((h >> 24) & 0xff) - -#ifdef CONFIG_SMP -#define IS_SMP(x) (x.smp) -#define INTC_REG(d, x, c) (d->reg[(x)] + ((d->smp[(x)] & 0xff) * c)) -#define SMP_NR(d, x) ((d->smp[(x)] >> 8) ? (d->smp[(x)] >> 8) : 1) -#else -#define IS_SMP(x) 0 -#define INTC_REG(d, x, c) (d->reg[(x)]) -#define SMP_NR(d, x) 1 -#endif - -struct intc_handle_int { - unsigned int irq; - unsigned long handle; -}; - -struct intc_window { - phys_addr_t phys; - void __iomem *virt; - unsigned long size; -}; - -struct intc_map_entry { - intc_enum enum_id; - struct intc_desc_int *desc; -}; - -struct intc_subgroup_entry { - unsigned int pirq; - intc_enum enum_id; - unsigned long handle; -}; - -struct intc_desc_int { - struct list_head list; - struct sys_device sysdev; - struct radix_tree_root tree; - pm_message_t state; - raw_spinlock_t lock; - unsigned int index; - unsigned long *reg; -#ifdef CONFIG_SMP - unsigned long *smp; -#endif - unsigned int nr_reg; - struct intc_handle_int *prio; - unsigned int nr_prio; - struct intc_handle_int *sense; - unsigned int nr_sense; - struct intc_window *window; - unsigned int nr_windows; - struct irq_chip chip; -}; - - -enum { - REG_FN_ERR = 0, - REG_FN_TEST_BASE = 1, - REG_FN_WRITE_BASE = 5, - REG_FN_MODIFY_BASE = 9 -}; - -enum { MODE_ENABLE_REG = 0, /* Bit(s) set -> interrupt enabled */ - MODE_MASK_REG, /* Bit(s) set -> interrupt disabled */ - MODE_DUAL_REG, /* Two registers, set bit to enable / disable */ - MODE_PRIO_REG, /* Priority value written to enable interrupt */ - MODE_PCLR_REG, /* Above plus all bits set to disable interrupt */ -}; - -static inline struct intc_desc_int *get_intc_desc(unsigned int irq) -{ - struct irq_chip *chip = get_irq_chip(irq); - - return container_of(chip, struct intc_desc_int, chip); -} - -/* - * Grumble. - */ -static inline void activate_irq(int irq) -{ -#ifdef CONFIG_ARM - /* ARM requires an extra step to clear IRQ_NOREQUEST, which it - * sets on behalf of every irq_chip. Also sets IRQ_NOPROBE. - */ - set_irq_flags(irq, IRQF_VALID); -#else - /* same effect on other architectures */ - set_irq_noprobe(irq); -#endif -} - -/* access.c */ -extern unsigned long -(*intc_reg_fns[])(unsigned long addr, unsigned long h, unsigned long data); - -extern unsigned long -(*intc_enable_fns[])(unsigned long addr, unsigned long handle, - unsigned long (*fn)(unsigned long, - unsigned long, unsigned long), - unsigned int irq); -extern unsigned long -(*intc_disable_fns[])(unsigned long addr, unsigned long handle, - unsigned long (*fn)(unsigned long, - unsigned long, unsigned long), - unsigned int irq); -extern unsigned long -(*intc_enable_noprio_fns[])(unsigned long addr, unsigned long handle, - unsigned long (*fn)(unsigned long, - unsigned long, unsigned long), - unsigned int irq); - -unsigned long intc_phys_to_virt(struct intc_desc_int *d, unsigned long address); -unsigned int intc_get_reg(struct intc_desc_int *d, unsigned long address); -unsigned int intc_set_field_from_handle(unsigned int value, - unsigned int field_value, - unsigned int handle); -unsigned long intc_get_field_from_handle(unsigned int value, - unsigned int handle); - -/* balancing.c */ -#ifdef CONFIG_INTC_BALANCING -void intc_balancing_enable(unsigned int irq); -void intc_balancing_disable(unsigned int irq); -void intc_set_dist_handle(unsigned int irq, struct intc_desc *desc, - struct intc_desc_int *d, intc_enum id); -#else -static inline void intc_balancing_enable(unsigned int irq) { } -static inline void intc_balancing_disable(unsigned int irq) { } -static inline void -intc_set_dist_handle(unsigned int irq, struct intc_desc *desc, - struct intc_desc_int *d, intc_enum id) { } -#endif - -/* chip.c */ -extern struct irq_chip intc_irq_chip; -void _intc_enable(unsigned int irq, unsigned long handle); - -/* core.c */ -extern struct list_head intc_list; -extern raw_spinlock_t intc_big_lock; -extern unsigned int nr_intc_controllers; -extern struct sysdev_class intc_sysdev_class; - -unsigned int intc_get_dfl_prio_level(void); -unsigned int intc_get_prio_level(unsigned int irq); -void intc_set_prio_level(unsigned int irq, unsigned int level); - -/* handle.c */ -unsigned int intc_get_mask_handle(struct intc_desc *desc, - struct intc_desc_int *d, - intc_enum enum_id, int do_grps); -unsigned int intc_get_prio_handle(struct intc_desc *desc, - struct intc_desc_int *d, - intc_enum enum_id, int do_grps); -unsigned int intc_get_sense_handle(struct intc_desc *desc, - struct intc_desc_int *d, - intc_enum enum_id); -void intc_set_ack_handle(unsigned int irq, struct intc_desc *desc, - struct intc_desc_int *d, intc_enum id); -unsigned long intc_get_ack_handle(unsigned int irq); -void intc_enable_disable_enum(struct intc_desc *desc, struct intc_desc_int *d, - intc_enum enum_id, int enable); - -/* virq.c */ -void intc_subgroup_init(struct intc_desc *desc, struct intc_desc_int *d); -void intc_irq_xlate_set(unsigned int irq, intc_enum id, struct intc_desc_int *d); -struct intc_map_entry *intc_irq_xlate_get(unsigned int irq); diff --git a/trunk/drivers/sh/intc/userimask.c b/trunk/drivers/sh/intc/userimask.c deleted file mode 100644 index e32304b66cf1..000000000000 --- a/trunk/drivers/sh/intc/userimask.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Support for hardware-assisted userspace interrupt masking. - * - * Copyright (C) 2010 Paul Mundt - * - * 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. - */ -#define pr_fmt(fmt) "intc: " fmt - -#include -#include -#include -#include -#include -#include "internals.h" - -static void __iomem *uimask; - -static ssize_t -show_intc_userimask(struct sysdev_class *cls, - struct sysdev_class_attribute *attr, char *buf) -{ - return sprintf(buf, "%d\n", (__raw_readl(uimask) >> 4) & 0xf); -} - -static ssize_t -store_intc_userimask(struct sysdev_class *cls, - struct sysdev_class_attribute *attr, - const char *buf, size_t count) -{ - unsigned long level; - - level = simple_strtoul(buf, NULL, 10); - - /* - * Minimal acceptable IRQ levels are in the 2 - 16 range, but - * these are chomped so as to not interfere with normal IRQs. - * - * Level 1 is a special case on some CPUs in that it's not - * directly settable, but given that USERIMASK cuts off below a - * certain level, we don't care about this limitation here. - * Level 0 on the other hand equates to user masking disabled. - * - * We use the default priority level as a cut off so that only - * special case opt-in IRQs can be mangled. - */ - if (level >= intc_get_dfl_prio_level()) - return -EINVAL; - - __raw_writel(0xa5 << 24 | level << 4, uimask); - - return count; -} - -static SYSDEV_CLASS_ATTR(userimask, S_IRUSR | S_IWUSR, - show_intc_userimask, store_intc_userimask); - - -static int __init userimask_sysdev_init(void) -{ - if (unlikely(!uimask)) - return -ENXIO; - - return sysdev_class_create_file(&intc_sysdev_class, &attr_userimask); -} -late_initcall(userimask_sysdev_init); - -int register_intc_userimask(unsigned long addr) -{ - if (unlikely(uimask)) - return -EBUSY; - - uimask = ioremap_nocache(addr, SZ_4K); - if (unlikely(!uimask)) - return -ENOMEM; - - pr_info("userimask support registered for levels 0 -> %d\n", - intc_get_dfl_prio_level() - 1); - - return 0; -} diff --git a/trunk/drivers/sh/intc/virq-debugfs.c b/trunk/drivers/sh/intc/virq-debugfs.c deleted file mode 100644 index 9e62ba9311f0..000000000000 --- a/trunk/drivers/sh/intc/virq-debugfs.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Support for virtual IRQ subgroups debugfs mapping. - * - * Copyright (C) 2010 Paul Mundt - * - * Modelled after arch/powerpc/kernel/irq.c. - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - */ -#include -#include -#include -#include -#include -#include "internals.h" - -static int intc_irq_xlate_debug(struct seq_file *m, void *priv) -{ - int i; - - seq_printf(m, "%-5s %-7s %-15s\n", "irq", "enum", "chip name"); - - for (i = 1; i < nr_irqs; i++) { - struct intc_map_entry *entry = intc_irq_xlate_get(i); - struct intc_desc_int *desc = entry->desc; - - if (!desc) - continue; - - seq_printf(m, "%5d ", i); - seq_printf(m, "0x%05x ", entry->enum_id); - seq_printf(m, "%-15s\n", desc->chip.name); - } - - return 0; -} - -static int intc_irq_xlate_open(struct inode *inode, struct file *file) -{ - return single_open(file, intc_irq_xlate_debug, inode->i_private); -} - -static const struct file_operations intc_irq_xlate_fops = { - .open = intc_irq_xlate_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int __init intc_irq_xlate_init(void) -{ - /* - * XXX.. use arch_debugfs_dir here when all of the intc users are - * converted. - */ - if (debugfs_create_file("intc_irq_xlate", S_IRUGO, NULL, NULL, - &intc_irq_xlate_fops) == NULL) - return -ENOMEM; - - return 0; -} -fs_initcall(intc_irq_xlate_init); diff --git a/trunk/drivers/sh/intc/virq.c b/trunk/drivers/sh/intc/virq.c deleted file mode 100644 index 643dfd4d2057..000000000000 --- a/trunk/drivers/sh/intc/virq.c +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Support for virtual IRQ subgroups. - * - * Copyright (C) 2010 Paul Mundt - * - * 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. - */ -#define pr_fmt(fmt) "intc: " fmt - -#include -#include -#include -#include -#include -#include "internals.h" - -static struct intc_map_entry intc_irq_xlate[NR_IRQS]; - -struct intc_virq_list { - unsigned int irq; - struct intc_virq_list *next; -}; - -#define for_each_virq(entry, head) \ - for (entry = head; entry; entry = entry->next) - -/* - * Tags for the radix tree - */ -#define INTC_TAG_VIRQ_NEEDS_ALLOC 0 - -void intc_irq_xlate_set(unsigned int irq, intc_enum id, struct intc_desc_int *d) -{ - unsigned long flags; - - raw_spin_lock_irqsave(&intc_big_lock, flags); - intc_irq_xlate[irq].enum_id = id; - intc_irq_xlate[irq].desc = d; - raw_spin_unlock_irqrestore(&intc_big_lock, flags); -} - -struct intc_map_entry *intc_irq_xlate_get(unsigned int irq) -{ - return intc_irq_xlate + irq; -} - -int intc_irq_lookup(const char *chipname, intc_enum enum_id) -{ - struct intc_map_entry *ptr; - struct intc_desc_int *d; - int irq = -1; - - list_for_each_entry(d, &intc_list, list) { - int tagged; - - if (strcmp(d->chip.name, chipname) != 0) - continue; - - /* - * Catch early lookups for subgroup VIRQs that have not - * yet been allocated an IRQ. This already includes a - * fast-path out if the tree is untagged, so there is no - * need to explicitly test the root tree. - */ - tagged = radix_tree_tag_get(&d->tree, enum_id, - INTC_TAG_VIRQ_NEEDS_ALLOC); - if (unlikely(tagged)) - break; - - ptr = radix_tree_lookup(&d->tree, enum_id); - if (ptr) { - irq = ptr - intc_irq_xlate; - break; - } - } - - return irq; -} -EXPORT_SYMBOL_GPL(intc_irq_lookup); - -static int add_virq_to_pirq(unsigned int irq, unsigned int virq) -{ - struct intc_virq_list **last, *entry; - struct irq_desc *desc = irq_to_desc(irq); - - /* scan for duplicates */ - last = (struct intc_virq_list **)&desc->handler_data; - for_each_virq(entry, desc->handler_data) { - if (entry->irq == virq) - return 0; - last = &entry->next; - } - - entry = kzalloc(sizeof(struct intc_virq_list), GFP_ATOMIC); - if (!entry) { - pr_err("can't allocate VIRQ mapping for %d\n", virq); - return -ENOMEM; - } - - entry->irq = virq; - - *last = entry; - - return 0; -} - -static void intc_virq_handler(unsigned int irq, struct irq_desc *desc) -{ - struct intc_virq_list *entry, *vlist = get_irq_data(irq); - struct intc_desc_int *d = get_intc_desc(irq); - - desc->chip->mask_ack(irq); - - for_each_virq(entry, vlist) { - unsigned long addr, handle; - - handle = (unsigned long)get_irq_data(entry->irq); - addr = INTC_REG(d, _INTC_ADDR_E(handle), 0); - - if (intc_reg_fns[_INTC_FN(handle)](addr, handle, 0)) - generic_handle_irq(entry->irq); - } - - desc->chip->unmask(irq); -} - -static unsigned long __init intc_subgroup_data(struct intc_subgroup *subgroup, - struct intc_desc_int *d, - unsigned int index) -{ - unsigned int fn = REG_FN_TEST_BASE + (subgroup->reg_width >> 3) - 1; - - return _INTC_MK(fn, MODE_ENABLE_REG, intc_get_reg(d, subgroup->reg), - 0, 1, (subgroup->reg_width - 1) - index); -} - -static void __init intc_subgroup_init_one(struct intc_desc *desc, - struct intc_desc_int *d, - struct intc_subgroup *subgroup) -{ - struct intc_map_entry *mapped; - unsigned int pirq; - unsigned long flags; - int i; - - mapped = radix_tree_lookup(&d->tree, subgroup->parent_id); - if (!mapped) { - WARN_ON(1); - return; - } - - pirq = mapped - intc_irq_xlate; - - raw_spin_lock_irqsave(&d->lock, flags); - - for (i = 0; i < ARRAY_SIZE(subgroup->enum_ids); i++) { - struct intc_subgroup_entry *entry; - int err; - - if (!subgroup->enum_ids[i]) - continue; - - entry = kmalloc(sizeof(*entry), GFP_NOWAIT); - if (!entry) - break; - - entry->pirq = pirq; - entry->enum_id = subgroup->enum_ids[i]; - entry->handle = intc_subgroup_data(subgroup, d, i); - - err = radix_tree_insert(&d->tree, entry->enum_id, entry); - if (unlikely(err < 0)) - break; - - radix_tree_tag_set(&d->tree, entry->enum_id, - INTC_TAG_VIRQ_NEEDS_ALLOC); - } - - raw_spin_unlock_irqrestore(&d->lock, flags); -} - -void __init intc_subgroup_init(struct intc_desc *desc, struct intc_desc_int *d) -{ - int i; - - if (!desc->hw.subgroups) - return; - - for (i = 0; i < desc->hw.nr_subgroups; i++) - intc_subgroup_init_one(desc, d, desc->hw.subgroups + i); -} - -static void __init intc_subgroup_map(struct intc_desc_int *d) -{ - struct intc_subgroup_entry *entries[32]; - unsigned long flags; - unsigned int nr_found; - int i; - - raw_spin_lock_irqsave(&d->lock, flags); - -restart: - nr_found = radix_tree_gang_lookup_tag_slot(&d->tree, - (void ***)entries, 0, ARRAY_SIZE(entries), - INTC_TAG_VIRQ_NEEDS_ALLOC); - - for (i = 0; i < nr_found; i++) { - struct intc_subgroup_entry *entry; - int irq; - - entry = radix_tree_deref_slot((void **)entries[i]); - if (unlikely(!entry)) - continue; - if (unlikely(entry == RADIX_TREE_RETRY)) - goto restart; - - irq = create_irq(); - if (unlikely(irq < 0)) { - pr_err("no more free IRQs, bailing..\n"); - break; - } - - pr_info("Setting up a chained VIRQ from %d -> %d\n", - irq, entry->pirq); - - intc_irq_xlate_set(irq, entry->enum_id, d); - - set_irq_chip_and_handler_name(irq, get_irq_chip(entry->pirq), - handle_simple_irq, "virq"); - set_irq_chip_data(irq, get_irq_chip_data(entry->pirq)); - - set_irq_data(irq, (void *)entry->handle); - - set_irq_chained_handler(entry->pirq, intc_virq_handler); - add_virq_to_pirq(entry->pirq, irq); - - radix_tree_tag_clear(&d->tree, entry->enum_id, - INTC_TAG_VIRQ_NEEDS_ALLOC); - radix_tree_replace_slot((void **)entries[i], - &intc_irq_xlate[irq]); - } - - raw_spin_unlock_irqrestore(&d->lock, flags); -} - -void __init intc_finalize(void) -{ - struct intc_desc_int *d; - - list_for_each_entry(d, &intc_list, list) - if (radix_tree_tagged(&d->tree, INTC_TAG_VIRQ_NEEDS_ALLOC)) - intc_subgroup_map(d); -} diff --git a/trunk/drivers/sh/pfc.c b/trunk/drivers/sh/pfc.c index 75934e3ea34e..cf0303acab8e 100644 --- a/trunk/drivers/sh/pfc.c +++ b/trunk/drivers/sh/pfc.c @@ -7,8 +7,6 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include #include #include @@ -561,8 +559,10 @@ static int sh_gpio_get_value(struct pinmux_info *gpioc, unsigned gpio) struct pinmux_data_reg *dr = NULL; int bit = 0; - if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0) - return -EINVAL; + if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0) { + BUG(); + return 0; + } return gpio_read_reg(dr->reg, dr->reg_width, 1, bit); } @@ -581,7 +581,7 @@ int register_pinmux(struct pinmux_info *pip) { struct gpio_chip *chip = &pip->chip; - pr_info("%s handling gpio %d -> %d\n", + pr_info("sh pinmux: %s handling gpio %d -> %d\n", pip->name, pip->first_gpio, pip->last_gpio); setup_data_regs(pip); @@ -602,10 +602,3 @@ int register_pinmux(struct pinmux_info *pip) return gpiochip_add(chip); } - -int unregister_pinmux(struct pinmux_info *pip) -{ - pr_info("%s deregistering\n", pip->name); - - return gpiochip_remove(&pip->chip); -} diff --git a/trunk/drivers/staging/Kconfig b/trunk/drivers/staging/Kconfig index 8e03e7600239..335311a98fdc 100644 --- a/trunk/drivers/staging/Kconfig +++ b/trunk/drivers/staging/Kconfig @@ -139,6 +139,8 @@ source "drivers/staging/adis16255/Kconfig" source "drivers/staging/xgifb/Kconfig" +source "drivers/staging/mrst-touchscreen/Kconfig" + source "drivers/staging/msm/Kconfig" source "drivers/staging/lirc/Kconfig" diff --git a/trunk/drivers/staging/Makefile b/trunk/drivers/staging/Makefile index 0e7d7559d379..e3f1e1b6095e 100644 --- a/trunk/drivers/staging/Makefile +++ b/trunk/drivers/staging/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_CXT1E1) += cxt1e1/ obj-$(CONFIG_TI_ST) += ti-st/ obj-$(CONFIG_ADIS16255) += adis16255/ obj-$(CONFIG_FB_XGI) += xgifb/ +obj-$(CONFIG_TOUCHSCREEN_MRSTOUCH) += mrst-touchscreen/ obj-$(CONFIG_MSM_STAGING) += msm/ obj-$(CONFIG_EASYCAP) += easycap/ obj-$(CONFIG_SOLO6X10) += solo6x10/ diff --git a/trunk/drivers/staging/mrst-touchscreen/Kconfig b/trunk/drivers/staging/mrst-touchscreen/Kconfig new file mode 100644 index 000000000000..c2af49217084 --- /dev/null +++ b/trunk/drivers/staging/mrst-touchscreen/Kconfig @@ -0,0 +1,7 @@ +config TOUCHSCREEN_INTEL_MID + tristate "Intel MID platform resistive touchscreen" + depends on INTEL_SCU_IPC + default y + help + Say Y here if you have a Intel MID based touchscreen + If unsure, say N. diff --git a/trunk/drivers/staging/mrst-touchscreen/Makefile b/trunk/drivers/staging/mrst-touchscreen/Makefile new file mode 100644 index 000000000000..2d638b0d70bf --- /dev/null +++ b/trunk/drivers/staging/mrst-touchscreen/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) := intel_mid_touch.o + + diff --git a/trunk/drivers/staging/mrst-touchscreen/TODO b/trunk/drivers/staging/mrst-touchscreen/TODO new file mode 100644 index 000000000000..7157028d634a --- /dev/null +++ b/trunk/drivers/staging/mrst-touchscreen/TODO @@ -0,0 +1,2 @@ +- Move the driver to not think it is SPI (requires fixing some of the SFI + and firmware side) diff --git a/trunk/drivers/staging/mrst-touchscreen/intel-mid-touch.c b/trunk/drivers/staging/mrst-touchscreen/intel-mid-touch.c new file mode 100644 index 000000000000..abba22f921be --- /dev/null +++ b/trunk/drivers/staging/mrst-touchscreen/intel-mid-touch.c @@ -0,0 +1,864 @@ +/* + * intel_mid_touch.c - Intel MID Resistive Touch Screen Driver + * + * Copyright (C) 2008 Intel Corp + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * 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; ifnot, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * Questions/Comments/Bug fixes to Sreedhara (sreedhara.ds@intel.com) + * Ramesh Agarwal (ramesh.agarwal@intel.com) + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * TODO: + * kill off mrstouch_debug eventually + * review conversion of r/m/w sequences + * Replace interrupt mutex abuse + * Kill of mrstouchdevp pointer + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#if defined(MRSTOUCH_DEBUG) +#define mrstouch_debug(fmt, args...)\ + do { \ + printk(KERN_DEBUG "\n[MRSTOUCH(%d)] - ", __LINE__); \ + printk(KERN_DEBUG fmt, ##args); \ + } while (0); +#else +#define mrstouch_debug(fmt, args...) +#endif + +/* PMIC Interrupt registers */ +#define PMIC_REG_ID1 0x00 /*PMIC ID1 register */ + +/* PMIC Interrupt registers */ +#define PMIC_REG_INT 0x04 /*PMIC interrupt register */ +#define PMIC_REG_MINT 0x05 /*PMIC interrupt mask register */ + +/* ADC Interrupt registers */ +#define PMIC_REG_ADCINT 0x5F /*ADC interrupt register */ +#define PMIC_REG_MADCINT 0x60 /*ADC interrupt mask register */ + +/* ADC Control registers */ +#define PMIC_REG_ADCCNTL1 0x61 /*ADC control register */ + +/* ADC Channel Selection registers */ +#define PMICADDR0 0xA4 +#define END_OF_CHANNEL 0x1F + +/* ADC Result register */ +#define PMIC_REG_ADCSNS0H 0x64 + +/* ADC channels for touch screen */ +#define MRST_TS_CHAN10 0xA /* Touch screen X+ connection */ +#define MRST_TS_CHAN11 0xB /* Touch screen X- connection */ +#define MRST_TS_CHAN12 0xC /* Touch screen Y+ connection */ +#define MRST_TS_CHAN13 0xD /* Touch screen Y- connection */ + +/* Touch screen coordinate constants */ +#define TOUCH_PRESSURE 50 +#define TOUCH_PRESSURE_FS 100 + +#define XMOVE_LIMIT 5 +#define YMOVE_LIMIT 5 +#define XYMOVE_CNT 3 + +#define MAX_10BIT ((1<<10)-1) + +/* Touch screen channel BIAS constants */ +#define XBIAS 0x20 +#define YBIAS 0x40 +#define ZBIAS 0x80 + +/* Touch screen coordinates */ +#define MIN_X 10 +#define MAX_X 1024 +#define MIN_Y 10 +#define MAX_Y 1024 +#define WAIT_ADC_COMPLETION 10 + +/* PMIC ADC round robin delays */ +#define ADC_LOOP_DELAY0 0x0 /* Continuous loop */ +#define ADC_LOOP_DELAY1 0x1 /* 4.5 ms approximate */ + +/* PMIC Vendor Identifiers */ +#define PMIC_VENDOR_FS 0 /* PMIC vendor FreeScale */ +#define PMIC_VENDOR_MAXIM 1 /* PMIC vendor MAXIM */ +#define PMIC_VENDOR_NEC 2 /* PMIC vendor NEC */ +#define MRSTOUCH_MAX_CHANNELS 32 /* Maximum ADC channels */ + +/* Touch screen device structure */ +struct mrstouch_dev { + struct spi_device *spi; /* SPI device associated with touch screen */ + struct input_dev *input; /* input device for touchscreen*/ + char phys[32]; /* Device name */ + struct task_struct *pendet_thrd; /* PENDET interrupt handler */ + struct mutex lock; /* Sync between interrupt and PENDET handler */ + bool busy; /* Busy flag */ + u16 asr; /* Address selection register */ + int irq; /* Touch screen IRQ # */ + uint vendor; /* PMIC vendor */ + uint rev; /* PMIC revision */ + bool suspended; /* Device suspended status */ + bool disabled; /* Device disabled status */ + u16 x; /* X coordinate */ + u16 y; /* Y coordinate */ + bool pendown; /* PEN position */ +} ; + + +/* Global Pointer to Touch screen device */ +static struct mrstouch_dev *mrstouchdevp; + +/* Utility to read PMIC ID */ +static int mrstouch_pmic_id(uint *vendor, uint *rev) +{ + int err; + u8 r; + + err = intel_scu_ipc_ioread8(PMIC_REG_ID1, &r); + if (err) + return err; + + *vendor = r & 0x7; + *rev = (r >> 3) & 0x7; + + return 0; +} + +/* + * Parse ADC channels to find end of the channel configured by other ADC user + * NEC and MAXIM requires 4 channels and FreeScale needs 18 channels + */ +static int mrstouch_chan_parse(struct mrstouch_dev *tsdev) +{ + int err, i, j, found; + u32 r32; + + found = -1; + + for (i = 0; i < MRSTOUCH_MAX_CHANNELS; i++) { + if (found >= 0) + break; + + err = intel_scu_ipc_ioread32(PMICADDR0, &r32); + if (err) + return err; + + for (j = 0; j < 32; j+= 8) { + if (((r32 >> j) & 0xFF) == END_OF_CHANNEL) { + found = i; + break; + } + } + } + if (found < 0) + return 0; + + if (tsdev->vendor == PMIC_VENDOR_FS) { + if (found && found > (MRSTOUCH_MAX_CHANNELS - 18)) + return -ENOSPC; + } else { + if (found && found > (MRSTOUCH_MAX_CHANNELS - 4)) + return -ENOSPC; + } + return found; +} + +/* Utility to enable/disable pendet. + * pendet set to true enables PENDET interrupt + * pendet set to false disables PENDET interrupt + * Also clears RND mask bit +*/ +static int pendet_enable(struct mrstouch_dev *tsdev, bool pendet) +{ + u16 reg; + u8 r; + u8 pendet_enabled = 0; + int retry = 0; + int err; + + err = intel_scu_ipc_ioread16(PMIC_REG_MADCINT, ®); + if (err) + return err; + + if (pendet) { + reg &= ~0x0005; + reg |= 0x2000; /* Enable pendet */ + } else + reg &= 0xDFFF; /* Disable pendet */ + + /* Set MADCINT and update ADCCNTL1 (next reg byte) */ + err = intel_scu_ipc_iowrite16(PMIC_REG_MADCINT, reg); + if (!pendet || err) + return err; + + /* + * Sometimes even after the register write succeeds + * the PMIC register value is not updated. Retry few iterations + * to enable pendet. + */ + + err = intel_scu_ipc_ioread8(PMIC_REG_ADCCNTL1, &r); + pendet_enabled = (r >> 5) & 0x01; + + retry = 0; + while (!err && !pendet_enabled) { + retry++; + msleep(10); + err = intel_scu_ipc_iowrite8(PMIC_REG_ADCCNTL1, reg >> 8); + if (err) + break; + err = intel_scu_ipc_ioread8(PMIC_REG_ADCCNTL1, &r); + if (err == 0) + pendet_enabled = (r >> 5) & 0x01; + if (retry >= 10) { + dev_err(&tsdev->spi->dev, "Touch screen disabled.\n"); + return -EIO; + } + } + return 0; +} + +/* To read PMIC ADC touch screen result + * Reads ADC storage registers for higher 7 and lower 3 bits + * converts the two readings to single value and turns off gain bit + */ +static int mrstouch_ts_chan_read(u16 offset, u16 chan, u16 *vp, u16 *vm) +{ + int err; + u16 result; + u32 res; + + result = PMIC_REG_ADCSNS0H + offset; + + if (chan == MRST_TS_CHAN12) + result += 4; + + err = intel_scu_ipc_ioread32(result, &res); + if (err) + return err; + + /* Mash the bits up */ + + *vp = (res & 0xFF) << 3; /* Highest 7 bits */ + *vp |= (res >> 8) & 0x07; /* Lower 3 bits */ + *vp &= 0x3FF; + + res >>= 16; + + *vm = (res & 0xFF) << 3; /* Highest 7 bits */ + *vm |= (res >> 8) & 0x07; /* Lower 3 bits */ + *vm &= 0x3FF; + + return 0; +} + +/* To configure touch screen channels + * Writes touch screen channels to ADC address selection registers + */ +static int mrstouch_ts_chan_set(uint offset) +{ + int count; + u16 chan; + u16 reg[5]; + u8 data[5]; + + chan = PMICADDR0 + offset; + for (count = 0; count <= 3; count++) { + reg[count] = chan++; + data[count] = MRST_TS_CHAN10 + count; + } + reg[count] = chan; + data[count] = END_OF_CHANNEL; + + return intel_scu_ipc_writev(reg, data, 5); +} + +/* Initialize ADC */ +static int mrstouch_adc_init(struct mrstouch_dev *tsdev) +{ + int err, start; + u8 ra, rm; + + err = mrstouch_pmic_id(&tsdev->vendor, &tsdev->rev); + if (err) { + dev_err(&tsdev->spi->dev, "Unable to read PMIC id\n"); + return err; + } + + start = mrstouch_chan_parse(tsdev); + if (start < 0) { + dev_err(&tsdev->spi->dev, "Unable to parse channels\n"); + return start; + } + + tsdev->asr = start; + + mrstouch_debug("Channel offset(%d): 0x%X\n", tsdev->asr, tsdev->vendor); + + /* ADC power on, start, enable PENDET and set loop delay + * ADC loop delay is set to 4.5 ms approximately + * Loop delay more than this results in jitter in adc readings + * Setting loop delay to 0 (continous loop) in MAXIM stops PENDET + * interrupt generation sometimes. + */ + + if (tsdev->vendor == PMIC_VENDOR_FS) { + ra = 0xE0 | ADC_LOOP_DELAY0; + rm = 0x5; + } else { + /* NEC and MAXIm not consistent with loop delay 0 */ + ra = 0xE0 | ADC_LOOP_DELAY1; + rm = 0x0; + + /* configure touch screen channels */ + err = mrstouch_ts_chan_set(tsdev->asr); + if (err) + return err; + } + err = intel_scu_ipc_update_register(PMIC_REG_ADCCNTL1, ra, 0xE7); + if (err == 0) + err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, rm, 0x03); + return err; +} + +/* Reports x,y coordinates to event subsystem */ +static void mrstouch_report_xy(struct mrstouch_dev *tsdev, u16 x, u16 y, u16 z) +{ + int xdiff, ydiff; + + if (tsdev->pendown && z <= TOUCH_PRESSURE) { + /* Pen removed, report button release */ + mrstouch_debug("BTN REL(%d)", z); + input_report_key(tsdev->input, BTN_TOUCH, 0); + tsdev->pendown = false; + } + + xdiff = abs(x - tsdev->x); + ydiff = abs(y - tsdev->y); + + /* + if x and y values changes for XYMOVE_CNT readings it is considered + as stylus is moving. This is required to differentiate between stylus + movement and jitter + */ + if (x < MIN_X || x > MAX_X || y < MIN_Y || y > MAX_Y) { + /* Spurious values, release button if touched and return */ + if (tsdev->pendown) { + mrstouch_debug("BTN REL(%d)", z); + input_report_key(tsdev->input, BTN_TOUCH, 0); + tsdev->pendown = false; + } + return; + } else if (xdiff >= XMOVE_LIMIT || ydiff >= YMOVE_LIMIT) { + tsdev->x = x; + tsdev->y = y; + + input_report_abs(tsdev->input, ABS_X, x); + input_report_abs(tsdev->input, ABS_Y, y); + input_sync(tsdev->input); + } + + + if (!tsdev->pendown && z > TOUCH_PRESSURE) { + /* Pen touched, report button touch */ + mrstouch_debug("BTN TCH(%d, %d, %d)", x, y, z); + input_report_key(tsdev->input, BTN_TOUCH, 1); + tsdev->pendown = true; + } +} + + +/* Utility to start ADC, used by freescale handler */ +static int pendet_mask(void) +{ + return intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x02, 0x02); +} + +/* Utility to stop ADC, used by freescale handler */ +static int pendet_umask(void) +{ + return intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x00, 0x02); +} + +/* Utility to read ADC, used by freescale handler */ +static int mrstouch_pmic_fs_adc_read(struct mrstouch_dev *tsdev) +{ + int err; + u16 x, y, z, result; + u16 reg[4]; + u8 data[4]; + + result = PMIC_REG_ADCSNS0H + tsdev->asr; + + reg[0] = result + 4; + reg[1] = result + 5; + reg[2] = result + 16; + reg[3] = result + 17; + + err = intel_scu_ipc_readv(reg, data, 4); + if (err) + goto ipc_error; + + x = data[0] << 3; /* Higher 7 bits */ + x |= data[1] & 0x7; /* Lower 3 bits */ + x &= 0x3FF; + + y = data[2] << 3; /* Higher 7 bits */ + y |= data[3] & 0x7; /* Lower 3 bits */ + y &= 0x3FF; + + /* Read Z value */ + reg[0] = result + 28; + reg[1] = result + 29; + + err = intel_scu_ipc_readv(reg, data, 4); + if (err) + goto ipc_error; + + z = data[0] << 3; /* Higher 7 bits */ + z |= data[1] & 0x7; /* Lower 3 bits */ + z &= 0x3FF; + +#if defined(MRSTOUCH_PRINT_XYZP) + mrstouch_debug("X: %d, Y: %d, Z: %d", x, y, z); +#endif + + if (z >= TOUCH_PRESSURE_FS) { + mrstouch_report_xy(tsdev, x, y, TOUCH_PRESSURE - 1); /* Pen Removed */ + return TOUCH_PRESSURE - 1; + } else { + mrstouch_report_xy(tsdev, x, y, TOUCH_PRESSURE + 1); /* Pen Touched */ + return TOUCH_PRESSURE + 1; + } + + return 0; + +ipc_error: + dev_err(&tsdev->spi->dev, "ipc error during fs_adc read\n"); + return err; +} + +/* To handle free scale pmic pendet interrupt */ +static int pmic0_pendet(void *dev_id) +{ + int err, count; + u16 chan; + unsigned int touched; + struct mrstouch_dev *tsdev = (struct mrstouch_dev *)dev_id; + u16 reg[5]; + u8 data[5]; + + chan = PMICADDR0 + tsdev->asr; + + /* Set X BIAS */ + for (count = 0; count <= 3; count++) { + reg[count] = chan++; + data[count] = 0x2A; + } + reg[count] = chan++; /* Dummy */ + data[count] = 0; + + err = intel_scu_ipc_writev(reg, data, 5); + if (err) + goto ipc_error; + + msleep(WAIT_ADC_COMPLETION); + + /* Set Y BIAS */ + for (count = 0; count <= 3; count++) { + reg[count] = chan++; + data[count] = 0x4A; + } + reg[count] = chan++; /* Dummy */ + data[count] = 0; + + err = intel_scu_ipc_writev(reg, data, 5); + if (err) + goto ipc_error; + + msleep(WAIT_ADC_COMPLETION); + + /* Set Z BIAS */ + err = intel_scu_ipc_iowrite32(chan + 2, 0x8A8A8A8A); + if (err) + goto ipc_error; + + msleep(WAIT_ADC_COMPLETION); + + /*Read touch screen channels till pen removed + * Freescale reports constant value of z for all points + * z is high when screen is not touched and low when touched + * Map high z value to not touched and low z value to pen touched + */ + touched = mrstouch_pmic_fs_adc_read(tsdev); + while (touched > TOUCH_PRESSURE) { + touched = mrstouch_pmic_fs_adc_read(tsdev); + msleep(WAIT_ADC_COMPLETION); + } + + /* Clear all TS channels */ + chan = PMICADDR0 + tsdev->asr; + for (count = 0; count <= 4; count++) { + reg[count] = chan++; + data[count] = 0; + } + err = intel_scu_ipc_writev(reg, data, 5); + if (err) + goto ipc_error; + + for (count = 0; count <= 4; count++) { + reg[count] = chan++; + data[count] = 0; + } + err = intel_scu_ipc_writev(reg, data, 5); + if (err) + goto ipc_error; + + err = intel_scu_ipc_iowrite32(chan + 2, 0x00000000); + if (err) + goto ipc_error; + + return 0; + +ipc_error: + dev_err(&tsdev->spi->dev, "ipc error during pendet\n"); + return err; +} + + +/* To enable X, Y and Z bias values + * Enables YPYM for X channels and XPXM for Y channels + */ +static int mrstouch_ts_bias_set(uint offset, uint bias) +{ + int count; + u16 chan, start; + u16 reg[4]; + u8 data[4]; + + chan = PMICADDR0 + offset; + start = MRST_TS_CHAN10; + + for (count = 0; count <= 3; count++) { + reg[count] = chan++; + data[count] = bias | (start + count); + } + return intel_scu_ipc_writev(reg, data, 4); +} + +/* To read touch screen channel values */ +static int mrstouch_adc_read(struct mrstouch_dev *tsdev) +{ + int err; + u16 xp, xm, yp, ym, zp, zm; + + /* configure Y bias for X channels */ + err = mrstouch_ts_bias_set(tsdev->asr, YBIAS); + if (err) + goto ipc_error; + + msleep(WAIT_ADC_COMPLETION); + + /* read x+ and x- channels */ + err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, &xp, &xm); + if (err) + goto ipc_error; + + /* configure x bias for y channels */ + err = mrstouch_ts_bias_set(tsdev->asr, XBIAS); + if (err) + goto ipc_error; + + msleep(WAIT_ADC_COMPLETION); + + /* read y+ and y- channels */ + err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN12, &yp, &ym); + if (err) + goto ipc_error; + + /* configure z bias for x and y channels */ + err = mrstouch_ts_bias_set(tsdev->asr, ZBIAS); + if (err) + goto ipc_error; + + msleep(WAIT_ADC_COMPLETION); + + /* read z+ and z- channels */ + err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, &zp, &zm); + if (err) + goto ipc_error; + +#if defined(MRSTOUCH_PRINT_XYZP) + printk(KERN_INFO "X+: %d, Y+: %d, Z+: %d\n", xp, yp, zp); +#endif + +#if defined(MRSTOUCH_PRINT_XYZM) + printk(KERN_INFO "X-: %d, Y-: %d, Z-: %d\n", xm, ym, zm); +#endif + + mrstouch_report_xy(tsdev, xp, yp, zp); /* report x and y to eventX */ + + return zp; + +ipc_error: + dev_err(&tsdev->spi->dev, "ipc error during adc read\n"); + return err; +} + +/* PENDET interrupt handler function for NEC and MAXIM */ +static void pmic12_pendet(void *data) +{ + unsigned int touched; + struct mrstouch_dev *tsdev = (struct mrstouch_dev *)data; + + /* read touch screen channels till pen removed */ + do { + touched = mrstouch_adc_read(tsdev); + } while (touched > TOUCH_PRESSURE); +} + +/* Handler to process PENDET interrupt */ +int mrstouch_pendet(void *data) +{ + struct mrstouch_dev *tsdev = (struct mrstouch_dev *)data; + while (1) { + /* Wait for PENDET interrupt */ + if (mutex_lock_interruptible(&tsdev->lock)) { + msleep(WAIT_ADC_COMPLETION); + continue; + } + + if (tsdev->busy) + return 0; + + tsdev->busy = true; + + if (tsdev->vendor == PMIC_VENDOR_NEC || + tsdev->vendor == PMIC_VENDOR_MAXIM) { + /* PENDET must be disabled in NEC before reading ADC */ + pendet_enable(tsdev,false); /* Disbale PENDET */ + pmic12_pendet(tsdev); + pendet_enable(tsdev, true); /*Enable PENDET */ + } else if (tsdev->vendor == PMIC_VENDOR_FS) { + pendet_umask(); /* Stop ADC */ + pmic0_pendet(tsdev); + pendet_mask(); /* Stop ADC */ + } else + dev_err(&tsdev->spi->dev, "Unsupported touchscreen: %d\n", + tsdev->vendor); + + tsdev->busy = false; + + } + return 0; +} + +/* PENDET interrupt handler */ +static irqreturn_t pendet_intr_handler(int irq, void *handle) +{ + struct mrstouch_dev *tsdev = (struct mrstouch_dev *)handle; + + mutex_unlock(&tsdev->lock); + return IRQ_HANDLED; +} + +/* Intializes input device and registers with input subsystem */ +static int ts_input_dev_init(struct mrstouch_dev *tsdev, struct spi_device *spi) +{ + int err = 0; + + mrstouch_debug("%s", __func__); + + tsdev->input = input_allocate_device(); + if (!tsdev->input) { + dev_err(&tsdev->spi->dev, "Unable to allocate input device.\n"); + return -EINVAL; + } + + tsdev->input->name = "mrst_touchscreen"; + snprintf(tsdev->phys, sizeof(tsdev->phys), + "%s/input0", dev_name(&spi->dev)); + tsdev->input->phys = tsdev->phys; + tsdev->input->dev.parent = &spi->dev; + + tsdev->input->id.vendor = tsdev->vendor; + tsdev->input->id.version = tsdev->rev; + + tsdev->input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + tsdev->input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_abs_params(tsdev->input, ABS_X, MIN_X, MIN_Y, 0, 0); + input_set_abs_params(tsdev->input, ABS_Y, MIN_X, MIN_Y, 0, 0); + + err = input_register_device(tsdev->input); + if (err) { + dev_err(&tsdev->spi->dev, "unable to register input device\n"); + input_free_device(tsdev->input); + return err; + } + + mrstouch_debug("%s", "mrstouch initialized"); + + return 0; + +} + +/* Probe function for touch screen driver */ +static int __devinit mrstouch_probe(struct spi_device *mrstouch_spi) +{ + int err; + unsigned int myirq; + struct mrstouch_dev *tsdev; + + mrstouch_debug("%s(%p)", __func__, mrstouch_spi); + + mrstouchdevp = NULL; + myirq = mrstouch_spi->irq; + + if (!mrstouch_spi->irq) { + dev_err(&mrstouch_spi->dev, "no interrupt assigned\n"); + return -EINVAL; + } + + tsdev = kzalloc(sizeof(struct mrstouch_dev), GFP_KERNEL); + if (!tsdev) { + dev_err(&mrstouch_spi->dev, "unable to allocate memory\n"); + return -ENOMEM; + } + + tsdev->irq = myirq; + mrstouchdevp = tsdev; + + err = mrstouch_adc_init(tsdev); + if (err) { + dev_err(&mrstouch_spi->dev, "ADC init failed\n"); + goto mrstouch_err_free_mem; + } + + dev_set_drvdata(&mrstouch_spi->dev, tsdev); + tsdev->spi = mrstouch_spi; + + err = ts_input_dev_init(tsdev, mrstouch_spi); + if (err) { + dev_err(&tsdev->spi->dev, "ts_input_dev_init failed"); + goto mrstouch_err_free_mem; + } + + mutex_init(&tsdev->lock); + mutex_lock(&tsdev->lock) + + mrstouch_debug("Requesting IRQ-%d", myirq); + err = request_irq(myirq, pendet_intr_handler, + 0, "mrstouch", tsdev); + if (err) { + dev_err(&tsdev->spi->dev, "unable to allocate irq\n"); + goto mrstouch_err_free_mem; + } + + tsdev->pendet_thrd = kthread_run(mrstouch_pendet, + (void *)tsdev, "pendet handler"); + if (IS_ERR(tsdev->pendet_thrd)) { + dev_err(&tsdev->spi->dev, "kthread_run failed\n"); + err = PTR_ERR(tsdev->pendet_thrd); + goto mrstouch_err_free_mem; + } + mrstouch_debug("%s", "Driver initialized"); + return 0; + +mrstouch_err_free_mem: + kfree(tsdev); + return err; +} + +static int mrstouch_suspend(struct spi_device *spi, pm_message_t msg) +{ + mrstouch_debug("%s", __func__); + mrstouchdevp->suspended = 1; + return 0; +} + +static int mrstouch_resume(struct spi_device *spi) +{ + mrstouch_debug("%s", __func__); + mrstouchdevp->suspended = 0; + return 0; +} + +static int mrstouch_remove(struct spi_device *spi) +{ + mrstouch_debug("%s", __func__); + free_irq(mrstouchdevp->irq, mrstouchdevp); + input_unregister_device(mrstouchdevp->input); + input_free_device(mrstouchdevp->input); + if (mrstouchdevp->pendet_thrd) + kthread_stop(mrstouchdevp->pendet_thrd); + kfree(mrstouchdevp); + return 0; +} + +static struct spi_driver mrstouch_driver = { + .driver = { + .name = "pmic_touch", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = mrstouch_probe, + .suspend = mrstouch_suspend, + .resume = mrstouch_resume, + .remove = mrstouch_remove, +}; + +static int __init mrstouch_module_init(void) +{ + int err; + + mrstouch_debug("%s", __func__); + err = spi_register_driver(&mrstouch_driver); + if (err) { + mrstouch_debug("%s(%d)", "SPI PENDET failed", err); + return -1; + } + + return 0; +} + +static void __exit mrstouch_module_exit(void) +{ + mrstouch_debug("%s", __func__); + spi_unregister_driver(&mrstouch_driver); + return; +} + +module_init(mrstouch_module_init); +module_exit(mrstouch_module_exit); + +MODULE_AUTHOR("Sreedhara Murthy. D.S, sreedhara.ds@intel.com"); +MODULE_DESCRIPTION("Intel Moorestown Resistive Touch Screen Driver"); +MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/staging/xgifb/TODO b/trunk/drivers/staging/xgifb/TODO index c85ff5e9e700..7d71019b84c2 100644 --- a/trunk/drivers/staging/xgifb/TODO +++ b/trunk/drivers/staging/xgifb/TODO @@ -12,4 +12,4 @@ TODO: - get rid of non-linux related stuff Please send patches to: -Arnaud Patard +Arnaud Patard diff --git a/trunk/drivers/usb/host/r8a66597-hcd.c b/trunk/drivers/usb/host/r8a66597-hcd.c index 3076b1cc05df..77be3c24a427 100644 --- a/trunk/drivers/usb/host/r8a66597-hcd.c +++ b/trunk/drivers/usb/host/r8a66597-hcd.c @@ -2397,7 +2397,7 @@ static const struct dev_pm_ops r8a66597_dev_pm_ops = { #define R8A66597_DEV_PM_OPS NULL #endif -static int __devexit r8a66597_remove(struct platform_device *pdev) +static int __init_or_module r8a66597_remove(struct platform_device *pdev) { struct r8a66597 *r8a66597 = dev_get_drvdata(&pdev->dev); struct usb_hcd *hcd = r8a66597_to_hcd(r8a66597); @@ -2542,7 +2542,7 @@ static int __devinit r8a66597_probe(struct platform_device *pdev) static struct platform_driver r8a66597_driver = { .probe = r8a66597_probe, - .remove = __devexit_p(r8a66597_remove), + .remove = r8a66597_remove, .driver = { .name = (char *) hcd_name, .owner = THIS_MODULE, diff --git a/trunk/drivers/uwb/Kconfig b/trunk/drivers/uwb/Kconfig index d100f54ed650..bac8e7a6f17b 100644 --- a/trunk/drivers/uwb/Kconfig +++ b/trunk/drivers/uwb/Kconfig @@ -12,7 +12,8 @@ menuconfig UWB technology using a wide spectrum (3.1-10.6GHz). It is optimized for in-room use (480Mbps at 2 meters, 110Mbps at 10m). It serves as the transport layer for other protocols, - such as Wireless USB (WUSB). + such as Wireless USB (WUSB), IP (WLP) and upcoming + Bluetooth and 1394 The topology is peer to peer; however, higher level protocols (such as WUSB) might impose a master/slave @@ -57,6 +58,13 @@ config UWB_WHCI To compile this driver select Y (built in) or M (module). It is safe to select any even if you do not have the hardware. +config UWB_WLP + tristate "Support WiMedia Link Protocol (Ethernet/IP over UWB)" + depends on UWB && NET + help + This is a common library for drivers that implement + networking over UWB. + config UWB_I1480U tristate "Support for Intel Wireless UWB Link 1480 HWA" depends on UWB_HWA @@ -69,4 +77,14 @@ config UWB_I1480U To compile this driver select Y (built in) or M (module). It is safe to select any even if you do not have the hardware. +config UWB_I1480U_WLP + tristate "Support for Intel Wireless UWB Link 1480 HWA's WLP interface" + depends on UWB_I1480U && UWB_WLP && NET + help + This driver enables WLP support for the i1480 when connected via + USB. WLP is the WiMedia Link Protocol, or IP over UWB. + + To compile this driver select Y (built in) or M (module). It + is safe to select any even if you don't have the hardware. + endif # UWB diff --git a/trunk/drivers/uwb/Makefile b/trunk/drivers/uwb/Makefile index d47dd6e2942c..2f98d080fe78 100644 --- a/trunk/drivers/uwb/Makefile +++ b/trunk/drivers/uwb/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_UWB) += uwb.o +obj-$(CONFIG_UWB_WLP) += wlp/ obj-$(CONFIG_UWB_WHCI) += umc.o whci.o whc-rc.o obj-$(CONFIG_UWB_HWA) += hwa-rc.o obj-$(CONFIG_UWB_I1480U) += i1480/ diff --git a/trunk/drivers/uwb/i1480/Makefile b/trunk/drivers/uwb/i1480/Makefile index d69da1684cfb..212bbc7d4c32 100644 --- a/trunk/drivers/uwb/i1480/Makefile +++ b/trunk/drivers/uwb/i1480/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_UWB_I1480U) += dfu/ i1480-est.o +obj-$(CONFIG_UWB_I1480U_WLP) += i1480u-wlp/ diff --git a/trunk/drivers/uwb/i1480/i1480-wlp.h b/trunk/drivers/uwb/i1480/i1480-wlp.h new file mode 100644 index 000000000000..18a8b0e4567b --- /dev/null +++ b/trunk/drivers/uwb/i1480/i1480-wlp.h @@ -0,0 +1,200 @@ +/* + * Intel 1480 Wireless UWB Link + * WLP specific definitions + * + * + * Copyright (C) 2005-2006 Intel Corporation + * Inaky Perez-Gonzalez + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * FIXME: docs + */ + +#ifndef __i1480_wlp_h__ +#define __i1480_wlp_h__ + +#include +#include +#include +#include +#include + +/* New simplified header format? */ +#undef WLP_HDR_FMT_2 /* FIXME: rename */ + +/** + * Values of the Delivery ID & Type field when PCA or DRP + * + * The Delivery ID & Type field in the WLP TX header indicates whether + * the frame is PCA or DRP. This is done based on the high level bit of + * this field. + * We use this constant to test if the traffic is PCA or DRP as follows: + * if (wlp_tx_hdr_delivery_id_type(wlp_tx_hdr) & WLP_DRP) + * this is DRP traffic + * else + * this is PCA traffic + */ +enum deliver_id_type_bit { + WLP_DRP = 8, +}; + +/** + * WLP TX header + * + * Indicates UWB/WLP-specific transmission parameters for a network + * packet. + */ +struct wlp_tx_hdr { + /* dword 0 */ + struct uwb_dev_addr dstaddr; + u8 key_index; + u8 mac_params; + /* dword 1 */ + u8 phy_params; +#ifndef WLP_HDR_FMT_2 + u8 reserved; + __le16 oui01; /* FIXME: not so sure if __le16 or u8[2] */ + /* dword 2 */ + u8 oui2; /* if all LE, it could be merged */ + __le16 prid; +#endif +} __attribute__((packed)); + +static inline int wlp_tx_hdr_delivery_id_type(const struct wlp_tx_hdr *hdr) +{ + return hdr->mac_params & 0x0f; +} + +static inline int wlp_tx_hdr_ack_policy(const struct wlp_tx_hdr *hdr) +{ + return (hdr->mac_params >> 4) & 0x07; +} + +static inline int wlp_tx_hdr_rts_cts(const struct wlp_tx_hdr *hdr) +{ + return (hdr->mac_params >> 7) & 0x01; +} + +static inline void wlp_tx_hdr_set_delivery_id_type(struct wlp_tx_hdr *hdr, int id) +{ + hdr->mac_params = (hdr->mac_params & ~0x0f) | id; +} + +static inline void wlp_tx_hdr_set_ack_policy(struct wlp_tx_hdr *hdr, + enum uwb_ack_pol policy) +{ + hdr->mac_params = (hdr->mac_params & ~0x70) | (policy << 4); +} + +static inline void wlp_tx_hdr_set_rts_cts(struct wlp_tx_hdr *hdr, int rts_cts) +{ + hdr->mac_params = (hdr->mac_params & ~0x80) | (rts_cts << 7); +} + +static inline enum uwb_phy_rate wlp_tx_hdr_phy_rate(const struct wlp_tx_hdr *hdr) +{ + return hdr->phy_params & 0x0f; +} + +static inline int wlp_tx_hdr_tx_power(const struct wlp_tx_hdr *hdr) +{ + return (hdr->phy_params >> 4) & 0x0f; +} + +static inline void wlp_tx_hdr_set_phy_rate(struct wlp_tx_hdr *hdr, enum uwb_phy_rate rate) +{ + hdr->phy_params = (hdr->phy_params & ~0x0f) | rate; +} + +static inline void wlp_tx_hdr_set_tx_power(struct wlp_tx_hdr *hdr, int pwr) +{ + hdr->phy_params = (hdr->phy_params & ~0xf0) | (pwr << 4); +} + + +/** + * WLP RX header + * + * Provides UWB/WLP-specific transmission data for a received + * network packet. + */ +struct wlp_rx_hdr { + /* dword 0 */ + struct uwb_dev_addr dstaddr; + struct uwb_dev_addr srcaddr; + /* dword 1 */ + u8 LQI; + s8 RSSI; + u8 reserved3; +#ifndef WLP_HDR_FMT_2 + u8 oui0; + /* dword 2 */ + __le16 oui12; + __le16 prid; +#endif +} __attribute__((packed)); + + +/** User configurable options for WLP */ +struct wlp_options { + struct mutex mutex; /* access to user configurable options*/ + struct wlp_tx_hdr def_tx_hdr; /* default tx hdr */ + u8 pca_base_priority; + u8 bw_alloc; /*index into bw_allocs[] for PCA/DRP reservations*/ +}; + + +static inline +void wlp_options_init(struct wlp_options *options) +{ + mutex_init(&options->mutex); + wlp_tx_hdr_set_ack_policy(&options->def_tx_hdr, UWB_ACK_INM); + wlp_tx_hdr_set_rts_cts(&options->def_tx_hdr, 1); + /* FIXME: default to phy caps */ + wlp_tx_hdr_set_phy_rate(&options->def_tx_hdr, UWB_PHY_RATE_480); +#ifndef WLP_HDR_FMT_2 + options->def_tx_hdr.prid = cpu_to_le16(0x0000); +#endif +} + + +/* sysfs helpers */ + +extern ssize_t uwb_pca_base_priority_store(struct wlp_options *, + const char *, size_t); +extern ssize_t uwb_pca_base_priority_show(const struct wlp_options *, char *); +extern ssize_t uwb_bw_alloc_store(struct wlp_options *, const char *, size_t); +extern ssize_t uwb_bw_alloc_show(const struct wlp_options *, char *); +extern ssize_t uwb_ack_policy_store(struct wlp_options *, + const char *, size_t); +extern ssize_t uwb_ack_policy_show(const struct wlp_options *, char *); +extern ssize_t uwb_rts_cts_store(struct wlp_options *, const char *, size_t); +extern ssize_t uwb_rts_cts_show(const struct wlp_options *, char *); +extern ssize_t uwb_phy_rate_store(struct wlp_options *, const char *, size_t); +extern ssize_t uwb_phy_rate_show(const struct wlp_options *, char *); + + +/** Simple bandwidth allocation (temporary and too simple) */ +struct wlp_bw_allocs { + const char *name; + struct { + u8 mask, stream; + } tx, rx; +}; + + +#endif /* #ifndef __i1480_wlp_h__ */ diff --git a/trunk/drivers/uwb/i1480/i1480u-wlp/Makefile b/trunk/drivers/uwb/i1480/i1480u-wlp/Makefile new file mode 100644 index 000000000000..fe6709b8e68b --- /dev/null +++ b/trunk/drivers/uwb/i1480/i1480u-wlp/Makefile @@ -0,0 +1,8 @@ +obj-$(CONFIG_UWB_I1480U_WLP) += i1480u-wlp.o + +i1480u-wlp-objs := \ + lc.o \ + netdev.o \ + rx.o \ + sysfs.o \ + tx.o diff --git a/trunk/drivers/uwb/i1480/i1480u-wlp/i1480u-wlp.h b/trunk/drivers/uwb/i1480/i1480u-wlp/i1480u-wlp.h new file mode 100644 index 000000000000..2e31f536a347 --- /dev/null +++ b/trunk/drivers/uwb/i1480/i1480u-wlp/i1480u-wlp.h @@ -0,0 +1,283 @@ +/* + * Intel 1480 Wireless UWB Link USB + * Header formats, constants, general internal interfaces + * + * + * Copyright (C) 2005-2006 Intel Corporation + * Inaky Perez-Gonzalez + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * This is not an standard interface. + * + * FIXME: docs + * + * i1480u-wlp is pretty simple: two endpoints, one for tx, one for + * rx. rx is polled. Network packets (ethernet, whatever) are wrapped + * in i1480 TX or RX headers (for sending over the air), and these + * packets are wrapped in UNTD headers (for sending to the WLP UWB + * controller). + * + * UNTD packets (UNTD hdr + i1480 hdr + network packet) packets + * cannot be bigger than i1480u_MAX_FRG_SIZE. When this happens, the + * i1480 packet is broken in chunks/packets: + * + * UNTD-1st.hdr + i1480.hdr + payload + * UNTD-next.hdr + payload + * ... + * UNTD-last.hdr + payload + * + * so that each packet is smaller or equal than i1480u_MAX_FRG_SIZE. + * + * All HW structures and bitmaps are little endian, so we need to play + * ugly tricks when defining bitfields. Hoping for the day GCC + * implements __attribute__((endian(1234))). + * + * FIXME: ROADMAP to the whole implementation + */ + +#ifndef __i1480u_wlp_h__ +#define __i1480u_wlp_h__ + +#include +#include +#include /* struct uwb_rc, struct uwb_notifs_handler */ +#include +#include "../i1480-wlp.h" + +#undef i1480u_FLOW_CONTROL /* Enable flow control code */ + +/** + * Basic flow control + */ +enum { + i1480u_TX_INFLIGHT_MAX = 1000, + i1480u_TX_INFLIGHT_THRESHOLD = 100, +}; + +/** Maximum size of a transaction that we can tx/rx */ +enum { + /* Maximum packet size computed as follows: max UNTD header (8) + + * i1480 RX header (8) + max Ethernet header and payload (4096) + + * Padding added by skb_reserve (2) to make post Ethernet payload + * start on 16 byte boundary*/ + i1480u_MAX_RX_PKT_SIZE = 4114, + i1480u_MAX_FRG_SIZE = 512, + i1480u_RX_BUFS = 9, +}; + + +/** + * UNTD packet type + * + * We need to fragment any payload whose UNTD packet is going to be + * bigger than i1480u_MAX_FRG_SIZE. + */ +enum i1480u_pkt_type { + i1480u_PKT_FRAG_1ST = 0x1, + i1480u_PKT_FRAG_NXT = 0x0, + i1480u_PKT_FRAG_LST = 0x2, + i1480u_PKT_FRAG_CMP = 0x3 +}; +enum { + i1480u_PKT_NONE = 0x4, +}; + +/** USB Network Transfer Descriptor - common */ +struct untd_hdr { + u8 type; + __le16 len; +} __attribute__((packed)); + +static inline enum i1480u_pkt_type untd_hdr_type(const struct untd_hdr *hdr) +{ + return hdr->type & 0x03; +} + +static inline int untd_hdr_rx_tx(const struct untd_hdr *hdr) +{ + return (hdr->type >> 2) & 0x01; +} + +static inline void untd_hdr_set_type(struct untd_hdr *hdr, enum i1480u_pkt_type type) +{ + hdr->type = (hdr->type & ~0x03) | type; +} + +static inline void untd_hdr_set_rx_tx(struct untd_hdr *hdr, int rx_tx) +{ + hdr->type = (hdr->type & ~0x04) | (rx_tx << 2); +} + + +/** + * USB Network Transfer Descriptor - Complete Packet + * + * This is for a packet that is smaller (header + payload) than + * i1480u_MAX_FRG_SIZE. + * + * @hdr.total_len is the size of the payload; the payload doesn't + * count this header nor the padding, but includes the size of i1480 + * header. + */ +struct untd_hdr_cmp { + struct untd_hdr hdr; + u8 padding; +} __attribute__((packed)); + + +/** + * USB Network Transfer Descriptor - First fragment + * + * @hdr.len is the size of the *whole packet* (excluding UNTD + * headers); @fragment_len is the size of the payload (excluding UNTD + * headers, but including i1480 headers). + */ +struct untd_hdr_1st { + struct untd_hdr hdr; + __le16 fragment_len; + u8 padding[3]; +} __attribute__((packed)); + + +/** + * USB Network Transfer Descriptor - Next / Last [Rest] + * + * @hdr.len is the size of the payload, not including headrs. + */ +struct untd_hdr_rst { + struct untd_hdr hdr; + u8 padding; +} __attribute__((packed)); + + +/** + * Transmission context + * + * Wraps all the stuff needed to track a pending/active tx + * operation. + */ +struct i1480u_tx { + struct list_head list_node; + struct i1480u *i1480u; + struct urb *urb; + + struct sk_buff *skb; + struct wlp_tx_hdr *wlp_tx_hdr; + + void *buf; /* if NULL, no new buf was used */ + size_t buf_size; +}; + +/** + * Basic flow control + * + * We maintain a basic flow control counter. "count" how many TX URBs are + * outstanding. Only allow "max" + * TX URBs to be outstanding. If this value is reached the queue will be + * stopped. The queue will be restarted when there are + * "threshold" URBs outstanding. + * Maintain a counter of how many time the TX queue needed to be restarted + * due to the "max" being exceeded and the "threshold" reached again. The + * timestamp "restart_ts" is to keep track from when the counter was last + * queried (see sysfs handling of file wlp_tx_inflight). + */ +struct i1480u_tx_inflight { + atomic_t count; + unsigned long max; + unsigned long threshold; + unsigned long restart_ts; + atomic_t restart_count; +}; + +/** + * Instance of a i1480u WLP interface + * + * Keeps references to the USB device that wraps it, as well as it's + * interface and associated UWB host controller. As well, it also + * keeps a link to the netdevice for integration into the networking + * stack. + * We maintian separate error history for the tx and rx endpoints because + * the implementation does not rely on locking - having one shared + * structure between endpoints may cause problems. Adding locking to the + * implementation will have higher cost than adding a separate structure. + */ +struct i1480u { + struct usb_device *usb_dev; + struct usb_interface *usb_iface; + struct net_device *net_dev; + + spinlock_t lock; + + /* RX context handling */ + struct sk_buff *rx_skb; + struct uwb_dev_addr rx_srcaddr; + size_t rx_untd_pkt_size; + struct i1480u_rx_buf { + struct i1480u *i1480u; /* back pointer */ + struct urb *urb; + struct sk_buff *data; /* i1480u_MAX_RX_PKT_SIZE each */ + } rx_buf[i1480u_RX_BUFS]; /* N bufs */ + + spinlock_t tx_list_lock; /* TX context */ + struct list_head tx_list; + u8 tx_stream; + + struct stats lqe_stats, rssi_stats; /* radio statistics */ + + /* Options we can set from sysfs */ + struct wlp_options options; + struct uwb_notifs_handler uwb_notifs_handler; + struct edc tx_errors; + struct edc rx_errors; + struct wlp wlp; +#ifdef i1480u_FLOW_CONTROL + struct urb *notif_urb; + struct edc notif_edc; /* error density counter */ + u8 notif_buffer[1]; +#endif + struct i1480u_tx_inflight tx_inflight; +}; + +/* Internal interfaces */ +extern void i1480u_rx_cb(struct urb *urb); +extern int i1480u_rx_setup(struct i1480u *); +extern void i1480u_rx_release(struct i1480u *); +extern void i1480u_tx_release(struct i1480u *); +extern int i1480u_xmit_frame(struct wlp *, struct sk_buff *, + struct uwb_dev_addr *); +extern void i1480u_stop_queue(struct wlp *); +extern void i1480u_start_queue(struct wlp *); +extern int i1480u_sysfs_setup(struct i1480u *); +extern void i1480u_sysfs_release(struct i1480u *); + +/* netdev interface */ +extern int i1480u_open(struct net_device *); +extern int i1480u_stop(struct net_device *); +extern netdev_tx_t i1480u_hard_start_xmit(struct sk_buff *, + struct net_device *); +extern void i1480u_tx_timeout(struct net_device *); +extern int i1480u_set_config(struct net_device *, struct ifmap *); +extern int i1480u_change_mtu(struct net_device *, int); +extern void i1480u_uwb_notifs_cb(void *, struct uwb_dev *, enum uwb_notifs); + +/* bandwidth allocation callback */ +extern void i1480u_bw_alloc_cb(struct uwb_rsv *); + +/* Sys FS */ +extern struct attribute_group i1480u_wlp_attr_group; + +#endif /* #ifndef __i1480u_wlp_h__ */ diff --git a/trunk/drivers/uwb/i1480/i1480u-wlp/lc.c b/trunk/drivers/uwb/i1480/i1480u-wlp/lc.c new file mode 100644 index 000000000000..def778cf2216 --- /dev/null +++ b/trunk/drivers/uwb/i1480/i1480u-wlp/lc.c @@ -0,0 +1,424 @@ +/* + * WUSB Wire Adapter: WLP interface + * Driver for the Linux Network stack. + * + * Copyright (C) 2005-2006 Intel Corporation + * Inaky Perez-Gonzalez + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * FIXME: docs + * + * This implements a very simple network driver for the WLP USB + * device that is associated to a UWB (Ultra Wide Band) host. + * + * This is seen as an interface of a composite device. Once the UWB + * host has an association to another WLP capable device, the + * networking interface (aka WLP) can start to send packets back and + * forth. + * + * Limitations: + * + * - Hand cranked; can't ifup the interface until there is an association + * + * - BW allocation very simplistic [see i1480u_mas_set() and callees]. + * + * + * ROADMAP: + * + * ENTRY POINTS (driver model): + * + * i1480u_driver_{exit,init}(): initialization of the driver. + * + * i1480u_probe(): called by the driver code when a device + * matching 'i1480u_id_table' is connected. + * + * This allocs a netdev instance, inits with + * i1480u_add(), then registers_netdev(). + * i1480u_init() + * i1480u_add() + * + * i1480u_disconnect(): device has been disconnected/module + * is being removed. + * i1480u_rm() + */ +#include +#include +#include + +#include "i1480u-wlp.h" + + + +static inline +void i1480u_init(struct i1480u *i1480u) +{ + /* nothing so far... doesn't it suck? */ + spin_lock_init(&i1480u->lock); + INIT_LIST_HEAD(&i1480u->tx_list); + spin_lock_init(&i1480u->tx_list_lock); + wlp_options_init(&i1480u->options); + edc_init(&i1480u->tx_errors); + edc_init(&i1480u->rx_errors); +#ifdef i1480u_FLOW_CONTROL + edc_init(&i1480u->notif_edc); +#endif + stats_init(&i1480u->lqe_stats); + stats_init(&i1480u->rssi_stats); + wlp_init(&i1480u->wlp); +} + +/** + * Fill WLP device information structure + * + * The structure will contain a few character arrays, each ending with a + * null terminated string. Each string has to fit (excluding terminating + * character) into a specified range obtained from the WLP substack. + * + * It is still not clear exactly how this device information should be + * obtained. Until we find out we use the USB device descriptor as backup, some + * information elements have intuitive mappings, other not. + */ +static +void i1480u_fill_device_info(struct wlp *wlp, struct wlp_device_info *dev_info) +{ + struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp); + struct usb_device *usb_dev = i1480u->usb_dev; + /* Treat device name and model name the same */ + if (usb_dev->descriptor.iProduct) { + usb_string(usb_dev, usb_dev->descriptor.iProduct, + dev_info->name, sizeof(dev_info->name)); + usb_string(usb_dev, usb_dev->descriptor.iProduct, + dev_info->model_name, sizeof(dev_info->model_name)); + } + if (usb_dev->descriptor.iManufacturer) + usb_string(usb_dev, usb_dev->descriptor.iManufacturer, + dev_info->manufacturer, + sizeof(dev_info->manufacturer)); + scnprintf(dev_info->model_nr, sizeof(dev_info->model_nr), "%04x", + __le16_to_cpu(usb_dev->descriptor.bcdDevice)); + if (usb_dev->descriptor.iSerialNumber) + usb_string(usb_dev, usb_dev->descriptor.iSerialNumber, + dev_info->serial, sizeof(dev_info->serial)); + /* FIXME: where should we obtain category? */ + dev_info->prim_dev_type.category = cpu_to_le16(WLP_DEV_CAT_OTHER); + /* FIXME: Complete OUI and OUIsubdiv attributes */ +} + +#ifdef i1480u_FLOW_CONTROL +/** + * Callback for the notification endpoint + * + * This mostly controls the xon/xoff protocol. In case of hard error, + * we stop the queue. If not, we always retry. + */ +static +void i1480u_notif_cb(struct urb *urb, struct pt_regs *regs) +{ + struct i1480u *i1480u = urb->context; + struct usb_interface *usb_iface = i1480u->usb_iface; + struct device *dev = &usb_iface->dev; + int result; + + switch (urb->status) { + case 0: /* Got valid data, do xon/xoff */ + switch (i1480u->notif_buffer[0]) { + case 'N': + dev_err(dev, "XOFF STOPPING queue at %lu\n", jiffies); + netif_stop_queue(i1480u->net_dev); + break; + case 'A': + dev_err(dev, "XON STARTING queue at %lu\n", jiffies); + netif_start_queue(i1480u->net_dev); + break; + default: + dev_err(dev, "NEP: unknown data 0x%02hhx\n", + i1480u->notif_buffer[0]); + } + break; + case -ECONNRESET: /* Controlled situation ... */ + case -ENOENT: /* we killed the URB... */ + dev_err(dev, "NEP: URB reset/noent %d\n", urb->status); + goto error; + case -ESHUTDOWN: /* going away! */ + dev_err(dev, "NEP: URB down %d\n", urb->status); + goto error; + default: /* Retry unless it gets ugly */ + if (edc_inc(&i1480u->notif_edc, EDC_MAX_ERRORS, + EDC_ERROR_TIMEFRAME)) { + dev_err(dev, "NEP: URB max acceptable errors " + "exceeded; resetting device\n"); + goto error_reset; + } + dev_err(dev, "NEP: URB error %d\n", urb->status); + break; + } + result = usb_submit_urb(urb, GFP_ATOMIC); + if (result < 0) { + dev_err(dev, "NEP: Can't resubmit URB: %d; resetting device\n", + result); + goto error_reset; + } + return; + +error_reset: + wlp_reset_all(&i1480-wlp); +error: + netif_stop_queue(i1480u->net_dev); + return; +} +#endif + +static const struct net_device_ops i1480u_netdev_ops = { + .ndo_open = i1480u_open, + .ndo_stop = i1480u_stop, + .ndo_start_xmit = i1480u_hard_start_xmit, + .ndo_tx_timeout = i1480u_tx_timeout, + .ndo_set_config = i1480u_set_config, + .ndo_change_mtu = i1480u_change_mtu, +}; + +static +int i1480u_add(struct i1480u *i1480u, struct usb_interface *iface) +{ + int result = -ENODEV; + struct wlp *wlp = &i1480u->wlp; + struct usb_device *usb_dev = interface_to_usbdev(iface); + struct net_device *net_dev = i1480u->net_dev; + struct uwb_rc *rc; + struct uwb_dev *uwb_dev; +#ifdef i1480u_FLOW_CONTROL + struct usb_endpoint_descriptor *epd; +#endif + + i1480u->usb_dev = usb_get_dev(usb_dev); + i1480u->usb_iface = iface; + rc = uwb_rc_get_by_grandpa(&i1480u->usb_dev->dev); + if (rc == NULL) { + dev_err(&iface->dev, "Cannot get associated UWB Radio " + "Controller\n"); + goto out; + } + wlp->xmit_frame = i1480u_xmit_frame; + wlp->fill_device_info = i1480u_fill_device_info; + wlp->stop_queue = i1480u_stop_queue; + wlp->start_queue = i1480u_start_queue; + result = wlp_setup(wlp, rc, net_dev); + if (result < 0) { + dev_err(&iface->dev, "Cannot setup WLP\n"); + goto error_wlp_setup; + } + result = 0; + ether_setup(net_dev); /* make it an etherdevice */ + uwb_dev = &rc->uwb_dev; + /* FIXME: hookup address change notifications? */ + + memcpy(net_dev->dev_addr, uwb_dev->mac_addr.data, + sizeof(net_dev->dev_addr)); + + net_dev->hard_header_len = sizeof(struct untd_hdr_cmp) + + sizeof(struct wlp_tx_hdr) + + WLP_DATA_HLEN + + ETH_HLEN; + net_dev->mtu = 3500; + net_dev->tx_queue_len = 20; /* FIXME: maybe use 1000? */ + +/* net_dev->flags &= ~IFF_BROADCAST; FIXME: BUG in firmware */ + /* FIXME: multicast disabled */ + net_dev->flags &= ~IFF_MULTICAST; + net_dev->features &= ~NETIF_F_SG; + net_dev->features &= ~NETIF_F_FRAGLIST; + /* All NETIF_F_*_CSUM disabled */ + net_dev->features |= NETIF_F_HIGHDMA; + net_dev->watchdog_timeo = 5*HZ; /* FIXME: a better default? */ + + net_dev->netdev_ops = &i1480u_netdev_ops; + +#ifdef i1480u_FLOW_CONTROL + /* Notification endpoint setup (submitted when we open the device) */ + i1480u->notif_urb = usb_alloc_urb(0, GFP_KERNEL); + if (i1480u->notif_urb == NULL) { + dev_err(&iface->dev, "Unable to allocate notification URB\n"); + result = -ENOMEM; + goto error_urb_alloc; + } + epd = &iface->cur_altsetting->endpoint[0].desc; + usb_fill_int_urb(i1480u->notif_urb, usb_dev, + usb_rcvintpipe(usb_dev, epd->bEndpointAddress), + i1480u->notif_buffer, sizeof(i1480u->notif_buffer), + i1480u_notif_cb, i1480u, epd->bInterval); + +#endif + + i1480u->tx_inflight.max = i1480u_TX_INFLIGHT_MAX; + i1480u->tx_inflight.threshold = i1480u_TX_INFLIGHT_THRESHOLD; + i1480u->tx_inflight.restart_ts = jiffies; + usb_set_intfdata(iface, i1480u); + return result; + +#ifdef i1480u_FLOW_CONTROL +error_urb_alloc: +#endif + wlp_remove(wlp); +error_wlp_setup: + uwb_rc_put(rc); +out: + usb_put_dev(i1480u->usb_dev); + return result; +} + +static void i1480u_rm(struct i1480u *i1480u) +{ + struct uwb_rc *rc = i1480u->wlp.rc; + usb_set_intfdata(i1480u->usb_iface, NULL); +#ifdef i1480u_FLOW_CONTROL + usb_kill_urb(i1480u->notif_urb); + usb_free_urb(i1480u->notif_urb); +#endif + wlp_remove(&i1480u->wlp); + uwb_rc_put(rc); + usb_put_dev(i1480u->usb_dev); +} + +/** Just setup @net_dev's i1480u private data */ +static void i1480u_netdev_setup(struct net_device *net_dev) +{ + struct i1480u *i1480u = netdev_priv(net_dev); + /* Initialize @i1480u */ + memset(i1480u, 0, sizeof(*i1480u)); + i1480u_init(i1480u); +} + +/** + * Probe a i1480u interface and register it + * + * @iface: USB interface to link to + * @id: USB class/subclass/protocol id + * @returns: 0 if ok, < 0 errno code on error. + * + * Does basic housekeeping stuff and then allocs a netdev with space + * for the i1480u data. Initializes, registers in i1480u, registers in + * netdev, ready to go. + */ +static int i1480u_probe(struct usb_interface *iface, + const struct usb_device_id *id) +{ + int result; + struct net_device *net_dev; + struct device *dev = &iface->dev; + struct i1480u *i1480u; + + /* Allocate instance [calls i1480u_netdev_setup() on it] */ + result = -ENOMEM; + net_dev = alloc_netdev(sizeof(*i1480u), "wlp%d", i1480u_netdev_setup); + if (net_dev == NULL) { + dev_err(dev, "no memory for network device instance\n"); + goto error_alloc_netdev; + } + SET_NETDEV_DEV(net_dev, dev); + i1480u = netdev_priv(net_dev); + i1480u->net_dev = net_dev; + result = i1480u_add(i1480u, iface); /* Now setup all the wlp stuff */ + if (result < 0) { + dev_err(dev, "cannot add i1480u device: %d\n", result); + goto error_i1480u_add; + } + result = register_netdev(net_dev); /* Okey dokey, bring it up */ + if (result < 0) { + dev_err(dev, "cannot register network device: %d\n", result); + goto error_register_netdev; + } + i1480u_sysfs_setup(i1480u); + if (result < 0) + goto error_sysfs_init; + return 0; + +error_sysfs_init: + unregister_netdev(net_dev); +error_register_netdev: + i1480u_rm(i1480u); +error_i1480u_add: + free_netdev(net_dev); +error_alloc_netdev: + return result; +} + + +/** + * Disconect a i1480u from the system. + * + * i1480u_stop() has been called before, so al the rx and tx contexts + * have been taken down already. Make sure the queue is stopped, + * unregister netdev and i1480u, free and kill. + */ +static void i1480u_disconnect(struct usb_interface *iface) +{ + struct i1480u *i1480u; + struct net_device *net_dev; + + i1480u = usb_get_intfdata(iface); + net_dev = i1480u->net_dev; + netif_stop_queue(net_dev); +#ifdef i1480u_FLOW_CONTROL + usb_kill_urb(i1480u->notif_urb); +#endif + i1480u_sysfs_release(i1480u); + unregister_netdev(net_dev); + i1480u_rm(i1480u); + free_netdev(net_dev); +} + +static struct usb_device_id i1480u_id_table[] = { + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE \ + | USB_DEVICE_ID_MATCH_DEV_INFO \ + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x8086, + .idProduct = 0x0c3b, + .bDeviceClass = 0xef, + .bDeviceSubClass = 0x02, + .bDeviceProtocol = 0x02, + .bInterfaceClass = 0xff, + .bInterfaceSubClass = 0xff, + .bInterfaceProtocol = 0xff, + }, + {}, +}; +MODULE_DEVICE_TABLE(usb, i1480u_id_table); + +static struct usb_driver i1480u_driver = { + .name = KBUILD_MODNAME, + .probe = i1480u_probe, + .disconnect = i1480u_disconnect, + .id_table = i1480u_id_table, +}; + +static int __init i1480u_driver_init(void) +{ + return usb_register(&i1480u_driver); +} +module_init(i1480u_driver_init); + + +static void __exit i1480u_driver_exit(void) +{ + usb_deregister(&i1480u_driver); +} +module_exit(i1480u_driver_exit); + +MODULE_AUTHOR("Inaky Perez-Gonzalez "); +MODULE_DESCRIPTION("i1480 Wireless UWB Link WLP networking for USB"); +MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/uwb/i1480/i1480u-wlp/netdev.c b/trunk/drivers/uwb/i1480/i1480u-wlp/netdev.c new file mode 100644 index 000000000000..f98f6ce8b9e7 --- /dev/null +++ b/trunk/drivers/uwb/i1480/i1480u-wlp/netdev.c @@ -0,0 +1,331 @@ +/* + * WUSB Wire Adapter: WLP interface + * Driver for the Linux Network stack. + * + * Copyright (C) 2005-2006 Intel Corporation + * Inaky Perez-Gonzalez + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * FIXME: docs + * + * Implementation of the netdevice linkage (except tx and rx related stuff). + * + * ROADMAP: + * + * ENTRY POINTS (Net device): + * + * i1480u_open(): Called when we ifconfig up the interface; + * associates to a UWB host controller, reserves + * bandwidth (MAS), sets up RX USB URB and starts + * the queue. + * + * i1480u_stop(): Called when we ifconfig down a interface; + * reverses _open(). + * + * i1480u_set_config(): + */ + +#include +#include +#include + +#include "i1480u-wlp.h" + +struct i1480u_cmd_set_ip_mas { + struct uwb_rccb rccb; + struct uwb_dev_addr addr; + u8 stream; + u8 owner; + u8 type; /* enum uwb_drp_type */ + u8 baMAS[32]; +} __attribute__((packed)); + + +static +int i1480u_set_ip_mas( + struct uwb_rc *rc, + const struct uwb_dev_addr *dstaddr, + u8 stream, u8 owner, u8 type, unsigned long *mas) +{ + + int result; + struct i1480u_cmd_set_ip_mas *cmd; + struct uwb_rc_evt_confirm reply; + + result = -ENOMEM; + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + goto error_kzalloc; + cmd->rccb.bCommandType = 0xfd; + cmd->rccb.wCommand = cpu_to_le16(0x000e); + cmd->addr = *dstaddr; + cmd->stream = stream; + cmd->owner = owner; + cmd->type = type; + if (mas == NULL) + memset(cmd->baMAS, 0x00, sizeof(cmd->baMAS)); + else + memcpy(cmd->baMAS, mas, sizeof(cmd->baMAS)); + reply.rceb.bEventType = 0xfd; + reply.rceb.wEvent = cpu_to_le16(0x000e); + result = uwb_rc_cmd(rc, "SET-IP-MAS", &cmd->rccb, sizeof(*cmd), + &reply.rceb, sizeof(reply)); + if (result < 0) + goto error_cmd; + if (reply.bResultCode != UWB_RC_RES_FAIL) { + dev_err(&rc->uwb_dev.dev, + "SET-IP-MAS: command execution failed: %d\n", + reply.bResultCode); + result = -EIO; + } +error_cmd: + kfree(cmd); +error_kzalloc: + return result; +} + +/* + * Inform a WLP interface of a MAS reservation + * + * @rc is assumed refcnted. + */ +/* FIXME: detect if remote device is WLP capable? */ +static int i1480u_mas_set_dev(struct uwb_dev *uwb_dev, struct uwb_rc *rc, + u8 stream, u8 owner, u8 type, unsigned long *mas) +{ + int result = 0; + struct device *dev = &rc->uwb_dev.dev; + + result = i1480u_set_ip_mas(rc, &uwb_dev->dev_addr, stream, owner, + type, mas); + if (result < 0) { + char rcaddrbuf[UWB_ADDR_STRSIZE], devaddrbuf[UWB_ADDR_STRSIZE]; + uwb_dev_addr_print(rcaddrbuf, sizeof(rcaddrbuf), + &rc->uwb_dev.dev_addr); + uwb_dev_addr_print(devaddrbuf, sizeof(devaddrbuf), + &uwb_dev->dev_addr); + dev_err(dev, "Set IP MAS (%s to %s) failed: %d\n", + rcaddrbuf, devaddrbuf, result); + } + return result; +} + +/** + * Called by bandwidth allocator when change occurs in reservation. + * + * @rsv: The reservation that is being established, modified, or + * terminated. + * + * When a reservation is established, modified, or terminated the upper layer + * (WLP here) needs set/update the currently available Media Access Slots + * that can be use for IP traffic. + * + * Our action taken during failure depends on how the reservation is being + * changed: + * - if reservation is being established we do nothing if we cannot set the + * new MAS to be used + * - if reservation is being terminated we revert back to PCA whether the + * SET IP MAS command succeeds or not. + */ +void i1480u_bw_alloc_cb(struct uwb_rsv *rsv) +{ + int result = 0; + struct i1480u *i1480u = rsv->pal_priv; + struct device *dev = &i1480u->usb_iface->dev; + struct uwb_dev *target_dev = rsv->target.dev; + struct uwb_rc *rc = i1480u->wlp.rc; + u8 stream = rsv->stream; + int type = rsv->type; + int is_owner = rsv->owner == &rc->uwb_dev; + unsigned long *bmp = rsv->mas.bm; + + dev_err(dev, "WLP callback called - sending set ip mas\n"); + /*user cannot change options while setting configuration*/ + mutex_lock(&i1480u->options.mutex); + switch (rsv->state) { + case UWB_RSV_STATE_T_ACCEPTED: + case UWB_RSV_STATE_O_ESTABLISHED: + result = i1480u_mas_set_dev(target_dev, rc, stream, is_owner, + type, bmp); + if (result < 0) { + dev_err(dev, "MAS reservation failed: %d\n", result); + goto out; + } + if (is_owner) { + wlp_tx_hdr_set_delivery_id_type(&i1480u->options.def_tx_hdr, + WLP_DRP | stream); + wlp_tx_hdr_set_rts_cts(&i1480u->options.def_tx_hdr, 0); + } + break; + case UWB_RSV_STATE_NONE: + /* revert back to PCA */ + result = i1480u_mas_set_dev(target_dev, rc, stream, is_owner, + type, bmp); + if (result < 0) + dev_err(dev, "MAS reservation failed: %d\n", result); + /* Revert to PCA even though SET IP MAS failed. */ + wlp_tx_hdr_set_delivery_id_type(&i1480u->options.def_tx_hdr, + i1480u->options.pca_base_priority); + wlp_tx_hdr_set_rts_cts(&i1480u->options.def_tx_hdr, 1); + break; + default: + dev_err(dev, "unexpected WLP reservation state: %s (%d).\n", + uwb_rsv_state_str(rsv->state), rsv->state); + break; + } +out: + mutex_unlock(&i1480u->options.mutex); + return; +} + +/** + * + * Called on 'ifconfig up' + */ +int i1480u_open(struct net_device *net_dev) +{ + int result; + struct i1480u *i1480u = netdev_priv(net_dev); + struct wlp *wlp = &i1480u->wlp; + struct uwb_rc *rc; + struct device *dev = &i1480u->usb_iface->dev; + + rc = wlp->rc; + result = i1480u_rx_setup(i1480u); /* Alloc RX stuff */ + if (result < 0) + goto error_rx_setup; + + result = uwb_radio_start(&wlp->pal); + if (result < 0) + goto error_radio_start; + + netif_wake_queue(net_dev); +#ifdef i1480u_FLOW_CONTROL + result = usb_submit_urb(i1480u->notif_urb, GFP_KERNEL); + if (result < 0) { + dev_err(dev, "Can't submit notification URB: %d\n", result); + goto error_notif_urb_submit; + } +#endif + /* Interface is up with an address, now we can create WSS */ + result = wlp_wss_setup(net_dev, &wlp->wss); + if (result < 0) { + dev_err(dev, "Can't create WSS: %d. \n", result); + goto error_wss_setup; + } + return 0; +error_wss_setup: +#ifdef i1480u_FLOW_CONTROL + usb_kill_urb(i1480u->notif_urb); +error_notif_urb_submit: +#endif + uwb_radio_stop(&wlp->pal); +error_radio_start: + netif_stop_queue(net_dev); + i1480u_rx_release(i1480u); +error_rx_setup: + return result; +} + + +/** + * Called on 'ifconfig down' + */ +int i1480u_stop(struct net_device *net_dev) +{ + struct i1480u *i1480u = netdev_priv(net_dev); + struct wlp *wlp = &i1480u->wlp; + + BUG_ON(wlp->rc == NULL); + wlp_wss_remove(&wlp->wss); + netif_carrier_off(net_dev); +#ifdef i1480u_FLOW_CONTROL + usb_kill_urb(i1480u->notif_urb); +#endif + netif_stop_queue(net_dev); + uwb_radio_stop(&wlp->pal); + i1480u_rx_release(i1480u); + i1480u_tx_release(i1480u); + return 0; +} + +/** + * + * Change the interface config--we probably don't have to do anything. + */ +int i1480u_set_config(struct net_device *net_dev, struct ifmap *map) +{ + int result; + struct i1480u *i1480u = netdev_priv(net_dev); + BUG_ON(i1480u->wlp.rc == NULL); + result = 0; + return result; +} + +/** + * Change the MTU of the interface + */ +int i1480u_change_mtu(struct net_device *net_dev, int mtu) +{ + static union { + struct wlp_tx_hdr tx; + struct wlp_rx_hdr rx; + } i1480u_all_hdrs; + + if (mtu < ETH_HLEN) /* We encap eth frames */ + return -ERANGE; + if (mtu > 4000 - sizeof(i1480u_all_hdrs)) + return -ERANGE; + net_dev->mtu = mtu; + return 0; +} + +/** + * Stop the network queue + * + * Enable WLP substack to stop network queue. We also set the flow control + * threshold at this time to prevent the flow control from restarting the + * queue. + * + * we are loosing the current threshold value here ... FIXME? + */ +void i1480u_stop_queue(struct wlp *wlp) +{ + struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp); + struct net_device *net_dev = i1480u->net_dev; + i1480u->tx_inflight.threshold = 0; + netif_stop_queue(net_dev); +} + +/** + * Start the network queue + * + * Enable WLP substack to start network queue. Also re-enable the flow + * control to manage the queue again. + * + * We re-enable the flow control by storing the default threshold in the + * flow control threshold. This means that if the user modified the + * threshold before the queue was stopped and restarted that information + * will be lost. FIXME? + */ +void i1480u_start_queue(struct wlp *wlp) +{ + struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp); + struct net_device *net_dev = i1480u->net_dev; + i1480u->tx_inflight.threshold = i1480u_TX_INFLIGHT_THRESHOLD; + netif_start_queue(net_dev); +} diff --git a/trunk/drivers/uwb/i1480/i1480u-wlp/rx.c b/trunk/drivers/uwb/i1480/i1480u-wlp/rx.c new file mode 100644 index 000000000000..d4e51e108aa4 --- /dev/null +++ b/trunk/drivers/uwb/i1480/i1480u-wlp/rx.c @@ -0,0 +1,474 @@ +/* + * WUSB Wire Adapter: WLP interface + * Driver for the Linux Network stack. + * + * Copyright (C) 2005-2006 Intel Corporation + * Inaky Perez-Gonzalez + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * i1480u's RX handling is simple. i1480u will send the received + * network packets broken up in fragments; 1 to N fragments make a + * packet, we assemble them together and deliver the packet with netif_rx(). + * + * Beacuse each USB transfer is a *single* fragment (except when the + * transfer contains a first fragment), each URB called thus + * back contains one or two fragments. So we queue N URBs, each with its own + * fragment buffer. When a URB is done, we process it (adding to the + * current skb from the fragment buffer until complete). Once + * processed, we requeue the URB. There is always a bunch of URBs + * ready to take data, so the intergap should be minimal. + * + * An URB's transfer buffer is the data field of a socket buffer. This + * reduces copying as data can be passed directly to network layer. If a + * complete packet or 1st fragment is received the URB's transfer buffer is + * taken away from it and used to send data to the network layer. In this + * case a new transfer buffer is allocated to the URB before being requeued. + * If a "NEXT" or "LAST" fragment is received, the fragment contents is + * appended to the RX packet under construction and the transfer buffer + * is reused. To be able to use this buffer to assemble complete packets + * we set each buffer's size to that of the MAX ethernet packet that can + * be received. There is thus room for improvement in memory usage. + * + * When the max tx fragment size increases, we should be able to read + * data into the skbs directly with very simple code. + * + * ROADMAP: + * + * ENTRY POINTS: + * + * i1480u_rx_setup(): setup RX context [from i1480u_open()] + * + * i1480u_rx_release(): release RX context [from i1480u_stop()] + * + * i1480u_rx_cb(): called when the RX USB URB receives a + * packet. It removes the header and pushes it up + * the Linux netdev stack with netif_rx(). + * + * i1480u_rx_buffer() + * i1480u_drop() and i1480u_fix() + * i1480u_skb_deliver + * + */ + +#include +#include +#include +#include "i1480u-wlp.h" + +/* + * Setup the RX context + * + * Each URB is provided with a transfer_buffer that is the data field + * of a new socket buffer. + */ +int i1480u_rx_setup(struct i1480u *i1480u) +{ + int result, cnt; + struct device *dev = &i1480u->usb_iface->dev; + struct net_device *net_dev = i1480u->net_dev; + struct usb_endpoint_descriptor *epd; + struct sk_buff *skb; + + /* Alloc RX stuff */ + i1480u->rx_skb = NULL; /* not in process of receiving packet */ + result = -ENOMEM; + epd = &i1480u->usb_iface->cur_altsetting->endpoint[1].desc; + for (cnt = 0; cnt < i1480u_RX_BUFS; cnt++) { + struct i1480u_rx_buf *rx_buf = &i1480u->rx_buf[cnt]; + rx_buf->i1480u = i1480u; + skb = dev_alloc_skb(i1480u_MAX_RX_PKT_SIZE); + if (!skb) { + dev_err(dev, + "RX: cannot allocate RX buffer %d\n", cnt); + result = -ENOMEM; + goto error; + } + skb->dev = net_dev; + skb->ip_summed = CHECKSUM_NONE; + skb_reserve(skb, 2); + rx_buf->data = skb; + rx_buf->urb = usb_alloc_urb(0, GFP_KERNEL); + if (unlikely(rx_buf->urb == NULL)) { + dev_err(dev, "RX: cannot allocate URB %d\n", cnt); + result = -ENOMEM; + goto error; + } + usb_fill_bulk_urb(rx_buf->urb, i1480u->usb_dev, + usb_rcvbulkpipe(i1480u->usb_dev, epd->bEndpointAddress), + rx_buf->data->data, i1480u_MAX_RX_PKT_SIZE - 2, + i1480u_rx_cb, rx_buf); + result = usb_submit_urb(rx_buf->urb, GFP_NOIO); + if (unlikely(result < 0)) { + dev_err(dev, "RX: cannot submit URB %d: %d\n", + cnt, result); + goto error; + } + } + return 0; + +error: + i1480u_rx_release(i1480u); + return result; +} + + +/* Release resources associated to the rx context */ +void i1480u_rx_release(struct i1480u *i1480u) +{ + int cnt; + for (cnt = 0; cnt < i1480u_RX_BUFS; cnt++) { + if (i1480u->rx_buf[cnt].data) + dev_kfree_skb(i1480u->rx_buf[cnt].data); + if (i1480u->rx_buf[cnt].urb) { + usb_kill_urb(i1480u->rx_buf[cnt].urb); + usb_free_urb(i1480u->rx_buf[cnt].urb); + } + } + if (i1480u->rx_skb != NULL) + dev_kfree_skb(i1480u->rx_skb); +} + +static +void i1480u_rx_unlink_urbs(struct i1480u *i1480u) +{ + int cnt; + for (cnt = 0; cnt < i1480u_RX_BUFS; cnt++) { + if (i1480u->rx_buf[cnt].urb) + usb_unlink_urb(i1480u->rx_buf[cnt].urb); + } +} + +/* Fix an out-of-sequence packet */ +#define i1480u_fix(i1480u, msg...) \ +do { \ + if (printk_ratelimit()) \ + dev_err(&i1480u->usb_iface->dev, msg); \ + dev_kfree_skb_irq(i1480u->rx_skb); \ + i1480u->rx_skb = NULL; \ + i1480u->rx_untd_pkt_size = 0; \ +} while (0) + + +/* Drop an out-of-sequence packet */ +#define i1480u_drop(i1480u, msg...) \ +do { \ + if (printk_ratelimit()) \ + dev_err(&i1480u->usb_iface->dev, msg); \ + i1480u->net_dev->stats.rx_dropped++; \ +} while (0) + + + + +/* Finalizes setting up the SKB and delivers it + * + * We first pass the incoming frame to WLP substack for verification. It + * may also be a WLP association frame in which case WLP will take over the + * processing. If WLP does not take it over it will still verify it, if the + * frame is invalid the skb will be freed by WLP and we will not continue + * parsing. + * */ +static +void i1480u_skb_deliver(struct i1480u *i1480u) +{ + int should_parse; + struct net_device *net_dev = i1480u->net_dev; + struct device *dev = &i1480u->usb_iface->dev; + + should_parse = wlp_receive_frame(dev, &i1480u->wlp, i1480u->rx_skb, + &i1480u->rx_srcaddr); + if (!should_parse) + goto out; + i1480u->rx_skb->protocol = eth_type_trans(i1480u->rx_skb, net_dev); + net_dev->stats.rx_packets++; + net_dev->stats.rx_bytes += i1480u->rx_untd_pkt_size; + + netif_rx(i1480u->rx_skb); /* deliver */ +out: + i1480u->rx_skb = NULL; + i1480u->rx_untd_pkt_size = 0; +} + + +/* + * Process a buffer of data received from the USB RX endpoint + * + * First fragment arrives with next or last fragment. All other fragments + * arrive alone. + * + * /me hates long functions. + */ +static +void i1480u_rx_buffer(struct i1480u_rx_buf *rx_buf) +{ + unsigned pkt_completed = 0; /* !0 when we got all pkt fragments */ + size_t untd_hdr_size, untd_frg_size; + size_t i1480u_hdr_size; + struct wlp_rx_hdr *i1480u_hdr = NULL; + + struct i1480u *i1480u = rx_buf->i1480u; + struct sk_buff *skb = rx_buf->data; + int size_left = rx_buf->urb->actual_length; + void *ptr = rx_buf->urb->transfer_buffer; /* also rx_buf->data->data */ + struct untd_hdr *untd_hdr; + + struct net_device *net_dev = i1480u->net_dev; + struct device *dev = &i1480u->usb_iface->dev; + struct sk_buff *new_skb; + +#if 0 + dev_fnstart(dev, + "(i1480u %p ptr %p size_left %zu)\n", i1480u, ptr, size_left); + dev_err(dev, "RX packet, %zu bytes\n", size_left); + dump_bytes(dev, ptr, size_left); +#endif + i1480u_hdr_size = sizeof(struct wlp_rx_hdr); + + while (size_left > 0) { + if (pkt_completed) { + i1480u_drop(i1480u, "RX: fragment follows completed" + "packet in same buffer. Dropping\n"); + break; + } + untd_hdr = ptr; + if (size_left < sizeof(*untd_hdr)) { /* Check the UNTD header */ + i1480u_drop(i1480u, "RX: short UNTD header! Dropping\n"); + goto out; + } + if (unlikely(untd_hdr_rx_tx(untd_hdr) == 0)) { /* Paranoia: TX set? */ + i1480u_drop(i1480u, "RX: TX bit set! Dropping\n"); + goto out; + } + switch (untd_hdr_type(untd_hdr)) { /* Check the UNTD header type */ + case i1480u_PKT_FRAG_1ST: { + struct untd_hdr_1st *untd_hdr_1st = (void *) untd_hdr; + dev_dbg(dev, "1st fragment\n"); + untd_hdr_size = sizeof(struct untd_hdr_1st); + if (i1480u->rx_skb != NULL) + i1480u_fix(i1480u, "RX: 1st fragment out of " + "sequence! Fixing\n"); + if (size_left < untd_hdr_size + i1480u_hdr_size) { + i1480u_drop(i1480u, "RX: short 1st fragment! " + "Dropping\n"); + goto out; + } + i1480u->rx_untd_pkt_size = le16_to_cpu(untd_hdr->len) + - i1480u_hdr_size; + untd_frg_size = le16_to_cpu(untd_hdr_1st->fragment_len); + if (size_left < untd_hdr_size + untd_frg_size) { + i1480u_drop(i1480u, + "RX: short payload! Dropping\n"); + goto out; + } + i1480u->rx_skb = skb; + i1480u_hdr = (void *) untd_hdr_1st + untd_hdr_size; + i1480u->rx_srcaddr = i1480u_hdr->srcaddr; + skb_put(i1480u->rx_skb, untd_hdr_size + untd_frg_size); + skb_pull(i1480u->rx_skb, untd_hdr_size + i1480u_hdr_size); + stats_add_sample(&i1480u->lqe_stats, (s8) i1480u_hdr->LQI - 7); + stats_add_sample(&i1480u->rssi_stats, i1480u_hdr->RSSI + 18); + rx_buf->data = NULL; /* need to create new buffer */ + break; + } + case i1480u_PKT_FRAG_NXT: { + dev_dbg(dev, "nxt fragment\n"); + untd_hdr_size = sizeof(struct untd_hdr_rst); + if (i1480u->rx_skb == NULL) { + i1480u_drop(i1480u, "RX: next fragment out of " + "sequence! Dropping\n"); + goto out; + } + if (size_left < untd_hdr_size) { + i1480u_drop(i1480u, "RX: short NXT fragment! " + "Dropping\n"); + goto out; + } + untd_frg_size = le16_to_cpu(untd_hdr->len); + if (size_left < untd_hdr_size + untd_frg_size) { + i1480u_drop(i1480u, + "RX: short payload! Dropping\n"); + goto out; + } + memmove(skb_put(i1480u->rx_skb, untd_frg_size), + ptr + untd_hdr_size, untd_frg_size); + break; + } + case i1480u_PKT_FRAG_LST: { + dev_dbg(dev, "Lst fragment\n"); + untd_hdr_size = sizeof(struct untd_hdr_rst); + if (i1480u->rx_skb == NULL) { + i1480u_drop(i1480u, "RX: last fragment out of " + "sequence! Dropping\n"); + goto out; + } + if (size_left < untd_hdr_size) { + i1480u_drop(i1480u, "RX: short LST fragment! " + "Dropping\n"); + goto out; + } + untd_frg_size = le16_to_cpu(untd_hdr->len); + if (size_left < untd_frg_size + untd_hdr_size) { + i1480u_drop(i1480u, + "RX: short payload! Dropping\n"); + goto out; + } + memmove(skb_put(i1480u->rx_skb, untd_frg_size), + ptr + untd_hdr_size, untd_frg_size); + pkt_completed = 1; + break; + } + case i1480u_PKT_FRAG_CMP: { + dev_dbg(dev, "cmp fragment\n"); + untd_hdr_size = sizeof(struct untd_hdr_cmp); + if (i1480u->rx_skb != NULL) + i1480u_fix(i1480u, "RX: fix out-of-sequence CMP" + " fragment!\n"); + if (size_left < untd_hdr_size + i1480u_hdr_size) { + i1480u_drop(i1480u, "RX: short CMP fragment! " + "Dropping\n"); + goto out; + } + i1480u->rx_untd_pkt_size = le16_to_cpu(untd_hdr->len); + untd_frg_size = i1480u->rx_untd_pkt_size; + if (size_left < i1480u->rx_untd_pkt_size + untd_hdr_size) { + i1480u_drop(i1480u, + "RX: short payload! Dropping\n"); + goto out; + } + i1480u->rx_skb = skb; + i1480u_hdr = (void *) untd_hdr + untd_hdr_size; + i1480u->rx_srcaddr = i1480u_hdr->srcaddr; + stats_add_sample(&i1480u->lqe_stats, (s8) i1480u_hdr->LQI - 7); + stats_add_sample(&i1480u->rssi_stats, i1480u_hdr->RSSI + 18); + skb_put(i1480u->rx_skb, untd_hdr_size + i1480u->rx_untd_pkt_size); + skb_pull(i1480u->rx_skb, untd_hdr_size + i1480u_hdr_size); + rx_buf->data = NULL; /* for hand off skb to network stack */ + pkt_completed = 1; + i1480u->rx_untd_pkt_size -= i1480u_hdr_size; /* accurate stat */ + break; + } + default: + i1480u_drop(i1480u, "RX: unknown packet type %u! " + "Dropping\n", untd_hdr_type(untd_hdr)); + goto out; + } + size_left -= untd_hdr_size + untd_frg_size; + if (size_left > 0) + ptr += untd_hdr_size + untd_frg_size; + } + if (pkt_completed) + i1480u_skb_deliver(i1480u); +out: + /* recreate needed RX buffers*/ + if (rx_buf->data == NULL) { + /* buffer is being used to receive packet, create new */ + new_skb = dev_alloc_skb(i1480u_MAX_RX_PKT_SIZE); + if (!new_skb) { + if (printk_ratelimit()) + dev_err(dev, + "RX: cannot allocate RX buffer\n"); + } else { + new_skb->dev = net_dev; + new_skb->ip_summed = CHECKSUM_NONE; + skb_reserve(new_skb, 2); + rx_buf->data = new_skb; + } + } + return; +} + + +/* + * Called when an RX URB has finished receiving or has found some kind + * of error condition. + * + * LIMITATIONS: + * + * - We read USB-transfers, each transfer contains a SINGLE fragment + * (can contain a complete packet, or a 1st, next, or last fragment + * of a packet). + * Looks like a transfer can contain more than one fragment (07/18/06) + * + * - Each transfer buffer is the size of the maximum packet size (minus + * headroom), i1480u_MAX_PKT_SIZE - 2 + * + * - We always read the full USB-transfer, no partials. + * + * - Each transfer is read directly into a skb. This skb will be used to + * send data to the upper layers if it is the first fragment or a complete + * packet. In the other cases the data will be copied from the skb to + * another skb that is being prepared for the upper layers from a prev + * first fragment. + * + * It is simply too much of a pain. Gosh, there should be a unified + * SG infrastructure for *everything* [so that I could declare a SG + * buffer, pass it to USB for receiving, append some space to it if + * I wish, receive more until I have the whole chunk, adapt + * pointers on each fragment to remove hardware headers and then + * attach that to an skbuff and netif_rx()]. + */ +void i1480u_rx_cb(struct urb *urb) +{ + int result; + int do_parse_buffer = 1; + struct i1480u_rx_buf *rx_buf = urb->context; + struct i1480u *i1480u = rx_buf->i1480u; + struct device *dev = &i1480u->usb_iface->dev; + unsigned long flags; + u8 rx_buf_idx = rx_buf - i1480u->rx_buf; + + switch (urb->status) { + case 0: + break; + case -ECONNRESET: /* Not an error, but a controlled situation; */ + case -ENOENT: /* (we killed the URB)...so, no broadcast */ + case -ESHUTDOWN: /* going away! */ + dev_err(dev, "RX URB[%u]: goind down %d\n", + rx_buf_idx, urb->status); + goto error; + default: + dev_err(dev, "RX URB[%u]: unknown status %d\n", + rx_buf_idx, urb->status); + if (edc_inc(&i1480u->rx_errors, EDC_MAX_ERRORS, + EDC_ERROR_TIMEFRAME)) { + dev_err(dev, "RX: max acceptable errors exceeded," + " resetting device.\n"); + i1480u_rx_unlink_urbs(i1480u); + wlp_reset_all(&i1480u->wlp); + goto error; + } + do_parse_buffer = 0; + break; + } + spin_lock_irqsave(&i1480u->lock, flags); + /* chew the data fragments, extract network packets */ + if (do_parse_buffer) { + i1480u_rx_buffer(rx_buf); + if (rx_buf->data) { + rx_buf->urb->transfer_buffer = rx_buf->data->data; + result = usb_submit_urb(rx_buf->urb, GFP_ATOMIC); + if (result < 0) { + dev_err(dev, "RX URB[%u]: cannot submit %d\n", + rx_buf_idx, result); + } + } + } + spin_unlock_irqrestore(&i1480u->lock, flags); +error: + return; +} + diff --git a/trunk/drivers/uwb/i1480/i1480u-wlp/sysfs.c b/trunk/drivers/uwb/i1480/i1480u-wlp/sysfs.c new file mode 100644 index 000000000000..4ffaf546cc6c --- /dev/null +++ b/trunk/drivers/uwb/i1480/i1480u-wlp/sysfs.c @@ -0,0 +1,407 @@ +/* + * WUSB Wire Adapter: WLP interface + * Sysfs interfaces + * + * Copyright (C) 2005-2006 Intel Corporation + * Inaky Perez-Gonzalez + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * FIXME: docs + */ + +#include +#include +#include + +#include "i1480u-wlp.h" + + +/** + * + * @dev: Class device from the net_device; assumed refcnted. + * + * Yes, I don't lock--we assume it is refcounted and I am getting a + * single byte value that is kind of atomic to read. + */ +ssize_t uwb_phy_rate_show(const struct wlp_options *options, char *buf) +{ + return sprintf(buf, "%u\n", + wlp_tx_hdr_phy_rate(&options->def_tx_hdr)); +} +EXPORT_SYMBOL_GPL(uwb_phy_rate_show); + + +ssize_t uwb_phy_rate_store(struct wlp_options *options, + const char *buf, size_t size) +{ + ssize_t result; + unsigned rate; + + result = sscanf(buf, "%u\n", &rate); + if (result != 1) { + result = -EINVAL; + goto out; + } + result = -EINVAL; + if (rate >= UWB_PHY_RATE_INVALID) + goto out; + wlp_tx_hdr_set_phy_rate(&options->def_tx_hdr, rate); + result = 0; +out: + return result < 0 ? result : size; +} +EXPORT_SYMBOL_GPL(uwb_phy_rate_store); + + +ssize_t uwb_rts_cts_show(const struct wlp_options *options, char *buf) +{ + return sprintf(buf, "%u\n", + wlp_tx_hdr_rts_cts(&options->def_tx_hdr)); +} +EXPORT_SYMBOL_GPL(uwb_rts_cts_show); + + +ssize_t uwb_rts_cts_store(struct wlp_options *options, + const char *buf, size_t size) +{ + ssize_t result; + unsigned value; + + result = sscanf(buf, "%u\n", &value); + if (result != 1) { + result = -EINVAL; + goto out; + } + result = -EINVAL; + wlp_tx_hdr_set_rts_cts(&options->def_tx_hdr, !!value); + result = 0; +out: + return result < 0 ? result : size; +} +EXPORT_SYMBOL_GPL(uwb_rts_cts_store); + + +ssize_t uwb_ack_policy_show(const struct wlp_options *options, char *buf) +{ + return sprintf(buf, "%u\n", + wlp_tx_hdr_ack_policy(&options->def_tx_hdr)); +} +EXPORT_SYMBOL_GPL(uwb_ack_policy_show); + + +ssize_t uwb_ack_policy_store(struct wlp_options *options, + const char *buf, size_t size) +{ + ssize_t result; + unsigned value; + + result = sscanf(buf, "%u\n", &value); + if (result != 1 || value > UWB_ACK_B_REQ) { + result = -EINVAL; + goto out; + } + wlp_tx_hdr_set_ack_policy(&options->def_tx_hdr, value); + result = 0; +out: + return result < 0 ? result : size; +} +EXPORT_SYMBOL_GPL(uwb_ack_policy_store); + + +/** + * Show the PCA base priority. + * + * We can access without locking, as the value is (for now) orthogonal + * to other values. + */ +ssize_t uwb_pca_base_priority_show(const struct wlp_options *options, + char *buf) +{ + return sprintf(buf, "%u\n", + options->pca_base_priority); +} +EXPORT_SYMBOL_GPL(uwb_pca_base_priority_show); + + +/** + * Set the PCA base priority. + * + * We can access without locking, as the value is (for now) orthogonal + * to other values. + */ +ssize_t uwb_pca_base_priority_store(struct wlp_options *options, + const char *buf, size_t size) +{ + ssize_t result = -EINVAL; + u8 pca_base_priority; + + result = sscanf(buf, "%hhu\n", &pca_base_priority); + if (result != 1) { + result = -EINVAL; + goto out; + } + result = -EINVAL; + if (pca_base_priority >= 8) + goto out; + options->pca_base_priority = pca_base_priority; + /* Update TX header if we are currently using PCA. */ + if (result >= 0 && (wlp_tx_hdr_delivery_id_type(&options->def_tx_hdr) & WLP_DRP) == 0) + wlp_tx_hdr_set_delivery_id_type(&options->def_tx_hdr, options->pca_base_priority); + result = 0; +out: + return result < 0 ? result : size; +} +EXPORT_SYMBOL_GPL(uwb_pca_base_priority_store); + +/** + * Show current inflight values + * + * Will print the current MAX and THRESHOLD values for the basic flow + * control. In addition it will report how many times the TX queue needed + * to be restarted since the last time this query was made. + */ +static ssize_t wlp_tx_inflight_show(struct i1480u_tx_inflight *inflight, + char *buf) +{ + ssize_t result; + unsigned long sec_elapsed = (jiffies - inflight->restart_ts)/HZ; + unsigned long restart_count = atomic_read(&inflight->restart_count); + + result = scnprintf(buf, PAGE_SIZE, "%lu %lu %d %lu %lu %lu\n" + "#read: threshold max inflight_count restarts " + "seconds restarts/sec\n" + "#write: threshold max\n", + inflight->threshold, inflight->max, + atomic_read(&inflight->count), + restart_count, sec_elapsed, + sec_elapsed == 0 ? 0 : restart_count/sec_elapsed); + inflight->restart_ts = jiffies; + atomic_set(&inflight->restart_count, 0); + return result; +} + +static +ssize_t wlp_tx_inflight_store(struct i1480u_tx_inflight *inflight, + const char *buf, size_t size) +{ + unsigned long in_threshold, in_max; + ssize_t result; + result = sscanf(buf, "%lu %lu", &in_threshold, &in_max); + if (result != 2) + return -EINVAL; + if (in_max <= in_threshold) + return -EINVAL; + inflight->max = in_max; + inflight->threshold = in_threshold; + return size; +} +/* + * Glue (or function adaptors) for accesing info on sysfs + * + * [we need this indirection because the PCI driver does almost the + * same] + * + * Linux 2.6.21 changed how 'struct netdevice' does attributes (from + * having a 'struct class_dev' to having a 'struct device'). That is + * quite of a pain. + * + * So we try to abstract that here. i1480u_SHOW() and i1480u_STORE() + * create adaptors for extracting the 'struct i1480u' from a 'struct + * dev' and calling a function for doing a sysfs operation (as we have + * them factorized already). i1480u_ATTR creates the attribute file + * (CLASS_DEVICE_ATTR or DEVICE_ATTR) and i1480u_ATTR_NAME produces a + * class_device_attr_NAME or device_attr_NAME (for group registration). + */ + +#define i1480u_SHOW(name, fn, param) \ +static ssize_t i1480u_show_##name(struct device *dev, \ + struct device_attribute *attr,\ + char *buf) \ +{ \ + struct i1480u *i1480u = netdev_priv(to_net_dev(dev)); \ + return fn(&i1480u->param, buf); \ +} + +#define i1480u_STORE(name, fn, param) \ +static ssize_t i1480u_store_##name(struct device *dev, \ + struct device_attribute *attr,\ + const char *buf, size_t size)\ +{ \ + struct i1480u *i1480u = netdev_priv(to_net_dev(dev)); \ + return fn(&i1480u->param, buf, size); \ +} + +#define i1480u_ATTR(name, perm) static DEVICE_ATTR(name, perm, \ + i1480u_show_##name,\ + i1480u_store_##name) + +#define i1480u_ATTR_SHOW(name) static DEVICE_ATTR(name, \ + S_IRUGO, \ + i1480u_show_##name, NULL) + +#define i1480u_ATTR_NAME(a) (dev_attr_##a) + + +/* + * Sysfs adaptors + */ +i1480u_SHOW(uwb_phy_rate, uwb_phy_rate_show, options); +i1480u_STORE(uwb_phy_rate, uwb_phy_rate_store, options); +i1480u_ATTR(uwb_phy_rate, S_IRUGO | S_IWUSR); + +i1480u_SHOW(uwb_rts_cts, uwb_rts_cts_show, options); +i1480u_STORE(uwb_rts_cts, uwb_rts_cts_store, options); +i1480u_ATTR(uwb_rts_cts, S_IRUGO | S_IWUSR); + +i1480u_SHOW(uwb_ack_policy, uwb_ack_policy_show, options); +i1480u_STORE(uwb_ack_policy, uwb_ack_policy_store, options); +i1480u_ATTR(uwb_ack_policy, S_IRUGO | S_IWUSR); + +i1480u_SHOW(uwb_pca_base_priority, uwb_pca_base_priority_show, options); +i1480u_STORE(uwb_pca_base_priority, uwb_pca_base_priority_store, options); +i1480u_ATTR(uwb_pca_base_priority, S_IRUGO | S_IWUSR); + +i1480u_SHOW(wlp_eda, wlp_eda_show, wlp); +i1480u_STORE(wlp_eda, wlp_eda_store, wlp); +i1480u_ATTR(wlp_eda, S_IRUGO | S_IWUSR); + +i1480u_SHOW(wlp_uuid, wlp_uuid_show, wlp); +i1480u_STORE(wlp_uuid, wlp_uuid_store, wlp); +i1480u_ATTR(wlp_uuid, S_IRUGO | S_IWUSR); + +i1480u_SHOW(wlp_dev_name, wlp_dev_name_show, wlp); +i1480u_STORE(wlp_dev_name, wlp_dev_name_store, wlp); +i1480u_ATTR(wlp_dev_name, S_IRUGO | S_IWUSR); + +i1480u_SHOW(wlp_dev_manufacturer, wlp_dev_manufacturer_show, wlp); +i1480u_STORE(wlp_dev_manufacturer, wlp_dev_manufacturer_store, wlp); +i1480u_ATTR(wlp_dev_manufacturer, S_IRUGO | S_IWUSR); + +i1480u_SHOW(wlp_dev_model_name, wlp_dev_model_name_show, wlp); +i1480u_STORE(wlp_dev_model_name, wlp_dev_model_name_store, wlp); +i1480u_ATTR(wlp_dev_model_name, S_IRUGO | S_IWUSR); + +i1480u_SHOW(wlp_dev_model_nr, wlp_dev_model_nr_show, wlp); +i1480u_STORE(wlp_dev_model_nr, wlp_dev_model_nr_store, wlp); +i1480u_ATTR(wlp_dev_model_nr, S_IRUGO | S_IWUSR); + +i1480u_SHOW(wlp_dev_serial, wlp_dev_serial_show, wlp); +i1480u_STORE(wlp_dev_serial, wlp_dev_serial_store, wlp); +i1480u_ATTR(wlp_dev_serial, S_IRUGO | S_IWUSR); + +i1480u_SHOW(wlp_dev_prim_category, wlp_dev_prim_category_show, wlp); +i1480u_STORE(wlp_dev_prim_category, wlp_dev_prim_category_store, wlp); +i1480u_ATTR(wlp_dev_prim_category, S_IRUGO | S_IWUSR); + +i1480u_SHOW(wlp_dev_prim_OUI, wlp_dev_prim_OUI_show, wlp); +i1480u_STORE(wlp_dev_prim_OUI, wlp_dev_prim_OUI_store, wlp); +i1480u_ATTR(wlp_dev_prim_OUI, S_IRUGO | S_IWUSR); + +i1480u_SHOW(wlp_dev_prim_OUI_sub, wlp_dev_prim_OUI_sub_show, wlp); +i1480u_STORE(wlp_dev_prim_OUI_sub, wlp_dev_prim_OUI_sub_store, wlp); +i1480u_ATTR(wlp_dev_prim_OUI_sub, S_IRUGO | S_IWUSR); + +i1480u_SHOW(wlp_dev_prim_subcat, wlp_dev_prim_subcat_show, wlp); +i1480u_STORE(wlp_dev_prim_subcat, wlp_dev_prim_subcat_store, wlp); +i1480u_ATTR(wlp_dev_prim_subcat, S_IRUGO | S_IWUSR); + +i1480u_SHOW(wlp_neighborhood, wlp_neighborhood_show, wlp); +i1480u_ATTR_SHOW(wlp_neighborhood); + +i1480u_SHOW(wss_activate, wlp_wss_activate_show, wlp.wss); +i1480u_STORE(wss_activate, wlp_wss_activate_store, wlp.wss); +i1480u_ATTR(wss_activate, S_IRUGO | S_IWUSR); + +/* + * Show the (min, max, avg) Line Quality Estimate (LQE, in dB) as over + * the last 256 received WLP frames (ECMA-368 13.3). + * + * [the -7dB that have to be substracted from the LQI to make the LQE + * are already taken into account]. + */ +i1480u_SHOW(wlp_lqe, stats_show, lqe_stats); +i1480u_STORE(wlp_lqe, stats_store, lqe_stats); +i1480u_ATTR(wlp_lqe, S_IRUGO | S_IWUSR); + +/* + * Show the Receive Signal Strength Indicator averaged over all the + * received WLP frames (ECMA-368 13.3). Still is not clear what + * this value is, but is kind of a percentage of the signal strength + * at the antenna. + */ +i1480u_SHOW(wlp_rssi, stats_show, rssi_stats); +i1480u_STORE(wlp_rssi, stats_store, rssi_stats); +i1480u_ATTR(wlp_rssi, S_IRUGO | S_IWUSR); + +/** + * We maintain a basic flow control counter. "count" how many TX URBs are + * outstanding. Only allow "max" + * TX URBs to be outstanding. If this value is reached the queue will be + * stopped. The queue will be restarted when there are + * "threshold" URBs outstanding. + */ +i1480u_SHOW(wlp_tx_inflight, wlp_tx_inflight_show, tx_inflight); +i1480u_STORE(wlp_tx_inflight, wlp_tx_inflight_store, tx_inflight); +i1480u_ATTR(wlp_tx_inflight, S_IRUGO | S_IWUSR); + +static struct attribute *i1480u_attrs[] = { + &i1480u_ATTR_NAME(uwb_phy_rate).attr, + &i1480u_ATTR_NAME(uwb_rts_cts).attr, + &i1480u_ATTR_NAME(uwb_ack_policy).attr, + &i1480u_ATTR_NAME(uwb_pca_base_priority).attr, + &i1480u_ATTR_NAME(wlp_lqe).attr, + &i1480u_ATTR_NAME(wlp_rssi).attr, + &i1480u_ATTR_NAME(wlp_eda).attr, + &i1480u_ATTR_NAME(wlp_uuid).attr, + &i1480u_ATTR_NAME(wlp_dev_name).attr, + &i1480u_ATTR_NAME(wlp_dev_manufacturer).attr, + &i1480u_ATTR_NAME(wlp_dev_model_name).attr, + &i1480u_ATTR_NAME(wlp_dev_model_nr).attr, + &i1480u_ATTR_NAME(wlp_dev_serial).attr, + &i1480u_ATTR_NAME(wlp_dev_prim_category).attr, + &i1480u_ATTR_NAME(wlp_dev_prim_OUI).attr, + &i1480u_ATTR_NAME(wlp_dev_prim_OUI_sub).attr, + &i1480u_ATTR_NAME(wlp_dev_prim_subcat).attr, + &i1480u_ATTR_NAME(wlp_neighborhood).attr, + &i1480u_ATTR_NAME(wss_activate).attr, + &i1480u_ATTR_NAME(wlp_tx_inflight).attr, + NULL, +}; + +static struct attribute_group i1480u_attr_group = { + .name = NULL, /* we want them in the same directory */ + .attrs = i1480u_attrs, +}; + +int i1480u_sysfs_setup(struct i1480u *i1480u) +{ + int result; + struct device *dev = &i1480u->usb_iface->dev; + result = sysfs_create_group(&i1480u->net_dev->dev.kobj, + &i1480u_attr_group); + if (result < 0) + dev_err(dev, "cannot initialize sysfs attributes: %d\n", + result); + return result; +} + + +void i1480u_sysfs_release(struct i1480u *i1480u) +{ + sysfs_remove_group(&i1480u->net_dev->dev.kobj, + &i1480u_attr_group); +} diff --git a/trunk/drivers/uwb/i1480/i1480u-wlp/tx.c b/trunk/drivers/uwb/i1480/i1480u-wlp/tx.c new file mode 100644 index 000000000000..3c117a364564 --- /dev/null +++ b/trunk/drivers/uwb/i1480/i1480u-wlp/tx.c @@ -0,0 +1,584 @@ +/* + * WUSB Wire Adapter: WLP interface + * Deal with TX (massaging data to transmit, handling it) + * + * Copyright (C) 2005-2006 Intel Corporation + * Inaky Perez-Gonzalez + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * Transmission engine. Get an skb, create from that a WLP transmit + * context, add a WLP TX header (which we keep prefilled in the + * device's instance), fill out the target-specific fields and + * fire it. + * + * ROADMAP: + * + * Entry points: + * + * i1480u_tx_release(): called by i1480u_disconnect() to release + * pending tx contexts. + * + * i1480u_tx_cb(): callback for TX contexts (USB URBs) + * i1480u_tx_destroy(): + * + * i1480u_tx_timeout(): called for timeout handling from the + * network stack. + * + * i1480u_hard_start_xmit(): called for transmitting an skb from + * the network stack. Will interact with WLP + * substack to verify and prepare frame. + * i1480u_xmit_frame(): actual transmission on hardware + * + * i1480u_tx_create() Creates TX context + * i1480u_tx_create_1() For packets in 1 fragment + * i1480u_tx_create_n() For packets in >1 fragments + * + * TODO: + * + * - FIXME: rewrite using usb_sg_*(), add asynch support to + * usb_sg_*(). It might not make too much sense as most of + * the times the MTU will be smaller than one page... + */ + +#include +#include "i1480u-wlp.h" + +enum { + /* This is only for Next and Last TX packets */ + i1480u_MAX_PL_SIZE = i1480u_MAX_FRG_SIZE + - sizeof(struct untd_hdr_rst), +}; + +/* Free resources allocated to a i1480u tx context. */ +static +void i1480u_tx_free(struct i1480u_tx *wtx) +{ + kfree(wtx->buf); + if (wtx->skb) + dev_kfree_skb_irq(wtx->skb); + usb_free_urb(wtx->urb); + kfree(wtx); +} + +static +void i1480u_tx_destroy(struct i1480u *i1480u, struct i1480u_tx *wtx) +{ + unsigned long flags; + spin_lock_irqsave(&i1480u->tx_list_lock, flags); /* not active any more */ + list_del(&wtx->list_node); + i1480u_tx_free(wtx); + spin_unlock_irqrestore(&i1480u->tx_list_lock, flags); +} + +static +void i1480u_tx_unlink_urbs(struct i1480u *i1480u) +{ + unsigned long flags; + struct i1480u_tx *wtx, *next; + + spin_lock_irqsave(&i1480u->tx_list_lock, flags); + list_for_each_entry_safe(wtx, next, &i1480u->tx_list, list_node) { + usb_unlink_urb(wtx->urb); + } + spin_unlock_irqrestore(&i1480u->tx_list_lock, flags); +} + + +/* + * Callback for a completed tx USB URB. + * + * TODO: + * + * - FIXME: recover errors more gracefully + * - FIXME: handle NAKs (I dont think they come here) for flow ctl + */ +static +void i1480u_tx_cb(struct urb *urb) +{ + struct i1480u_tx *wtx = urb->context; + struct i1480u *i1480u = wtx->i1480u; + struct net_device *net_dev = i1480u->net_dev; + struct device *dev = &i1480u->usb_iface->dev; + unsigned long flags; + + switch (urb->status) { + case 0: + spin_lock_irqsave(&i1480u->lock, flags); + net_dev->stats.tx_packets++; + net_dev->stats.tx_bytes += urb->actual_length; + spin_unlock_irqrestore(&i1480u->lock, flags); + break; + case -ECONNRESET: /* Not an error, but a controlled situation; */ + case -ENOENT: /* (we killed the URB)...so, no broadcast */ + dev_dbg(dev, "notif endp: reset/noent %d\n", urb->status); + netif_stop_queue(net_dev); + break; + case -ESHUTDOWN: /* going away! */ + dev_dbg(dev, "notif endp: down %d\n", urb->status); + netif_stop_queue(net_dev); + break; + default: + dev_err(dev, "TX: unknown URB status %d\n", urb->status); + if (edc_inc(&i1480u->tx_errors, EDC_MAX_ERRORS, + EDC_ERROR_TIMEFRAME)) { + dev_err(dev, "TX: max acceptable errors exceeded." + "Reset device.\n"); + netif_stop_queue(net_dev); + i1480u_tx_unlink_urbs(i1480u); + wlp_reset_all(&i1480u->wlp); + } + break; + } + i1480u_tx_destroy(i1480u, wtx); + if (atomic_dec_return(&i1480u->tx_inflight.count) + <= i1480u->tx_inflight.threshold + && netif_queue_stopped(net_dev) + && i1480u->tx_inflight.threshold != 0) { + netif_start_queue(net_dev); + atomic_inc(&i1480u->tx_inflight.restart_count); + } + return; +} + + +/* + * Given a buffer that doesn't fit in a single fragment, create an + * scatter/gather structure for delivery to the USB pipe. + * + * Implements functionality of i1480u_tx_create(). + * + * @wtx: tx descriptor + * @skb: skb to send + * @gfp_mask: gfp allocation mask + * @returns: Pointer to @wtx if ok, NULL on error. + * + * Sorry, TOO LONG a function, but breaking it up is kind of hard + * + * This will break the buffer in chunks smaller than + * i1480u_MAX_FRG_SIZE (including the header) and add proper headers + * to each: + * + * 1st header \ + * i1480 tx header | fragment 1 + * fragment data / + * nxt header \ fragment 2 + * fragment data / + * .. + * .. + * last header \ fragment 3 + * last fragment data / + * + * This does not fill the i1480 TX header, it is left up to the + * caller to do that; you can get it from @wtx->wlp_tx_hdr. + * + * This function consumes the skb unless there is an error. + */ +static +int i1480u_tx_create_n(struct i1480u_tx *wtx, struct sk_buff *skb, + gfp_t gfp_mask) +{ + int result; + void *pl; + size_t pl_size; + + void *pl_itr, *buf_itr; + size_t pl_size_left, frgs, pl_size_1st, frg_pl_size = 0; + struct untd_hdr_1st *untd_hdr_1st; + struct wlp_tx_hdr *wlp_tx_hdr; + struct untd_hdr_rst *untd_hdr_rst; + + wtx->skb = NULL; + pl = skb->data; + pl_itr = pl; + pl_size = skb->len; + pl_size_left = pl_size; /* payload size */ + /* First fragment; fits as much as i1480u_MAX_FRG_SIZE minus + * the headers */ + pl_size_1st = i1480u_MAX_FRG_SIZE + - sizeof(struct untd_hdr_1st) - sizeof(struct wlp_tx_hdr); + BUG_ON(pl_size_1st > pl_size); + pl_size_left -= pl_size_1st; + /* The rest have an smaller header (no i1480 TX header). We + * need to break up the payload in blocks smaller than + * i1480u_MAX_PL_SIZE (payload excluding header). */ + frgs = (pl_size_left + i1480u_MAX_PL_SIZE - 1) / i1480u_MAX_PL_SIZE; + /* Allocate space for the new buffer. In this new buffer we'll + * place the headers followed by the data fragment, headers, + * data fragments, etc.. + */ + result = -ENOMEM; + wtx->buf_size = sizeof(*untd_hdr_1st) + + sizeof(*wlp_tx_hdr) + + frgs * sizeof(*untd_hdr_rst) + + pl_size; + wtx->buf = kmalloc(wtx->buf_size, gfp_mask); + if (wtx->buf == NULL) + goto error_buf_alloc; + + buf_itr = wtx->buf; /* We got the space, let's fill it up */ + /* Fill 1st fragment */ + untd_hdr_1st = buf_itr; + buf_itr += sizeof(*untd_hdr_1st); + untd_hdr_set_type(&untd_hdr_1st->hdr, i1480u_PKT_FRAG_1ST); + untd_hdr_set_rx_tx(&untd_hdr_1st->hdr, 0); + untd_hdr_1st->hdr.len = cpu_to_le16(pl_size + sizeof(*wlp_tx_hdr)); + untd_hdr_1st->fragment_len = + cpu_to_le16(pl_size_1st + sizeof(*wlp_tx_hdr)); + memset(untd_hdr_1st->padding, 0, sizeof(untd_hdr_1st->padding)); + /* Set up i1480 header info */ + wlp_tx_hdr = wtx->wlp_tx_hdr = buf_itr; + buf_itr += sizeof(*wlp_tx_hdr); + /* Copy the first fragment */ + memcpy(buf_itr, pl_itr, pl_size_1st); + pl_itr += pl_size_1st; + buf_itr += pl_size_1st; + + /* Now do each remaining fragment */ + result = -EINVAL; + while (pl_size_left > 0) { + if (buf_itr + sizeof(*untd_hdr_rst) - wtx->buf + > wtx->buf_size) { + printk(KERN_ERR "BUG: no space for header\n"); + goto error_bug; + } + untd_hdr_rst = buf_itr; + buf_itr += sizeof(*untd_hdr_rst); + if (pl_size_left > i1480u_MAX_PL_SIZE) { + frg_pl_size = i1480u_MAX_PL_SIZE; + untd_hdr_set_type(&untd_hdr_rst->hdr, i1480u_PKT_FRAG_NXT); + } else { + frg_pl_size = pl_size_left; + untd_hdr_set_type(&untd_hdr_rst->hdr, i1480u_PKT_FRAG_LST); + } + untd_hdr_set_rx_tx(&untd_hdr_rst->hdr, 0); + untd_hdr_rst->hdr.len = cpu_to_le16(frg_pl_size); + untd_hdr_rst->padding = 0; + if (buf_itr + frg_pl_size - wtx->buf + > wtx->buf_size) { + printk(KERN_ERR "BUG: no space for payload\n"); + goto error_bug; + } + memcpy(buf_itr, pl_itr, frg_pl_size); + buf_itr += frg_pl_size; + pl_itr += frg_pl_size; + pl_size_left -= frg_pl_size; + } + dev_kfree_skb_irq(skb); + return 0; + +error_bug: + printk(KERN_ERR + "BUG: skb %u bytes\n" + "BUG: frg_pl_size %zd i1480u_MAX_FRG_SIZE %u\n" + "BUG: buf_itr %zu buf_size %zu pl_size_left %zu\n", + skb->len, + frg_pl_size, i1480u_MAX_FRG_SIZE, + buf_itr - wtx->buf, wtx->buf_size, pl_size_left); + + kfree(wtx->buf); +error_buf_alloc: + return result; +} + + +/* + * Given a buffer that fits in a single fragment, fill out a @wtx + * struct for transmitting it down the USB pipe. + * + * Uses the fact that we have space reserved in front of the skbuff + * for hardware headers :] + * + * This does not fill the i1480 TX header, it is left up to the + * caller to do that; you can get it from @wtx->wlp_tx_hdr. + * + * @pl: pointer to payload data + * @pl_size: size of the payuload + * + * This function does not consume the @skb. + */ +static +int i1480u_tx_create_1(struct i1480u_tx *wtx, struct sk_buff *skb, + gfp_t gfp_mask) +{ + struct untd_hdr_cmp *untd_hdr_cmp; + struct wlp_tx_hdr *wlp_tx_hdr; + + wtx->buf = NULL; + wtx->skb = skb; + BUG_ON(skb_headroom(skb) < sizeof(*wlp_tx_hdr)); + wlp_tx_hdr = (void *) __skb_push(skb, sizeof(*wlp_tx_hdr)); + wtx->wlp_tx_hdr = wlp_tx_hdr; + BUG_ON(skb_headroom(skb) < sizeof(*untd_hdr_cmp)); + untd_hdr_cmp = (void *) __skb_push(skb, sizeof(*untd_hdr_cmp)); + + untd_hdr_set_type(&untd_hdr_cmp->hdr, i1480u_PKT_FRAG_CMP); + untd_hdr_set_rx_tx(&untd_hdr_cmp->hdr, 0); + untd_hdr_cmp->hdr.len = cpu_to_le16(skb->len - sizeof(*untd_hdr_cmp)); + untd_hdr_cmp->padding = 0; + return 0; +} + + +/* + * Given a skb to transmit, massage it to become palatable for the TX pipe + * + * This will break the buffer in chunks smaller than + * i1480u_MAX_FRG_SIZE and add proper headers to each. + * + * 1st header \ + * i1480 tx header | fragment 1 + * fragment data / + * nxt header \ fragment 2 + * fragment data / + * .. + * .. + * last header \ fragment 3 + * last fragment data / + * + * Each fragment will be always smaller or equal to i1480u_MAX_FRG_SIZE. + * + * If the first fragment is smaller than i1480u_MAX_FRG_SIZE, then the + * following is composed: + * + * complete header \ + * i1480 tx header | single fragment + * packet data / + * + * We were going to use s/g support, but because the interface is + * synch and at the end there is plenty of overhead to do it, it + * didn't seem that worth for data that is going to be smaller than + * one page. + */ +static +struct i1480u_tx *i1480u_tx_create(struct i1480u *i1480u, + struct sk_buff *skb, gfp_t gfp_mask) +{ + int result; + struct usb_endpoint_descriptor *epd; + int usb_pipe; + unsigned long flags; + + struct i1480u_tx *wtx; + const size_t pl_max_size = + i1480u_MAX_FRG_SIZE - sizeof(struct untd_hdr_cmp) + - sizeof(struct wlp_tx_hdr); + + wtx = kmalloc(sizeof(*wtx), gfp_mask); + if (wtx == NULL) + goto error_wtx_alloc; + wtx->urb = usb_alloc_urb(0, gfp_mask); + if (wtx->urb == NULL) + goto error_urb_alloc; + epd = &i1480u->usb_iface->cur_altsetting->endpoint[2].desc; + usb_pipe = usb_sndbulkpipe(i1480u->usb_dev, epd->bEndpointAddress); + /* Fits in a single complete packet or need to split? */ + if (skb->len > pl_max_size) { + result = i1480u_tx_create_n(wtx, skb, gfp_mask); + if (result < 0) + goto error_create; + usb_fill_bulk_urb(wtx->urb, i1480u->usb_dev, usb_pipe, + wtx->buf, wtx->buf_size, i1480u_tx_cb, wtx); + } else { + result = i1480u_tx_create_1(wtx, skb, gfp_mask); + if (result < 0) + goto error_create; + usb_fill_bulk_urb(wtx->urb, i1480u->usb_dev, usb_pipe, + skb->data, skb->len, i1480u_tx_cb, wtx); + } + spin_lock_irqsave(&i1480u->tx_list_lock, flags); + list_add(&wtx->list_node, &i1480u->tx_list); + spin_unlock_irqrestore(&i1480u->tx_list_lock, flags); + return wtx; + +error_create: + kfree(wtx->urb); +error_urb_alloc: + kfree(wtx); +error_wtx_alloc: + return NULL; +} + +/* + * Actual fragmentation and transmission of frame + * + * @wlp: WLP substack data structure + * @skb: To be transmitted + * @dst: Device address of destination + * @returns: 0 on success, <0 on failure + * + * This function can also be called directly (not just from + * hard_start_xmit), so we also check here if the interface is up before + * taking sending anything. + */ +int i1480u_xmit_frame(struct wlp *wlp, struct sk_buff *skb, + struct uwb_dev_addr *dst) +{ + int result = -ENXIO; + struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp); + struct device *dev = &i1480u->usb_iface->dev; + struct net_device *net_dev = i1480u->net_dev; + struct i1480u_tx *wtx; + struct wlp_tx_hdr *wlp_tx_hdr; + static unsigned char dev_bcast[2] = { 0xff, 0xff }; + + BUG_ON(i1480u->wlp.rc == NULL); + if ((net_dev->flags & IFF_UP) == 0) + goto out; + result = -EBUSY; + if (atomic_read(&i1480u->tx_inflight.count) >= i1480u->tx_inflight.max) { + netif_stop_queue(net_dev); + goto error_max_inflight; + } + result = -ENOMEM; + wtx = i1480u_tx_create(i1480u, skb, GFP_ATOMIC); + if (unlikely(wtx == NULL)) { + if (printk_ratelimit()) + dev_err(dev, "TX: no memory for WLP TX URB," + "dropping packet (in flight %d)\n", + atomic_read(&i1480u->tx_inflight.count)); + netif_stop_queue(net_dev); + goto error_wtx_alloc; + } + wtx->i1480u = i1480u; + /* Fill out the i1480 header; @i1480u->def_tx_hdr read without + * locking. We do so because they are kind of orthogonal to + * each other (and thus not changed in an atomic batch). + * The ETH header is right after the WLP TX header. */ + wlp_tx_hdr = wtx->wlp_tx_hdr; + *wlp_tx_hdr = i1480u->options.def_tx_hdr; + wlp_tx_hdr->dstaddr = *dst; + if (!memcmp(&wlp_tx_hdr->dstaddr, dev_bcast, sizeof(dev_bcast)) + && (wlp_tx_hdr_delivery_id_type(wlp_tx_hdr) & WLP_DRP)) { + /*Broadcast message directed to DRP host. Send as best effort + * on PCA. */ + wlp_tx_hdr_set_delivery_id_type(wlp_tx_hdr, i1480u->options.pca_base_priority); + } + + result = usb_submit_urb(wtx->urb, GFP_ATOMIC); /* Go baby */ + if (result < 0) { + dev_err(dev, "TX: cannot submit URB: %d\n", result); + /* We leave the freeing of skb to calling function */ + wtx->skb = NULL; + goto error_tx_urb_submit; + } + atomic_inc(&i1480u->tx_inflight.count); + net_dev->trans_start = jiffies; + return result; + +error_tx_urb_submit: + i1480u_tx_destroy(i1480u, wtx); +error_wtx_alloc: +error_max_inflight: +out: + return result; +} + + +/* + * Transmit an skb Called when an skbuf has to be transmitted + * + * The skb is first passed to WLP substack to ensure this is a valid + * frame. If valid the device address of destination will be filled and + * the WLP header prepended to the skb. If this step fails we fake sending + * the frame, if we return an error the network stack will just keep trying. + * + * Broadcast frames inside a WSS needs to be treated special as multicast is + * not supported. A broadcast frame is sent as unicast to each member of the + * WSS - this is done by the WLP substack when it finds a broadcast frame. + * So, we test if the WLP substack took over the skb and only transmit it + * if it has not (been taken over). + * + * @net_dev->xmit_lock is held + */ +netdev_tx_t i1480u_hard_start_xmit(struct sk_buff *skb, + struct net_device *net_dev) +{ + int result; + struct i1480u *i1480u = netdev_priv(net_dev); + struct device *dev = &i1480u->usb_iface->dev; + struct uwb_dev_addr dst; + + if ((net_dev->flags & IFF_UP) == 0) + goto error; + result = wlp_prepare_tx_frame(dev, &i1480u->wlp, skb, &dst); + if (result < 0) { + dev_err(dev, "WLP verification of TX frame failed (%d). " + "Dropping packet.\n", result); + goto error; + } else if (result == 1) { + /* trans_start time will be set when WLP actually transmits + * the frame */ + goto out; + } + result = i1480u_xmit_frame(&i1480u->wlp, skb, &dst); + if (result < 0) { + dev_err(dev, "Frame TX failed (%d).\n", result); + goto error; + } + return NETDEV_TX_OK; +error: + dev_kfree_skb_any(skb); + net_dev->stats.tx_dropped++; +out: + return NETDEV_TX_OK; +} + + +/* + * Called when a pkt transmission doesn't complete in a reasonable period + * Device reset may sleep - do it outside of interrupt context (delayed) + */ +void i1480u_tx_timeout(struct net_device *net_dev) +{ + struct i1480u *i1480u = netdev_priv(net_dev); + + wlp_reset_all(&i1480u->wlp); +} + + +void i1480u_tx_release(struct i1480u *i1480u) +{ + unsigned long flags; + struct i1480u_tx *wtx, *next; + int count = 0, empty; + + spin_lock_irqsave(&i1480u->tx_list_lock, flags); + list_for_each_entry_safe(wtx, next, &i1480u->tx_list, list_node) { + count++; + usb_unlink_urb(wtx->urb); + } + spin_unlock_irqrestore(&i1480u->tx_list_lock, flags); + count = count*10; /* i1480ut 200ms per unlinked urb (intervals of 20ms) */ + /* + * We don't like this sollution too much (dirty as it is), but + * it is cheaper than putting a refcount on each i1480u_tx and + * i1480uting for all of them to go away... + * + * Called when no more packets can be added to tx_list + * so can i1480ut for it to be empty. + */ + while (1) { + spin_lock_irqsave(&i1480u->tx_list_lock, flags); + empty = list_empty(&i1480u->tx_list); + spin_unlock_irqrestore(&i1480u->tx_list_lock, flags); + if (empty) + break; + count--; + BUG_ON(count == 0); + msleep(20); + } +} diff --git a/trunk/drivers/uwb/wlp/Makefile b/trunk/drivers/uwb/wlp/Makefile new file mode 100644 index 000000000000..c72c11db5b1b --- /dev/null +++ b/trunk/drivers/uwb/wlp/Makefile @@ -0,0 +1,10 @@ +obj-$(CONFIG_UWB_WLP) := wlp.o + +wlp-objs := \ + driver.o \ + eda.o \ + messages.o \ + sysfs.o \ + txrx.o \ + wlp-lc.o \ + wss-lc.o diff --git a/trunk/drivers/uwb/wlp/driver.c b/trunk/drivers/uwb/wlp/driver.c new file mode 100644 index 000000000000..cb8d699b6a67 --- /dev/null +++ b/trunk/drivers/uwb/wlp/driver.c @@ -0,0 +1,43 @@ +/* + * WiMedia Logical Link Control Protocol (WLP) + * + * Copyright (C) 2007 Intel Corporation + * Reinette Chatre + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * Life cycle of WLP substack + * + * FIXME: Docs + */ + +#include + +static int __init wlp_subsys_init(void) +{ + return 0; +} +module_init(wlp_subsys_init); + +static void __exit wlp_subsys_exit(void) +{ + return; +} +module_exit(wlp_subsys_exit); + +MODULE_AUTHOR("Reinette Chatre "); +MODULE_DESCRIPTION("WiMedia Logical Link Control Protocol (WLP)"); +MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/uwb/wlp/eda.c b/trunk/drivers/uwb/wlp/eda.c new file mode 100644 index 000000000000..086fc0cf9401 --- /dev/null +++ b/trunk/drivers/uwb/wlp/eda.c @@ -0,0 +1,415 @@ +/* + * WUSB Wire Adapter: WLP interface + * Ethernet to device address cache + * + * Copyright (C) 2005-2006 Intel Corporation + * Inaky Perez-Gonzalez + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * We need to be able to map ethernet addresses to device addresses + * and back because there is not explicit relationship between the eth + * addresses used in the ETH frames and the device addresses (no, it + * would not have been simpler to force as ETH address the MBOA MAC + * address...no, not at all :). + * + * A device has one MBOA MAC address and one device address. It is possible + * for a device to have more than one virtual MAC address (although a + * virtual address can be the same as the MBOA MAC address). The device + * address is guaranteed to be unique among the devices in the extended + * beacon group (see ECMA 17.1.1). We thus use the device address as index + * to this cache. We do allow searching based on virtual address as this + * is how Ethernet frames will be addressed. + * + * We need to support virtual EUI-48. Although, right now the virtual + * EUI-48 will always be the same as the MAC SAP address. The EDA cache + * entry thus contains a MAC SAP address as well as the virtual address + * (used to map the network stack address to a neighbor). When we move + * to support more than one virtual MAC on a host then this organization + * will have to change. Perhaps a neighbor has a list of WSSs, each with a + * tag and virtual EUI-48. + * + * On data transmission + * it is used to determine if the neighbor is connected and what WSS it + * belongs to. With this we know what tag to add to the WLP frame. Storing + * the WSS in the EDA cache may be overkill because we only support one + * WSS. Hopefully we will support more than one WSS at some point. + * On data reception it is used to determine the WSS based on + * the tag and address of the transmitting neighbor. + */ + +#include +#include +#include +#include +#include "wlp-internal.h" + + +/* FIXME: cache is not purged, only on device close */ + +/* FIXME: does not scale, change to dynamic array */ + +/* + * Initialize the EDA cache + * + * @returns 0 if ok, < 0 errno code on error + * + * Call when the interface is being brought up + * + * NOTE: Keep it as a separate function as the implementation will + * change and be more complex. + */ +void wlp_eda_init(struct wlp_eda *eda) +{ + INIT_LIST_HEAD(&eda->cache); + spin_lock_init(&eda->lock); +} + +/* + * Release the EDA cache + * + * @returns 0 if ok, < 0 errno code on error + * + * Called when the interface is brought down + */ +void wlp_eda_release(struct wlp_eda *eda) +{ + unsigned long flags; + struct wlp_eda_node *itr, *next; + + spin_lock_irqsave(&eda->lock, flags); + list_for_each_entry_safe(itr, next, &eda->cache, list_node) { + list_del(&itr->list_node); + kfree(itr); + } + spin_unlock_irqrestore(&eda->lock, flags); +} + +/* + * Add an address mapping + * + * @returns 0 if ok, < 0 errno code on error + * + * An address mapping is initially created when the neighbor device is seen + * for the first time (it is "onair"). At this time the neighbor is not + * connected or associated with a WSS so we only populate the Ethernet and + * Device address fields. + * + */ +int wlp_eda_create_node(struct wlp_eda *eda, + const unsigned char eth_addr[ETH_ALEN], + const struct uwb_dev_addr *dev_addr) +{ + int result = 0; + struct wlp_eda_node *itr; + unsigned long flags; + + BUG_ON(dev_addr == NULL || eth_addr == NULL); + spin_lock_irqsave(&eda->lock, flags); + list_for_each_entry(itr, &eda->cache, list_node) { + if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) { + printk(KERN_ERR "EDA cache already contains entry " + "for neighbor %02x:%02x\n", + dev_addr->data[1], dev_addr->data[0]); + result = -EEXIST; + goto out_unlock; + } + } + itr = kzalloc(sizeof(*itr), GFP_ATOMIC); + if (itr != NULL) { + memcpy(itr->eth_addr, eth_addr, sizeof(itr->eth_addr)); + itr->dev_addr = *dev_addr; + list_add(&itr->list_node, &eda->cache); + } else + result = -ENOMEM; +out_unlock: + spin_unlock_irqrestore(&eda->lock, flags); + return result; +} + +/* + * Remove entry from EDA cache + * + * This is done when the device goes off air. + */ +void wlp_eda_rm_node(struct wlp_eda *eda, const struct uwb_dev_addr *dev_addr) +{ + struct wlp_eda_node *itr, *next; + unsigned long flags; + + spin_lock_irqsave(&eda->lock, flags); + list_for_each_entry_safe(itr, next, &eda->cache, list_node) { + if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) { + list_del(&itr->list_node); + kfree(itr); + break; + } + } + spin_unlock_irqrestore(&eda->lock, flags); +} + +/* + * Update an address mapping + * + * @returns 0 if ok, < 0 errno code on error + */ +int wlp_eda_update_node(struct wlp_eda *eda, + const struct uwb_dev_addr *dev_addr, + struct wlp_wss *wss, + const unsigned char virt_addr[ETH_ALEN], + const u8 tag, const enum wlp_wss_connect state) +{ + int result = -ENOENT; + struct wlp_eda_node *itr; + unsigned long flags; + + spin_lock_irqsave(&eda->lock, flags); + list_for_each_entry(itr, &eda->cache, list_node) { + if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) { + /* Found it, update it */ + itr->wss = wss; + memcpy(itr->virt_addr, virt_addr, + sizeof(itr->virt_addr)); + itr->tag = tag; + itr->state = state; + result = 0; + goto out_unlock; + } + } + /* Not found */ +out_unlock: + spin_unlock_irqrestore(&eda->lock, flags); + return result; +} + +/* + * Update only state field of an address mapping + * + * @returns 0 if ok, < 0 errno code on error + */ +int wlp_eda_update_node_state(struct wlp_eda *eda, + const struct uwb_dev_addr *dev_addr, + const enum wlp_wss_connect state) +{ + int result = -ENOENT; + struct wlp_eda_node *itr; + unsigned long flags; + + spin_lock_irqsave(&eda->lock, flags); + list_for_each_entry(itr, &eda->cache, list_node) { + if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) { + /* Found it, update it */ + itr->state = state; + result = 0; + goto out_unlock; + } + } + /* Not found */ +out_unlock: + spin_unlock_irqrestore(&eda->lock, flags); + return result; +} + +/* + * Return contents of EDA cache entry + * + * @dev_addr: index to EDA cache + * @eda_entry: pointer to where contents of EDA cache will be copied + */ +int wlp_copy_eda_node(struct wlp_eda *eda, struct uwb_dev_addr *dev_addr, + struct wlp_eda_node *eda_entry) +{ + int result = -ENOENT; + struct wlp_eda_node *itr; + unsigned long flags; + + spin_lock_irqsave(&eda->lock, flags); + list_for_each_entry(itr, &eda->cache, list_node) { + if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) { + *eda_entry = *itr; + result = 0; + goto out_unlock; + } + } + /* Not found */ +out_unlock: + spin_unlock_irqrestore(&eda->lock, flags); + return result; +} + +/* + * Execute function for every element in the cache + * + * @function: function to execute on element of cache (must be atomic) + * @priv: private data of function + * @returns: result of first function that failed, or last function + * executed if no function failed. + * + * Stop executing when function returns error for any element in cache. + * + * IMPORTANT: We are using a spinlock here: the function executed on each + * element has to be atomic. + */ +int wlp_eda_for_each(struct wlp_eda *eda, wlp_eda_for_each_f function, + void *priv) +{ + int result = 0; + struct wlp *wlp = container_of(eda, struct wlp, eda); + struct wlp_eda_node *entry; + unsigned long flags; + + spin_lock_irqsave(&eda->lock, flags); + list_for_each_entry(entry, &eda->cache, list_node) { + result = (*function)(wlp, entry, priv); + if (result < 0) + break; + } + spin_unlock_irqrestore(&eda->lock, flags); + return result; +} + +/* + * Execute function for single element in the cache (return dev addr) + * + * @virt_addr: index into EDA cache used to determine which element to + * execute the function on + * @dev_addr: device address of element in cache will be returned using + * @dev_addr + * @function: function to execute on element of cache (must be atomic) + * @priv: private data of function + * @returns: result of function + * + * IMPORTANT: We are using a spinlock here: the function executed on the + * element has to be atomic. + */ +int wlp_eda_for_virtual(struct wlp_eda *eda, + const unsigned char virt_addr[ETH_ALEN], + struct uwb_dev_addr *dev_addr, + wlp_eda_for_each_f function, + void *priv) +{ + int result = 0; + struct wlp *wlp = container_of(eda, struct wlp, eda); + struct wlp_eda_node *itr; + unsigned long flags; + int found = 0; + + spin_lock_irqsave(&eda->lock, flags); + list_for_each_entry(itr, &eda->cache, list_node) { + if (!memcmp(itr->virt_addr, virt_addr, + sizeof(itr->virt_addr))) { + result = (*function)(wlp, itr, priv); + *dev_addr = itr->dev_addr; + found = 1; + break; + } + } + if (!found) + result = -ENODEV; + spin_unlock_irqrestore(&eda->lock, flags); + return result; +} + +static const char *__wlp_wss_connect_state[] = { "WLP_WSS_UNCONNECTED", + "WLP_WSS_CONNECTED", + "WLP_WSS_CONNECT_FAILED", +}; + +static const char *wlp_wss_connect_state_str(unsigned id) +{ + if (id >= ARRAY_SIZE(__wlp_wss_connect_state)) + return "unknown WSS connection state"; + return __wlp_wss_connect_state[id]; +} + +/* + * View EDA cache from user space + * + * A debugging feature to give user visibility into the EDA cache. Also + * used to display members of WSS to user (called from wlp_wss_members_show()) + */ +ssize_t wlp_eda_show(struct wlp *wlp, char *buf) +{ + ssize_t result = 0; + struct wlp_eda_node *entry; + unsigned long flags; + struct wlp_eda *eda = &wlp->eda; + spin_lock_irqsave(&eda->lock, flags); + result = scnprintf(buf, PAGE_SIZE, "#eth_addr dev_addr wss_ptr " + "tag state virt_addr\n"); + list_for_each_entry(entry, &eda->cache, list_node) { + result += scnprintf(buf + result, PAGE_SIZE - result, + "%pM %02x:%02x %p 0x%02x %s %pM\n", + entry->eth_addr, + entry->dev_addr.data[1], + entry->dev_addr.data[0], entry->wss, + entry->tag, + wlp_wss_connect_state_str(entry->state), + entry->virt_addr); + if (result >= PAGE_SIZE) + break; + } + spin_unlock_irqrestore(&eda->lock, flags); + return result; +} +EXPORT_SYMBOL_GPL(wlp_eda_show); + +/* + * Add new EDA cache entry based on user input in sysfs + * + * Should only be used for debugging. + * + * The WSS is assumed to be the only WSS supported. This needs to be + * redesigned when we support more than one WSS. + */ +ssize_t wlp_eda_store(struct wlp *wlp, const char *buf, size_t size) +{ + ssize_t result; + struct wlp_eda *eda = &wlp->eda; + u8 eth_addr[6]; + struct uwb_dev_addr dev_addr; + u8 tag; + unsigned state; + + result = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx " + "%02hhx:%02hhx %02hhx %u\n", + ð_addr[0], ð_addr[1], + ð_addr[2], ð_addr[3], + ð_addr[4], ð_addr[5], + &dev_addr.data[1], &dev_addr.data[0], &tag, &state); + switch (result) { + case 6: /* no dev addr specified -- remove entry NOT IMPLEMENTED */ + /*result = wlp_eda_rm(eda, eth_addr, &dev_addr);*/ + result = -ENOSYS; + break; + case 10: + state = state >= 1 ? 1 : 0; + result = wlp_eda_create_node(eda, eth_addr, &dev_addr); + if (result < 0 && result != -EEXIST) + goto error; + /* Set virtual addr to be same as MAC */ + result = wlp_eda_update_node(eda, &dev_addr, &wlp->wss, + eth_addr, tag, state); + if (result < 0) + goto error; + break; + default: /* bad format */ + result = -EINVAL; + } +error: + return result < 0 ? result : size; +} +EXPORT_SYMBOL_GPL(wlp_eda_store); diff --git a/trunk/drivers/uwb/wlp/messages.c b/trunk/drivers/uwb/wlp/messages.c new file mode 100644 index 000000000000..3a8e033dce21 --- /dev/null +++ b/trunk/drivers/uwb/wlp/messages.c @@ -0,0 +1,1798 @@ +/* + * WiMedia Logical Link Control Protocol (WLP) + * Message construction and parsing + * + * Copyright (C) 2007 Intel Corporation + * Reinette Chatre + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * FIXME: docs + */ + +#include +#include + +#include "wlp-internal.h" + +static +const char *__wlp_assoc_frame[] = { + [WLP_ASSOC_D1] = "WLP_ASSOC_D1", + [WLP_ASSOC_D2] = "WLP_ASSOC_D2", + [WLP_ASSOC_M1] = "WLP_ASSOC_M1", + [WLP_ASSOC_M2] = "WLP_ASSOC_M2", + [WLP_ASSOC_M3] = "WLP_ASSOC_M3", + [WLP_ASSOC_M4] = "WLP_ASSOC_M4", + [WLP_ASSOC_M5] = "WLP_ASSOC_M5", + [WLP_ASSOC_M6] = "WLP_ASSOC_M6", + [WLP_ASSOC_M7] = "WLP_ASSOC_M7", + [WLP_ASSOC_M8] = "WLP_ASSOC_M8", + [WLP_ASSOC_F0] = "WLP_ASSOC_F0", + [WLP_ASSOC_E1] = "WLP_ASSOC_E1", + [WLP_ASSOC_E2] = "WLP_ASSOC_E2", + [WLP_ASSOC_C1] = "WLP_ASSOC_C1", + [WLP_ASSOC_C2] = "WLP_ASSOC_C2", + [WLP_ASSOC_C3] = "WLP_ASSOC_C3", + [WLP_ASSOC_C4] = "WLP_ASSOC_C4", +}; + +static const char *wlp_assoc_frame_str(unsigned id) +{ + if (id >= ARRAY_SIZE(__wlp_assoc_frame)) + return "unknown association frame"; + return __wlp_assoc_frame[id]; +} + +static const char *__wlp_assc_error[] = { + "none", + "Authenticator Failure", + "Rogue activity suspected", + "Device busy", + "Setup Locked", + "Registrar not ready", + "Invalid WSS selection", + "Message timeout", + "Enrollment session timeout", + "Device password invalid", + "Unsupported version", + "Internal error", + "Undefined error", + "Numeric comparison failure", + "Waiting for user input", +}; + +static const char *wlp_assc_error_str(unsigned id) +{ + if (id >= ARRAY_SIZE(__wlp_assc_error)) + return "unknown WLP association error"; + return __wlp_assc_error[id]; +} + +static inline void wlp_set_attr_hdr(struct wlp_attr_hdr *hdr, unsigned type, + size_t len) +{ + hdr->type = cpu_to_le16(type); + hdr->length = cpu_to_le16(len); +} + +/* + * Populate fields of a constant sized attribute + * + * @returns: total size of attribute including size of new value + * + * We have two instances of this function (wlp_pset and wlp_set): one takes + * the value as a parameter, the other takes a pointer to the value as + * parameter. They thus only differ in how the value is assigned to the + * attribute. + * + * We use sizeof(*attr) - sizeof(struct wlp_attr_hdr) instead of + * sizeof(type) to be able to use this same code for the structures that + * contain 8bit enum values and be able to deal with pointer types. + */ +#define wlp_set(type, type_code, name) \ +static size_t wlp_set_##name(struct wlp_attr_##name *attr, type value) \ +{ \ + wlp_set_attr_hdr(&attr->hdr, type_code, \ + sizeof(*attr) - sizeof(struct wlp_attr_hdr)); \ + attr->name = value; \ + return sizeof(*attr); \ +} + +#define wlp_pset(type, type_code, name) \ +static size_t wlp_set_##name(struct wlp_attr_##name *attr, type value) \ +{ \ + wlp_set_attr_hdr(&attr->hdr, type_code, \ + sizeof(*attr) - sizeof(struct wlp_attr_hdr)); \ + attr->name = *value; \ + return sizeof(*attr); \ +} + +/** + * Populate fields of a variable attribute + * + * @returns: total size of attribute including size of new value + * + * Provided with a pointer to the memory area reserved for the + * attribute structure, the field is populated with the value. The + * reserved memory has to contain enough space for the value. + */ +#define wlp_vset(type, type_code, name) \ +static size_t wlp_set_##name(struct wlp_attr_##name *attr, type value, \ + size_t len) \ +{ \ + wlp_set_attr_hdr(&attr->hdr, type_code, len); \ + memcpy(attr->name, value, len); \ + return sizeof(*attr) + len; \ +} + +wlp_vset(char *, WLP_ATTR_DEV_NAME, dev_name) +wlp_vset(char *, WLP_ATTR_MANUF, manufacturer) +wlp_set(enum wlp_assoc_type, WLP_ATTR_MSG_TYPE, msg_type) +wlp_vset(char *, WLP_ATTR_MODEL_NAME, model_name) +wlp_vset(char *, WLP_ATTR_MODEL_NR, model_nr) +wlp_vset(char *, WLP_ATTR_SERIAL, serial) +wlp_vset(char *, WLP_ATTR_WSS_NAME, wss_name) +wlp_pset(struct wlp_uuid *, WLP_ATTR_UUID_E, uuid_e) +wlp_pset(struct wlp_uuid *, WLP_ATTR_UUID_R, uuid_r) +wlp_pset(struct wlp_uuid *, WLP_ATTR_WSSID, wssid) +wlp_pset(struct wlp_dev_type *, WLP_ATTR_PRI_DEV_TYPE, prim_dev_type) +/*wlp_pset(struct wlp_dev_type *, WLP_ATTR_SEC_DEV_TYPE, sec_dev_type)*/ +wlp_set(u8, WLP_ATTR_WLP_VER, version) +wlp_set(enum wlp_assc_error, WLP_ATTR_WLP_ASSC_ERR, wlp_assc_err) +wlp_set(enum wlp_wss_sel_mthd, WLP_ATTR_WSS_SEL_MTHD, wss_sel_mthd) +wlp_set(u8, WLP_ATTR_ACC_ENRL, accept_enrl) +wlp_set(u8, WLP_ATTR_WSS_SEC_STAT, wss_sec_status) +wlp_pset(struct uwb_mac_addr *, WLP_ATTR_WSS_BCAST, wss_bcast) +wlp_pset(struct wlp_nonce *, WLP_ATTR_ENRL_NONCE, enonce) +wlp_pset(struct wlp_nonce *, WLP_ATTR_REG_NONCE, rnonce) +wlp_set(u8, WLP_ATTR_WSS_TAG, wss_tag) +wlp_pset(struct uwb_mac_addr *, WLP_ATTR_WSS_VIRT, wss_virt) + +/** + * Fill in the WSS information attributes + * + * We currently only support one WSS, and this is assumed in this function + * that can populate only one WSS information attribute. + */ +static size_t wlp_set_wss_info(struct wlp_attr_wss_info *attr, + struct wlp_wss *wss) +{ + size_t datalen; + void *ptr = attr->wss_info; + size_t used = sizeof(*attr); + + datalen = sizeof(struct wlp_wss_info) + strlen(wss->name); + wlp_set_attr_hdr(&attr->hdr, WLP_ATTR_WSS_INFO, datalen); + used = wlp_set_wssid(ptr, &wss->wssid); + used += wlp_set_wss_name(ptr + used, wss->name, strlen(wss->name)); + used += wlp_set_accept_enrl(ptr + used, wss->accept_enroll); + used += wlp_set_wss_sec_status(ptr + used, wss->secure_status); + used += wlp_set_wss_bcast(ptr + used, &wss->bcast); + return sizeof(*attr) + used; +} + +/** + * Verify attribute header + * + * @hdr: Pointer to attribute header that will be verified. + * @type: Expected attribute type. + * @len: Expected length of attribute value (excluding header). + * + * Most attribute values have a known length even when they do have a + * length field. This knowledge can be used via this function to verify + * that the length field matches the expected value. + */ +static int wlp_check_attr_hdr(struct wlp *wlp, struct wlp_attr_hdr *hdr, + enum wlp_attr_type type, unsigned len) +{ + struct device *dev = &wlp->rc->uwb_dev.dev; + + if (le16_to_cpu(hdr->type) != type) { + dev_err(dev, "WLP: unexpected header type. Expected " + "%u, got %u.\n", type, le16_to_cpu(hdr->type)); + return -EINVAL; + } + if (le16_to_cpu(hdr->length) != len) { + dev_err(dev, "WLP: unexpected length in header. Expected " + "%u, got %u.\n", len, le16_to_cpu(hdr->length)); + return -EINVAL; + } + return 0; +} + +/** + * Check if header of WSS information attribute valid + * + * @returns: length of WSS attributes (value of length attribute field) if + * valid WSS information attribute found + * -ENODATA if no WSS information attribute found + * -EIO other error occured + * + * The WSS information attribute is optional. The function will be provided + * with a pointer to data that could _potentially_ be a WSS information + * attribute. If a valid WSS information attribute is found it will return + * 0, if no WSS information attribute is found it will return -ENODATA, and + * another error will be returned if it is a WSS information attribute, but + * some parsing failure occured. + */ +static int wlp_check_wss_info_attr_hdr(struct wlp *wlp, + struct wlp_attr_hdr *hdr, size_t buflen) +{ + struct device *dev = &wlp->rc->uwb_dev.dev; + size_t len; + int result = 0; + + if (buflen < sizeof(*hdr)) { + dev_err(dev, "WLP: Not enough space in buffer to parse" + " WSS information attribute header.\n"); + result = -EIO; + goto out; + } + if (le16_to_cpu(hdr->type) != WLP_ATTR_WSS_INFO) { + /* WSS information is optional */ + result = -ENODATA; + goto out; + } + len = le16_to_cpu(hdr->length); + if (buflen < sizeof(*hdr) + len) { + dev_err(dev, "WLP: Not enough space in buffer to parse " + "variable data. Got %d, expected %d.\n", + (int)buflen, (int)(sizeof(*hdr) + len)); + result = -EIO; + goto out; + } + result = len; +out: + return result; +} + + +static ssize_t wlp_get_attribute(struct wlp *wlp, u16 type_code, + struct wlp_attr_hdr *attr_hdr, void *value, ssize_t value_len, + ssize_t buflen) +{ + struct device *dev = &wlp->rc->uwb_dev.dev; + ssize_t attr_len = sizeof(*attr_hdr) + value_len; + if (buflen < 0) + return -EINVAL; + if (buflen < attr_len) { + dev_err(dev, "WLP: Not enough space in buffer to parse" + " attribute field. Need %d, received %zu\n", + (int)attr_len, buflen); + return -EIO; + } + if (wlp_check_attr_hdr(wlp, attr_hdr, type_code, value_len) < 0) { + dev_err(dev, "WLP: Header verification failed. \n"); + return -EINVAL; + } + memcpy(value, (void *)attr_hdr + sizeof(*attr_hdr), value_len); + return attr_len; +} + +static ssize_t wlp_vget_attribute(struct wlp *wlp, u16 type_code, + struct wlp_attr_hdr *attr_hdr, void *value, ssize_t max_value_len, + ssize_t buflen) +{ + struct device *dev = &wlp->rc->uwb_dev.dev; + size_t len; + if (buflen < 0) + return -EINVAL; + if (buflen < sizeof(*attr_hdr)) { + dev_err(dev, "WLP: Not enough space in buffer to parse" + " header.\n"); + return -EIO; + } + if (le16_to_cpu(attr_hdr->type) != type_code) { + dev_err(dev, "WLP: Unexpected attribute type. Got %u, " + "expected %u.\n", le16_to_cpu(attr_hdr->type), + type_code); + return -EINVAL; + } + len = le16_to_cpu(attr_hdr->length); + if (len > max_value_len) { + dev_err(dev, "WLP: Attribute larger than maximum " + "allowed. Received %zu, max is %d.\n", len, + (int)max_value_len); + return -EFBIG; + } + if (buflen < sizeof(*attr_hdr) + len) { + dev_err(dev, "WLP: Not enough space in buffer to parse " + "variable data.\n"); + return -EIO; + } + memcpy(value, (void *)attr_hdr + sizeof(*attr_hdr), len); + return sizeof(*attr_hdr) + len; +} + +/** + * Get value of attribute from fixed size attribute field. + * + * @attr: Pointer to attribute field. + * @value: Pointer to variable in which attribute value will be placed. + * @buflen: Size of buffer in which attribute field (including header) + * can be found. + * @returns: Amount of given buffer consumed by parsing for this attribute. + * + * The size and type of the value is known by the type of the attribute. + */ +#define wlp_get(type, type_code, name) \ +ssize_t wlp_get_##name(struct wlp *wlp, struct wlp_attr_##name *attr, \ + type *value, ssize_t buflen) \ +{ \ + return wlp_get_attribute(wlp, (type_code), &attr->hdr, \ + value, sizeof(*value), buflen); \ +} + +#define wlp_get_sparse(type, type_code, name) \ + static wlp_get(type, type_code, name) + +/** + * Get value of attribute from variable sized attribute field. + * + * @max: The maximum size of this attribute. This value is dictated by + * the maximum value from the WLP specification. + * + * @attr: Pointer to attribute field. + * @value: Pointer to variable that will contain the value. The memory + * must already have been allocated for this value. + * @buflen: Size of buffer in which attribute field (including header) + * can be found. + * @returns: Amount of given bufferconsumed by parsing for this attribute. + */ +#define wlp_vget(type_val, type_code, name, max) \ +static ssize_t wlp_get_##name(struct wlp *wlp, \ + struct wlp_attr_##name *attr, \ + type_val *value, ssize_t buflen) \ +{ \ + return wlp_vget_attribute(wlp, (type_code), &attr->hdr, \ + value, (max), buflen); \ +} + +wlp_get(u8, WLP_ATTR_WLP_VER, version) +wlp_get_sparse(enum wlp_wss_sel_mthd, WLP_ATTR_WSS_SEL_MTHD, wss_sel_mthd) +wlp_get_sparse(struct wlp_dev_type, WLP_ATTR_PRI_DEV_TYPE, prim_dev_type) +wlp_get_sparse(enum wlp_assc_error, WLP_ATTR_WLP_ASSC_ERR, wlp_assc_err) +wlp_get_sparse(struct wlp_uuid, WLP_ATTR_UUID_E, uuid_e) +wlp_get_sparse(struct wlp_uuid, WLP_ATTR_UUID_R, uuid_r) +wlp_get(struct wlp_uuid, WLP_ATTR_WSSID, wssid) +wlp_get_sparse(u8, WLP_ATTR_ACC_ENRL, accept_enrl) +wlp_get_sparse(u8, WLP_ATTR_WSS_SEC_STAT, wss_sec_status) +wlp_get_sparse(struct uwb_mac_addr, WLP_ATTR_WSS_BCAST, wss_bcast) +wlp_get_sparse(u8, WLP_ATTR_WSS_TAG, wss_tag) +wlp_get_sparse(struct uwb_mac_addr, WLP_ATTR_WSS_VIRT, wss_virt) +wlp_get_sparse(struct wlp_nonce, WLP_ATTR_ENRL_NONCE, enonce) +wlp_get_sparse(struct wlp_nonce, WLP_ATTR_REG_NONCE, rnonce) + +/* The buffers for the device info attributes can be found in the + * wlp_device_info struct. These buffers contain one byte more than the + * max allowed by the spec - this is done to be able to add the + * terminating \0 for user display. This terminating byte is not required + * in the actual attribute field (because it has a length field) so the + * maximum allowed for this value is one less than its size in the + * structure. + */ +wlp_vget(char, WLP_ATTR_WSS_NAME, wss_name, + FIELD_SIZEOF(struct wlp_wss, name) - 1) +wlp_vget(char, WLP_ATTR_DEV_NAME, dev_name, + FIELD_SIZEOF(struct wlp_device_info, name) - 1) +wlp_vget(char, WLP_ATTR_MANUF, manufacturer, + FIELD_SIZEOF(struct wlp_device_info, manufacturer) - 1) +wlp_vget(char, WLP_ATTR_MODEL_NAME, model_name, + FIELD_SIZEOF(struct wlp_device_info, model_name) - 1) +wlp_vget(char, WLP_ATTR_MODEL_NR, model_nr, + FIELD_SIZEOF(struct wlp_device_info, model_nr) - 1) +wlp_vget(char, WLP_ATTR_SERIAL, serial, + FIELD_SIZEOF(struct wlp_device_info, serial) - 1) + +/** + * Retrieve WSS Name, Accept enroll, Secure status, Broadcast from WSS info + * + * @attr: pointer to WSS name attribute in WSS information attribute field + * @info: structure that will be populated with data from WSS information + * field (WSS name, Accept enroll, secure status, broadcast address) + * @buflen: size of buffer + * + * Although the WSSID attribute forms part of the WSS info attribute it is + * retrieved separately and stored in a different location. + */ +static ssize_t wlp_get_wss_info_attrs(struct wlp *wlp, + struct wlp_attr_hdr *attr, + struct wlp_wss_tmp_info *info, + ssize_t buflen) +{ + struct device *dev = &wlp->rc->uwb_dev.dev; + void *ptr = attr; + size_t used = 0; + ssize_t result = -EINVAL; + + result = wlp_get_wss_name(wlp, ptr, info->name, buflen); + if (result < 0) { + dev_err(dev, "WLP: unable to obtain WSS name from " + "WSS info in D2 message.\n"); + goto error_parse; + } + used += result; + + result = wlp_get_accept_enrl(wlp, ptr + used, &info->accept_enroll, + buflen - used); + if (result < 0) { + dev_err(dev, "WLP: unable to obtain accepting " + "enrollment from WSS info in D2 message.\n"); + goto error_parse; + } + if (info->accept_enroll != 0 && info->accept_enroll != 1) { + dev_err(dev, "WLP: invalid value for accepting " + "enrollment in D2 message.\n"); + result = -EINVAL; + goto error_parse; + } + used += result; + + result = wlp_get_wss_sec_status(wlp, ptr + used, &info->sec_status, + buflen - used); + if (result < 0) { + dev_err(dev, "WLP: unable to obtain secure " + "status from WSS info in D2 message.\n"); + goto error_parse; + } + if (info->sec_status != 0 && info->sec_status != 1) { + dev_err(dev, "WLP: invalid value for secure " + "status in D2 message.\n"); + result = -EINVAL; + goto error_parse; + } + used += result; + + result = wlp_get_wss_bcast(wlp, ptr + used, &info->bcast, + buflen - used); + if (result < 0) { + dev_err(dev, "WLP: unable to obtain broadcast " + "address from WSS info in D2 message.\n"); + goto error_parse; + } + used += result; + result = used; +error_parse: + return result; +} + +/** + * Create a new WSSID entry for the neighbor, allocate temporary storage + * + * Each neighbor can have many WSS active. We maintain a list of WSSIDs + * advertised by neighbor. During discovery we also cache information about + * these WSS in temporary storage. + * + * The temporary storage will be removed after it has been used (eg. + * displayed to user), the wssid element will be removed from the list when + * the neighbor is rediscovered or when it disappears. + */ +static struct wlp_wssid_e *wlp_create_wssid_e(struct wlp *wlp, + struct wlp_neighbor_e *neighbor) +{ + struct device *dev = &wlp->rc->uwb_dev.dev; + struct wlp_wssid_e *wssid_e; + + wssid_e = kzalloc(sizeof(*wssid_e), GFP_KERNEL); + if (wssid_e == NULL) { + dev_err(dev, "WLP: unable to allocate memory " + "for WSS information.\n"); + goto error_alloc; + } + wssid_e->info = kzalloc(sizeof(struct wlp_wss_tmp_info), GFP_KERNEL); + if (wssid_e->info == NULL) { + dev_err(dev, "WLP: unable to allocate memory " + "for temporary WSS information.\n"); + kfree(wssid_e); + wssid_e = NULL; + goto error_alloc; + } + list_add(&wssid_e->node, &neighbor->wssid); +error_alloc: + return wssid_e; +} + +/** + * Parse WSS information attribute + * + * @attr: pointer to WSS information attribute header + * @buflen: size of buffer in which WSS information attribute appears + * @wssid: will place wssid from WSS info attribute in this location + * @wss_info: will place other information from WSS information attribute + * in this location + * + * memory for @wssid and @wss_info must be allocated when calling this + */ +static ssize_t wlp_get_wss_info(struct wlp *wlp, struct wlp_attr_wss_info *attr, + size_t buflen, struct wlp_uuid *wssid, + struct wlp_wss_tmp_info *wss_info) +{ + struct device *dev = &wlp->rc->uwb_dev.dev; + ssize_t result; + size_t len; + size_t used = 0; + void *ptr; + + result = wlp_check_wss_info_attr_hdr(wlp, (struct wlp_attr_hdr *)attr, + buflen); + if (result < 0) + goto out; + len = result; + used = sizeof(*attr); + ptr = attr; + + result = wlp_get_wssid(wlp, ptr + used, wssid, buflen - used); + if (result < 0) { + dev_err(dev, "WLP: unable to obtain WSSID from WSS info.\n"); + goto out; + } + used += result; + result = wlp_get_wss_info_attrs(wlp, ptr + used, wss_info, + buflen - used); + if (result < 0) { + dev_err(dev, "WLP: unable to obtain WSS information " + "from WSS information attributes. \n"); + goto out; + } + used += result; + if (len + sizeof(*attr) != used) { + dev_err(dev, "WLP: Amount of data parsed does not " + "match length field. Parsed %zu, length " + "field %zu. \n", used, len); + result = -EINVAL; + goto out; + } + result = used; +out: + return result; +} + +/** + * Retrieve WSS info from association frame + * + * @attr: pointer to WSS information attribute + * @neighbor: ptr to neighbor being discovered, NULL if enrollment in + * progress + * @wss: ptr to WSS being enrolled in, NULL if discovery in progress + * @buflen: size of buffer in which WSS information appears + * + * The WSS information attribute appears in the D2 association message. + * This message is used in two ways: to discover all neighbors or to enroll + * into a WSS activated by a neighbor. During discovery we only want to + * store the WSS info in a cache, to be deleted right after it has been + * used (eg. displayed to the user). During enrollment we store the WSS + * information for the lifetime of enrollment. + * + * During discovery we are interested in all WSS information, during + * enrollment we are only interested in the WSS being enrolled in. Even so, + * when in enrollment we keep parsing the message after finding the WSS of + * interest, this simplifies the calling routine in that it can be sure + * that all WSS information attributes have been parsed out of the message. + * + * Association frame is process with nbmutex held. The list access is safe. + */ +static ssize_t wlp_get_all_wss_info(struct wlp *wlp, + struct wlp_attr_wss_info *attr, + struct wlp_neighbor_e *neighbor, + struct wlp_wss *wss, ssize_t buflen) +{ + struct device *dev = &wlp->rc->uwb_dev.dev; + size_t used = 0; + ssize_t result = -EINVAL; + struct wlp_attr_wss_info *cur; + struct wlp_uuid wssid; + struct wlp_wss_tmp_info wss_info; + unsigned enroll; /* 0 - discovery to cache, 1 - enrollment */ + struct wlp_wssid_e *wssid_e; + char buf[WLP_WSS_UUID_STRSIZE]; + + if (buflen < 0) + goto out; + + if (neighbor != NULL && wss == NULL) + enroll = 0; /* discovery */ + else if (wss != NULL && neighbor == NULL) + enroll = 1; /* enrollment */ + else + goto out; + + cur = attr; + while (buflen - used > 0) { + memset(&wss_info, 0, sizeof(wss_info)); + cur = (void *)cur + used; + result = wlp_get_wss_info(wlp, cur, buflen - used, &wssid, + &wss_info); + if (result == -ENODATA) { + result = used; + goto out; + } else if (result < 0) { + dev_err(dev, "WLP: Unable to parse WSS information " + "from WSS information attribute. \n"); + result = -EINVAL; + goto error_parse; + } + if (enroll && !memcmp(&wssid, &wss->wssid, sizeof(wssid))) { + if (wss_info.accept_enroll != 1) { + dev_err(dev, "WLP: Requested WSS does " + "not accept enrollment.\n"); + result = -EINVAL; + goto out; + } + memcpy(wss->name, wss_info.name, sizeof(wss->name)); + wss->bcast = wss_info.bcast; + wss->secure_status = wss_info.sec_status; + wss->accept_enroll = wss_info.accept_enroll; + wss->state = WLP_WSS_STATE_PART_ENROLLED; + wlp_wss_uuid_print(buf, sizeof(buf), &wssid); + dev_dbg(dev, "WLP: Found WSS %s. Enrolling.\n", buf); + } else { + wssid_e = wlp_create_wssid_e(wlp, neighbor); + if (wssid_e == NULL) { + dev_err(dev, "WLP: Cannot create new WSSID " + "entry for neighbor %02x:%02x.\n", + neighbor->uwb_dev->dev_addr.data[1], + neighbor->uwb_dev->dev_addr.data[0]); + result = -ENOMEM; + goto out; + } + wssid_e->wssid = wssid; + *wssid_e->info = wss_info; + } + used += result; + } + result = used; +error_parse: + if (result < 0 && !enroll) /* this was a discovery */ + wlp_remove_neighbor_tmp_info(neighbor); +out: + return result; + +} + +/** + * Parse WSS information attributes into cache for discovery + * + * @attr: the first WSS information attribute in message + * @neighbor: the neighbor whose cache will be populated + * @buflen: size of the input buffer + */ +static ssize_t wlp_get_wss_info_to_cache(struct wlp *wlp, + struct wlp_attr_wss_info *attr, + struct wlp_neighbor_e *neighbor, + ssize_t buflen) +{ + return wlp_get_all_wss_info(wlp, attr, neighbor, NULL, buflen); +} + +/** + * Parse WSS information attributes into WSS struct for enrollment + * + * @attr: the first WSS information attribute in message + * @wss: the WSS that will be enrolled + * @buflen: size of the input buffer + */ +static ssize_t wlp_get_wss_info_to_enroll(struct wlp *wlp, + struct wlp_attr_wss_info *attr, + struct wlp_wss *wss, ssize_t buflen) +{ + return wlp_get_all_wss_info(wlp, attr, NULL, wss, buflen); +} + +/** + * Construct a D1 association frame + * + * We use the radio control functions to determine the values of the device + * properties. These are of variable length and the total space needed is + * tallied first before we start constructing the message. The radio + * control functions return strings that are terminated with \0. This + * character should not be included in the message (there is a length field + * accompanying it in the attribute). + */ +static int wlp_build_assoc_d1(struct wlp *wlp, struct wlp_wss *wss, + struct sk_buff **skb) +{ + + struct device *dev = &wlp->rc->uwb_dev.dev; + int result = 0; + struct wlp_device_info *info; + size_t used = 0; + struct wlp_frame_assoc *_d1; + struct sk_buff *_skb; + void *d1_itr; + + if (wlp->dev_info == NULL) { + result = __wlp_setup_device_info(wlp); + if (result < 0) { + dev_err(dev, "WLP: Unable to setup device " + "information for D1 message.\n"); + goto error; + } + } + info = wlp->dev_info; + _skb = dev_alloc_skb(sizeof(*_d1) + + sizeof(struct wlp_attr_uuid_e) + + sizeof(struct wlp_attr_wss_sel_mthd) + + sizeof(struct wlp_attr_dev_name) + + strlen(info->name) + + sizeof(struct wlp_attr_manufacturer) + + strlen(info->manufacturer) + + sizeof(struct wlp_attr_model_name) + + strlen(info->model_name) + + sizeof(struct wlp_attr_model_nr) + + strlen(info->model_nr) + + sizeof(struct wlp_attr_serial) + + strlen(info->serial) + + sizeof(struct wlp_attr_prim_dev_type) + + sizeof(struct wlp_attr_wlp_assc_err)); + if (_skb == NULL) { + dev_err(dev, "WLP: Cannot allocate memory for association " + "message.\n"); + result = -ENOMEM; + goto error; + } + _d1 = (void *) _skb->data; + _d1->hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID); + _d1->hdr.type = WLP_FRAME_ASSOCIATION; + _d1->type = WLP_ASSOC_D1; + + wlp_set_version(&_d1->version, WLP_VERSION); + wlp_set_msg_type(&_d1->msg_type, WLP_ASSOC_D1); + d1_itr = _d1->attr; + used = wlp_set_uuid_e(d1_itr, &wlp->uuid); + used += wlp_set_wss_sel_mthd(d1_itr + used, WLP_WSS_REG_SELECT); + used += wlp_set_dev_name(d1_itr + used, info->name, + strlen(info->name)); + used += wlp_set_manufacturer(d1_itr + used, info->manufacturer, + strlen(info->manufacturer)); + used += wlp_set_model_name(d1_itr + used, info->model_name, + strlen(info->model_name)); + used += wlp_set_model_nr(d1_itr + used, info->model_nr, + strlen(info->model_nr)); + used += wlp_set_serial(d1_itr + used, info->serial, + strlen(info->serial)); + used += wlp_set_prim_dev_type(d1_itr + used, &info->prim_dev_type); + used += wlp_set_wlp_assc_err(d1_itr + used, WLP_ASSOC_ERROR_NONE); + skb_put(_skb, sizeof(*_d1) + used); + *skb = _skb; +error: + return result; +} + +/** + * Construct a D2 association frame + * + * We use the radio control functions to determine the values of the device + * properties. These are of variable length and the total space needed is + * tallied first before we start constructing the message. The radio + * control functions return strings that are terminated with \0. This + * character should not be included in the message (there is a length field + * accompanying it in the attribute). + */ +static +int wlp_build_assoc_d2(struct wlp *wlp, struct wlp_wss *wss, + struct sk_buff **skb, struct wlp_uuid *uuid_e) +{ + + struct device *dev = &wlp->rc->uwb_dev.dev; + int result = 0; + struct wlp_device_info *info; + size_t used = 0; + struct wlp_frame_assoc *_d2; + struct sk_buff *_skb; + void *d2_itr; + size_t mem_needed; + + if (wlp->dev_info == NULL) { + result = __wlp_setup_device_info(wlp); + if (result < 0) { + dev_err(dev, "WLP: Unable to setup device " + "information for D2 message.\n"); + goto error; + } + } + info = wlp->dev_info; + mem_needed = sizeof(*_d2) + + sizeof(struct wlp_attr_uuid_e) + + sizeof(struct wlp_attr_uuid_r) + + sizeof(struct wlp_attr_dev_name) + + strlen(info->name) + + sizeof(struct wlp_attr_manufacturer) + + strlen(info->manufacturer) + + sizeof(struct wlp_attr_model_name) + + strlen(info->model_name) + + sizeof(struct wlp_attr_model_nr) + + strlen(info->model_nr) + + sizeof(struct wlp_attr_serial) + + strlen(info->serial) + + sizeof(struct wlp_attr_prim_dev_type) + + sizeof(struct wlp_attr_wlp_assc_err); + if (wlp->wss.state >= WLP_WSS_STATE_ACTIVE) + mem_needed += sizeof(struct wlp_attr_wss_info) + + sizeof(struct wlp_wss_info) + + strlen(wlp->wss.name); + _skb = dev_alloc_skb(mem_needed); + if (_skb == NULL) { + dev_err(dev, "WLP: Cannot allocate memory for association " + "message.\n"); + result = -ENOMEM; + goto error; + } + _d2 = (void *) _skb->data; + _d2->hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID); + _d2->hdr.type = WLP_FRAME_ASSOCIATION; + _d2->type = WLP_ASSOC_D2; + + wlp_set_version(&_d2->version, WLP_VERSION); + wlp_set_msg_type(&_d2->msg_type, WLP_ASSOC_D2); + d2_itr = _d2->attr; + used = wlp_set_uuid_e(d2_itr, uuid_e); + used += wlp_set_uuid_r(d2_itr + used, &wlp->uuid); + if (wlp->wss.state >= WLP_WSS_STATE_ACTIVE) + used += wlp_set_wss_info(d2_itr + used, &wlp->wss); + used += wlp_set_dev_name(d2_itr + used, info->name, + strlen(info->name)); + used += wlp_set_manufacturer(d2_itr + used, info->manufacturer, + strlen(info->manufacturer)); + used += wlp_set_model_name(d2_itr + used, info->model_name, + strlen(info->model_name)); + used += wlp_set_model_nr(d2_itr + used, info->model_nr, + strlen(info->model_nr)); + used += wlp_set_serial(d2_itr + used, info->serial, + strlen(info->serial)); + used += wlp_set_prim_dev_type(d2_itr + used, &info->prim_dev_type); + used += wlp_set_wlp_assc_err(d2_itr + used, WLP_ASSOC_ERROR_NONE); + skb_put(_skb, sizeof(*_d2) + used); + *skb = _skb; +error: + return result; +} + +/** + * Allocate memory for and populate fields of F0 association frame + * + * Currently (while focusing on unsecure enrollment) we ignore the + * nonce's that could be placed in the message. Only the error field is + * populated by the value provided by the caller. + */ +static +int wlp_build_assoc_f0(struct wlp *wlp, struct sk_buff **skb, + enum wlp_assc_error error) +{ + struct device *dev = &wlp->rc->uwb_dev.dev; + int result = -ENOMEM; + struct { + struct wlp_frame_assoc f0_hdr; + struct wlp_attr_enonce enonce; + struct wlp_attr_rnonce rnonce; + struct wlp_attr_wlp_assc_err assc_err; + } *f0; + struct sk_buff *_skb; + struct wlp_nonce tmp; + + _skb = dev_alloc_skb(sizeof(*f0)); + if (_skb == NULL) { + dev_err(dev, "WLP: Unable to allocate memory for F0 " + "association frame. \n"); + goto error_alloc; + } + f0 = (void *) _skb->data; + f0->f0_hdr.hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID); + f0->f0_hdr.hdr.type = WLP_FRAME_ASSOCIATION; + f0->f0_hdr.type = WLP_ASSOC_F0; + wlp_set_version(&f0->f0_hdr.version, WLP_VERSION); + wlp_set_msg_type(&f0->f0_hdr.msg_type, WLP_ASSOC_F0); + memset(&tmp, 0, sizeof(tmp)); + wlp_set_enonce(&f0->enonce, &tmp); + wlp_set_rnonce(&f0->rnonce, &tmp); + wlp_set_wlp_assc_err(&f0->assc_err, error); + skb_put(_skb, sizeof(*f0)); + *skb = _skb; + result = 0; +error_alloc: + return result; +} + +/** + * Parse F0 frame + * + * We just retrieve the values and print it as an error to the user. + * Calling function already knows an error occured (F0 indicates error), so + * we just parse the content as debug for higher layers. + */ +int wlp_parse_f0(struct wlp *wlp, struct sk_buff *skb) +{ + struct device *dev = &wlp->rc->uwb_dev.dev; + struct wlp_frame_assoc *f0 = (void *) skb->data; + void *ptr = skb->data; + size_t len = skb->len; + size_t used; + ssize_t result; + struct wlp_nonce enonce, rnonce; + enum wlp_assc_error assc_err; + char enonce_buf[WLP_WSS_NONCE_STRSIZE]; + char rnonce_buf[WLP_WSS_NONCE_STRSIZE]; + + used = sizeof(*f0); + result = wlp_get_enonce(wlp, ptr + used, &enonce, len - used); + if (result < 0) { + dev_err(dev, "WLP: unable to obtain Enrollee nonce " + "attribute from F0 message.\n"); + goto error_parse; + } + used += result; + result = wlp_get_rnonce(wlp, ptr + used, &rnonce, len - used); + if (result < 0) { + dev_err(dev, "WLP: unable to obtain Registrar nonce " + "attribute from F0 message.\n"); + goto error_parse; + } + used += result; + result = wlp_get_wlp_assc_err(wlp, ptr + used, &assc_err, len - used); + if (result < 0) { + dev_err(dev, "WLP: unable to obtain WLP Association error " + "attribute from F0 message.\n"); + goto error_parse; + } + wlp_wss_nonce_print(enonce_buf, sizeof(enonce_buf), &enonce); + wlp_wss_nonce_print(rnonce_buf, sizeof(rnonce_buf), &rnonce); + dev_err(dev, "WLP: Received F0 error frame from neighbor. Enrollee " + "nonce: %s, Registrar nonce: %s, WLP Association error: %s.\n", + enonce_buf, rnonce_buf, wlp_assc_error_str(assc_err)); + result = 0; +error_parse: + return result; +} + +/** + * Retrieve variable device information from association message + * + * The device information parsed is not required in any message. This + * routine will thus not fail if an attribute is not present. + * The attributes are expected in a certain order, even if all are not + * present. The "attribute type" value is used to ensure the attributes + * are parsed in the correct order. + * + * If an error is encountered during parsing the function will return an + * error code, when this happens the given device_info structure may be + * partially filled. + */ +static +int wlp_get_variable_info(struct wlp *wlp, void *data, + struct wlp_device_info *dev_info, ssize_t len) +{ + struct device *dev = &wlp->rc->uwb_dev.dev; + size_t used = 0; + struct wlp_attr_hdr *hdr; + ssize_t result = 0; + unsigned last = 0; + + while (len - used > 0) { + if (len - used < sizeof(*hdr)) { + dev_err(dev, "WLP: Partial data in frame, cannot " + "parse. \n"); + goto error_parse; + } + hdr = data + used; + switch (le16_to_cpu(hdr->type)) { + case WLP_ATTR_MANUF: + if (last >= WLP_ATTR_MANUF) { + dev_err(dev, "WLP: Incorrect order of " + "attribute values in D1 msg.\n"); + goto error_parse; + } + result = wlp_get_manufacturer(wlp, data + used, + dev_info->manufacturer, + len - used); + if (result < 0) { + dev_err(dev, "WLP: Unable to obtain " + "Manufacturer attribute from D1 " + "message.\n"); + goto error_parse; + } + last = WLP_ATTR_MANUF; + used += result; + break; + case WLP_ATTR_MODEL_NAME: + if (last >= WLP_ATTR_MODEL_NAME) { + dev_err(dev, "WLP: Incorrect order of " + "attribute values in D1 msg.\n"); + goto error_parse; + } + result = wlp_get_model_name(wlp, data + used, + dev_info->model_name, + len - used); + if (result < 0) { + dev_err(dev, "WLP: Unable to obtain Model " + "name attribute from D1 message.\n"); + goto error_parse; + } + last = WLP_ATTR_MODEL_NAME; + used += result; + break; + case WLP_ATTR_MODEL_NR: + if (last >= WLP_ATTR_MODEL_NR) { + dev_err(dev, "WLP: Incorrect order of " + "attribute values in D1 msg.\n"); + goto error_parse; + } + result = wlp_get_model_nr(wlp, data + used, + dev_info->model_nr, + len - used); + if (result < 0) { + dev_err(dev, "WLP: Unable to obtain Model " + "number attribute from D1 message.\n"); + goto error_parse; + } + last = WLP_ATTR_MODEL_NR; + used += result; + break; + case WLP_ATTR_SERIAL: + if (last >= WLP_ATTR_SERIAL) { + dev_err(dev, "WLP: Incorrect order of " + "attribute values in D1 msg.\n"); + goto error_parse; + } + result = wlp_get_serial(wlp, data + used, + dev_info->serial, len - used); + if (result < 0) { + dev_err(dev, "WLP: Unable to obtain Serial " + "number attribute from D1 message.\n"); + goto error_parse; + } + last = WLP_ATTR_SERIAL; + used += result; + break; + case WLP_ATTR_PRI_DEV_TYPE: + if (last >= WLP_ATTR_PRI_DEV_TYPE) { + dev_err(dev, "WLP: Incorrect order of " + "attribute values in D1 msg.\n"); + goto error_parse; + } + result = wlp_get_prim_dev_type(wlp, data + used, + &dev_info->prim_dev_type, + len - used); + if (result < 0) { + dev_err(dev, "WLP: Unable to obtain Primary " + "device type attribute from D1 " + "message.\n"); + goto error_parse; + } + dev_info->prim_dev_type.category = + le16_to_cpu(dev_info->prim_dev_type.category); + dev_info->prim_dev_type.subID = + le16_to_cpu(dev_info->prim_dev_type.subID); + last = WLP_ATTR_PRI_DEV_TYPE; + used += result; + break; + default: + /* This is not variable device information. */ + goto out; + break; + } + } +out: + return used; +error_parse: + return -EINVAL; +} + +/** + * Parse incoming D1 frame, populate attribute values + * + * Caller provides pointers to memory already allocated for attributes + * expected in the D1 frame. These variables will be populated. + */ +static +int wlp_parse_d1_frame(struct wlp *wlp, struct sk_buff *skb, + struct wlp_uuid *uuid_e, + enum wlp_wss_sel_mthd *sel_mthd, + struct wlp_device_info *dev_info, + enum wlp_assc_error *assc_err) +{ + struct device *dev = &wlp->rc->uwb_dev.dev; + struct wlp_frame_assoc *d1 = (void *) skb->data; + void *ptr = skb->data; + size_t len = skb->len; + size_t used; + ssize_t result; + + used = sizeof(*d1); + result = wlp_get_uuid_e(wlp, ptr + used, uuid_e, len - used); + if (result < 0) { + dev_err(dev, "WLP: unable to obtain UUID-E attribute from D1 " + "message.\n"); + goto error_parse; + } + used += result; + result = wlp_get_wss_sel_mthd(wlp, ptr + used, sel_mthd, len - used); + if (result < 0) { + dev_err(dev, "WLP: unable to obtain WSS selection method " + "from D1 message.\n"); + goto error_parse; + } + used += result; + result = wlp_get_dev_name(wlp, ptr + used, dev_info->name, + len - used); + if (result < 0) { + dev_err(dev, "WLP: unable to obtain Device Name from D1 " + "message.\n"); + goto error_parse; + } + used += result; + result = wlp_get_variable_info(wlp, ptr + used, dev_info, len - used); + if (result < 0) { + dev_err(dev, "WLP: unable to obtain Device Information from " + "D1 message.\n"); + goto error_parse; + } + used += result; + result = wlp_get_wlp_assc_err(wlp, ptr + used, assc_err, len - used); + if (result < 0) { + dev_err(dev, "WLP: unable to obtain WLP Association Error " + "Information from D1 message.\n"); + goto error_parse; + } + result = 0; +error_parse: + return result; +} +/** + * Handle incoming D1 frame + * + * The frame has already been verified to contain an Association header with + * the correct version number. Parse the incoming frame, construct and send + * a D2 frame in response. + * + * It is not clear what to do with most fields in the incoming D1 frame. We + * retrieve and discard the information here for now. + */ +void wlp_handle_d1_frame(struct work_struct *ws) +{ + struct wlp_assoc_frame_ctx *frame_ctx = container_of(ws, + struct wlp_assoc_frame_ctx, + ws); + struct wlp *wlp = frame_ctx->wlp; + struct wlp_wss *wss = &wlp->wss; + struct sk_buff *skb = frame_ctx->skb; + struct uwb_dev_addr *src = &frame_ctx->src; + int result; + struct device *dev = &wlp->rc->uwb_dev.dev; + struct wlp_uuid uuid_e; + enum wlp_wss_sel_mthd sel_mthd = 0; + struct wlp_device_info dev_info; + enum wlp_assc_error assc_err; + struct sk_buff *resp = NULL; + + /* Parse D1 frame */ + mutex_lock(&wss->mutex); + mutex_lock(&wlp->mutex); /* to access wlp->uuid */ + memset(&dev_info, 0, sizeof(dev_info)); + result = wlp_parse_d1_frame(wlp, skb, &uuid_e, &sel_mthd, &dev_info, + &assc_err); + if (result < 0) { + dev_err(dev, "WLP: Unable to parse incoming D1 frame.\n"); + kfree_skb(skb); + goto out; + } + + kfree_skb(skb); + if (!wlp_uuid_is_set(&wlp->uuid)) { + dev_err(dev, "WLP: UUID is not set. Set via sysfs to " + "proceed. Respong to D1 message with error F0.\n"); + result = wlp_build_assoc_f0(wlp, &resp, + WLP_ASSOC_ERROR_NOT_READY); + if (result < 0) { + dev_err(dev, "WLP: Unable to construct F0 message.\n"); + goto out; + } + } else { + /* Construct D2 frame */ + result = wlp_build_assoc_d2(wlp, wss, &resp, &uuid_e); + if (result < 0) { + dev_err(dev, "WLP: Unable to construct D2 message.\n"); + goto out; + } + } + /* Send D2 frame */ + BUG_ON(wlp->xmit_frame == NULL); + result = wlp->xmit_frame(wlp, resp, src); + if (result < 0) { + dev_err(dev, "WLP: Unable to transmit D2 association " + "message: %d\n", result); + if (result == -ENXIO) + dev_err(dev, "WLP: Is network interface up? \n"); + /* We could try again ... */ + dev_kfree_skb_any(resp); /* we need to free if tx fails */ + } +out: + kfree(frame_ctx); + mutex_unlock(&wlp->mutex); + mutex_unlock(&wss->mutex); +} + +/** + * Parse incoming D2 frame, create and populate temporary cache + * + * @skb: socket buffer in which D2 frame can be found + * @neighbor: the neighbor that sent the D2 frame + * + * Will allocate memory for temporary storage of information learned during + * discovery. + */ +int wlp_parse_d2_frame_to_cache(struct wlp *wlp, struct sk_buff *skb, + struct wlp_neighbor_e *neighbor) +{ + struct device *dev = &wlp->rc->uwb_dev.dev; + struct wlp_frame_assoc *d2 = (void *) skb->data; + void *ptr = skb->data; + size_t len = skb->len; + size_t used; + ssize_t result; + struct wlp_uuid uuid_e; + struct wlp_device_info *nb_info; + enum wlp_assc_error assc_err; + + used = sizeof(*d2); + result = wlp_get_uuid_e(wlp, ptr + used, &uuid_e, len - used); + if (result < 0) { + dev_err(dev, "WLP: unable to obtain UUID-E attribute from D2 " + "message.\n"); + goto error_parse; + } + if (memcmp(&uuid_e, &wlp->uuid, sizeof(uuid_e))) { + dev_err(dev, "WLP: UUID-E in incoming D2 does not match " + "local UUID sent in D1. \n"); + goto error_parse; + } + used += result; + result = wlp_get_uuid_r(wlp, ptr + used, &neighbor->uuid, len - used); + if (result < 0) { + dev_err(dev, "WLP: unable to obtain UUID-R attribute from D2 " + "message.\n"); + goto error_parse; + } + used += result; + result = wlp_get_wss_info_to_cache(wlp, ptr + used, neighbor, + len - used); + if (result < 0) { + dev_err(dev, "WLP: unable to obtain WSS information " + "from D2 message.\n"); + goto error_parse; + } + used += result; + neighbor->info = kzalloc(sizeof(struct wlp_device_info), GFP_KERNEL); + if (neighbor->info == NULL) { + dev_err(dev, "WLP: cannot allocate memory to store device " + "info.\n"); + result = -ENOMEM; + goto error_parse; + } + nb_info = neighbor->info; + result = wlp_get_dev_name(wlp, ptr + used, nb_info->name, + len - used); + if (result < 0) { + dev_err(dev, "WLP: unable to obtain Device Name from D2 " + "message.\n"); + goto error_parse; + } + used += result; + result = wlp_get_variable_info(wlp, ptr + used, nb_info, len - used); + if (result < 0) { + dev_err(dev, "WLP: unable to obtain Device Information from " + "D2 message.\n"); + goto error_parse; + } + used += result; + result = wlp_get_wlp_assc_err(wlp, ptr + used, &assc_err, len - used); + if (result < 0) { + dev_err(dev, "WLP: unable to obtain WLP Association Error " + "Information from D2 message.\n"); + goto error_parse; + } + if (assc_err != WLP_ASSOC_ERROR_NONE) { + dev_err(dev, "WLP: neighbor device returned association " + "error %d\n", assc_err); + result = -EINVAL; + goto error_parse; + } + result = 0; +error_parse: + if (result < 0) + wlp_remove_neighbor_tmp_info(neighbor); + return result; +} + +/** + * Parse incoming D2 frame, populate attribute values of WSS bein enrolled in + * + * @wss: our WSS that will be enrolled + * @skb: socket buffer in which D2 frame can be found + * @neighbor: the neighbor that sent the D2 frame + * @wssid: the wssid of the WSS in which we want to enroll + * + * Forms part of enrollment sequence. We are trying to enroll in WSS with + * @wssid by using @neighbor as registrar. A D1 message was sent to + * @neighbor and now we need to parse the D2 response. The neighbor's + * response is searched for the requested WSS and if found (and it accepts + * enrollment), we store the information. + */ +int wlp_parse_d2_frame_to_enroll(struct wlp_wss *wss, struct sk_buff *skb, + struct wlp_neighbor_e *neighbor, + struct wlp_uuid *wssid) +{ + struct wlp *wlp = container_of(wss, struct wlp, wss); + struct device *dev = &wlp->rc->uwb_dev.dev; + void *ptr = skb->data; + size_t len = skb->len; + size_t used; + ssize_t result; + struct wlp_uuid uuid_e; + struct wlp_uuid uuid_r; + struct wlp_device_info nb_info; + enum wlp_assc_error assc_err; + char uuid_bufA[WLP_WSS_UUID_STRSIZE]; + char uuid_bufB[WLP_WSS_UUID_STRSIZE]; + + used = sizeof(struct wlp_frame_assoc); + result = wlp_get_uuid_e(wlp, ptr + used, &uuid_e, len - used); + if (result < 0) { + dev_err(dev, "WLP: unable to obtain UUID-E attribute from D2 " + "message.\n"); + goto error_parse; + } + if (memcmp(&uuid_e, &wlp->uuid, sizeof(uuid_e))) { + dev_err(dev, "WLP: UUID-E in incoming D2 does not match " + "local UUID sent in D1. \n"); + goto error_parse; + } + used += result; + result = wlp_get_uuid_r(wlp, ptr + used, &uuid_r, len - used); + if (result < 0) { + dev_err(dev, "WLP: unable to obtain UUID-R attribute from D2 " + "message.\n"); + goto error_parse; + } + if (memcmp(&uuid_r, &neighbor->uuid, sizeof(uuid_r))) { + wlp_wss_uuid_print(uuid_bufA, sizeof(uuid_bufA), + &neighbor->uuid); + wlp_wss_uuid_print(uuid_bufB, sizeof(uuid_bufB), &uuid_r); + dev_err(dev, "WLP: UUID of neighbor does not match UUID " + "learned during discovery. Originally discovered: %s, " + "now from D2 message: %s\n", uuid_bufA, uuid_bufB); + result = -EINVAL; + goto error_parse; + } + used += result; + wss->wssid = *wssid; + result = wlp_get_wss_info_to_enroll(wlp, ptr + used, wss, len - used); + if (result < 0) { + dev_err(dev, "WLP: unable to obtain WSS information " + "from D2 message.\n"); + goto error_parse; + } + if (wss->state != WLP_WSS_STATE_PART_ENROLLED) { + dev_err(dev, "WLP: D2 message did not contain information " + "for successful enrollment. \n"); + result = -EINVAL; + goto error_parse; + } + used += result; + /* Place device information on stack to continue parsing of message */ + result = wlp_get_dev_name(wlp, ptr + used, nb_info.name, + len - used); + if (result < 0) { + dev_err(dev, "WLP: unable to obtain Device Name from D2 " + "message.\n"); + goto error_parse; + } + used += result; + result = wlp_get_variable_info(wlp, ptr + used, &nb_info, len - used); + if (result < 0) { + dev_err(dev, "WLP: unable to obtain Device Information from " + "D2 message.\n"); + goto error_parse; + } + used += result; + result = wlp_get_wlp_assc_err(wlp, ptr + used, &assc_err, len - used); + if (result < 0) { + dev_err(dev, "WLP: unable to obtain WLP Association Error " + "Information from D2 message.\n"); + goto error_parse; + } + if (assc_err != WLP_ASSOC_ERROR_NONE) { + dev_err(dev, "WLP: neighbor device returned association " + "error %d\n", assc_err); + if (wss->state == WLP_WSS_STATE_PART_ENROLLED) { + dev_err(dev, "WLP: Enrolled in WSS (should not " + "happen according to spec). Undoing. \n"); + wlp_wss_reset(wss); + } + result = -EINVAL; + goto error_parse; + } + result = 0; +error_parse: + return result; +} + +/** + * Parse C3/C4 frame into provided variables + * + * @wssid: will point to copy of wssid retrieved from C3/C4 frame + * @tag: will point to copy of tag retrieved from C3/C4 frame + * @virt_addr: will point to copy of virtual address retrieved from C3/C4 + * frame. + * + * Calling function has to allocate memory for these values. + * + * skb contains a valid C3/C4 frame, return the individual fields of this + * frame in the provided variables. + */ +int wlp_parse_c3c4_frame(struct wlp *wlp, struct sk_buff *skb, + struct wlp_uuid *wssid, u8 *tag, + struct uwb_mac_addr *virt_addr) +{ + struct device *dev = &wlp->rc->uwb_dev.dev; + int result; + void *ptr = skb->data; + size_t len = skb->len; + size_t used; + struct wlp_frame_assoc *assoc = ptr; + + used = sizeof(*assoc); + result = wlp_get_wssid(wlp, ptr + used, wssid, len - used); + if (result < 0) { + dev_err(dev, "WLP: unable to obtain WSSID attribute from " + "%s message.\n", wlp_assoc_frame_str(assoc->type)); + goto error_parse; + } + used += result; + result = wlp_get_wss_tag(wlp, ptr + used, tag, len - used); + if (result < 0) { + dev_err(dev, "WLP: unable to obtain WSS tag attribute from " + "%s message.\n", wlp_assoc_frame_str(assoc->type)); + goto error_parse; + } + used += result; + result = wlp_get_wss_virt(wlp, ptr + used, virt_addr, len - used); + if (result < 0) { + dev_err(dev, "WLP: unable to obtain WSS virtual address " + "attribute from %s message.\n", + wlp_assoc_frame_str(assoc->type)); + goto error_parse; + } +error_parse: + return result; +} + +/** + * Allocate memory for and populate fields of C1 or C2 association frame + * + * The C1 and C2 association frames appear identical - except for the type. + */ +static +int wlp_build_assoc_c1c2(struct wlp *wlp, struct wlp_wss *wss, + struct sk_buff **skb, enum wlp_assoc_type type) +{ + struct device *dev = &wlp->rc->uwb_dev.dev; + int result = -ENOMEM; + struct { + struct wlp_frame_assoc c_hdr; + struct wlp_attr_wssid wssid; + } *c; + struct sk_buff *_skb; + + _skb = dev_alloc_skb(sizeof(*c)); + if (_skb == NULL) { + dev_err(dev, "WLP: Unable to allocate memory for C1/C2 " + "association frame. \n"); + goto error_alloc; + } + c = (void *) _skb->data; + c->c_hdr.hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID); + c->c_hdr.hdr.type = WLP_FRAME_ASSOCIATION; + c->c_hdr.type = type; + wlp_set_version(&c->c_hdr.version, WLP_VERSION); + wlp_set_msg_type(&c->c_hdr.msg_type, type); + wlp_set_wssid(&c->wssid, &wss->wssid); + skb_put(_skb, sizeof(*c)); + *skb = _skb; + result = 0; +error_alloc: + return result; +} + + +static +int wlp_build_assoc_c1(struct wlp *wlp, struct wlp_wss *wss, + struct sk_buff **skb) +{ + return wlp_build_assoc_c1c2(wlp, wss, skb, WLP_ASSOC_C1); +} + +static +int wlp_build_assoc_c2(struct wlp *wlp, struct wlp_wss *wss, + struct sk_buff **skb) +{ + return wlp_build_assoc_c1c2(wlp, wss, skb, WLP_ASSOC_C2); +} + + +/** + * Allocate memory for and populate fields of C3 or C4 association frame + * + * The C3 and C4 association frames appear identical - except for the type. + */ +static +int wlp_build_assoc_c3c4(struct wlp *wlp, struct wlp_wss *wss, + struct sk_buff **skb, enum wlp_assoc_type type) +{ + struct device *dev = &wlp->rc->uwb_dev.dev; + int result = -ENOMEM; + struct { + struct wlp_frame_assoc c_hdr; + struct wlp_attr_wssid wssid; + struct wlp_attr_wss_tag wss_tag; + struct wlp_attr_wss_virt wss_virt; + } *c; + struct sk_buff *_skb; + + _skb = dev_alloc_skb(sizeof(*c)); + if (_skb == NULL) { + dev_err(dev, "WLP: Unable to allocate memory for C3/C4 " + "association frame. \n"); + goto error_alloc; + } + c = (void *) _skb->data; + c->c_hdr.hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID); + c->c_hdr.hdr.type = WLP_FRAME_ASSOCIATION; + c->c_hdr.type = type; + wlp_set_version(&c->c_hdr.version, WLP_VERSION); + wlp_set_msg_type(&c->c_hdr.msg_type, type); + wlp_set_wssid(&c->wssid, &wss->wssid); + wlp_set_wss_tag(&c->wss_tag, wss->tag); + wlp_set_wss_virt(&c->wss_virt, &wss->virtual_addr); + skb_put(_skb, sizeof(*c)); + *skb = _skb; + result = 0; +error_alloc: + return result; +} + +static +int wlp_build_assoc_c3(struct wlp *wlp, struct wlp_wss *wss, + struct sk_buff **skb) +{ + return wlp_build_assoc_c3c4(wlp, wss, skb, WLP_ASSOC_C3); +} + +static +int wlp_build_assoc_c4(struct wlp *wlp, struct wlp_wss *wss, + struct sk_buff **skb) +{ + return wlp_build_assoc_c3c4(wlp, wss, skb, WLP_ASSOC_C4); +} + + +#define wlp_send_assoc(type, id) \ +static int wlp_send_assoc_##type(struct wlp *wlp, struct wlp_wss *wss, \ + struct uwb_dev_addr *dev_addr) \ +{ \ + struct device *dev = &wlp->rc->uwb_dev.dev; \ + int result; \ + struct sk_buff *skb = NULL; \ + \ + /* Build the frame */ \ + result = wlp_build_assoc_##type(wlp, wss, &skb); \ + if (result < 0) { \ + dev_err(dev, "WLP: Unable to construct %s association " \ + "frame: %d\n", wlp_assoc_frame_str(id), result);\ + goto error_build_assoc; \ + } \ + /* Send the frame */ \ + BUG_ON(wlp->xmit_frame == NULL); \ + result = wlp->xmit_frame(wlp, skb, dev_addr); \ + if (result < 0) { \ + dev_err(dev, "WLP: Unable to transmit %s association " \ + "message: %d\n", wlp_assoc_frame_str(id), \ + result); \ + if (result == -ENXIO) \ + dev_err(dev, "WLP: Is network interface " \ + "up? \n"); \ + goto error_xmit; \ + } \ + return 0; \ +error_xmit: \ + /* We could try again ... */ \ + dev_kfree_skb_any(skb);/*we need to free if tx fails*/ \ +error_build_assoc: \ + return result; \ +} + +wlp_send_assoc(d1, WLP_ASSOC_D1) +wlp_send_assoc(c1, WLP_ASSOC_C1) +wlp_send_assoc(c3, WLP_ASSOC_C3) + +int wlp_send_assoc_frame(struct wlp *wlp, struct wlp_wss *wss, + struct uwb_dev_addr *dev_addr, + enum wlp_assoc_type type) +{ + int result = 0; + struct device *dev = &wlp->rc->uwb_dev.dev; + switch (type) { + case WLP_ASSOC_D1: + result = wlp_send_assoc_d1(wlp, wss, dev_addr); + break; + case WLP_ASSOC_C1: + result = wlp_send_assoc_c1(wlp, wss, dev_addr); + break; + case WLP_ASSOC_C3: + result = wlp_send_assoc_c3(wlp, wss, dev_addr); + break; + default: + dev_err(dev, "WLP: Received request to send unknown " + "association message.\n"); + result = -EINVAL; + break; + } + return result; +} + +/** + * Handle incoming C1 frame + * + * The frame has already been verified to contain an Association header with + * the correct version number. Parse the incoming frame, construct and send + * a C2 frame in response. + */ +void wlp_handle_c1_frame(struct work_struct *ws) +{ + struct wlp_assoc_frame_ctx *frame_ctx = container_of(ws, + struct wlp_assoc_frame_ctx, + ws); + struct wlp *wlp = frame_ctx->wlp; + struct wlp_wss *wss = &wlp->wss; + struct device *dev = &wlp->rc->uwb_dev.dev; + struct wlp_frame_assoc *c1 = (void *) frame_ctx->skb->data; + unsigned int len = frame_ctx->skb->len; + struct uwb_dev_addr *src = &frame_ctx->src; + int result; + struct wlp_uuid wssid; + struct sk_buff *resp = NULL; + + /* Parse C1 frame */ + mutex_lock(&wss->mutex); + result = wlp_get_wssid(wlp, (void *)c1 + sizeof(*c1), &wssid, + len - sizeof(*c1)); + if (result < 0) { + dev_err(dev, "WLP: unable to obtain WSSID from C1 frame.\n"); + goto out; + } + if (!memcmp(&wssid, &wss->wssid, sizeof(wssid)) + && wss->state == WLP_WSS_STATE_ACTIVE) { + /* Construct C2 frame */ + result = wlp_build_assoc_c2(wlp, wss, &resp); + if (result < 0) { + dev_err(dev, "WLP: Unable to construct C2 message.\n"); + goto out; + } + } else { + /* Construct F0 frame */ + result = wlp_build_assoc_f0(wlp, &resp, WLP_ASSOC_ERROR_INV); + if (result < 0) { + dev_err(dev, "WLP: Unable to construct F0 message.\n"); + goto out; + } + } + /* Send C2 frame */ + BUG_ON(wlp->xmit_frame == NULL); + result = wlp->xmit_frame(wlp, resp, src); + if (result < 0) { + dev_err(dev, "WLP: Unable to transmit response association " + "message: %d\n", result); + if (result == -ENXIO) + dev_err(dev, "WLP: Is network interface up? \n"); + /* We could try again ... */ + dev_kfree_skb_any(resp); /* we need to free if tx fails */ + } +out: + kfree_skb(frame_ctx->skb); + kfree(frame_ctx); + mutex_unlock(&wss->mutex); +} + +/** + * Handle incoming C3 frame + * + * The frame has already been verified to contain an Association header with + * the correct version number. Parse the incoming frame, construct and send + * a C4 frame in response. If the C3 frame identifies a WSS that is locally + * active then we connect to this neighbor (add it to our EDA cache). + */ +void wlp_handle_c3_frame(struct work_struct *ws) +{ + struct wlp_assoc_frame_ctx *frame_ctx = container_of(ws, + struct wlp_assoc_frame_ctx, + ws); + struct wlp *wlp = frame_ctx->wlp; + struct wlp_wss *wss = &wlp->wss; + struct device *dev = &wlp->rc->uwb_dev.dev; + struct sk_buff *skb = frame_ctx->skb; + struct uwb_dev_addr *src = &frame_ctx->src; + int result; + struct sk_buff *resp = NULL; + struct wlp_uuid wssid; + u8 tag; + struct uwb_mac_addr virt_addr; + + /* Parse C3 frame */ + mutex_lock(&wss->mutex); + result = wlp_parse_c3c4_frame(wlp, skb, &wssid, &tag, &virt_addr); + if (result < 0) { + dev_err(dev, "WLP: unable to obtain values from C3 frame.\n"); + goto out; + } + if (!memcmp(&wssid, &wss->wssid, sizeof(wssid)) + && wss->state >= WLP_WSS_STATE_ACTIVE) { + result = wlp_eda_update_node(&wlp->eda, src, wss, + (void *) virt_addr.data, tag, + WLP_WSS_CONNECTED); + if (result < 0) { + dev_err(dev, "WLP: Unable to update EDA cache " + "with new connected neighbor information.\n"); + result = wlp_build_assoc_f0(wlp, &resp, + WLP_ASSOC_ERROR_INT); + if (result < 0) { + dev_err(dev, "WLP: Unable to construct F0 " + "message.\n"); + goto out; + } + } else { + wss->state = WLP_WSS_STATE_CONNECTED; + /* Construct C4 frame */ + result = wlp_build_assoc_c4(wlp, wss, &resp); + if (result < 0) { + dev_err(dev, "WLP: Unable to construct C4 " + "message.\n"); + goto out; + } + } + } else { + /* Construct F0 frame */ + result = wlp_build_assoc_f0(wlp, &resp, WLP_ASSOC_ERROR_INV); + if (result < 0) { + dev_err(dev, "WLP: Unable to construct F0 message.\n"); + goto out; + } + } + /* Send C4 frame */ + BUG_ON(wlp->xmit_frame == NULL); + result = wlp->xmit_frame(wlp, resp, src); + if (result < 0) { + dev_err(dev, "WLP: Unable to transmit response association " + "message: %d\n", result); + if (result == -ENXIO) + dev_err(dev, "WLP: Is network interface up? \n"); + /* We could try again ... */ + dev_kfree_skb_any(resp); /* we need to free if tx fails */ + } +out: + kfree_skb(frame_ctx->skb); + kfree(frame_ctx); + mutex_unlock(&wss->mutex); +} + + diff --git a/trunk/drivers/uwb/wlp/sysfs.c b/trunk/drivers/uwb/wlp/sysfs.c new file mode 100644 index 000000000000..6627c94cc854 --- /dev/null +++ b/trunk/drivers/uwb/wlp/sysfs.c @@ -0,0 +1,708 @@ +/* + * WiMedia Logical Link Control Protocol (WLP) + * sysfs functions + * + * Copyright (C) 2007 Intel Corporation + * Reinette Chatre + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * FIXME: Docs + * + */ +#include + +#include "wlp-internal.h" + +static +size_t wlp_wss_wssid_e_print(char *buf, size_t bufsize, + struct wlp_wssid_e *wssid_e) +{ + size_t used = 0; + used += scnprintf(buf, bufsize, " WSS: "); + used += wlp_wss_uuid_print(buf + used, bufsize - used, + &wssid_e->wssid); + + if (wssid_e->info != NULL) { + used += scnprintf(buf + used, bufsize - used, " "); + used += uwb_mac_addr_print(buf + used, bufsize - used, + &wssid_e->info->bcast); + used += scnprintf(buf + used, bufsize - used, " %u %u %s\n", + wssid_e->info->accept_enroll, + wssid_e->info->sec_status, + wssid_e->info->name); + } + return used; +} + +/** + * Print out information learned from neighbor discovery + * + * Some fields being printed may not be included in the device discovery + * information (it is not mandatory). We are thus careful how the + * information is printed to ensure it is clear to the user what field is + * being referenced. + * The information being printed is for one time use - temporary storage is + * cleaned after it is printed. + * + * Ideally sysfs output should be on one line. The information printed here + * contain a few strings so it will be hard to parse if they are all + * printed on the same line - without agreeing on a standard field + * separator. + */ +static +ssize_t wlp_wss_neighborhood_print_remove(struct wlp *wlp, char *buf, + size_t bufsize) +{ + size_t used = 0; + struct wlp_neighbor_e *neighb; + struct wlp_wssid_e *wssid_e; + + mutex_lock(&wlp->nbmutex); + used = scnprintf(buf, bufsize, "#Neighbor information\n" + "#uuid dev_addr\n" + "# Device Name:\n# Model Name:\n# Manufacturer:\n" + "# Model Nr:\n# Serial:\n" + "# Pri Dev type: CategoryID OUI OUISubdiv " + "SubcategoryID\n" + "# WSS: WSSID WSS_name accept_enroll sec_status " + "bcast\n" + "# WSS: WSSID WSS_name accept_enroll sec_status " + "bcast\n\n"); + list_for_each_entry(neighb, &wlp->neighbors, node) { + if (bufsize - used <= 0) + goto out; + used += wlp_wss_uuid_print(buf + used, bufsize - used, + &neighb->uuid); + buf[used++] = ' '; + used += uwb_dev_addr_print(buf + used, bufsize - used, + &neighb->uwb_dev->dev_addr); + if (neighb->info != NULL) + used += scnprintf(buf + used, bufsize - used, + "\n Device Name: %s\n" + " Model Name: %s\n" + " Manufacturer:%s \n" + " Model Nr: %s\n" + " Serial: %s\n" + " Pri Dev type: " + "%u %02x:%02x:%02x %u %u\n", + neighb->info->name, + neighb->info->model_name, + neighb->info->manufacturer, + neighb->info->model_nr, + neighb->info->serial, + neighb->info->prim_dev_type.category, + neighb->info->prim_dev_type.OUI[0], + neighb->info->prim_dev_type.OUI[1], + neighb->info->prim_dev_type.OUI[2], + neighb->info->prim_dev_type.OUIsubdiv, + neighb->info->prim_dev_type.subID); + list_for_each_entry(wssid_e, &neighb->wssid, node) { + used += wlp_wss_wssid_e_print(buf + used, + bufsize - used, + wssid_e); + } + buf[used++] = '\n'; + wlp_remove_neighbor_tmp_info(neighb); + } + + +out: + mutex_unlock(&wlp->nbmutex); + return used; +} + + +/** + * Show properties of all WSS in neighborhood. + * + * Will trigger a complete discovery of WSS activated by this device and + * its neighbors. + */ +ssize_t wlp_neighborhood_show(struct wlp *wlp, char *buf) +{ + wlp_discover(wlp); + return wlp_wss_neighborhood_print_remove(wlp, buf, PAGE_SIZE); +} +EXPORT_SYMBOL_GPL(wlp_neighborhood_show); + +static +ssize_t __wlp_wss_properties_show(struct wlp_wss *wss, char *buf, + size_t bufsize) +{ + ssize_t result; + + result = wlp_wss_uuid_print(buf, bufsize, &wss->wssid); + result += scnprintf(buf + result, bufsize - result, " "); + result += uwb_mac_addr_print(buf + result, bufsize - result, + &wss->bcast); + result += scnprintf(buf + result, bufsize - result, + " 0x%02x %u ", wss->hash, wss->secure_status); + result += wlp_wss_key_print(buf + result, bufsize - result, + wss->master_key); + result += scnprintf(buf + result, bufsize - result, " 0x%02x ", + wss->tag); + result += uwb_mac_addr_print(buf + result, bufsize - result, + &wss->virtual_addr); + result += scnprintf(buf + result, bufsize - result, " %s", wss->name); + result += scnprintf(buf + result, bufsize - result, + "\n\n#WSSID\n#WSS broadcast address\n" + "#WSS hash\n#WSS secure status\n" + "#WSS master key\n#WSS local tag\n" + "#WSS local virtual EUI-48\n#WSS name\n"); + return result; +} + +/** + * Show which WSS is activated. + */ +ssize_t wlp_wss_activate_show(struct wlp_wss *wss, char *buf) +{ + int result = 0; + + if (mutex_lock_interruptible(&wss->mutex)) + goto out; + if (wss->state >= WLP_WSS_STATE_ACTIVE) + result = __wlp_wss_properties_show(wss, buf, PAGE_SIZE); + else + result = scnprintf(buf, PAGE_SIZE, "No local WSS active.\n"); + result += scnprintf(buf + result, PAGE_SIZE - result, + "\n\n" + "# echo WSSID SECURE_STATUS ACCEPT_ENROLLMENT " + "NAME #create new WSS\n" + "# echo WSSID [DEV ADDR] #enroll in and activate " + "existing WSS, can request registrar\n" + "#\n" + "# WSSID is a 16 byte hex array. Eg. 12 A3 3B ... \n" + "# SECURE_STATUS 0 - unsecure, 1 - secure (default)\n" + "# ACCEPT_ENROLLMENT 0 - no, 1 - yes (default)\n" + "# NAME is the text string identifying the WSS\n" + "# DEV ADDR is the device address of neighbor " + "that should be registrar. Eg. 32:AB\n"); + + mutex_unlock(&wss->mutex); +out: + return result; + +} +EXPORT_SYMBOL_GPL(wlp_wss_activate_show); + +/** + * Create/activate a new WSS or enroll/activate in neighboring WSS + * + * The user can provide the WSSID of a WSS in which it wants to enroll. + * Only the WSSID is necessary if the WSS have been discovered before. If + * the WSS has not been discovered before, or the user wants to use a + * particular neighbor as its registrar, then the user can also provide a + * device address or the neighbor that will be used as registrar. + * + * A new WSS is created when the user provides a WSSID, secure status, and + * WSS name. + */ +ssize_t wlp_wss_activate_store(struct wlp_wss *wss, + const char *buf, size_t size) +{ + ssize_t result = -EINVAL; + struct wlp_uuid wssid; + struct uwb_dev_addr dev; + struct uwb_dev_addr bcast = {.data = {0xff, 0xff} }; + char name[65]; + unsigned sec_status, accept; + memset(name, 0, sizeof(name)); + result = sscanf(buf, "%02hhx %02hhx %02hhx %02hhx " + "%02hhx %02hhx %02hhx %02hhx " + "%02hhx %02hhx %02hhx %02hhx " + "%02hhx %02hhx %02hhx %02hhx " + "%02hhx:%02hhx", + &wssid.data[0] , &wssid.data[1], + &wssid.data[2] , &wssid.data[3], + &wssid.data[4] , &wssid.data[5], + &wssid.data[6] , &wssid.data[7], + &wssid.data[8] , &wssid.data[9], + &wssid.data[10], &wssid.data[11], + &wssid.data[12], &wssid.data[13], + &wssid.data[14], &wssid.data[15], + &dev.data[1], &dev.data[0]); + if (result == 16 || result == 17) { + result = sscanf(buf, "%02hhx %02hhx %02hhx %02hhx " + "%02hhx %02hhx %02hhx %02hhx " + "%02hhx %02hhx %02hhx %02hhx " + "%02hhx %02hhx %02hhx %02hhx " + "%u %u %64c", + &wssid.data[0] , &wssid.data[1], + &wssid.data[2] , &wssid.data[3], + &wssid.data[4] , &wssid.data[5], + &wssid.data[6] , &wssid.data[7], + &wssid.data[8] , &wssid.data[9], + &wssid.data[10], &wssid.data[11], + &wssid.data[12], &wssid.data[13], + &wssid.data[14], &wssid.data[15], + &sec_status, &accept, name); + if (result == 16) + result = wlp_wss_enroll_activate(wss, &wssid, &bcast); + else if (result == 19) { + sec_status = sec_status == 0 ? 0 : 1; + accept = accept == 0 ? 0 : 1; + /* We read name using %c, so the newline needs to be + * removed */ + if (strlen(name) != sizeof(name) - 1) + name[strlen(name) - 1] = '\0'; + result = wlp_wss_create_activate(wss, &wssid, name, + sec_status, accept); + } else + result = -EINVAL; + } else if (result == 18) + result = wlp_wss_enroll_activate(wss, &wssid, &dev); + else + result = -EINVAL; + return result < 0 ? result : size; +} +EXPORT_SYMBOL_GPL(wlp_wss_activate_store); + +/** + * Show the UUID of this host + */ +ssize_t wlp_uuid_show(struct wlp *wlp, char *buf) +{ + ssize_t result = 0; + + mutex_lock(&wlp->mutex); + result = wlp_wss_uuid_print(buf, PAGE_SIZE, &wlp->uuid); + buf[result++] = '\n'; + mutex_unlock(&wlp->mutex); + return result; +} +EXPORT_SYMBOL_GPL(wlp_uuid_show); + +/** + * Store a new UUID for this host + * + * According to the spec this should be encoded as an octet string in the + * order the octets are shown in string representation in RFC 4122 (WLP + * 0.99 [Table 6]) + * + * We do not check value provided by user. + */ +ssize_t wlp_uuid_store(struct wlp *wlp, const char *buf, size_t size) +{ + ssize_t result; + struct wlp_uuid uuid; + + mutex_lock(&wlp->mutex); + result = sscanf(buf, "%02hhx %02hhx %02hhx %02hhx " + "%02hhx %02hhx %02hhx %02hhx " + "%02hhx %02hhx %02hhx %02hhx " + "%02hhx %02hhx %02hhx %02hhx ", + &uuid.data[0] , &uuid.data[1], + &uuid.data[2] , &uuid.data[3], + &uuid.data[4] , &uuid.data[5], + &uuid.data[6] , &uuid.data[7], + &uuid.data[8] , &uuid.data[9], + &uuid.data[10], &uuid.data[11], + &uuid.data[12], &uuid.data[13], + &uuid.data[14], &uuid.data[15]); + if (result != 16) { + result = -EINVAL; + goto error; + } + wlp->uuid = uuid; +error: + mutex_unlock(&wlp->mutex); + return result < 0 ? result : size; +} +EXPORT_SYMBOL_GPL(wlp_uuid_store); + +/** + * Show contents of members of device information structure + */ +#define wlp_dev_info_show(type) \ +ssize_t wlp_dev_##type##_show(struct wlp *wlp, char *buf) \ +{ \ + ssize_t result = 0; \ + mutex_lock(&wlp->mutex); \ + if (wlp->dev_info == NULL) { \ + result = __wlp_setup_device_info(wlp); \ + if (result < 0) \ + goto out; \ + } \ + result = scnprintf(buf, PAGE_SIZE, "%s\n", wlp->dev_info->type);\ +out: \ + mutex_unlock(&wlp->mutex); \ + return result; \ +} \ +EXPORT_SYMBOL_GPL(wlp_dev_##type##_show); + +wlp_dev_info_show(name) +wlp_dev_info_show(model_name) +wlp_dev_info_show(model_nr) +wlp_dev_info_show(manufacturer) +wlp_dev_info_show(serial) + +/** + * Store contents of members of device information structure + */ +#define wlp_dev_info_store(type, len) \ +ssize_t wlp_dev_##type##_store(struct wlp *wlp, const char *buf, size_t size)\ +{ \ + ssize_t result; \ + char format[10]; \ + mutex_lock(&wlp->mutex); \ + if (wlp->dev_info == NULL) { \ + result = __wlp_alloc_device_info(wlp); \ + if (result < 0) \ + goto out; \ + } \ + memset(wlp->dev_info->type, 0, sizeof(wlp->dev_info->type)); \ + sprintf(format, "%%%uc", len); \ + result = sscanf(buf, format, wlp->dev_info->type); \ +out: \ + mutex_unlock(&wlp->mutex); \ + return result < 0 ? result : size; \ +} \ +EXPORT_SYMBOL_GPL(wlp_dev_##type##_store); + +wlp_dev_info_store(name, 32) +wlp_dev_info_store(manufacturer, 64) +wlp_dev_info_store(model_name, 32) +wlp_dev_info_store(model_nr, 32) +wlp_dev_info_store(serial, 32) + +static +const char *__wlp_dev_category[] = { + [WLP_DEV_CAT_COMPUTER] = "Computer", + [WLP_DEV_CAT_INPUT] = "Input device", + [WLP_DEV_CAT_PRINT_SCAN_FAX_COPIER] = "Printer, scanner, FAX, or " + "Copier", + [WLP_DEV_CAT_CAMERA] = "Camera", + [WLP_DEV_CAT_STORAGE] = "Storage Network", + [WLP_DEV_CAT_INFRASTRUCTURE] = "Infrastructure", + [WLP_DEV_CAT_DISPLAY] = "Display", + [WLP_DEV_CAT_MULTIM] = "Multimedia device", + [WLP_DEV_CAT_GAMING] = "Gaming device", + [WLP_DEV_CAT_TELEPHONE] = "Telephone", + [WLP_DEV_CAT_OTHER] = "Other", +}; + +static +const char *wlp_dev_category_str(unsigned cat) +{ + if ((cat >= WLP_DEV_CAT_COMPUTER && cat <= WLP_DEV_CAT_TELEPHONE) + || cat == WLP_DEV_CAT_OTHER) + return __wlp_dev_category[cat]; + return "unknown category"; +} + +ssize_t wlp_dev_prim_category_show(struct wlp *wlp, char *buf) +{ + ssize_t result = 0; + mutex_lock(&wlp->mutex); + if (wlp->dev_info == NULL) { + result = __wlp_setup_device_info(wlp); + if (result < 0) + goto out; + } + result = scnprintf(buf, PAGE_SIZE, "%s\n", + wlp_dev_category_str(wlp->dev_info->prim_dev_type.category)); +out: + mutex_unlock(&wlp->mutex); + return result; +} +EXPORT_SYMBOL_GPL(wlp_dev_prim_category_show); + +ssize_t wlp_dev_prim_category_store(struct wlp *wlp, const char *buf, + size_t size) +{ + ssize_t result; + u16 cat; + mutex_lock(&wlp->mutex); + if (wlp->dev_info == NULL) { + result = __wlp_alloc_device_info(wlp); + if (result < 0) + goto out; + } + result = sscanf(buf, "%hu", &cat); + if ((cat >= WLP_DEV_CAT_COMPUTER && cat <= WLP_DEV_CAT_TELEPHONE) + || cat == WLP_DEV_CAT_OTHER) + wlp->dev_info->prim_dev_type.category = cat; + else + result = -EINVAL; +out: + mutex_unlock(&wlp->mutex); + return result < 0 ? result : size; +} +EXPORT_SYMBOL_GPL(wlp_dev_prim_category_store); + +ssize_t wlp_dev_prim_OUI_show(struct wlp *wlp, char *buf) +{ + ssize_t result = 0; + mutex_lock(&wlp->mutex); + if (wlp->dev_info == NULL) { + result = __wlp_setup_device_info(wlp); + if (result < 0) + goto out; + } + result = scnprintf(buf, PAGE_SIZE, "%02x:%02x:%02x\n", + wlp->dev_info->prim_dev_type.OUI[0], + wlp->dev_info->prim_dev_type.OUI[1], + wlp->dev_info->prim_dev_type.OUI[2]); +out: + mutex_unlock(&wlp->mutex); + return result; +} +EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_show); + +ssize_t wlp_dev_prim_OUI_store(struct wlp *wlp, const char *buf, size_t size) +{ + ssize_t result; + u8 OUI[3]; + mutex_lock(&wlp->mutex); + if (wlp->dev_info == NULL) { + result = __wlp_alloc_device_info(wlp); + if (result < 0) + goto out; + } + result = sscanf(buf, "%hhx:%hhx:%hhx", + &OUI[0], &OUI[1], &OUI[2]); + if (result != 3) { + result = -EINVAL; + goto out; + } else + memcpy(wlp->dev_info->prim_dev_type.OUI, OUI, sizeof(OUI)); +out: + mutex_unlock(&wlp->mutex); + return result < 0 ? result : size; +} +EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_store); + + +ssize_t wlp_dev_prim_OUI_sub_show(struct wlp *wlp, char *buf) +{ + ssize_t result = 0; + mutex_lock(&wlp->mutex); + if (wlp->dev_info == NULL) { + result = __wlp_setup_device_info(wlp); + if (result < 0) + goto out; + } + result = scnprintf(buf, PAGE_SIZE, "%u\n", + wlp->dev_info->prim_dev_type.OUIsubdiv); +out: + mutex_unlock(&wlp->mutex); + return result; +} +EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_sub_show); + +ssize_t wlp_dev_prim_OUI_sub_store(struct wlp *wlp, const char *buf, + size_t size) +{ + ssize_t result; + unsigned sub; + u8 max_sub = ~0; + mutex_lock(&wlp->mutex); + if (wlp->dev_info == NULL) { + result = __wlp_alloc_device_info(wlp); + if (result < 0) + goto out; + } + result = sscanf(buf, "%u", &sub); + if (sub <= max_sub) + wlp->dev_info->prim_dev_type.OUIsubdiv = sub; + else + result = -EINVAL; +out: + mutex_unlock(&wlp->mutex); + return result < 0 ? result : size; +} +EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_sub_store); + +ssize_t wlp_dev_prim_subcat_show(struct wlp *wlp, char *buf) +{ + ssize_t result = 0; + mutex_lock(&wlp->mutex); + if (wlp->dev_info == NULL) { + result = __wlp_setup_device_info(wlp); + if (result < 0) + goto out; + } + result = scnprintf(buf, PAGE_SIZE, "%u\n", + wlp->dev_info->prim_dev_type.subID); +out: + mutex_unlock(&wlp->mutex); + return result; +} +EXPORT_SYMBOL_GPL(wlp_dev_prim_subcat_show); + +ssize_t wlp_dev_prim_subcat_store(struct wlp *wlp, const char *buf, + size_t size) +{ + ssize_t result; + unsigned sub; + __le16 max_sub = ~0; + mutex_lock(&wlp->mutex); + if (wlp->dev_info == NULL) { + result = __wlp_alloc_device_info(wlp); + if (result < 0) + goto out; + } + result = sscanf(buf, "%u", &sub); + if (sub <= max_sub) + wlp->dev_info->prim_dev_type.subID = sub; + else + result = -EINVAL; +out: + mutex_unlock(&wlp->mutex); + return result < 0 ? result : size; +} +EXPORT_SYMBOL_GPL(wlp_dev_prim_subcat_store); + +/** + * Subsystem implementation for interaction with individual WSS via sysfs + * + * Followed instructions for subsystem in Documentation/filesystems/sysfs.txt + */ + +#define kobj_to_wlp_wss(obj) container_of(obj, struct wlp_wss, kobj) +#define attr_to_wlp_wss_attr(_attr) \ + container_of(_attr, struct wlp_wss_attribute, attr) + +/** + * Sysfs subsystem: forward read calls + * + * Sysfs operation for forwarding read call to the show method of the + * attribute owner + */ +static +ssize_t wlp_wss_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct wlp_wss_attribute *wss_attr = attr_to_wlp_wss_attr(attr); + struct wlp_wss *wss = kobj_to_wlp_wss(kobj); + ssize_t ret = -EIO; + + if (wss_attr->show) + ret = wss_attr->show(wss, buf); + return ret; +} +/** + * Sysfs subsystem: forward write calls + * + * Sysfs operation for forwarding write call to the store method of the + * attribute owner + */ +static +ssize_t wlp_wss_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + struct wlp_wss_attribute *wss_attr = attr_to_wlp_wss_attr(attr); + struct wlp_wss *wss = kobj_to_wlp_wss(kobj); + ssize_t ret = -EIO; + + if (wss_attr->store) + ret = wss_attr->store(wss, buf, count); + return ret; +} + +static const struct sysfs_ops wss_sysfs_ops = { + .show = wlp_wss_attr_show, + .store = wlp_wss_attr_store, +}; + +struct kobj_type wss_ktype = { + .release = wlp_wss_release, + .sysfs_ops = &wss_sysfs_ops, +}; + + +/** + * Sysfs files for individual WSS + */ + +/** + * Print static properties of this WSS + * + * The name of a WSS may not be null teminated. It's max size is 64 bytes + * so we copy it to a larger array just to make sure we print sane data. + */ +static ssize_t wlp_wss_properties_show(struct wlp_wss *wss, char *buf) +{ + int result = 0; + + if (mutex_lock_interruptible(&wss->mutex)) + goto out; + result = __wlp_wss_properties_show(wss, buf, PAGE_SIZE); + mutex_unlock(&wss->mutex); +out: + return result; +} +WSS_ATTR(properties, S_IRUGO, wlp_wss_properties_show, NULL); + +/** + * Print all connected members of this WSS + * The EDA cache contains all members of WSS neighborhood. + */ +static ssize_t wlp_wss_members_show(struct wlp_wss *wss, char *buf) +{ + struct wlp *wlp = container_of(wss, struct wlp, wss); + return wlp_eda_show(wlp, buf); +} +WSS_ATTR(members, S_IRUGO, wlp_wss_members_show, NULL); + +static +const char *__wlp_strstate[] = { + "none", + "partially enrolled", + "enrolled", + "active", + "connected", +}; + +static const char *wlp_wss_strstate(unsigned state) +{ + if (state >= ARRAY_SIZE(__wlp_strstate)) + return "unknown state"; + return __wlp_strstate[state]; +} + +/* + * Print current state of this WSS + */ +static ssize_t wlp_wss_state_show(struct wlp_wss *wss, char *buf) +{ + int result = 0; + + if (mutex_lock_interruptible(&wss->mutex)) + goto out; + result = scnprintf(buf, PAGE_SIZE, "%s\n", + wlp_wss_strstate(wss->state)); + mutex_unlock(&wss->mutex); +out: + return result; +} +WSS_ATTR(state, S_IRUGO, wlp_wss_state_show, NULL); + + +static +struct attribute *wss_attrs[] = { + &wss_attr_properties.attr, + &wss_attr_members.attr, + &wss_attr_state.attr, + NULL, +}; + +struct attribute_group wss_attr_group = { + .name = NULL, /* we want them in the same directory */ + .attrs = wss_attrs, +}; diff --git a/trunk/drivers/uwb/wlp/txrx.c b/trunk/drivers/uwb/wlp/txrx.c new file mode 100644 index 000000000000..05dde44b3592 --- /dev/null +++ b/trunk/drivers/uwb/wlp/txrx.c @@ -0,0 +1,354 @@ +/* + * WiMedia Logical Link Control Protocol (WLP) + * Message exchange infrastructure + * + * Copyright (C) 2007 Intel Corporation + * Reinette Chatre + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * FIXME: Docs + * + */ + +#include +#include +#include + +#include "wlp-internal.h" + +/* + * Direct incoming association msg to correct parsing routine + * + * We only expect D1, E1, C1, C3 messages as new. All other incoming + * association messages should form part of an established session that is + * handled elsewhere. + * The handling of these messages often require calling sleeping functions + * - this cannot be done in interrupt context. We use the kernel's + * workqueue to handle these messages. + */ +static +void wlp_direct_assoc_frame(struct wlp *wlp, struct sk_buff *skb, + struct uwb_dev_addr *src) +{ + struct device *dev = &wlp->rc->uwb_dev.dev; + struct wlp_frame_assoc *assoc = (void *) skb->data; + struct wlp_assoc_frame_ctx *frame_ctx; + + frame_ctx = kmalloc(sizeof(*frame_ctx), GFP_ATOMIC); + if (frame_ctx == NULL) { + dev_err(dev, "WLP: Unable to allocate memory for association " + "frame handling.\n"); + kfree_skb(skb); + return; + } + frame_ctx->wlp = wlp; + frame_ctx->skb = skb; + frame_ctx->src = *src; + switch (assoc->type) { + case WLP_ASSOC_D1: + INIT_WORK(&frame_ctx->ws, wlp_handle_d1_frame); + schedule_work(&frame_ctx->ws); + break; + case WLP_ASSOC_E1: + kfree_skb(skb); /* Temporary until we handle it */ + kfree(frame_ctx); /* Temporary until we handle it */ + break; + case WLP_ASSOC_C1: + INIT_WORK(&frame_ctx->ws, wlp_handle_c1_frame); + schedule_work(&frame_ctx->ws); + break; + case WLP_ASSOC_C3: + INIT_WORK(&frame_ctx->ws, wlp_handle_c3_frame); + schedule_work(&frame_ctx->ws); + break; + default: + dev_err(dev, "Received unexpected association frame. " + "Type = %d \n", assoc->type); + kfree_skb(skb); + kfree(frame_ctx); + break; + } +} + +/* + * Process incoming association frame + * + * Although it could be possible to deal with some incoming association + * messages without creating a new session we are keeping things simple. We + * do not accept new association messages if there is a session in progress + * and the messages do not belong to that session. + * + * If an association message arrives that causes the creation of a session + * (WLP_ASSOC_E1) while we are in the process of creating a session then we + * rely on the neighbor mutex to protect the data. That is, the new session + * will not be started until the previous is completed. + */ +static +void wlp_receive_assoc_frame(struct wlp *wlp, struct sk_buff *skb, + struct uwb_dev_addr *src) +{ + struct device *dev = &wlp->rc->uwb_dev.dev; + struct wlp_frame_assoc *assoc = (void *) skb->data; + struct wlp_session *session = wlp->session; + u8 version; + + if (wlp_get_version(wlp, &assoc->version, &version, + sizeof(assoc->version)) < 0) + goto error; + if (version != WLP_VERSION) { + dev_err(dev, "Unsupported WLP version in association " + "message.\n"); + goto error; + } + if (session != NULL) { + /* Function that created this session is still holding the + * &wlp->mutex to protect this session. */ + if (assoc->type == session->exp_message || + assoc->type == WLP_ASSOC_F0) { + if (!memcmp(&session->neighbor_addr, src, + sizeof(*src))) { + session->data = skb; + (session->cb)(wlp); + } else { + dev_err(dev, "Received expected message from " + "unexpected source. Expected message " + "%d or F0 from %02x:%02x, but received " + "it from %02x:%02x. Dropping.\n", + session->exp_message, + session->neighbor_addr.data[1], + session->neighbor_addr.data[0], + src->data[1], src->data[0]); + goto error; + } + } else { + dev_err(dev, "Association already in progress. " + "Dropping.\n"); + goto error; + } + } else { + wlp_direct_assoc_frame(wlp, skb, src); + } + return; +error: + kfree_skb(skb); +} + +/* + * Verify incoming frame is from connected neighbor, prep to pass to WLP client + * + * Verification proceeds according to WLP 0.99 [7.3.1]. The source address + * is used to determine which neighbor is sending the frame and the WSS tag + * is used to know to which WSS the frame belongs (we only support one WSS + * so this test is straight forward). + * With the WSS found we need to ensure that we are connected before + * allowing the exchange of data frames. + */ +static +int wlp_verify_prep_rx_frame(struct wlp *wlp, struct sk_buff *skb, + struct uwb_dev_addr *src) +{ + struct device *dev = &wlp->rc->uwb_dev.dev; + int result = -EINVAL; + struct wlp_eda_node eda_entry; + struct wlp_frame_std_abbrv_hdr *hdr = (void *) skb->data; + + /*verify*/ + result = wlp_copy_eda_node(&wlp->eda, src, &eda_entry); + if (result < 0) { + if (printk_ratelimit()) + dev_err(dev, "WLP: Incoming frame is from unknown " + "neighbor %02x:%02x.\n", src->data[1], + src->data[0]); + goto out; + } + if (hdr->tag != eda_entry.tag) { + if (printk_ratelimit()) + dev_err(dev, "WLP: Tag of incoming frame from " + "%02x:%02x does not match expected tag. " + "Received 0x%02x, expected 0x%02x. \n", + src->data[1], src->data[0], hdr->tag, + eda_entry.tag); + result = -EINVAL; + goto out; + } + if (eda_entry.state != WLP_WSS_CONNECTED) { + if (printk_ratelimit()) + dev_err(dev, "WLP: Incoming frame from " + "%02x:%02x does is not from connected WSS.\n", + src->data[1], src->data[0]); + result = -EINVAL; + goto out; + } + /*prep*/ + skb_pull(skb, sizeof(*hdr)); +out: + return result; +} + +/* + * Receive a WLP frame from device + * + * @returns: 1 if calling function should free the skb + * 0 if it successfully handled skb and freed it + * 0 if error occured, will free skb in this case + */ +int wlp_receive_frame(struct device *dev, struct wlp *wlp, struct sk_buff *skb, + struct uwb_dev_addr *src) +{ + unsigned len = skb->len; + void *ptr = skb->data; + struct wlp_frame_hdr *hdr; + int result = 0; + + if (len < sizeof(*hdr)) { + dev_err(dev, "Not enough data to parse WLP header.\n"); + result = -EINVAL; + goto out; + } + hdr = ptr; + if (le16_to_cpu(hdr->mux_hdr) != WLP_PROTOCOL_ID) { + dev_err(dev, "Not a WLP frame type.\n"); + result = -EINVAL; + goto out; + } + switch (hdr->type) { + case WLP_FRAME_STANDARD: + if (len < sizeof(struct wlp_frame_std_abbrv_hdr)) { + dev_err(dev, "Not enough data to parse Standard " + "WLP header.\n"); + goto out; + } + result = wlp_verify_prep_rx_frame(wlp, skb, src); + if (result < 0) { + if (printk_ratelimit()) + dev_err(dev, "WLP: Verification of frame " + "from neighbor %02x:%02x failed.\n", + src->data[1], src->data[0]); + goto out; + } + result = 1; + break; + case WLP_FRAME_ABBREVIATED: + dev_err(dev, "Abbreviated frame received. FIXME?\n"); + kfree_skb(skb); + break; + case WLP_FRAME_CONTROL: + dev_err(dev, "Control frame received. FIXME?\n"); + kfree_skb(skb); + break; + case WLP_FRAME_ASSOCIATION: + if (len < sizeof(struct wlp_frame_assoc)) { + dev_err(dev, "Not enough data to parse Association " + "WLP header.\n"); + goto out; + } + wlp_receive_assoc_frame(wlp, skb, src); + break; + default: + dev_err(dev, "Invalid frame received.\n"); + result = -EINVAL; + break; + } +out: + if (result < 0) { + kfree_skb(skb); + result = 0; + } + return result; +} +EXPORT_SYMBOL_GPL(wlp_receive_frame); + + +/* + * Verify frame from network stack, prepare for further transmission + * + * @skb: the socket buffer that needs to be prepared for transmission (it + * is in need of a WLP header). If this is a broadcast frame we take + * over the entire transmission. + * If it is a unicast the WSS connection should already be established + * and transmission will be done by the calling function. + * @dst: On return this will contain the device address to which the + * frame is destined. + * @returns: 0 on success no tx : WLP header successfully applied to skb buffer, + * calling function can proceed with tx + * 1 on success with tx : WLP will take over transmission of this + * frame + * <0 on error + * + * The network stack (WLP client) is attempting to transmit a frame. We can + * only transmit data if a local WSS is at least active (connection will be + * done here if this is a broadcast frame and neighbor also has the WSS + * active). + * + * The frame can be either broadcast or unicast. Broadcast in a WSS is + * supported via multicast, but we don't support multicast yet (until + * devices start to support MAB IEs). If a broadcast frame needs to be + * transmitted it is treated as a unicast frame to each neighbor. In this + * case the WLP takes over transmission of the skb and returns 1 + * to the caller to indicate so. Also, in this case, if a neighbor has the + * same WSS activated but is not connected then the WSS connection will be + * done at this time. The neighbor's virtual address will be learned at + * this time. + * + * The destination address in a unicast frame is the virtual address of the + * neighbor. This address only becomes known when a WSS connection is + * established. We thus rely on a broadcast frame to trigger the setup of + * WSS connections to all neighbors before we are able to send unicast + * frames to them. This seems reasonable as IP would usually use ARP first + * before any unicast frames are sent. + * + * If we are already connected to the neighbor (neighbor's virtual address + * is known) we just prepare the WLP header and the caller will continue to + * send the frame. + * + * A failure in this function usually indicates something that cannot be + * fixed automatically. So, if this function fails (@return < 0) the calling + * function should not retry to send the frame as it will very likely keep + * failing. + * + */ +int wlp_prepare_tx_frame(struct device *dev, struct wlp *wlp, + struct sk_buff *skb, struct uwb_dev_addr *dst) +{ + int result = -EINVAL; + struct ethhdr *eth_hdr = (void *) skb->data; + + if (is_multicast_ether_addr(eth_hdr->h_dest)) { + result = wlp_eda_for_each(&wlp->eda, wlp_wss_send_copy, skb); + if (result < 0) { + if (printk_ratelimit()) + dev_err(dev, "Unable to handle broadcast " + "frame from WLP client.\n"); + goto out; + } + dev_kfree_skb_irq(skb); + result = 1; + /* Frame will be transmitted by WLP. */ + } else { + result = wlp_eda_for_virtual(&wlp->eda, eth_hdr->h_dest, dst, + wlp_wss_prep_hdr, skb); + if (unlikely(result < 0)) { + if (printk_ratelimit()) + dev_err(dev, "Unable to prepare " + "skb for transmission. \n"); + goto out; + } + } +out: + return result; +} +EXPORT_SYMBOL_GPL(wlp_prepare_tx_frame); diff --git a/trunk/drivers/uwb/wlp/wlp-internal.h b/trunk/drivers/uwb/wlp/wlp-internal.h new file mode 100644 index 000000000000..3e8d5de7c5b9 --- /dev/null +++ b/trunk/drivers/uwb/wlp/wlp-internal.h @@ -0,0 +1,224 @@ +/* + * WiMedia Logical Link Control Protocol (WLP) + * Internal API + * + * Copyright (C) 2007 Intel Corporation + * Reinette Chatre + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#ifndef __WLP_INTERNAL_H__ +#define __WLP_INTERNAL_H__ + +/** + * State of WSS connection + * + * A device needs to connect to a neighbor in an activated WSS before data + * can be transmitted. The spec also distinguishes between a new connection + * attempt and a connection attempt after previous connection attempts. The + * state WLP_WSS_CONNECT_FAILED is used for this scenario. See WLP 0.99 + * [7.2.6] + */ +enum wlp_wss_connect { + WLP_WSS_UNCONNECTED = 0, + WLP_WSS_CONNECTED, + WLP_WSS_CONNECT_FAILED, +}; + +extern struct kobj_type wss_ktype; +extern struct attribute_group wss_attr_group; + +/* This should be changed to a dynamic array where entries are sorted + * by eth_addr and search is done in a binary form + * + * Although thinking twice about it: this technologie's maximum reach + * is 10 meters...unless you want to pack too much stuff in around + * your radio controller/WLP device, the list will probably not be + * too big. + * + * In any case, there is probably some data structure in the kernel + * than we could reused for that already. + * + * The below structure is really just good while we support one WSS per + * host. + */ +struct wlp_eda_node { + struct list_head list_node; + unsigned char eth_addr[ETH_ALEN]; + struct uwb_dev_addr dev_addr; + struct wlp_wss *wss; + unsigned char virt_addr[ETH_ALEN]; + u8 tag; + enum wlp_wss_connect state; +}; + +typedef int (*wlp_eda_for_each_f)(struct wlp *, struct wlp_eda_node *, void *); + +extern void wlp_eda_init(struct wlp_eda *); +extern void wlp_eda_release(struct wlp_eda *); +extern int wlp_eda_create_node(struct wlp_eda *, + const unsigned char eth_addr[ETH_ALEN], + const struct uwb_dev_addr *); +extern void wlp_eda_rm_node(struct wlp_eda *, const struct uwb_dev_addr *); +extern int wlp_eda_update_node(struct wlp_eda *, + const struct uwb_dev_addr *, + struct wlp_wss *, + const unsigned char virt_addr[ETH_ALEN], + const u8, const enum wlp_wss_connect); +extern int wlp_eda_update_node_state(struct wlp_eda *, + const struct uwb_dev_addr *, + const enum wlp_wss_connect); + +extern int wlp_copy_eda_node(struct wlp_eda *, struct uwb_dev_addr *, + struct wlp_eda_node *); +extern int wlp_eda_for_each(struct wlp_eda *, wlp_eda_for_each_f , void *); +extern int wlp_eda_for_virtual(struct wlp_eda *, + const unsigned char eth_addr[ETH_ALEN], + struct uwb_dev_addr *, + wlp_eda_for_each_f , void *); + + +extern void wlp_remove_neighbor_tmp_info(struct wlp_neighbor_e *); + +extern size_t wlp_wss_key_print(char *, size_t, u8 *); + +/* Function called when no more references to WSS exists */ +extern void wlp_wss_release(struct kobject *); + +extern void wlp_wss_reset(struct wlp_wss *); +extern int wlp_wss_create_activate(struct wlp_wss *, struct wlp_uuid *, + char *, unsigned, unsigned); +extern int wlp_wss_enroll_activate(struct wlp_wss *, struct wlp_uuid *, + struct uwb_dev_addr *); +extern ssize_t wlp_discover(struct wlp *); + +extern int wlp_enroll_neighbor(struct wlp *, struct wlp_neighbor_e *, + struct wlp_wss *, struct wlp_uuid *); +extern int wlp_wss_is_active(struct wlp *, struct wlp_wss *, + struct uwb_dev_addr *); + +struct wlp_assoc_conn_ctx { + struct work_struct ws; + struct wlp *wlp; + struct sk_buff *skb; + struct wlp_eda_node eda_entry; +}; + + +extern int wlp_wss_connect_prep(struct wlp *, struct wlp_eda_node *, void *); +extern int wlp_wss_send_copy(struct wlp *, struct wlp_eda_node *, void *); + + +/* Message handling */ +struct wlp_assoc_frame_ctx { + struct work_struct ws; + struct wlp *wlp; + struct sk_buff *skb; + struct uwb_dev_addr src; +}; + +extern int wlp_wss_prep_hdr(struct wlp *, struct wlp_eda_node *, void *); +extern void wlp_handle_d1_frame(struct work_struct *); +extern int wlp_parse_d2_frame_to_cache(struct wlp *, struct sk_buff *, + struct wlp_neighbor_e *); +extern int wlp_parse_d2_frame_to_enroll(struct wlp_wss *, struct sk_buff *, + struct wlp_neighbor_e *, + struct wlp_uuid *); +extern void wlp_handle_c1_frame(struct work_struct *); +extern void wlp_handle_c3_frame(struct work_struct *); +extern int wlp_parse_c3c4_frame(struct wlp *, struct sk_buff *, + struct wlp_uuid *, u8 *, + struct uwb_mac_addr *); +extern int wlp_parse_f0(struct wlp *, struct sk_buff *); +extern int wlp_send_assoc_frame(struct wlp *, struct wlp_wss *, + struct uwb_dev_addr *, enum wlp_assoc_type); +extern ssize_t wlp_get_version(struct wlp *, struct wlp_attr_version *, + u8 *, ssize_t); +extern ssize_t wlp_get_wssid(struct wlp *, struct wlp_attr_wssid *, + struct wlp_uuid *, ssize_t); +extern int __wlp_alloc_device_info(struct wlp *); +extern int __wlp_setup_device_info(struct wlp *); + +extern struct wlp_wss_attribute wss_attribute_properties; +extern struct wlp_wss_attribute wss_attribute_members; +extern struct wlp_wss_attribute wss_attribute_state; + +static inline +size_t wlp_wss_uuid_print(char *buf, size_t bufsize, struct wlp_uuid *uuid) +{ + size_t result; + + result = scnprintf(buf, bufsize, + "%02x:%02x:%02x:%02x:%02x:%02x:" + "%02x:%02x:%02x:%02x:%02x:%02x:" + "%02x:%02x:%02x:%02x", + uuid->data[0], uuid->data[1], + uuid->data[2], uuid->data[3], + uuid->data[4], uuid->data[5], + uuid->data[6], uuid->data[7], + uuid->data[8], uuid->data[9], + uuid->data[10], uuid->data[11], + uuid->data[12], uuid->data[13], + uuid->data[14], uuid->data[15]); + return result; +} + +/** + * FIXME: How should a nonce be displayed? + */ +static inline +size_t wlp_wss_nonce_print(char *buf, size_t bufsize, struct wlp_nonce *nonce) +{ + size_t result; + + result = scnprintf(buf, bufsize, + "%02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x", + nonce->data[0], nonce->data[1], + nonce->data[2], nonce->data[3], + nonce->data[4], nonce->data[5], + nonce->data[6], nonce->data[7], + nonce->data[8], nonce->data[9], + nonce->data[10], nonce->data[11], + nonce->data[12], nonce->data[13], + nonce->data[14], nonce->data[15]); + return result; +} + + +static inline +void wlp_session_cb(struct wlp *wlp) +{ + struct completion *completion = wlp->session->cb_priv; + complete(completion); +} + +static inline +int wlp_uuid_is_set(struct wlp_uuid *uuid) +{ + struct wlp_uuid zero_uuid = { .data = { 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00} }; + + if (!memcmp(uuid, &zero_uuid, sizeof(*uuid))) + return 0; + return 1; +} + +#endif /* __WLP_INTERNAL_H__ */ diff --git a/trunk/drivers/uwb/wlp/wlp-lc.c b/trunk/drivers/uwb/wlp/wlp-lc.c new file mode 100644 index 000000000000..7f6a630bf26c --- /dev/null +++ b/trunk/drivers/uwb/wlp/wlp-lc.c @@ -0,0 +1,560 @@ +/* + * WiMedia Logical Link Control Protocol (WLP) + * + * Copyright (C) 2005-2006 Intel Corporation + * Reinette Chatre + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * FIXME: docs + */ +#include +#include + +#include "wlp-internal.h" + +static +void wlp_neighbor_init(struct wlp_neighbor_e *neighbor) +{ + INIT_LIST_HEAD(&neighbor->wssid); +} + +/** + * Create area for device information storage + * + * wlp->mutex must be held + */ +int __wlp_alloc_device_info(struct wlp *wlp) +{ + struct device *dev = &wlp->rc->uwb_dev.dev; + BUG_ON(wlp->dev_info != NULL); + wlp->dev_info = kzalloc(sizeof(struct wlp_device_info), GFP_KERNEL); + if (wlp->dev_info == NULL) { + dev_err(dev, "WLP: Unable to allocate memory for " + "device information.\n"); + return -ENOMEM; + } + return 0; +} + + +/** + * Fill in device information using function provided by driver + * + * wlp->mutex must be held + */ +static +void __wlp_fill_device_info(struct wlp *wlp) +{ + wlp->fill_device_info(wlp, wlp->dev_info); +} + +/** + * Setup device information + * + * Allocate area for device information and populate it. + * + * wlp->mutex must be held + */ +int __wlp_setup_device_info(struct wlp *wlp) +{ + int result; + struct device *dev = &wlp->rc->uwb_dev.dev; + + result = __wlp_alloc_device_info(wlp); + if (result < 0) { + dev_err(dev, "WLP: Unable to allocate area for " + "device information.\n"); + return result; + } + __wlp_fill_device_info(wlp); + return 0; +} + +/** + * Remove information about neighbor stored temporarily + * + * Information learned during discovey should only be stored when the + * device enrolls in the neighbor's WSS. We do need to store this + * information temporarily in order to present it to the user. + * + * We are only interested in keeping neighbor WSS information if that + * neighbor is accepting enrollment. + * + * should be called with wlp->nbmutex held + */ +void wlp_remove_neighbor_tmp_info(struct wlp_neighbor_e *neighbor) +{ + struct wlp_wssid_e *wssid_e, *next; + u8 keep; + if (!list_empty(&neighbor->wssid)) { + list_for_each_entry_safe(wssid_e, next, &neighbor->wssid, + node) { + if (wssid_e->info != NULL) { + keep = wssid_e->info->accept_enroll; + kfree(wssid_e->info); + wssid_e->info = NULL; + if (!keep) { + list_del(&wssid_e->node); + kfree(wssid_e); + } + } + } + } + if (neighbor->info != NULL) { + kfree(neighbor->info); + neighbor->info = NULL; + } +} + +/* + * Populate WLP neighborhood cache with neighbor information + * + * A new neighbor is found. If it is discoverable then we add it to the + * neighborhood cache. + * + */ +static +int wlp_add_neighbor(struct wlp *wlp, struct uwb_dev *dev) +{ + int result = 0; + int discoverable; + struct wlp_neighbor_e *neighbor; + + /* + * FIXME: + * Use contents of WLP IE found in beacon cache to determine if + * neighbor is discoverable. + * The device does not support WLP IE yet so this still needs to be + * done. Until then we assume all devices are discoverable. + */ + discoverable = 1; /* will be changed when FIXME disappears */ + if (discoverable) { + /* Add neighbor to cache for discovery */ + neighbor = kzalloc(sizeof(*neighbor), GFP_KERNEL); + if (neighbor == NULL) { + dev_err(&dev->dev, "Unable to create memory for " + "new neighbor. \n"); + result = -ENOMEM; + goto error_no_mem; + } + wlp_neighbor_init(neighbor); + uwb_dev_get(dev); + neighbor->uwb_dev = dev; + list_add(&neighbor->node, &wlp->neighbors); + } +error_no_mem: + return result; +} + +/** + * Remove one neighbor from cache + */ +static +void __wlp_neighbor_release(struct wlp_neighbor_e *neighbor) +{ + struct wlp_wssid_e *wssid_e, *next_wssid_e; + + list_for_each_entry_safe(wssid_e, next_wssid_e, + &neighbor->wssid, node) { + list_del(&wssid_e->node); + kfree(wssid_e); + } + uwb_dev_put(neighbor->uwb_dev); + list_del(&neighbor->node); + kfree(neighbor); +} + +/** + * Clear entire neighborhood cache. + */ +static +void __wlp_neighbors_release(struct wlp *wlp) +{ + struct wlp_neighbor_e *neighbor, *next; + if (list_empty(&wlp->neighbors)) + return; + list_for_each_entry_safe(neighbor, next, &wlp->neighbors, node) { + __wlp_neighbor_release(neighbor); + } +} + +static +void wlp_neighbors_release(struct wlp *wlp) +{ + mutex_lock(&wlp->nbmutex); + __wlp_neighbors_release(wlp); + mutex_unlock(&wlp->nbmutex); +} + + + +/** + * Send D1 message to neighbor, receive D2 message + * + * @neighbor: neighbor to which D1 message will be sent + * @wss: if not NULL, it is an enrollment request for this WSS + * @wssid: if wss not NULL, this is the wssid of the WSS in which we + * want to enroll + * + * A D1/D2 exchange is done for one of two reasons: discovery or + * enrollment. If done for discovery the D1 message is sent to the neighbor + * and the contents of the D2 response is stored in a temporary cache. + * If done for enrollment the @wss and @wssid are provided also. In this + * case the D1 message is sent to the neighbor, the D2 response is parsed + * for enrollment of the WSS with wssid. + * + * &wss->mutex is held + */ +static +int wlp_d1d2_exchange(struct wlp *wlp, struct wlp_neighbor_e *neighbor, + struct wlp_wss *wss, struct wlp_uuid *wssid) +{ + int result; + struct device *dev = &wlp->rc->uwb_dev.dev; + DECLARE_COMPLETION_ONSTACK(completion); + struct wlp_session session; + struct sk_buff *skb; + struct wlp_frame_assoc *resp; + struct uwb_dev_addr *dev_addr = &neighbor->uwb_dev->dev_addr; + + mutex_lock(&wlp->mutex); + if (!wlp_uuid_is_set(&wlp->uuid)) { + dev_err(dev, "WLP: UUID is not set. Set via sysfs to " + "proceed.\n"); + result = -ENXIO; + goto out; + } + /* Send D1 association frame */ + result = wlp_send_assoc_frame(wlp, wss, dev_addr, WLP_ASSOC_D1); + if (result < 0) { + dev_err(dev, "Unable to send D1 frame to neighbor " + "%02x:%02x (%d)\n", dev_addr->data[1], + dev_addr->data[0], result); + goto out; + } + /* Create session, wait for response */ + session.exp_message = WLP_ASSOC_D2; + session.cb = wlp_session_cb; + session.cb_priv = &completion; + session.neighbor_addr = *dev_addr; + BUG_ON(wlp->session != NULL); + wlp->session = &session; + /* Wait for D2/F0 frame */ + result = wait_for_completion_interruptible_timeout(&completion, + WLP_PER_MSG_TIMEOUT * HZ); + if (result == 0) { + result = -ETIMEDOUT; + dev_err(dev, "Timeout while sending D1 to neighbor " + "%02x:%02x.\n", dev_addr->data[1], + dev_addr->data[0]); + goto error_session; + } + if (result < 0) { + dev_err(dev, "Unable to discover/enroll neighbor %02x:%02x.\n", + dev_addr->data[1], dev_addr->data[0]); + goto error_session; + } + /* Parse message in session->data: it will be either D2 or F0 */ + skb = session.data; + resp = (void *) skb->data; + + if (resp->type == WLP_ASSOC_F0) { + result = wlp_parse_f0(wlp, skb); + if (result < 0) + dev_err(dev, "WLP: Unable to parse F0 from neighbor " + "%02x:%02x.\n", dev_addr->data[1], + dev_addr->data[0]); + result = -EINVAL; + goto error_resp_parse; + } + if (wss == NULL) { + /* Discovery */ + result = wlp_parse_d2_frame_to_cache(wlp, skb, neighbor); + if (result < 0) { + dev_err(dev, "WLP: Unable to parse D2 message from " + "neighbor %02x:%02x for discovery.\n", + dev_addr->data[1], dev_addr->data[0]); + goto error_resp_parse; + } + } else { + /* Enrollment */ + result = wlp_parse_d2_frame_to_enroll(wss, skb, neighbor, + wssid); + if (result < 0) { + dev_err(dev, "WLP: Unable to parse D2 message from " + "neighbor %02x:%02x for enrollment.\n", + dev_addr->data[1], dev_addr->data[0]); + goto error_resp_parse; + } + } +error_resp_parse: + kfree_skb(skb); +error_session: + wlp->session = NULL; +out: + mutex_unlock(&wlp->mutex); + return result; +} + +/** + * Enroll into WSS of provided WSSID by using neighbor as registrar + * + * &wss->mutex is held + */ +int wlp_enroll_neighbor(struct wlp *wlp, struct wlp_neighbor_e *neighbor, + struct wlp_wss *wss, struct wlp_uuid *wssid) +{ + int result = 0; + struct device *dev = &wlp->rc->uwb_dev.dev; + char buf[WLP_WSS_UUID_STRSIZE]; + struct uwb_dev_addr *dev_addr = &neighbor->uwb_dev->dev_addr; + + wlp_wss_uuid_print(buf, sizeof(buf), wssid); + + result = wlp_d1d2_exchange(wlp, neighbor, wss, wssid); + if (result < 0) { + dev_err(dev, "WLP: D1/D2 message exchange for enrollment " + "failed. result = %d \n", result); + goto out; + } + if (wss->state != WLP_WSS_STATE_PART_ENROLLED) { + dev_err(dev, "WLP: Unable to enroll into WSS %s using " + "neighbor %02x:%02x. \n", buf, + dev_addr->data[1], dev_addr->data[0]); + result = -EINVAL; + goto out; + } + if (wss->secure_status == WLP_WSS_SECURE) { + dev_err(dev, "FIXME: need to complete secure enrollment.\n"); + result = -EINVAL; + goto error; + } else { + wss->state = WLP_WSS_STATE_ENROLLED; + dev_dbg(dev, "WLP: Success Enrollment into unsecure WSS " + "%s using neighbor %02x:%02x. \n", + buf, dev_addr->data[1], dev_addr->data[0]); + } +out: + return result; +error: + wlp_wss_reset(wss); + return result; +} + +/** + * Discover WSS information of neighbor's active WSS + */ +static +int wlp_discover_neighbor(struct wlp *wlp, + struct wlp_neighbor_e *neighbor) +{ + return wlp_d1d2_exchange(wlp, neighbor, NULL, NULL); +} + + +/** + * Each neighbor in the neighborhood cache is discoverable. Discover it. + * + * Discovery is done through sending of D1 association frame and parsing + * the D2 association frame response. Only wssid from D2 will be included + * in neighbor cache, rest is just displayed to user and forgotten. + * + * The discovery is not done in parallel. This is simple and enables us to + * maintain only one association context. + * + * The discovery of one neighbor does not affect the other, but if the + * discovery of a neighbor fails it is removed from the neighborhood cache. + */ +static +int wlp_discover_all_neighbors(struct wlp *wlp) +{ + int result = 0; + struct device *dev = &wlp->rc->uwb_dev.dev; + struct wlp_neighbor_e *neighbor, *next; + + list_for_each_entry_safe(neighbor, next, &wlp->neighbors, node) { + result = wlp_discover_neighbor(wlp, neighbor); + if (result < 0) { + dev_err(dev, "WLP: Unable to discover neighbor " + "%02x:%02x, removing from neighborhood. \n", + neighbor->uwb_dev->dev_addr.data[1], + neighbor->uwb_dev->dev_addr.data[0]); + __wlp_neighbor_release(neighbor); + } + } + return result; +} + +static int wlp_add_neighbor_helper(struct device *dev, void *priv) +{ + struct wlp *wlp = priv; + struct uwb_dev *uwb_dev = to_uwb_dev(dev); + + return wlp_add_neighbor(wlp, uwb_dev); +} + +/** + * Discover WLP neighborhood + * + * Will send D1 association frame to all devices in beacon group that have + * discoverable bit set in WLP IE. D2 frames will be received, information + * displayed to user in @buf. Partial information (from D2 association + * frame) will be cached to assist with future association + * requests. + * + * The discovery of the WLP neighborhood is triggered by the user. This + * should occur infrequently and we thus free current cache and re-allocate + * memory if needed. + * + * If one neighbor fails during initial discovery (determining if it is a + * neighbor or not), we fail all - note that interaction with neighbor has + * not occured at this point so if a failure occurs we know something went wrong + * locally. We thus undo everything. + */ +ssize_t wlp_discover(struct wlp *wlp) +{ + int result = 0; + struct device *dev = &wlp->rc->uwb_dev.dev; + + mutex_lock(&wlp->nbmutex); + /* Clear current neighborhood cache. */ + __wlp_neighbors_release(wlp); + /* Determine which devices in neighborhood. Repopulate cache. */ + result = uwb_dev_for_each(wlp->rc, wlp_add_neighbor_helper, wlp); + if (result < 0) { + /* May have partial neighbor information, release all. */ + __wlp_neighbors_release(wlp); + goto error_dev_for_each; + } + /* Discover the properties of devices in neighborhood. */ + result = wlp_discover_all_neighbors(wlp); + /* In case of failure we still print our partial results. */ + if (result < 0) { + dev_err(dev, "Unable to fully discover neighborhood. \n"); + result = 0; + } +error_dev_for_each: + mutex_unlock(&wlp->nbmutex); + return result; +} + +/** + * Handle events from UWB stack + * + * We handle events conservatively. If a neighbor goes off the air we + * remove it from the neighborhood. If an association process is in + * progress this function will block waiting for the nbmutex to become + * free. The association process will thus be allowed to complete before it + * is removed. + */ +static +void wlp_uwb_notifs_cb(void *_wlp, struct uwb_dev *uwb_dev, + enum uwb_notifs event) +{ + struct wlp *wlp = _wlp; + struct device *dev = &wlp->rc->uwb_dev.dev; + struct wlp_neighbor_e *neighbor, *next; + int result; + switch (event) { + case UWB_NOTIF_ONAIR: + result = wlp_eda_create_node(&wlp->eda, + uwb_dev->mac_addr.data, + &uwb_dev->dev_addr); + if (result < 0) + dev_err(dev, "WLP: Unable to add new neighbor " + "%02x:%02x to EDA cache.\n", + uwb_dev->dev_addr.data[1], + uwb_dev->dev_addr.data[0]); + break; + case UWB_NOTIF_OFFAIR: + wlp_eda_rm_node(&wlp->eda, &uwb_dev->dev_addr); + mutex_lock(&wlp->nbmutex); + list_for_each_entry_safe(neighbor, next, &wlp->neighbors, node) { + if (neighbor->uwb_dev == uwb_dev) + __wlp_neighbor_release(neighbor); + } + mutex_unlock(&wlp->nbmutex); + break; + default: + dev_err(dev, "don't know how to handle event %d from uwb\n", + event); + } +} + +static void wlp_channel_changed(struct uwb_pal *pal, int channel) +{ + struct wlp *wlp = container_of(pal, struct wlp, pal); + + if (channel < 0) + netif_carrier_off(wlp->ndev); + else + netif_carrier_on(wlp->ndev); +} + +int wlp_setup(struct wlp *wlp, struct uwb_rc *rc, struct net_device *ndev) +{ + int result; + + BUG_ON(wlp->fill_device_info == NULL); + BUG_ON(wlp->xmit_frame == NULL); + BUG_ON(wlp->stop_queue == NULL); + BUG_ON(wlp->start_queue == NULL); + + wlp->rc = rc; + wlp->ndev = ndev; + wlp_eda_init(&wlp->eda);/* Set up address cache */ + wlp->uwb_notifs_handler.cb = wlp_uwb_notifs_cb; + wlp->uwb_notifs_handler.data = wlp; + uwb_notifs_register(rc, &wlp->uwb_notifs_handler); + + uwb_pal_init(&wlp->pal); + wlp->pal.rc = rc; + wlp->pal.channel_changed = wlp_channel_changed; + result = uwb_pal_register(&wlp->pal); + if (result < 0) + uwb_notifs_deregister(wlp->rc, &wlp->uwb_notifs_handler); + + return result; +} +EXPORT_SYMBOL_GPL(wlp_setup); + +void wlp_remove(struct wlp *wlp) +{ + wlp_neighbors_release(wlp); + uwb_pal_unregister(&wlp->pal); + uwb_notifs_deregister(wlp->rc, &wlp->uwb_notifs_handler); + wlp_eda_release(&wlp->eda); + mutex_lock(&wlp->mutex); + if (wlp->dev_info != NULL) + kfree(wlp->dev_info); + mutex_unlock(&wlp->mutex); + wlp->rc = NULL; +} +EXPORT_SYMBOL_GPL(wlp_remove); + +/** + * wlp_reset_all - reset the WLP hardware + * @wlp: the WLP device to reset. + * + * This schedules a full hardware reset of the WLP device. The radio + * controller and any other PALs will also be reset. + */ +void wlp_reset_all(struct wlp *wlp) +{ + uwb_rc_reset_all(wlp->rc); +} +EXPORT_SYMBOL_GPL(wlp_reset_all); diff --git a/trunk/drivers/uwb/wlp/wss-lc.c b/trunk/drivers/uwb/wlp/wss-lc.c new file mode 100644 index 000000000000..67872c83b679 --- /dev/null +++ b/trunk/drivers/uwb/wlp/wss-lc.c @@ -0,0 +1,959 @@ +/* + * WiMedia Logical Link Control Protocol (WLP) + * + * Copyright (C) 2007 Intel Corporation + * Reinette Chatre + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * Implementation of the WLP association protocol. + * + * FIXME: Docs + * + * A UWB network interface will configure a WSS through wlp_wss_setup() after + * the interface has been assigned a MAC address, typically after + * "ifconfig" has been called. When the interface goes down it should call + * wlp_wss_remove(). + * + * When the WSS is ready for use the user interacts via sysfs to create, + * discover, and activate WSS. + * + * wlp_wss_enroll_activate() + * + * wlp_wss_create_activate() + * wlp_wss_set_wssid_hash() + * wlp_wss_comp_wssid_hash() + * wlp_wss_sel_bcast_addr() + * wlp_wss_sysfs_add() + * + * Called when no more references to WSS exist: + * wlp_wss_release() + * wlp_wss_reset() + */ +#include /* for is_valid_ether_addr */ +#include +#include +#include + +#include "wlp-internal.h" + +size_t wlp_wss_key_print(char *buf, size_t bufsize, u8 *key) +{ + size_t result; + + result = scnprintf(buf, bufsize, + "%02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x", + key[0], key[1], key[2], key[3], + key[4], key[5], key[6], key[7], + key[8], key[9], key[10], key[11], + key[12], key[13], key[14], key[15]); + return result; +} + +/** + * Compute WSSID hash + * WLP Draft 0.99 [7.2.1] + * + * The WSSID hash for a WSSID is the result of an octet-wise exclusive-OR + * of all octets in the WSSID. + */ +static +u8 wlp_wss_comp_wssid_hash(struct wlp_uuid *wssid) +{ + return wssid->data[0] ^ wssid->data[1] ^ wssid->data[2] + ^ wssid->data[3] ^ wssid->data[4] ^ wssid->data[5] + ^ wssid->data[6] ^ wssid->data[7] ^ wssid->data[8] + ^ wssid->data[9] ^ wssid->data[10] ^ wssid->data[11] + ^ wssid->data[12] ^ wssid->data[13] ^ wssid->data[14] + ^ wssid->data[15]; +} + +/** + * Select a multicast EUI-48 for the WSS broadcast address. + * WLP Draft 0.99 [7.2.1] + * + * Selected based on the WiMedia Alliance OUI, 00-13-88, within the WLP + * range, [01-13-88-00-01-00, 01-13-88-00-01-FF] inclusive. + * + * This address is currently hardcoded. + * FIXME? + */ +static +struct uwb_mac_addr wlp_wss_sel_bcast_addr(struct wlp_wss *wss) +{ + struct uwb_mac_addr bcast = { + .data = { 0x01, 0x13, 0x88, 0x00, 0x01, 0x00 } + }; + return bcast; +} + +/** + * Clear the contents of the WSS structure - all except kobj, mutex, virtual + * + * We do not want to reinitialize - the internal kobj should not change as + * it still points to the parent received during setup. The mutex should + * remain also. We thus just reset values individually. + * The virutal address assigned to WSS will remain the same for the + * lifetime of the WSS. We only reset the fields that can change during its + * lifetime. + */ +void wlp_wss_reset(struct wlp_wss *wss) +{ + memset(&wss->wssid, 0, sizeof(wss->wssid)); + wss->hash = 0; + memset(&wss->name[0], 0, sizeof(wss->name)); + memset(&wss->bcast, 0, sizeof(wss->bcast)); + wss->secure_status = WLP_WSS_UNSECURE; + memset(&wss->master_key[0], 0, sizeof(wss->master_key)); + wss->tag = 0; + wss->state = WLP_WSS_STATE_NONE; +} + +/** + * Create sysfs infrastructure for WSS + * + * The WSS is configured to have the interface as parent (see wlp_wss_setup()) + * a new sysfs directory that includes wssid as its name is created in the + * interface's sysfs directory. The group of files interacting with WSS are + * created also. + */ +static +int wlp_wss_sysfs_add(struct wlp_wss *wss, char *wssid_str) +{ + struct wlp *wlp = container_of(wss, struct wlp, wss); + struct device *dev = &wlp->rc->uwb_dev.dev; + int result; + + result = kobject_set_name(&wss->kobj, "wss-%s", wssid_str); + if (result < 0) + return result; + wss->kobj.ktype = &wss_ktype; + result = kobject_init_and_add(&wss->kobj, + &wss_ktype, wss->kobj.parent, "wlp"); + if (result < 0) { + dev_err(dev, "WLP: Cannot register WSS kobject.\n"); + goto error_kobject_register; + } + result = sysfs_create_group(&wss->kobj, &wss_attr_group); + if (result < 0) { + dev_err(dev, "WLP: Cannot register WSS attributes: %d\n", + result); + goto error_sysfs_create_group; + } + return 0; +error_sysfs_create_group: + + kobject_put(&wss->kobj); /* will free name if needed */ + return result; +error_kobject_register: + kfree(wss->kobj.name); + wss->kobj.name = NULL; + wss->kobj.ktype = NULL; + return result; +} + + +/** + * Release WSS + * + * No more references exist to this WSS. We should undo everything that was + * done in wlp_wss_create_activate() except removing the group. The group + * is not removed because an object can be unregistered before the group is + * created. We also undo any additional operations on the WSS after this + * (addition of members). + * + * If memory was allocated for the kobject's name then it will + * be freed by the kobject system during this time. + * + * The EDA cache is removed and reinitialized when the WSS is removed. We + * thus loose knowledge of members of this WSS at that time and need not do + * it here. + */ +void wlp_wss_release(struct kobject *kobj) +{ + struct wlp_wss *wss = container_of(kobj, struct wlp_wss, kobj); + + wlp_wss_reset(wss); +} + +/** + * Enroll into a WSS using provided neighbor as registrar + * + * First search the neighborhood information to learn which neighbor is + * referred to, next proceed with enrollment. + * + * &wss->mutex is held + */ +static +int wlp_wss_enroll_target(struct wlp_wss *wss, struct wlp_uuid *wssid, + struct uwb_dev_addr *dest) +{ + struct wlp *wlp = container_of(wss, struct wlp, wss); + struct device *dev = &wlp->rc->uwb_dev.dev; + struct wlp_neighbor_e *neighbor; + int result = -ENXIO; + struct uwb_dev_addr *dev_addr; + + mutex_lock(&wlp->nbmutex); + list_for_each_entry(neighbor, &wlp->neighbors, node) { + dev_addr = &neighbor->uwb_dev->dev_addr; + if (!memcmp(dest, dev_addr, sizeof(*dest))) { + result = wlp_enroll_neighbor(wlp, neighbor, wss, wssid); + break; + } + } + if (result == -ENXIO) + dev_err(dev, "WLP: Cannot find neighbor %02x:%02x. \n", + dest->data[1], dest->data[0]); + mutex_unlock(&wlp->nbmutex); + return result; +} + +/** + * Enroll into a WSS previously discovered + * + * User provides WSSID of WSS, search for neighbor that has this WSS + * activated and attempt to enroll. + * + * &wss->mutex is held + */ +static +int wlp_wss_enroll_discovered(struct wlp_wss *wss, struct wlp_uuid *wssid) +{ + struct wlp *wlp = container_of(wss, struct wlp, wss); + struct device *dev = &wlp->rc->uwb_dev.dev; + struct wlp_neighbor_e *neighbor; + struct wlp_wssid_e *wssid_e; + char buf[WLP_WSS_UUID_STRSIZE]; + int result = -ENXIO; + + + mutex_lock(&wlp->nbmutex); + list_for_each_entry(neighbor, &wlp->neighbors, node) { + list_for_each_entry(wssid_e, &neighbor->wssid, node) { + if (!memcmp(wssid, &wssid_e->wssid, sizeof(*wssid))) { + result = wlp_enroll_neighbor(wlp, neighbor, + wss, wssid); + if (result == 0) /* enrollment success */ + goto out; + break; + } + } + } +out: + if (result == -ENXIO) { + wlp_wss_uuid_print(buf, sizeof(buf), wssid); + dev_err(dev, "WLP: Cannot find WSSID %s in cache. \n", buf); + } + mutex_unlock(&wlp->nbmutex); + return result; +} + +/** + * Enroll into WSS with provided WSSID, registrar may be provided + * + * @wss: out WSS that will be enrolled + * @wssid: wssid of neighboring WSS that we want to enroll in + * @devaddr: registrar can be specified, will be broadcast (ff:ff) if any + * neighbor can be used as registrar. + * + * &wss->mutex is held + */ +static +int wlp_wss_enroll(struct wlp_wss *wss, struct wlp_uuid *wssid, + struct uwb_dev_addr *devaddr) +{ + int result; + struct wlp *wlp = container_of(wss, struct wlp, wss); + struct device *dev = &wlp->rc->uwb_dev.dev; + char buf[WLP_WSS_UUID_STRSIZE]; + struct uwb_dev_addr bcast = {.data = {0xff, 0xff} }; + + wlp_wss_uuid_print(buf, sizeof(buf), wssid); + + if (wss->state != WLP_WSS_STATE_NONE) { + dev_err(dev, "WLP: Already enrolled in WSS %s.\n", buf); + result = -EEXIST; + goto error; + } + if (!memcmp(&bcast, devaddr, sizeof(bcast))) + result = wlp_wss_enroll_discovered(wss, wssid); + else + result = wlp_wss_enroll_target(wss, wssid, devaddr); + if (result < 0) { + dev_err(dev, "WLP: Unable to enroll into WSS %s, result %d \n", + buf, result); + goto error; + } + dev_dbg(dev, "Successfully enrolled into WSS %s \n", buf); + result = wlp_wss_sysfs_add(wss, buf); + if (result < 0) { + dev_err(dev, "WLP: Unable to set up sysfs for WSS kobject.\n"); + wlp_wss_reset(wss); + } +error: + return result; + +} + +/** + * Activate given WSS + * + * Prior to activation a WSS must be enrolled. To activate a WSS a device + * includes the WSS hash in the WLP IE in its beacon in each superframe. + * WLP 0.99 [7.2.5]. + * + * The WSS tag is also computed at this time. We only support one activated + * WSS so we can use the hash as a tag - there will never be a conflict. + * + * We currently only support one activated WSS so only one WSS hash is + * included in the WLP IE. + */ +static +int wlp_wss_activate(struct wlp_wss *wss) +{ + struct wlp *wlp = container_of(wss, struct wlp, wss); + struct device *dev = &wlp->rc->uwb_dev.dev; + struct uwb_rc *uwb_rc = wlp->rc; + int result; + struct { + struct wlp_ie wlp_ie; + u8 hash; /* only include one hash */ + } ie_data; + + BUG_ON(wss->state != WLP_WSS_STATE_ENROLLED); + wss->hash = wlp_wss_comp_wssid_hash(&wss->wssid); + wss->tag = wss->hash; + memset(&ie_data, 0, sizeof(ie_data)); + ie_data.wlp_ie.hdr.element_id = UWB_IE_WLP; + ie_data.wlp_ie.hdr.length = sizeof(ie_data) - sizeof(struct uwb_ie_hdr); + wlp_ie_set_hash_length(&ie_data.wlp_ie, sizeof(ie_data.hash)); + ie_data.hash = wss->hash; + result = uwb_rc_ie_add(uwb_rc, &ie_data.wlp_ie.hdr, + sizeof(ie_data)); + if (result < 0) { + dev_err(dev, "WLP: Unable to add WLP IE to beacon. " + "result = %d.\n", result); + goto error_wlp_ie; + } + wss->state = WLP_WSS_STATE_ACTIVE; + result = 0; +error_wlp_ie: + return result; +} + +/** + * Enroll in and activate WSS identified by provided WSSID + * + * The neighborhood cache should contain a list of all neighbors and the + * WSS they have activated. Based on that cache we search which neighbor we + * can perform the association process with. The user also has option to + * specify which neighbor it prefers as registrar. + * Successful enrollment is followed by activation. + * Successful activation will create the sysfs directory containing + * specific information regarding this WSS. + */ +int wlp_wss_enroll_activate(struct wlp_wss *wss, struct wlp_uuid *wssid, + struct uwb_dev_addr *devaddr) +{ + struct wlp *wlp = container_of(wss, struct wlp, wss); + struct device *dev = &wlp->rc->uwb_dev.dev; + int result = 0; + char buf[WLP_WSS_UUID_STRSIZE]; + + mutex_lock(&wss->mutex); + result = wlp_wss_enroll(wss, wssid, devaddr); + if (result < 0) { + wlp_wss_uuid_print(buf, sizeof(buf), &wss->wssid); + dev_err(dev, "WLP: Enrollment into WSS %s failed.\n", buf); + goto error_enroll; + } + result = wlp_wss_activate(wss); + if (result < 0) { + dev_err(dev, "WLP: Unable to activate WSS. Undoing enrollment " + "result = %d \n", result); + /* Undo enrollment */ + wlp_wss_reset(wss); + goto error_activate; + } +error_activate: +error_enroll: + mutex_unlock(&wss->mutex); + return result; +} + +/** + * Create, enroll, and activate a new WSS + * + * @wssid: new wssid provided by user + * @name: WSS name requested by used. + * @sec_status: security status requested by user + * + * A user requested the creation of a new WSS. All operations are done + * locally. The new WSS will be stored locally, the hash will be included + * in the WLP IE, and the sysfs infrastructure for this WSS will be + * created. + */ +int wlp_wss_create_activate(struct wlp_wss *wss, struct wlp_uuid *wssid, + char *name, unsigned sec_status, unsigned accept) +{ + struct wlp *wlp = container_of(wss, struct wlp, wss); + struct device *dev = &wlp->rc->uwb_dev.dev; + int result = 0; + char buf[WLP_WSS_UUID_STRSIZE]; + + result = wlp_wss_uuid_print(buf, sizeof(buf), wssid); + + if (!mutex_trylock(&wss->mutex)) { + dev_err(dev, "WLP: WLP association session in progress.\n"); + return -EBUSY; + } + if (wss->state != WLP_WSS_STATE_NONE) { + dev_err(dev, "WLP: WSS already exists. Not creating new.\n"); + result = -EEXIST; + goto out; + } + if (wss->kobj.parent == NULL) { + dev_err(dev, "WLP: WSS parent not ready. Is network interface " + "up?\n"); + result = -ENXIO; + goto out; + } + if (sec_status == WLP_WSS_SECURE) { + dev_err(dev, "WLP: FIXME Creation of secure WSS not " + "supported yet.\n"); + result = -EINVAL; + goto out; + } + wss->wssid = *wssid; + memcpy(wss->name, name, sizeof(wss->name)); + wss->bcast = wlp_wss_sel_bcast_addr(wss); + wss->secure_status = sec_status; + wss->accept_enroll = accept; + /*wss->virtual_addr is initialized in call to wlp_wss_setup*/ + /* sysfs infrastructure */ + result = wlp_wss_sysfs_add(wss, buf); + if (result < 0) { + dev_err(dev, "Cannot set up sysfs for WSS kobject.\n"); + wlp_wss_reset(wss); + goto out; + } else + result = 0; + wss->state = WLP_WSS_STATE_ENROLLED; + result = wlp_wss_activate(wss); + if (result < 0) { + dev_err(dev, "WLP: Unable to activate WSS. Undoing " + "enrollment\n"); + wlp_wss_reset(wss); + goto out; + } + result = 0; +out: + mutex_unlock(&wss->mutex); + return result; +} + +/** + * Determine if neighbor has WSS activated + * + * @returns: 1 if neighbor has WSS activated, zero otherwise + * + * This can be done in two ways: + * - send a C1 frame, parse C2/F0 response + * - examine the WLP IE sent by the neighbor + * + * The WLP IE is not fully supported in hardware so we use the C1/C2 frame + * exchange to determine if a WSS is activated. Using the WLP IE should be + * faster and should be used when it becomes possible. + */ +int wlp_wss_is_active(struct wlp *wlp, struct wlp_wss *wss, + struct uwb_dev_addr *dev_addr) +{ + int result = 0; + struct device *dev = &wlp->rc->uwb_dev.dev; + DECLARE_COMPLETION_ONSTACK(completion); + struct wlp_session session; + struct sk_buff *skb; + struct wlp_frame_assoc *resp; + struct wlp_uuid wssid; + + mutex_lock(&wlp->mutex); + /* Send C1 association frame */ + result = wlp_send_assoc_frame(wlp, wss, dev_addr, WLP_ASSOC_C1); + if (result < 0) { + dev_err(dev, "Unable to send C1 frame to neighbor " + "%02x:%02x (%d)\n", dev_addr->data[1], + dev_addr->data[0], result); + result = 0; + goto out; + } + /* Create session, wait for response */ + session.exp_message = WLP_ASSOC_C2; + session.cb = wlp_session_cb; + session.cb_priv = &completion; + session.neighbor_addr = *dev_addr; + BUG_ON(wlp->session != NULL); + wlp->session = &session; + /* Wait for C2/F0 frame */ + result = wait_for_completion_interruptible_timeout(&completion, + WLP_PER_MSG_TIMEOUT * HZ); + if (result == 0) { + dev_err(dev, "Timeout while sending C1 to neighbor " + "%02x:%02x.\n", dev_addr->data[1], + dev_addr->data[0]); + goto out; + } + if (result < 0) { + dev_err(dev, "Unable to send C1 to neighbor %02x:%02x.\n", + dev_addr->data[1], dev_addr->data[0]); + result = 0; + goto out; + } + /* Parse message in session->data: it will be either C2 or F0 */ + skb = session.data; + resp = (void *) skb->data; + if (resp->type == WLP_ASSOC_F0) { + result = wlp_parse_f0(wlp, skb); + if (result < 0) + dev_err(dev, "WLP: unable to parse incoming F0 " + "frame from neighbor %02x:%02x.\n", + dev_addr->data[1], dev_addr->data[0]); + result = 0; + goto error_resp_parse; + } + /* WLP version and message type fields have already been parsed */ + result = wlp_get_wssid(wlp, (void *)resp + sizeof(*resp), &wssid, + skb->len - sizeof(*resp)); + if (result < 0) { + dev_err(dev, "WLP: unable to obtain WSSID from C2 frame.\n"); + result = 0; + goto error_resp_parse; + } + if (!memcmp(&wssid, &wss->wssid, sizeof(wssid))) + result = 1; + else { + dev_err(dev, "WLP: Received a C2 frame without matching " + "WSSID.\n"); + result = 0; + } +error_resp_parse: + kfree_skb(skb); +out: + wlp->session = NULL; + mutex_unlock(&wlp->mutex); + return result; +} + +/** + * Activate connection with neighbor by updating EDA cache + * + * @wss: local WSS to which neighbor wants to connect + * @dev_addr: neighbor's address + * @wssid: neighbor's WSSID - must be same as our WSS's WSSID + * @tag: neighbor's WSS tag used to identify frames transmitted by it + * @virt_addr: neighbor's virtual EUI-48 + */ +static +int wlp_wss_activate_connection(struct wlp *wlp, struct wlp_wss *wss, + struct uwb_dev_addr *dev_addr, + struct wlp_uuid *wssid, u8 *tag, + struct uwb_mac_addr *virt_addr) +{ + struct device *dev = &wlp->rc->uwb_dev.dev; + int result = 0; + + if (!memcmp(wssid, &wss->wssid, sizeof(*wssid))) { + /* Update EDA cache */ + result = wlp_eda_update_node(&wlp->eda, dev_addr, wss, + (void *) virt_addr->data, *tag, + WLP_WSS_CONNECTED); + if (result < 0) + dev_err(dev, "WLP: Unable to update EDA cache " + "with new connected neighbor information.\n"); + } else { + dev_err(dev, "WLP: Neighbor does not have matching WSSID.\n"); + result = -EINVAL; + } + return result; +} + +/** + * Connect to WSS neighbor + * + * Use C3/C4 exchange to determine if neighbor has WSS activated and + * retrieve the WSS tag and virtual EUI-48 of the neighbor. + */ +static +int wlp_wss_connect_neighbor(struct wlp *wlp, struct wlp_wss *wss, + struct uwb_dev_addr *dev_addr) +{ + int result; + struct device *dev = &wlp->rc->uwb_dev.dev; + struct wlp_uuid wssid; + u8 tag; + struct uwb_mac_addr virt_addr; + DECLARE_COMPLETION_ONSTACK(completion); + struct wlp_session session; + struct wlp_frame_assoc *resp; + struct sk_buff *skb; + + mutex_lock(&wlp->mutex); + /* Send C3 association frame */ + result = wlp_send_assoc_frame(wlp, wss, dev_addr, WLP_ASSOC_C3); + if (result < 0) { + dev_err(dev, "Unable to send C3 frame to neighbor " + "%02x:%02x (%d)\n", dev_addr->data[1], + dev_addr->data[0], result); + goto out; + } + /* Create session, wait for response */ + session.exp_message = WLP_ASSOC_C4; + session.cb = wlp_session_cb; + session.cb_priv = &completion; + session.neighbor_addr = *dev_addr; + BUG_ON(wlp->session != NULL); + wlp->session = &session; + /* Wait for C4/F0 frame */ + result = wait_for_completion_interruptible_timeout(&completion, + WLP_PER_MSG_TIMEOUT * HZ); + if (result == 0) { + dev_err(dev, "Timeout while sending C3 to neighbor " + "%02x:%02x.\n", dev_addr->data[1], + dev_addr->data[0]); + result = -ETIMEDOUT; + goto out; + } + if (result < 0) { + dev_err(dev, "Unable to send C3 to neighbor %02x:%02x.\n", + dev_addr->data[1], dev_addr->data[0]); + goto out; + } + /* Parse message in session->data: it will be either C4 or F0 */ + skb = session.data; + resp = (void *) skb->data; + if (resp->type == WLP_ASSOC_F0) { + result = wlp_parse_f0(wlp, skb); + if (result < 0) + dev_err(dev, "WLP: unable to parse incoming F0 " + "frame from neighbor %02x:%02x.\n", + dev_addr->data[1], dev_addr->data[0]); + result = -EINVAL; + goto error_resp_parse; + } + result = wlp_parse_c3c4_frame(wlp, skb, &wssid, &tag, &virt_addr); + if (result < 0) { + dev_err(dev, "WLP: Unable to parse C4 frame from neighbor.\n"); + goto error_resp_parse; + } + result = wlp_wss_activate_connection(wlp, wss, dev_addr, &wssid, &tag, + &virt_addr); + if (result < 0) { + dev_err(dev, "WLP: Unable to activate connection to " + "neighbor %02x:%02x.\n", dev_addr->data[1], + dev_addr->data[0]); + goto error_resp_parse; + } +error_resp_parse: + kfree_skb(skb); +out: + /* Record that we unsuccessfully tried to connect to this neighbor */ + if (result < 0) + wlp_eda_update_node_state(&wlp->eda, dev_addr, + WLP_WSS_CONNECT_FAILED); + wlp->session = NULL; + mutex_unlock(&wlp->mutex); + return result; +} + +/** + * Connect to neighbor with common WSS, send pending frame + * + * This function is scheduled when a frame is destined to a neighbor with + * which we do not have a connection. A copy of the EDA cache entry is + * provided - not the actual cache entry (because it is protected by a + * spinlock). + * + * First determine if neighbor has the same WSS activated, connect if it + * does. The C3/C4 exchange is dual purpose to determine if neighbor has + * WSS activated and proceed with the connection. + * + * The frame that triggered the connection setup is sent after connection + * setup. + * + * network queue is stopped - we need to restart when done + * + */ +static +void wlp_wss_connect_send(struct work_struct *ws) +{ + struct wlp_assoc_conn_ctx *conn_ctx = container_of(ws, + struct wlp_assoc_conn_ctx, + ws); + struct wlp *wlp = conn_ctx->wlp; + struct sk_buff *skb = conn_ctx->skb; + struct wlp_eda_node *eda_entry = &conn_ctx->eda_entry; + struct uwb_dev_addr *dev_addr = &eda_entry->dev_addr; + struct wlp_wss *wss = &wlp->wss; + int result; + struct device *dev = &wlp->rc->uwb_dev.dev; + + mutex_lock(&wss->mutex); + if (wss->state < WLP_WSS_STATE_ACTIVE) { + if (printk_ratelimit()) + dev_err(dev, "WLP: Attempting to connect with " + "WSS that is not active or connected.\n"); + dev_kfree_skb(skb); + goto out; + } + /* Establish connection - send C3 rcv C4 */ + result = wlp_wss_connect_neighbor(wlp, wss, dev_addr); + if (result < 0) { + if (printk_ratelimit()) + dev_err(dev, "WLP: Unable to establish connection " + "with neighbor %02x:%02x.\n", + dev_addr->data[1], dev_addr->data[0]); + dev_kfree_skb(skb); + goto out; + } + /* EDA entry changed, update the local copy being used */ + result = wlp_copy_eda_node(&wlp->eda, dev_addr, eda_entry); + if (result < 0) { + if (printk_ratelimit()) + dev_err(dev, "WLP: Cannot find EDA entry for " + "neighbor %02x:%02x \n", + dev_addr->data[1], dev_addr->data[0]); + } + result = wlp_wss_prep_hdr(wlp, eda_entry, skb); + if (result < 0) { + if (printk_ratelimit()) + dev_err(dev, "WLP: Unable to prepare frame header for " + "transmission (neighbor %02x:%02x). \n", + dev_addr->data[1], dev_addr->data[0]); + dev_kfree_skb(skb); + goto out; + } + BUG_ON(wlp->xmit_frame == NULL); + result = wlp->xmit_frame(wlp, skb, dev_addr); + if (result < 0) { + if (printk_ratelimit()) + dev_err(dev, "WLP: Unable to transmit frame: %d\n", + result); + if (result == -ENXIO) + dev_err(dev, "WLP: Is network interface up? \n"); + /* We could try again ... */ + dev_kfree_skb(skb);/*we need to free if tx fails */ + } +out: + kfree(conn_ctx); + BUG_ON(wlp->start_queue == NULL); + wlp->start_queue(wlp); + mutex_unlock(&wss->mutex); +} + +/** + * Add WLP header to outgoing skb + * + * @eda_entry: pointer to neighbor's entry in the EDA cache + * @_skb: skb containing data destined to the neighbor + */ +int wlp_wss_prep_hdr(struct wlp *wlp, struct wlp_eda_node *eda_entry, + void *_skb) +{ + struct device *dev = &wlp->rc->uwb_dev.dev; + int result = 0; + unsigned char *eth_addr = eda_entry->eth_addr; + struct uwb_dev_addr *dev_addr = &eda_entry->dev_addr; + struct sk_buff *skb = _skb; + struct wlp_frame_std_abbrv_hdr *std_hdr; + + if (eda_entry->state == WLP_WSS_CONNECTED) { + /* Add WLP header */ + BUG_ON(skb_headroom(skb) < sizeof(*std_hdr)); + std_hdr = (void *) __skb_push(skb, sizeof(*std_hdr)); + std_hdr->hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID); + std_hdr->hdr.type = WLP_FRAME_STANDARD; + std_hdr->tag = eda_entry->wss->tag; + } else { + if (printk_ratelimit()) + dev_err(dev, "WLP: Destination neighbor (Ethernet: " + "%pM, Dev: %02x:%02x) is not connected.\n", + eth_addr, dev_addr->data[1], dev_addr->data[0]); + result = -EINVAL; + } + return result; +} + + +/** + * Prepare skb for neighbor: connect if not already and prep WLP header + * + * This function is called in interrupt context, but it needs to sleep. We + * temporarily stop the net queue to establish the WLP connection. + * Setup of the WLP connection and restart of queue is scheduled + * on the default work queue. + * + * run with eda->lock held (spinlock) + */ +int wlp_wss_connect_prep(struct wlp *wlp, struct wlp_eda_node *eda_entry, + void *_skb) +{ + int result = 0; + struct device *dev = &wlp->rc->uwb_dev.dev; + struct sk_buff *skb = _skb; + struct wlp_assoc_conn_ctx *conn_ctx; + + if (eda_entry->state == WLP_WSS_UNCONNECTED) { + /* We don't want any more packets while we set up connection */ + BUG_ON(wlp->stop_queue == NULL); + wlp->stop_queue(wlp); + conn_ctx = kmalloc(sizeof(*conn_ctx), GFP_ATOMIC); + if (conn_ctx == NULL) { + if (printk_ratelimit()) + dev_err(dev, "WLP: Unable to allocate memory " + "for connection handling.\n"); + result = -ENOMEM; + goto out; + } + conn_ctx->wlp = wlp; + conn_ctx->skb = skb; + conn_ctx->eda_entry = *eda_entry; + INIT_WORK(&conn_ctx->ws, wlp_wss_connect_send); + schedule_work(&conn_ctx->ws); + result = 1; + } else if (eda_entry->state == WLP_WSS_CONNECT_FAILED) { + /* Previous connection attempts failed, don't retry - see + * conditions for connection in WLP 0.99 [7.6.2] */ + if (printk_ratelimit()) + dev_err(dev, "Could not connect to neighbor " + "previously. Not retrying. \n"); + result = -ENONET; + goto out; + } else /* eda_entry->state == WLP_WSS_CONNECTED */ + result = wlp_wss_prep_hdr(wlp, eda_entry, skb); +out: + return result; +} + +/** + * Emulate broadcast: copy skb, send copy to neighbor (connect if not already) + * + * We need to copy skbs in the case where we emulate broadcast through + * unicast. We copy instead of clone because we are modifying the data of + * the frame after copying ... clones share data so we cannot emulate + * broadcast using clones. + * + * run with eda->lock held (spinlock) + */ +int wlp_wss_send_copy(struct wlp *wlp, struct wlp_eda_node *eda_entry, + void *_skb) +{ + int result = -ENOMEM; + struct device *dev = &wlp->rc->uwb_dev.dev; + struct sk_buff *skb = _skb; + struct sk_buff *copy; + struct uwb_dev_addr *dev_addr = &eda_entry->dev_addr; + + copy = skb_copy(skb, GFP_ATOMIC); + if (copy == NULL) { + if (printk_ratelimit()) + dev_err(dev, "WLP: Unable to copy skb for " + "transmission.\n"); + goto out; + } + result = wlp_wss_connect_prep(wlp, eda_entry, copy); + if (result < 0) { + if (printk_ratelimit()) + dev_err(dev, "WLP: Unable to connect/send skb " + "to neighbor.\n"); + dev_kfree_skb_irq(copy); + goto out; + } else if (result == 1) + /* Frame will be transmitted separately */ + goto out; + BUG_ON(wlp->xmit_frame == NULL); + result = wlp->xmit_frame(wlp, copy, dev_addr); + if (result < 0) { + if (printk_ratelimit()) + dev_err(dev, "WLP: Unable to transmit frame: %d\n", + result); + if ((result == -ENXIO) && printk_ratelimit()) + dev_err(dev, "WLP: Is network interface up? \n"); + /* We could try again ... */ + dev_kfree_skb_irq(copy);/*we need to free if tx fails */ + } +out: + return result; +} + + +/** + * Setup WSS + * + * Should be called by network driver after the interface has been given a + * MAC address. + */ +int wlp_wss_setup(struct net_device *net_dev, struct wlp_wss *wss) +{ + struct wlp *wlp = container_of(wss, struct wlp, wss); + struct device *dev = &wlp->rc->uwb_dev.dev; + int result = 0; + + mutex_lock(&wss->mutex); + wss->kobj.parent = &net_dev->dev.kobj; + if (!is_valid_ether_addr(net_dev->dev_addr)) { + dev_err(dev, "WLP: Invalid MAC address. Cannot use for" + "virtual.\n"); + result = -EINVAL; + goto out; + } + memcpy(wss->virtual_addr.data, net_dev->dev_addr, + sizeof(wss->virtual_addr.data)); +out: + mutex_unlock(&wss->mutex); + return result; +} +EXPORT_SYMBOL_GPL(wlp_wss_setup); + +/** + * Remove WSS + * + * Called by client that configured WSS through wlp_wss_setup(). This + * function is called when client no longer needs WSS, eg. client shuts + * down. + * + * We remove the WLP IE from the beacon before initiating local cleanup. + */ +void wlp_wss_remove(struct wlp_wss *wss) +{ + struct wlp *wlp = container_of(wss, struct wlp, wss); + + mutex_lock(&wss->mutex); + if (wss->state == WLP_WSS_STATE_ACTIVE) + uwb_rc_ie_rm(wlp->rc, UWB_IE_WLP); + if (wss->state != WLP_WSS_STATE_NONE) { + sysfs_remove_group(&wss->kobj, &wss_attr_group); + kobject_put(&wss->kobj); + } + wss->kobj.parent = NULL; + memset(&wss->virtual_addr, 0, sizeof(wss->virtual_addr)); + /* Cleanup EDA cache */ + wlp_eda_release(&wlp->eda); + wlp_eda_init(&wlp->eda); + mutex_unlock(&wss->mutex); +} +EXPORT_SYMBOL_GPL(wlp_wss_remove); diff --git a/trunk/drivers/video/Kconfig b/trunk/drivers/video/Kconfig index 596ef6b922bf..dc06ff134559 100644 --- a/trunk/drivers/video/Kconfig +++ b/trunk/drivers/video/Kconfig @@ -1919,9 +1919,6 @@ config FB_SH_MOBILE_HDMI tristate "SuperH Mobile HDMI controller support" depends on FB_SH_MOBILE_LCDC select FB_MODE_HELPERS - select SOUND - select SND - select SND_SOC ---help--- Driver for the on-chip SH-Mobile HDMI controller. diff --git a/trunk/drivers/video/aty/atyfb_base.c b/trunk/drivers/video/aty/atyfb_base.c index 5bf91236c701..f8d69ad36830 100644 --- a/trunk/drivers/video/aty/atyfb_base.c +++ b/trunk/drivers/video/aty/atyfb_base.c @@ -2970,8 +2970,7 @@ static int __devinit atyfb_setup_sparc(struct pci_dev *pdev, struct atyfb_par *par = info->par; struct device_node *dp; char prop[128]; - phandle node; - int len, i, j, ret; + int node, len, i, j, ret; u32 mem, chip_id; /* diff --git a/trunk/drivers/video/sh_mobile_hdmi.c b/trunk/drivers/video/sh_mobile_hdmi.c index ef989d94511c..2fde08cc66bf 100644 --- a/trunk/drivers/video/sh_mobile_hdmi.c +++ b/trunk/drivers/video/sh_mobile_hdmi.c @@ -22,8 +22,6 @@ #include #include #include -#include -#include #include